diff --git a/.run/Format.run.xml b/.run/Format.run.xml new file mode 100644 index 0000000..6ec08b1 --- /dev/null +++ b/.run/Format.run.xml @@ -0,0 +1,25 @@ + + + + + + + + true + true + false + false + + + \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index b9d0d2f..cb21c47 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,91 +10,98 @@ plugins { alias(libs.plugins.changelog) // Gradle Changelog Plugin alias(libs.plugins.qodana) // Gradle Qodana Plugin alias(libs.plugins.kover) // Gradle Kover Plugin + id("com.ncorti.ktfmt.gradle") version "0.24.0" // Gradle ktfmt Plugin } group = providers.gradleProperty("pluginGroup").get() + version = providers.gradleProperty("pluginVersion").get() // Set the JVM language level used to build the project. -kotlin { - jvmToolchain(21) -} +kotlin { jvmToolchain(21) } // Configure project's dependencies repositories { mavenCentral() - // IntelliJ Platform Gradle Plugin Repositories Extension - read more: https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-repositories-extension.html - intellijPlatform { - defaultRepositories() - } + // IntelliJ Platform Gradle Plugin Repositories Extension - read more: + // https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-repositories-extension.html + intellijPlatform { defaultRepositories() } } -sourceSets { - main { - java { - setSrcDirs(listOf("src/main/kotlin", "src/main/gen")) - } - } -} +sourceSets { main { java { setSrcDirs(listOf("src/main/kotlin", "src/main/gen")) } } } -// Dependencies are managed with Gradle version catalog - read more: https://docs.gradle.org/current/userguide/platforms.html#sub:version-catalog +// Dependencies are managed with Gradle version catalog - read more: +// https://docs.gradle.org/current/userguide/platforms.html#sub:version-catalog dependencies { testImplementation(libs.junit) testImplementation(libs.opentest4j) - // IntelliJ Platform Gradle Plugin Dependencies Extension - read more: https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-dependencies-extension.html + // IntelliJ Platform Gradle Plugin Dependencies Extension - read more: + // https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-dependencies-extension.html intellijPlatform { - create(providers.gradleProperty("platformType"), providers.gradleProperty("platformVersion")) + create( + providers.gradleProperty("platformType"), + providers.gradleProperty("platformVersion"), + ) - // Plugin Dependencies. Uses `platformBundledPlugins` property from the gradle.properties file for bundled IntelliJ Platform plugins. + // Plugin Dependencies. Uses `platformBundledPlugins` property from the gradle.properties + // file for bundled IntelliJ Platform plugins. bundledPlugins(providers.gradleProperty("platformBundledPlugins").map { it.split(',') }) - // Plugin Dependencies. Uses `platformPlugins` property from the gradle.properties file for plugin from JetBrains Marketplace. + // Plugin Dependencies. Uses `platformPlugins` property from the gradle.properties file for + // plugin from JetBrains Marketplace. plugins(providers.gradleProperty("platformPlugins").map { it.split(',') }) - // Module Dependencies. Uses `platformBundledModules` property from the gradle.properties file for bundled IntelliJ Platform modules. + // Module Dependencies. Uses `platformBundledModules` property from the gradle.properties + // file for bundled IntelliJ Platform modules. bundledModules(providers.gradleProperty("platformBundledModules").map { it.split(',') }) testFramework(TestFrameworkType.Platform) } } -// Configure IntelliJ Platform Gradle Plugin - read more: https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-extension.html +// Configure IntelliJ Platform Gradle Plugin - read more: +// https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-extension.html intellijPlatform { pluginConfiguration { name = providers.gradleProperty("pluginName") version = providers.gradleProperty("pluginVersion") - // Extract the section from README.md and provide for the plugin's manifest - description = providers.fileContents(layout.projectDirectory.file("README.md")).asText.map { - val start = "" - val end = "" - - with(it.lines()) { - if (!containsAll(listOf(start, end))) { - throw GradleException("Plugin description section not found in README.md:\n$start ... $end") + // Extract the section from README.md and provide for the + // plugin's manifest + description = + providers.fileContents(layout.projectDirectory.file("README.md")).asText.map { + val start = "" + val end = "" + + with(it.lines()) { + if (!containsAll(listOf(start, end))) { + throw GradleException( + "Plugin description section not found in README.md:\n$start ... $end" + ) + } + subList(indexOf(start) + 1, indexOf(end)) + .joinToString("\n") + .let(::markdownToHTML) } - subList(indexOf(start) + 1, indexOf(end)).joinToString("\n").let(::markdownToHTML) } - } val changelog = project.changelog // local variable for configuration cache compatibility // Get the latest available change notes from the changelog file - changeNotes = providers.gradleProperty("pluginVersion").map { pluginVersion -> - with(changelog) { - renderItem( - (getOrNull(pluginVersion) ?: getUnreleased()) - .withHeader(false) - .withEmptySections(false), - Changelog.OutputType.HTML, - ) + changeNotes = + providers.gradleProperty("pluginVersion").map { pluginVersion -> + with(changelog) { + renderItem( + (getOrNull(pluginVersion) ?: getUnreleased()) + .withHeader(false) + .withEmptySections(false), + Changelog.OutputType.HTML, + ) + } } - } - ideaVersion { - sinceBuild = providers.gradleProperty("pluginSinceBuild") - } + ideaVersion { sinceBuild = providers.gradleProperty("pluginSinceBuild") } } signing { @@ -105,44 +112,34 @@ intellijPlatform { publishing { token = providers.environmentVariable("PUBLISH_TOKEN") - // The pluginVersion is based on the SemVer (https://semver.org) and supports pre-release labels, like 2.1.7-alpha.3 - // Specify pre-release label to publish the plugin in a custom Release Channel automatically. Read more: + // The pluginVersion is based on the SemVer (https://semver.org) and supports pre-release + // labels, like 2.1.7-alpha.3 + // Specify pre-release label to publish the plugin in a custom Release Channel + // automatically. Read more: // https://plugins.jetbrains.com/docs/intellij/deployment.html#specifying-a-release-channel - channels = providers.gradleProperty("pluginVersion").map { listOf(it.substringAfter('-', "").substringBefore('.').ifEmpty { "default" }) } + channels = + providers.gradleProperty("pluginVersion").map { + listOf(it.substringAfter('-', "").substringBefore('.').ifEmpty { "default" }) + } } - pluginVerification { - ides { - recommended() - } - } + pluginVerification { ides { recommended() } } } -// Configure Gradle Changelog Plugin - read more: https://github.com/JetBrains/gradle-changelog-plugin +// Configure Gradle Changelog Plugin - read more: +// https://github.com/JetBrains/gradle-changelog-plugin changelog { groups.empty() repositoryUrl = providers.gradleProperty("pluginRepositoryUrl") } // Configure Gradle Kover Plugin - read more: https://github.com/Kotlin/kotlinx-kover#configuration -kover { - reports { - total { - xml { - onCheck = true - } - } - } -} +kover { reports { total { xml { onCheck = true } } } } tasks { - wrapper { - gradleVersion = providers.gradleProperty("gradleVersion").get() - } + wrapper { gradleVersion = providers.gradleProperty("gradleVersion").get() } - publishPlugin { - dependsOn(patchChangelog) - } + publishPlugin { dependsOn(patchChangelog) } } intellijPlatformTesting { @@ -161,9 +158,9 @@ intellijPlatformTesting { } } - plugins { - robotServerPlugin() - } + plugins { robotServerPlugin() } } } } + +ktfmt { kotlinLangStyle() } diff --git a/gradle.properties b/gradle.properties index 5729e0d..34aab22 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,17 +10,17 @@ pluginVersion = 2025.0.1 pluginSinceBuild = 243 # IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension -platformType = IU -platformVersion = 2025.1.1 +platformType = PS +platformVersion = 2025.2.1 # Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html # Example: platformPlugins = com.jetbrains.php:203.4449.22, org.intellij.scala:2023.3.27@EAP #platformPlugins=com.jetbrains.php:243.25659.59,com.jetbrains.hackathon.indices.viewer:1.28 -platformPlugins=com.jetbrains.php:251.25410.129,com.jetbrains.hackathon.indices.viewer:1.28 +platformPlugins=com.jetbrains.php:253.17525.99,com.jetbrains.hackathon.indices.viewer:1.31 # Example: platformBundledPlugins = com.intellij.java platformBundledPlugins = # Example: platformBundledModules = intellij.spellchecker -platformBundledModules = +platformBundledModules = intellij.spellchecker # Gradle Releases -> https://github.com/gradle/gradle/releases gradleVersion = 9.0.0 diff --git a/settings.gradle.kts b/settings.gradle.kts index 7ef1b01..d39d2ac 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,5 +1,3 @@ -plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" -} +plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" } rootProject.name = "tempest-phpstorm-plugin" diff --git a/src/main/kotlin/com/github/tempest/framework/ComponentGotoDeclarationHandler.kt b/src/main/kotlin/com/github/tempest/framework/ComponentGotoDeclarationHandler.kt new file mode 100644 index 0000000..8d8d7a4 --- /dev/null +++ b/src/main/kotlin/com/github/tempest/framework/ComponentGotoDeclarationHandler.kt @@ -0,0 +1,52 @@ +package com.github.tempest.framework + +import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.roots.ProjectFileIndex +import com.intellij.openapi.vfs.findPsiFile +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.html.HtmlTag +import com.intellij.psi.search.FilenameIndex +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.util.PsiTreeUtil + +class ComponentGotoDeclarationHandler : GotoDeclarationHandler { + override fun getGotoDeclarationTargets( + element: PsiElement?, + offset: Int, + editor: Editor?, + ): Array? { + if (element == null) return null + + val tag = PsiTreeUtil.getParentOfType(element, HtmlTag::class.java) ?: return null + if (!tag.name.startsWith("x-")) return null + + val project = tag.project + val projectFileIndex = ProjectFileIndex.getInstance(project) + val projectFiles = mutableListOf() + val libraryFiles = mutableListOf() + + FilenameIndex.processFilesByName( + tag.name + TempestFrameworkUtil.TEMPLATE_PREFIX, + true, + GlobalSearchScope.projectScope(project), + ) { virtualFile -> + val psiFile = virtualFile.findPsiFile(project) ?: return@processFilesByName true + + if (projectFileIndex.isInSourceContent(virtualFile)) { + projectFiles.add(psiFile) + } else { + libraryFiles.add(psiFile) + } + + true + } + + return when { + projectFiles.isNotEmpty() -> projectFiles.toTypedArray() + libraryFiles.isNotEmpty() -> libraryFiles.toTypedArray() + else -> null + } + } +} diff --git a/src/main/kotlin/com/github/tempest/framework/ComponentReferenceContributor.kt b/src/main/kotlin/com/github/tempest/framework/ComponentReferenceContributor.kt deleted file mode 100644 index b745684..0000000 --- a/src/main/kotlin/com/github/tempest/framework/ComponentReferenceContributor.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.github.tempest.framework - -import com.intellij.openapi.vfs.findPsiFile -import com.intellij.patterns.PlatformPatterns -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiReference -import com.intellij.psi.PsiReferenceBase -import com.intellij.psi.PsiReferenceContributor -import com.intellij.psi.PsiReferenceProvider -import com.intellij.psi.PsiReferenceRegistrar -import com.intellij.psi.html.HtmlTag -import com.intellij.psi.search.FilenameIndex -import com.intellij.psi.search.GlobalSearchScope -import com.intellij.psi.xml.XmlChildRole -import com.intellij.util.ProcessingContext - -class ComponentReferenceContributor : PsiReferenceContributor() { - override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) { - registrar.registerReferenceProvider( - PlatformPatterns.psiElement(HtmlTag::class.java), - object : PsiReferenceProvider() { - override fun getReferencesByElement( - element: PsiElement, - context: ProcessingContext - ): Array { - if (element !is HtmlTag) return emptyArray() - if (!element.name.startsWith("x-")) return emptyArray() - - val nameElement = XmlChildRole.START_TAG_NAME_FINDER.findChild(element.node) ?: return emptyArray() - - val project = element.project - - val result = mutableListOf() - - FilenameIndex.processFilesByName( - element.name + TempestFrameworkUtil.TEMPLATE_PREFIX, - true, - GlobalSearchScope.projectScope(project), - { - val psiFile = it.findPsiFile(project) ?: return@processFilesByName true -// println("found file $it for ${element.name}") - result.add( - object : PsiReferenceBase(element, nameElement.textRange, false) { - override fun resolve() = psiFile - } - ) - - true - }) - - return result.toTypedArray() - } - } - ) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/github/tempest/framework/ComponentTagNameProvider.kt b/src/main/kotlin/com/github/tempest/framework/ComponentTagNameProvider.kt index 8e539be..2c784e1 100644 --- a/src/main/kotlin/com/github/tempest/framework/ComponentTagNameProvider.kt +++ b/src/main/kotlin/com/github/tempest/framework/ComponentTagNameProvider.kt @@ -11,15 +11,18 @@ class ComponentTagNameProvider : XmlTagNameProvider { override fun addTagNameVariants( elements: MutableList, tag: XmlTag, - prefix: String? + prefix: String?, ) { val project = tag.project - val result = mutableListOf() + val result = mutableSetOf() FilenameIndex.processAllFileNames( { if (it.startsWith("x-") && it.endsWith(TempestFrameworkUtil.TEMPLATE_PREFIX)) { - result.add(it.removeSuffix(TempestFrameworkUtil.TEMPLATE_PREFIX)) + val name = it.removeSuffix(TempestFrameworkUtil.TEMPLATE_PREFIX) + if (!TempestFrameworkUtil.isBuiltInComponent(name)) { + result.add(name) + } } true @@ -28,13 +31,12 @@ class ComponentTagNameProvider : XmlTagNameProvider { null, ) - result - .distinct() - .map { + elements.addAll( + result.map { LookupElementBuilder.create(it) .withIcon(TempestIcons.TEMPEST) .withTypeText("Tempest Component") } - .apply { elements.addAll(this) } + ) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/github/tempest/framework/SpellcheckingDictionaryProvider.kt b/src/main/kotlin/com/github/tempest/framework/SpellcheckingDictionaryProvider.kt index 0d27ed4..5f2f234 100644 --- a/src/main/kotlin/com/github/tempest/framework/SpellcheckingDictionaryProvider.kt +++ b/src/main/kotlin/com/github/tempest/framework/SpellcheckingDictionaryProvider.kt @@ -4,4 +4,4 @@ import com.intellij.spellchecker.BundledDictionaryProvider class SpellcheckingDictionaryProvider : BundledDictionaryProvider { override fun getBundledDictionaries(): Array = arrayOf("/META-INF/spellcheck.dic") -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/github/tempest/framework/TempestBundle.kt b/src/main/kotlin/com/github/tempest/framework/TempestBundle.kt index d4663e6..abefc7f 100644 --- a/src/main/kotlin/com/github/tempest/framework/TempestBundle.kt +++ b/src/main/kotlin/com/github/tempest/framework/TempestBundle.kt @@ -4,8 +4,7 @@ import com.intellij.DynamicBundle import org.jetbrains.annotations.NonNls import org.jetbrains.annotations.PropertyKey -@NonNls -private const val BUNDLE = "messages.TempestBundle" +@NonNls private const val BUNDLE = "messages.TempestBundle" object TempestBundle : DynamicBundle(BUNDLE) { diff --git a/src/main/kotlin/com/github/tempest/framework/TempestFrameworkUtil.kt b/src/main/kotlin/com/github/tempest/framework/TempestFrameworkUtil.kt index f919f18..cebd918 100644 --- a/src/main/kotlin/com/github/tempest/framework/TempestFrameworkUtil.kt +++ b/src/main/kotlin/com/github/tempest/framework/TempestFrameworkUtil.kt @@ -2,4 +2,21 @@ package com.github.tempest.framework object TempestFrameworkUtil { const val TEMPLATE_PREFIX = ".view.php" -} \ No newline at end of file + + private val BUILT_IN_COMPONENTS = + setOf( + "x-base", + "x-form", + "x-input", + "x-submit", + "x-csrf-token", + "x-icon", + "x-vite-tags", + "x-template", + "x-slot", + "x-markdown", + "x-component", + ) + + fun isBuiltInComponent(name: String): Boolean = BUILT_IN_COMPONENTS.contains(name) +} diff --git a/src/main/kotlin/com/github/tempest/framework/TempestIcons.kt b/src/main/kotlin/com/github/tempest/framework/TempestIcons.kt index 1d53b50..64b7716 100644 --- a/src/main/kotlin/com/github/tempest/framework/TempestIcons.kt +++ b/src/main/kotlin/com/github/tempest/framework/TempestIcons.kt @@ -4,5 +4,6 @@ import com.intellij.openapi.util.IconLoader object TempestIcons { @JvmField - val TEMPEST = IconLoader.getIcon("/icons/tempest/icon.svg", TempestIcons::class.java) + val TEMPEST = + IconLoader.getIcon("/icons/tempest/icon_transparent.svg", TempestIcons::class.java) } diff --git a/src/main/kotlin/com/github/tempest/framework/views/injection/InjectionHostWrapper.kt b/src/main/kotlin/com/github/tempest/framework/views/injection/InjectionHostWrapper.kt index 0978589..71ed89a 100644 --- a/src/main/kotlin/com/github/tempest/framework/views/injection/InjectionHostWrapper.kt +++ b/src/main/kotlin/com/github/tempest/framework/views/injection/InjectionHostWrapper.kt @@ -8,7 +8,8 @@ import com.intellij.psi.PsiLanguageInjectionHost import com.intellij.psi.impl.source.html.HtmlRawTextImpl import com.intellij.psi.impl.source.xml.XmlTextImpl -abstract class InjectionHostWrapper(node: ASTNode) : ASTWrapperPsiElement(node), PsiLanguageInjectionHost { +abstract class InjectionHostWrapper(node: ASTNode) : + ASTWrapperPsiElement(node), PsiLanguageInjectionHost { override fun isValidHost(): Boolean = true override fun updateText(text: String): PsiLanguageInjectionHost { @@ -17,10 +18,12 @@ abstract class InjectionHostWrapper(node: ASTNode) : ASTWrapperPsiElement(node), } } -class XmlTextInjectionHostWrapper(val myElement: XmlTextImpl) : InjectionHostWrapper(myElement.node) { +class XmlTextInjectionHostWrapper(val myElement: XmlTextImpl) : + InjectionHostWrapper(myElement.node) { override fun createLiteralTextEscaper() = myElement.createLiteralTextEscaper() } -class HtmlTextInjectionHostWrapper(val myElement: HtmlRawTextImpl) : InjectionHostWrapper(myElement.node) { +class HtmlTextInjectionHostWrapper(val myElement: HtmlRawTextImpl) : + InjectionHostWrapper(myElement.node) { override fun createLiteralTextEscaper() = LiteralTextEscaper.createSimple(this, false) -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/github/tempest/framework/views/injection/PHPLanguageInjector.kt b/src/main/kotlin/com/github/tempest/framework/views/injection/PHPLanguageInjector.kt index 8e5b254..592b6fa 100644 --- a/src/main/kotlin/com/github/tempest/framework/views/injection/PHPLanguageInjector.kt +++ b/src/main/kotlin/com/github/tempest/framework/views/injection/PHPLanguageInjector.kt @@ -15,10 +15,7 @@ import com.intellij.psi.xml.XmlText import com.intellij.psi.xml.XmlToken class PHPLanguageInjector : MultiHostInjector { - override fun getLanguagesToInject( - registrar: MultiHostRegistrar, - element: PsiElement - ) { + override fun getLanguagesToInject(registrar: MultiHostRegistrar, element: PsiElement) { when (element) { is XmlAttributeValue -> { val attribute = element.parent as? XmlAttribute ?: return @@ -36,50 +33,52 @@ class PHPLanguageInjector : MultiHostInjector { is HtmlTag -> { element.children .mapNotNull { it as? HtmlRawTextImpl } - .forEach { child-> + .forEach { child -> injectIntoText(HtmlTextInjectionHostWrapper(child), registrar) } } is XmlText -> { -// println("element: ${element.text}, ${element.javaClass.name} ${element is PsiLanguageInjectionHost}") + // println("element: ${element.text}, ${element.javaClass.name} + // ${element is + // PsiLanguageInjectionHost}") val injectableHost = element as? PsiLanguageInjectionHost ?: return injectIntoText(injectableHost, registrar) } } } - val tagsMap = mapOf( - "{!!" to "!!}", - "{{" to "}}", - ) - private fun injectIntoText( - element: PsiLanguageInjectionHost, - registrar: MultiHostRegistrar - ) { - val children = element.node.children().toList() - .filter { it is XmlToken } - .apply { if (size < 2) return } + val tagsMap = mapOf("{!!" to "!!}", "{{" to "}}") -// println("children: $children") + private fun injectIntoText(element: PsiLanguageInjectionHost, registrar: MultiHostRegistrar) { + val children = + element.node + .children() + .toList() + .filter { it is XmlToken } + .apply { if (size < 2) return } + + // println("children: $children") val openTag = children.find { it.text == "{!!" || it.text == "{{" }?.psi ?: return val closeTag = children.find { it.text == tagsMap[openTag.text] }?.psi -// println("openTag: ${openTag.text}, closeTag: ${closeTag?.text}") - if ((openTag.text == "{!!" && closeTag?.text == "!!}") || (openTag.text == "{{" && closeTag?.text == "}}")) { + // println("openTag: ${openTag.text}, closeTag: ${closeTag?.text}") + if ( + (openTag.text == "{!!" && closeTag?.text == "!!}") || + (openTag.text == "{{" && closeTag?.text == "}}") + ) { val language = Language.findLanguageByID("PHP") ?: return - val textRange = TextRange(openTag.textRangeInParent.endOffset, closeTag.startOffsetInParent) -// println("injecting ${language} into $element, $textRange") - registrar.startInjecting(language) + val textRange = + TextRange(openTag.textRangeInParent.endOffset, closeTag.startOffsetInParent) + // println("injecting ${language} into $element, $textRange") + registrar + .startInjecting(language) .addPlace("", element, textRange) .doneInjecting() } } - override fun elementsToInjectIn() = listOf( - XmlAttributeValue::class.java, - XmlText::class.java, - HtmlTag::class.java, - ) -} \ No newline at end of file + override fun elementsToInjectIn() = + listOf(XmlAttributeValue::class.java, XmlText::class.java, HtmlTag::class.java) +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 15688d5..8d8d2d5 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -15,11 +15,10 @@ + - diff --git a/src/main/resources/META-INF/webTypes.json b/src/main/resources/META-INF/webTypes.json index dbb5c19..ca3c0d8 100644 --- a/src/main/resources/META-INF/webTypes.json +++ b/src/main/resources/META-INF/webTypes.json @@ -1,46 +1,310 @@ { "$schema": "https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json", - "name": "Tempest", + "name": "Tempest Component", "version": "1.0.0", - "description-markup": "markdown", - "default-icon": "icon", + "description-markup": "html", + "default-icon": "icons/tempest/icon_transparent.svg", "contributions": { "html": { "elements": [ + { + "name": "Custom Tempest Component", + "pattern": { + "regex": "x-.+" + }, + "attributes": [ + { + "name": "Component Attribute Expression", + "pattern": { + "regex": ":.+" + }, + "inject-language": "InjectablePHP", + "value": { + "type": "string", + "required": false + }, + "virtual": true + }, + { + "name": "Component Attribute", + "pattern": { + "regex": ".+" + }, + "value": { + "type": "string", + "required": false + }, + "virtual": true + } + ], + "doc-url": "https://tempestphp.com/1.x/internals/view-spec", + "virtual": true + }, { "name": "x-base", - "description": "A base template you can install into your own project as a starting point. This one includes the Tailwind CDN for quick prototyping.", - "doc-url": "https://tempestphp.com/1.x/internals/view-spec#x-base" + "description": "

A base template you can install into your own project as a starting point. This one includes the Tailwind CDN for quick prototyping.

Available named slots:

  • head
  • scripts
", + "doc-url": "https://tempestphp.com/1.x/internals/view-spec#x-base", + "attributes": [ + { + "name": ":title", + "description": "The webpage's title", + "inject-language": "InjectablePHP", + "value": { + "type": "string", + "required": false + } + }, + { + "name": "title", + "description": "The webpage's title", + "value": { + "type": "string", + "required": false + } + } + ] }, { "name": "x-form", - "description": "This component provides a form element that will post by default and includes the csrf token out of the box.", - "doc-url": "https://tempestphp.com/1.x/internals/view-spec#x-form" + "description": "

This component provides a form element that will post by default and includes the csrf token out of the box.

", + "doc-url": "https://tempestphp.com/1.x/internals/view-spec#x-form", + "attributes": [ + { + "name": ":action", + "description": "", + "inject-language": "InjectablePHP", + "value": { + "type": "string", + "required": false + } + }, + { + "name": "action", + "description": "", + "value": { + "type": "string", + "required": false + } + }, + { + "name": ":method", + "description": "", + "inject-language": "InjectablePHP", + "value": { + "type": "string", + "required": false + } + }, + { + "name": "method", + "description": "", + "value": { + "type": "string", + "required": false + } + }, + { + "name": ":enctype", + "description": "", + "inject-language": "InjectablePHP", + "value": { + "type": "string", + "required": false + } + }, + { + "name": "enctype", + "description": "", + "value": { + "type": "string", + "required": false + } + } + ] }, { "name": "x-input", - "description": "A versatile input component that will render labels and validation errors automatically.", - "doc-url": "https://tempestphp.com/1.x/internals/view-spec#x-input" + "description": "

A versatile input component that will render labels and validation errors automatically.

", + "doc-url": "https://tempestphp.com/1.x/internals/view-spec#x-input", + "attributes": [ + { + "name": ":name", + "description": "", + "inject-language": "InjectablePHP", + "value": { + "type": "string", + "required": true + } + }, + { + "name": "name", + "description": "", + "value": { + "type": "string", + "required": true + } + }, + { + "name": ":label", + "description": "", + "inject-language": "InjectablePHP", + "value": { + "type": "string", + "required": false + } + }, + { + "name": "label", + "description": "", + "value": { + "type": "string", + "required": false + } + }, + { + "name": ":id", + "description": "", + "inject-language": "InjectablePHP", + "value": { + "type": "string", + "required": false + } + }, + { + "name": "id", + "description": "", + "value": { + "type": "string", + "required": false + } + }, + { + "name": ":type", + "description": "", + "inject-language": "InjectablePHP", + "value": { + "type": "string", + "required": false + } + }, + { + "name": "type", + "description": "", + "value": { + "type": "string", + "required": false + } + }, + { + "name": ":default", + "description": "", + "inject-language": "InjectablePHP", + "value": { + "type": "string", + "required": false + } + }, + { + "name": "default", + "description": "", + "value": { + "type": "string", + "required": false + } + } + ] }, { "name": "x-submit", - "description": "A submit button component that prefills with a \"Submit\" label.", + "description": "

A submit button component that prefills with a \"Submit\" label.

", "doc-url": "https://tempestphp.com/1.x/internals/view-spec#x-submit" }, { "name": "x-csrf-token", - "description": "Includes the CSRF token in a form.", + "description": "

Includes the CSRF token in a form.

", "doc-url": "https://tempestphp.com/1.x/internals/view-spec#x-csrf-token" }, { "name": "x-icon", - "description": "This component provides the ability to inject any icon from the Iconify project in your templates.", - "doc-url": "https://tempestphp.com/1.x/internals/view-spec#x-icon" + "description": "

This component provides the ability to inject any icon from the Iconify project in your templates.

", + "doc-url": "https://tempestphp.com/1.x/internals/view-spec#x-icon", + "attributes": [ + { + "name": ":name", + "description": "", + "inject-language": "InjectablePHP", + "value": { + "type": "string", + "required": true + } + }, + { + "name": "name", + "description": "", + "value": { + "type": "string", + "required": true + } + }, + { + "name": ":class", + "description": "", + "inject-language": "InjectablePHP", + "value": { + "type": "string", + "required": false + } + }, + { + "name": "class", + "description": "", + "value": { + "type": "string", + "required": false + } + } + ] }, { "name": "x-vite-tags", - "description": "Tempest has built-in support for Vite, the most popular front-end development server and build tool. This component simply inject registered entrypoints where it is called.", - "doc-url": "https://tempestphp.com/1.x/internals/view-spec#x-vite-tags" + "description": "

Tempest has built-in support for Vite, the most popular front-end development server and build tool. This component simply inject registered entrypoints where it is called.

", + "doc-url": "https://tempestphp.com/1.x/internals/view-spec#x-vite-tags", + "attributes": [ + { + "name": ":entrypoints", + "description": "", + "inject-language": "InjectablePHP", + "value": { + "type": "array", + "required": false + } + }, + { + "name": "entrypoints", + "description": "", + "value": { + "type": "array", + "required": false + } + }, + { + "name": ":entrypoint", + "description": "", + "inject-language": "InjectablePHP", + "value": { + "type": "string", + "required": false + } + }, + { + "name": "entrypoint", + "description": "", + "value": { + "type": "string", + "required": false + } + } + ] }, { "name": "x-template", @@ -50,23 +314,80 @@ { "name": "x-slot", "description": "", - "doc-url": "https://tempestphp.com/1.x/internals/view-spec#x-slot" + "doc-url": "https://tempestphp.com/1.x/internals/view-spec#x-slot", + "attributes": [ + { + "name": ":name", + "description": "The name of the slot", + "inject-language": "InjectablePHP", + "value": { + "type": "string", + "required": false + } + }, + { + "name": "name", + "description": "The name of the slot", + "value": { + "type": "string", + "required": false + } + } + ] }, { "name": "x-markdown", "description": "", - "doc-url": "https://tempestphp.com/1.x/internals/view-spec#x-markdown" + "doc-url": "https://tempestphp.com/1.x/internals/view-spec#x-markdown", + "attributes": [ + { + "name": ":content", + "description": "The markdown content from a variable", + "inject-language": "InjectablePHP", + "value": { + "type": "string", + "required": false + } + }, + { + "name": "content", + "description": "The markdown content from a variable", + "value": { + "type": "string", + "required": false + } + } + ] }, { "name": "x-component", "description": "", - "doc-url": "https://tempestphp.com/1.x/internals/view-spec#x-component" + "doc-url": "https://tempestphp.com/1.x/internals/view-spec#x-component", + "attributes": [ + { + "name": ":is", + "description": "", + "inject-language": "InjectablePHP", + "value": { + "type": "string", + "required": true + } + }, + { + "name": "is", + "description": "", + "value": { + "type": "string", + "required": true + } + } + ] } ], "attributes": [ { "name": ":if", - "description": "Conditional attribute `if`. The element is present in the final html when the condition is true.", + "description": "Conditional attribute if. The element is present in the final html when the condition is true.", "doc-url": "https://tempestphp.com/1.x/internals/view-spec#control-structures", "inject-language": "InjectablePHP", "value": { @@ -76,7 +397,7 @@ }, { "name": ":elseif", - "description": "Conditional attribute `else if`. The element is present in the final html when the condition is true.", + "description": "Conditional attribute else if. The element is present in the final html when the condition is true.", "doc-url": "https://tempestphp.com/1.x/internals/view-spec#control-structures", "inject-language": "InjectablePHP", "value": { @@ -95,7 +416,7 @@ }, { "name": ":foreach", - "description": "Loop attribute `for each`. Values should be as PHP `foreach` loop. Introduce a local variable for each value in the collection.", + "description": "Loop attribute for each. Values should be as PHP foreach loop. Introduce a local variable for each value in the collection.", "doc-url": "https://tempestphp.com/1.x/internals/view-spec#control-structures", "inject-language": "InjectablePHP", "value": { @@ -105,7 +426,7 @@ }, { "name": ":forelse", - "description": "Loop attribute combined with `foreach`. The element is present in the final html when the collection is empty.", + "description": "Loop attribute combined with foreach. The element is present in the final html when the collection is empty.", "doc-url": "https://tempestphp.com/1.x/internals/view-spec#control-structures", "inject-language": "InjectablePHP", "value": { diff --git a/src/main/resources/icons/tempest/icon_transparent.svg b/src/main/resources/icons/tempest/icon_transparent.svg new file mode 100644 index 0000000..c09dd3d --- /dev/null +++ b/src/main/resources/icons/tempest/icon_transparent.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + diff --git a/src/test/kotlin/com/github/tempest/framework/.gitkeep b/src/test/kotlin/com/github/tempest/framework/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/test/kotlin/com/github/tempest/framework/MyPluginTest.kt b/src/test/kotlin/com/github/tempest/framework/MyPluginTest.kt deleted file mode 100644 index 7318c2d..0000000 --- a/src/test/kotlin/com/github/tempest/framework/MyPluginTest.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.github.tempest.framework - -import com.intellij.ide.highlighter.XmlFileType -import com.intellij.openapi.components.service -import com.intellij.psi.xml.XmlFile -import com.intellij.testFramework.TestDataPath -import com.intellij.testFramework.fixtures.BasePlatformTestCase -import com.intellij.util.PsiErrorElementUtil -import com.github.tempest.framework.services.MyProjectService - -@TestDataPath("\$CONTENT_ROOT/src/test/testData") -class MyPluginTest : BasePlatformTestCase() { - - fun testXMLFile() { - val psiFile = myFixture.configureByText(XmlFileType.INSTANCE, "bar") - val xmlFile = assertInstanceOf(psiFile, XmlFile::class.java) - - assertFalse(PsiErrorElementUtil.hasErrors(project, xmlFile.virtualFile)) - - assertNotNull(xmlFile.rootTag) - - xmlFile.rootTag?.let { - assertEquals("foo", it.name) - assertEquals("bar", it.value.text) - } - } - - fun testRename() { - myFixture.testRename("foo.xml", "foo_after.xml", "a2") - } - - fun testProjectService() { - val projectService = project.service() - - assertNotSame(projectService.getRandomNumber(), projectService.getRandomNumber()) - } - - override fun getTestDataPath() = "src/test/testData/rename" -} diff --git a/src/test/testData/rename/foo.xml b/src/test/testData/rename/foo.xml deleted file mode 100644 index b21e9f2..0000000 --- a/src/test/testData/rename/foo.xml +++ /dev/null @@ -1,3 +0,0 @@ - - 1>Foo - diff --git a/src/test/testData/rename/foo_after.xml b/src/test/testData/rename/foo_after.xml deleted file mode 100644 index 980ca96..0000000 --- a/src/test/testData/rename/foo_after.xml +++ /dev/null @@ -1,3 +0,0 @@ - - Foo -