From b8c73387b764fbfb6dd2248504a3b2e1a3521359 Mon Sep 17 00:00:00 2001 From: Khushboo Desai Date: Tue, 1 Aug 2023 15:02:17 -0700 Subject: [PATCH] Support IntelliJ 2023.2 * adds GitHub actions changes * add plugin info in the build.gradle.kts * changed gradle dependency to 7.6.2 * adds IC-232 source folder --- .github/workflows/build.yml | 2 +- .github/workflows/pr.yml | 2 +- .github/workflows/release.yml | 2 +- build.gradle.kts | 17 +++-- gradle/wrapper/gradle-wrapper.properties | 2 +- .../intellij/formatting/ASTNodeUtils.kt | 18 +++++ .../IonCodeStyleSettingsProvider.kt | 33 +++++++++ .../formatting/IonFormattingModelBuilder.kt | 34 ++++++++++ .../formatting/blocks/IonSExpressionBlock.kt | 68 +++++++++++++++++++ .../intellij/psi/PsiElementExtensions.kt | 11 +++ 10 files changed, 181 insertions(+), 8 deletions(-) create mode 100644 src/IC-232/kotlin/com/amazon/ion/plugin/intellij/formatting/ASTNodeUtils.kt create mode 100644 src/IC-232/kotlin/com/amazon/ion/plugin/intellij/formatting/IonCodeStyleSettingsProvider.kt create mode 100644 src/IC-232/kotlin/com/amazon/ion/plugin/intellij/formatting/IonFormattingModelBuilder.kt create mode 100644 src/IC-232/kotlin/com/amazon/ion/plugin/intellij/formatting/blocks/IonSExpressionBlock.kt create mode 100644 src/IC-232/kotlin/com/amazon/ion/plugin/intellij/psi/PsiElementExtensions.kt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a19f541..47b45ba 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -36,7 +36,7 @@ jobs: strategy: fail-fast: false matrix: - product: [ "IC-2022.2", "IC-2023.1"] + product: [ "IC-2022.2", "IC-2023.1", "IC-2023.2"] max-parallel: 5 env: PRODUCT_NAME: ${{ matrix.product }} diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index c744750..c37722e 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -34,7 +34,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - product: [ "IC-2022.2", "IC-2023.1"] + product: [ "IC-2022.2", "IC-2023.1", "IC-2023.2"] max-parallel: 5 env: PRODUCT_NAME: ${{ matrix.product }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5e42086..5c96e9e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: release: strategy: matrix: - product: [ "IC-2022.2", "IC-2023.1" ] + product: [ "IC-2022.2", "IC-2023.1", "IC-2023.2" ] max-parallel: 1 env: PRODUCT_NAME: ${{ matrix.product }} diff --git a/build.gradle.kts b/build.gradle.kts index 8dde211..76a31b6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,9 +12,7 @@ buildscript { plugins { kotlin("jvm") - // TODO: The version need to be updated to the latest version 1.15.0. - // There will be configuration issues need to be solved after upgrading to the latest version 1.15.0. - id("org.jetbrains.intellij") version "1.13.2" + id("org.jetbrains.intellij") version "1.15.0" } repositories { @@ -43,10 +41,21 @@ val plugins = listOf( apiVersion = "1.6" ), dependencies = listOf("java", "Kotlin") + ), + PluginDescriptor( + since = "232", + until = "232.*", + sdkVersion = "IC-2023.2", + platformType = PlatformType.IdeaCommunity, + sourceFolder = "IC-232", + kotlin = KotlinOptions( + apiVersion = "1.6" + ), + dependencies = listOf("java", "Kotlin") ) ) -val defaultProductName = "IC-2023.1" +val defaultProductName = "IC-2023.2" val productName = System.getenv("PRODUCT_NAME") ?: defaultProductName val maybeGithubRunNumber = System.getenv("GITHUB_RUN_NUMBER")?.toInt() val descriptor = plugins.first { it.sdkVersion == productName } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index aa991fc..98debb8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/IC-232/kotlin/com/amazon/ion/plugin/intellij/formatting/ASTNodeUtils.kt b/src/IC-232/kotlin/com/amazon/ion/plugin/intellij/formatting/ASTNodeUtils.kt new file mode 100644 index 0000000..3531811 --- /dev/null +++ b/src/IC-232/kotlin/com/amazon/ion/plugin/intellij/formatting/ASTNodeUtils.kt @@ -0,0 +1,18 @@ +package com.amazon.ion.plugin.intellij.formatting + +import com.amazon.ion.plugin.intellij.utils.filterWhitespace +import com.intellij.lang.ASTNode +import org.jetbrains.kotlin.idea.base.psi.getLineNumber +import org.jetbrains.kotlin.psi.psiUtil.siblings + +/** + * Determine if a node is on the same line as another node. + */ +fun ASTNode.sameLineAs(another: ASTNode) = + another.psi.getLineNumber(start = true) == this.psi.getLineNumber(start = true) + +/** + * Return the previous sibling of a node if it exists. + */ +fun ASTNode.previousSibling(): ASTNode? = + siblings(forward = false).filterWhitespace().firstOrNull() diff --git a/src/IC-232/kotlin/com/amazon/ion/plugin/intellij/formatting/IonCodeStyleSettingsProvider.kt b/src/IC-232/kotlin/com/amazon/ion/plugin/intellij/formatting/IonCodeStyleSettingsProvider.kt new file mode 100644 index 0000000..eb52d10 --- /dev/null +++ b/src/IC-232/kotlin/com/amazon/ion/plugin/intellij/formatting/IonCodeStyleSettingsProvider.kt @@ -0,0 +1,33 @@ +package com.amazon.ion.plugin.intellij.formatting + +import com.amazon.ion.plugin.intellij.IonLanguage +import com.intellij.application.options.CodeStyleAbstractConfigurable +import com.intellij.application.options.CodeStyleAbstractPanel +import com.intellij.application.options.TabbedLanguageCodeStylePanel +import com.intellij.openapi.options.Configurable +import com.intellij.psi.codeStyle.CodeStyleSettings +import com.intellij.psi.codeStyle.CodeStyleSettingsProvider + +private const val CODE_STYLE_SETTINGS_DISPLAY_NAME = "Ion" + +class IonCodeStyleSettingsProvider : CodeStyleSettingsProvider() { + override fun getConfigurableDisplayName(): String = CODE_STYLE_SETTINGS_DISPLAY_NAME + + override fun createSettingsPage(settings: CodeStyleSettings, modelSettings: CodeStyleSettings): Configurable = + CodeStyleConfigurableConfiguration(settings, modelSettings) +} + +private class CodeStyleConfigurableConfiguration(settings: CodeStyleSettings, modelSettings: CodeStyleSettings) + : CodeStyleAbstractConfigurable(settings, modelSettings, CODE_STYLE_SETTINGS_DISPLAY_NAME) { + + override fun createPanel(settings: CodeStyleSettings): CodeStyleAbstractPanel = IonCodeStyleMainPanel(currentSettings, settings) + override fun getHelpTopic(): String? = null +} + +private class IonCodeStyleMainPanel(currentSettings: CodeStyleSettings, settings: CodeStyleSettings) + : TabbedLanguageCodeStylePanel(IonLanguage.INSTANCE, currentSettings, settings) { + + override fun initTabs(settings: CodeStyleSettings?) { + addIndentOptionsTab(settings) + } +} diff --git a/src/IC-232/kotlin/com/amazon/ion/plugin/intellij/formatting/IonFormattingModelBuilder.kt b/src/IC-232/kotlin/com/amazon/ion/plugin/intellij/formatting/IonFormattingModelBuilder.kt new file mode 100644 index 0000000..51a531b --- /dev/null +++ b/src/IC-232/kotlin/com/amazon/ion/plugin/intellij/formatting/IonFormattingModelBuilder.kt @@ -0,0 +1,34 @@ +package com.amazon.ion.plugin.intellij.formatting + +import com.amazon.ion.plugin.intellij.formatting.blocks.IonBlockOptions +import com.amazon.ion.plugin.intellij.formatting.blocks.RootIonBlock +import com.intellij.formatting.FormattingContext +import com.intellij.formatting.FormattingModel +import com.intellij.formatting.FormattingModelBuilder +import com.intellij.formatting.FormattingModelProvider + +/** + * Creates the block model for an Ion file. + * + * The block model will determine how elements are spaced, indented and aligned. + */ +class IonFormattingModelBuilder : FormattingModelBuilder { + override fun createModel(formattingContext: FormattingContext): FormattingModel { + val element = formattingContext.psiElement + val settings = formattingContext.codeStyleSettings + + val rootBlock = RootIonBlock( + node = element.node, + options = IonBlockOptions( + spaceBuilder = IonCodeBlockSpacingProvider(settings), + codeStyle = settings + ) + ) + + + return FormattingModelProvider.createFormattingModelForPsiFile( + element.containingFile, + rootBlock, settings + ) + } +} diff --git a/src/IC-232/kotlin/com/amazon/ion/plugin/intellij/formatting/blocks/IonSExpressionBlock.kt b/src/IC-232/kotlin/com/amazon/ion/plugin/intellij/formatting/blocks/IonSExpressionBlock.kt new file mode 100644 index 0000000..12d64d3 --- /dev/null +++ b/src/IC-232/kotlin/com/amazon/ion/plugin/intellij/formatting/blocks/IonSExpressionBlock.kt @@ -0,0 +1,68 @@ +package com.amazon.ion.plugin.intellij.formatting.blocks + +import com.amazon.ion.plugin.intellij.formatting.previousSibling +import com.amazon.ion.plugin.intellij.formatting.sameLineAs +import com.amazon.ion.plugin.intellij.psi.IonTypes +import com.amazon.ion.plugin.intellij.psi.isOneLiner +import com.amazon.ion.plugin.intellij.utils.elementIsA +import com.intellij.lang.ASTNode +import com.intellij.openapi.diagnostic.debug +import com.intellij.openapi.diagnostic.logger +import com.intellij.psi.tree.IElementType +import org.jetbrains.kotlin.idea.base.psi.getLineNumber + +private val logger = logger() + +class IonSExpressionBlock( + node: ASTNode, + formatting: IonBlockFormattingOptions, + options: IonBlockOptions +) : AbstractIonBlock(node, formatting = formatting, options = options) { + + override val childIndentedTypes: Set = setOf( + IonTypes.VALUE, + IonTypes.COMMENT + ) + + override val childContainerTypes: Set = setOf( + IonTypes.SEXPRESSION_ELEMENTS + ) + + override val containerWrapperTypes: Set = setOf( + IonTypes.LPAREN, + IonTypes.RPAREN + ) + + override fun buildChildBlockFormatting(child: ASTNode): IonBlockFormattingOptions = + buildSpecialCaseChildBlockFormatting(child) ?: + super.buildChildBlockFormatting(child) + + private fun buildSpecialCaseChildBlockFormatting(child: ASTNode): IonBlockFormattingOptions? { + + // Lazy evaluate the previous sibling if needed. + val previous by lazy { child.previousSibling() } + + /** + * Check if we are the first comment within the expression, there is a special comment + * case where we don't want to apply the child alignment to the comment. For example: + * + * (join // special case comment which is inline with operator + * // child comments are inline with inner values + * anotherValue + * ) + */ + if (child elementIsA IonTypes.COMMENT && previous?.elementType == IonTypes.SEXPRESSION_OPERATOR) { + + logger.debug { "Formatting [${child.psi.getLineNumber()}] - Special case inline expression comment line" } + + val comment = child.psi + val expressionOperator = previous!! + + if (comment.isOneLiner() && child.sameLineAs(expressionOperator)) { + return IonBlockFormatting.sameAlignment(this) + } + } + + return null + } +} diff --git a/src/IC-232/kotlin/com/amazon/ion/plugin/intellij/psi/PsiElementExtensions.kt b/src/IC-232/kotlin/com/amazon/ion/plugin/intellij/psi/PsiElementExtensions.kt new file mode 100644 index 0000000..5c36ea7 --- /dev/null +++ b/src/IC-232/kotlin/com/amazon/ion/plugin/intellij/psi/PsiElementExtensions.kt @@ -0,0 +1,11 @@ +package com.amazon.ion.plugin.intellij.psi + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.idea.base.psi.getLineCount + +/** + * True if the element is all in a single line. + * + * Exists for Backwards Compatibility: <= IC-2020.2 + */ +fun PsiElement.isOneLiner() = getLineCount() == 1