Skip to content

Commit

Permalink
Add AW formatting, also identical to AT's
Browse files Browse the repository at this point in the history
  • Loading branch information
RedNesto committed Dec 16, 2024
1 parent 85d2569 commit 3e88e0c
Show file tree
Hide file tree
Showing 5 changed files with 375 additions and 0 deletions.
110 changes: 110 additions & 0 deletions src/main/kotlin/platform/mcp/aw/format/AwBlock.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Minecraft Development for IntelliJ
*
* https://mcdev.io/
*
* Copyright (C) 2024 minecraft-dev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, version 3.0 only.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.demonwav.mcdev.platform.mcp.aw.format

import com.demonwav.mcdev.platform.mcp.aw.gen.psi.AwTypes
import com.demonwav.mcdev.util.children
import com.intellij.formatting.Alignment
import com.intellij.formatting.Block
import com.intellij.formatting.Indent
import com.intellij.formatting.Spacing
import com.intellij.formatting.SpacingBuilder
import com.intellij.formatting.Wrap
import com.intellij.lang.ASTNode
import com.intellij.psi.TokenType
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.formatter.common.AbstractBlock
import com.intellij.psi.tree.IFileElementType

class AwBlock(
node: ASTNode,
wrap: Wrap?,
alignment: Alignment?,
val spacingBuilder: SpacingBuilder,
val codeStyleSettings: CodeStyleSettings,
val targetKindAlignment: Alignment? = null,
val entryClassAlignment: Alignment? = null,
val entryMemberAlignment: Alignment? = null,
) : AbstractBlock(node, wrap, alignment) {

override fun buildChildren(): List<Block> {
val blocks = mutableListOf<Block>()

var targetKindAlignment: Alignment? = targetKindAlignment
var entryClassAlignment: Alignment? = entryClassAlignment
var entryMemberAlignment: Alignment? = entryMemberAlignment

var newlineCount = 0
val alignGroups = node.elementType is IFileElementType &&
codeStyleSettings.getCustomSettings(AwCodeStyleSettings::class.java).ALIGN_ENTRY_CLASS_AND_MEMBER
for (child in node.children()) {
val childType = child.elementType
if (childType == TokenType.WHITE_SPACE) {
continue
}

if (alignGroups) {
if (childType == AwTypes.CRLF) {
newlineCount++
continue
} else if (childType != AwTypes.COMMENT) {
if (newlineCount >= 2) {
// Align different groups separately, comments are not counted towards any group
targetKindAlignment = Alignment.createAlignment(true)
entryClassAlignment = Alignment.createAlignment(true)
entryMemberAlignment = Alignment.createAlignment(true)
}
newlineCount = 0
}
}

val alignment = when (childType) {
AwTypes.CLASS_ELEMENT, AwTypes.FIELD_ELEMENT, AwTypes.METHOD_ELEMENT -> targetKindAlignment
AwTypes.CLASS_NAME -> entryClassAlignment
AwTypes.MEMBER_NAME, AwTypes.FIELD_DESC, AwTypes.METHOD_DESC -> entryMemberAlignment
else -> null
}

blocks.add(
AwBlock(
child,
null,
alignment,
spacingBuilder,
codeStyleSettings,
targetKindAlignment,
entryClassAlignment,
entryMemberAlignment
)
)
}

return blocks
}

override fun getIndent(): Indent? = Indent.getNoneIndent()

override fun getChildIndent(): Indent? = Indent.getNoneIndent()

override fun getSpacing(child1: Block?, child2: Block): Spacing? = spacingBuilder.getSpacing(this, child1, child2)

override fun isLeaf(): Boolean = node.firstChildNode == null
}
97 changes: 97 additions & 0 deletions src/main/kotlin/platform/mcp/aw/format/AwCodeStyleSettings.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Minecraft Development for IntelliJ
*
* https://mcdev.io/
*
* Copyright (C) 2024 minecraft-dev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, version 3.0 only.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.demonwav.mcdev.platform.mcp.aw.format

import com.demonwav.mcdev.platform.mcp.aw.AwLanguage
import com.intellij.application.options.CodeStyleAbstractConfigurable
import com.intellij.application.options.CodeStyleAbstractPanel
import com.intellij.application.options.TabbedLanguageCodeStylePanel
import com.intellij.lang.Language
import com.intellij.openapi.util.NlsContexts
import com.intellij.psi.codeStyle.CodeStyleConfigurable
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.codeStyle.CodeStyleSettingsCustomizable
import com.intellij.psi.codeStyle.CodeStyleSettingsProvider
import com.intellij.psi.codeStyle.CustomCodeStyleSettings
import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider

class AwCodeStyleSettings(val settings: CodeStyleSettings) : CustomCodeStyleSettings("AwCodeStyleSettings", settings) {
@JvmField
var SPACE_BEFORE_ENTRY_COMMENT = true

@JvmField
var ALIGN_ENTRY_CLASS_AND_MEMBER = true
}

class AwCodeStyleSettingsProvider : CodeStyleSettingsProvider() {
override fun createCustomSettings(settings: CodeStyleSettings): CustomCodeStyleSettings =
AwCodeStyleSettings(settings)

override fun getConfigurableDisplayName(): @NlsContexts.ConfigurableName String? = AwLanguage.displayName

override fun createConfigurable(
settings: CodeStyleSettings,
modelSettings: CodeStyleSettings
): CodeStyleConfigurable {
return object : CodeStyleAbstractConfigurable(settings, modelSettings, configurableDisplayName) {
override fun createPanel(settings: CodeStyleSettings): CodeStyleAbstractPanel {
return AwCodeStyleSettingsConfigurable(currentSettings, settings)
}
}
}
}

class AwCodeStyleSettingsConfigurable(currentSettings: CodeStyleSettings, settings: CodeStyleSettings) :
TabbedLanguageCodeStylePanel(AwLanguage, currentSettings, settings)

class AwLanguageCodeStyleSettingsProvider : LanguageCodeStyleSettingsProvider() {

override fun getLanguage(): Language = AwLanguage

override fun customizeSettings(consumer: CodeStyleSettingsCustomizable, settingsType: SettingsType) {
if (settingsType == SettingsType.SPACING_SETTINGS) {
consumer.showCustomOption(
AwCodeStyleSettings::class.java,
"SPACE_BEFORE_ENTRY_COMMENT",
"Space before entry comment",
"Spacing and alignment"
)
consumer.showCustomOption(
AwCodeStyleSettings::class.java,
"ALIGN_ENTRY_CLASS_AND_MEMBER",
"Align entry class name and member",
"Spacing and alignment"
)
}
}

override fun getCodeSample(settingsType: SettingsType): String? = """
# Some header comment
accessible method net/minecraft/client/Minecraft pickBlock ()V # This is an entry comment
accessible method net/minecraft/client/Minecraft userProperties ()Lcom/mojang/authlib/minecraft/UserApiService${'$'}UserProperties;
# Each group can be aligned independently
accessible field net/minecraft/client/gui/screens/inventory/AbstractContainerScreen clickedSlot I
accessible field net/minecraft/client/gui/screens/inventory/AbstractContainerScreen playerInventoryTitle Ljava/lang/String;
extendable method net/minecraft/client/gui/screens/inventory/AbstractContainerScreen findSlot (DD)Lnet/minecraft/world/inventory/Slot;
""".trimIndent()
}
68 changes: 68 additions & 0 deletions src/main/kotlin/platform/mcp/aw/format/AwFormattingModelBuilder.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Minecraft Development for IntelliJ
*
* https://mcdev.io/
*
* Copyright (C) 2024 minecraft-dev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, version 3.0 only.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.demonwav.mcdev.platform.mcp.aw.format

import com.demonwav.mcdev.platform.mcp.aw.AwLanguage
import com.demonwav.mcdev.platform.mcp.aw.gen.psi.AwTypes
import com.intellij.formatting.Alignment
import com.intellij.formatting.FormattingContext
import com.intellij.formatting.FormattingModel
import com.intellij.formatting.FormattingModelBuilder
import com.intellij.formatting.FormattingModelProvider
import com.intellij.formatting.SpacingBuilder
import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.tree.TokenSet

class AwFormattingModelBuilder : FormattingModelBuilder {

private fun createSpaceBuilder(settings: CodeStyleSettings): SpacingBuilder {
val atSettings = settings.getCustomSettings(AwCodeStyleSettings::class.java)
var targetKindTokens = TokenSet.create(AwTypes.CLASS_ELEMENT, AwTypes.METHOD_ELEMENT, AwTypes.FIELD_ELEMENT)
var entryTokens = TokenSet.create(AwTypes.CLASS_ENTRY, AwTypes.METHOD_ENTRY, AwTypes.FIELD_ENTRY)
return SpacingBuilder(settings, AwLanguage)
.between(entryTokens, AwTypes.COMMENT).spaceIf(atSettings.SPACE_BEFORE_ENTRY_COMMENT)
// Removes alignment spaces if it is disabled
.between(AwTypes.ACCESS_ELEMENT, targetKindTokens).spaces(1)
.between(targetKindTokens, AwTypes.CLASS_ELEMENT).spaces(1)
.between(AwTypes.CLASS_ELEMENT, AwTypes.MEMBER_NAME).spaces(1)
.between(AwTypes.MEMBER_NAME, AwTypes.FIELD_DESC).spaces(1)
.between(AwTypes.MEMBER_NAME, AwTypes.METHOD_DESC).spaces(1)
}

override fun createModel(formattingContext: FormattingContext): FormattingModel {
val codeStyleSettings = formattingContext.codeStyleSettings
val rootBlock = AwBlock(
formattingContext.node,
null,
null,
createSpaceBuilder(codeStyleSettings),
codeStyleSettings,
Alignment.createAlignment(true),
Alignment.createAlignment(true),
Alignment.createAlignment(true),
)
return FormattingModelProvider.createFormattingModelForPsiFile(
formattingContext.containingFile,
rootBlock,
codeStyleSettings
)
}
}
3 changes: 3 additions & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,9 @@
implementationClass="com.demonwav.mcdev.platform.mcp.aw.AwSyntaxHighlighterFactory"/>
<colorSettingsPage implementation="com.demonwav.mcdev.platform.mcp.aw.AwColorSettingsPage"/>
<lang.commenter language="Access Widener" implementationClass="com.demonwav.mcdev.platform.mcp.aw.AwCommenter"/>
<codeStyleSettingsProvider implementation="com.demonwav.mcdev.platform.mcp.aw.format.AwCodeStyleSettingsProvider"/>
<langCodeStyleSettingsProvider implementation="com.demonwav.mcdev.platform.mcp.aw.format.AwLanguageCodeStyleSettingsProvider"/>
<lang.formatter language="Access Widener" implementationClass="com.demonwav.mcdev.platform.mcp.aw.format.AwFormattingModelBuilder"/>
<annotator language="Access Widener" implementationClass="com.demonwav.mcdev.platform.mcp.aw.AwAnnotator"/>
<lang.inspectionSuppressor language="Access Widener" implementationClass="com.demonwav.mcdev.platform.mcp.aw.inspections.AwInspectionSuppressor"/>
<completion.contributor language="Access Widener"
Expand Down
97 changes: 97 additions & 0 deletions src/test/kotlin/platform/mcp/aw/AwFormatterTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Minecraft Development for IntelliJ
*
* https://mcdev.io/
*
* Copyright (C) 2024 minecraft-dev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, version 3.0 only.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.demonwav.mcdev.platform.mcp.aw

import com.demonwav.mcdev.framework.BaseMinecraftTest
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.psi.codeStyle.CodeStyleManager
import org.intellij.lang.annotations.Language
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test

@DisplayName("Access Widener Tests")
class AwFormatterTest : BaseMinecraftTest() {

private fun doTest(
@Language("Access Widener") before: String,
@Language("Access Widener") after: String,
) {

fixture.configureByText(AwFileType, before)
WriteCommandAction.runWriteCommandAction(fixture.project) {
CodeStyleManager.getInstance(project).reformat(fixture.file)
}

fixture.checkResult(after)
}

@Test
@DisplayName("Entry Comment Spacing")
fun entryCommentSpacing() {
doTest("accessible field Test field# A comment", "accessible field Test field # A comment")
}

@Test
@DisplayName("Single Group Alignment")
fun singleGroupAlignment() {
doTest(
"""
accessible field Test field # A comment
transitive-accessible method AnotherTest method ()V
""".trimIndent(),
"""
accessible field Test field # A comment
transitive-accessible method AnotherTest method ()V
""".trimIndent()
)
}

@Test
@DisplayName("Multiple Groups Alignments")
fun multipleGroupsAlignments() {
doTest(
"""
accessWidener v2 named
accessible field net/minecraft/Group1A field
transitive-extendable method net/minecraft/Group1BCD method ()V
accessible field net/minecraft/server/Group2A anotherField
extendable method net/minecraft/server/Group2BCD someMethod ()V
# A comment in the middle should not join the two groups
accessible field net/minecraft/world/Group3A anotherField
transitive-extendable method net/minecraft/world/Group2BCD someMethod ()V
""".trimIndent(),
"""
accessWidener v2 named
accessible field net/minecraft/Group1A field
transitive-extendable method net/minecraft/Group1BCD method ()V
accessible field net/minecraft/server/Group2A anotherField
extendable method net/minecraft/server/Group2BCD someMethod ()V
# A comment in the middle should not join the two groups
accessible field net/minecraft/world/Group3A anotherField
transitive-extendable method net/minecraft/world/Group2BCD someMethod ()V
""".trimIndent()
)
}
}

0 comments on commit 3e88e0c

Please sign in to comment.