Skip to content

Commit e3794b6

Browse files
committed
Add AT formatter
1 parent 224e880 commit e3794b6

File tree

5 files changed

+362
-1
lines changed

5 files changed

+362
-1
lines changed
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Minecraft Development for IntelliJ
3+
*
4+
* https://mcdev.io/
5+
*
6+
* Copyright (C) 2024 minecraft-dev
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Lesser General Public License as published
10+
* by the Free Software Foundation, version 3.0 only.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public License
18+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
19+
*/
20+
21+
package com.demonwav.mcdev.platform.mcp.at.format
22+
23+
import com.demonwav.mcdev.platform.mcp.at.gen.psi.AtTypes
24+
import com.intellij.formatting.Alignment
25+
import com.intellij.formatting.Block
26+
import com.intellij.formatting.Indent
27+
import com.intellij.formatting.Spacing
28+
import com.intellij.formatting.SpacingBuilder
29+
import com.intellij.formatting.Wrap
30+
import com.intellij.formatting.WrapType
31+
import com.intellij.lang.ASTNode
32+
import com.intellij.lang.tree.util.children
33+
import com.intellij.psi.TokenType
34+
import com.intellij.psi.codeStyle.CodeStyleSettings
35+
import com.intellij.psi.formatter.common.AbstractBlock
36+
import com.intellij.psi.tree.IFileElementType
37+
38+
class AtBlock(
39+
node: ASTNode,
40+
wrap: Wrap?,
41+
alignment: Alignment?,
42+
val spacingBuilder: SpacingBuilder,
43+
val codeStyleSettings: CodeStyleSettings,
44+
val entryClassAlignment: Alignment? = null,
45+
val entryMemberAlignment: Alignment? = null,
46+
) : AbstractBlock(node, wrap, alignment) {
47+
48+
override fun buildChildren(): List<Block> {
49+
val blocks = mutableListOf<Block>()
50+
51+
var entryClassAlignment: Alignment? = entryClassAlignment
52+
var entryMemberAlignment: Alignment? = entryMemberAlignment
53+
54+
var newlineCount = 0
55+
val alignGroups = node.elementType is IFileElementType &&
56+
codeStyleSettings.getCustomSettings(AtCodeStyleSettings::class.java).ALIGN_ENTRY_CLASS_AND_MEMBER
57+
for (child in node.children()) {
58+
val childType = child.elementType
59+
if (childType == TokenType.WHITE_SPACE) {
60+
continue
61+
}
62+
63+
if (alignGroups) {
64+
if (childType == AtTypes.CRLF) {
65+
newlineCount++
66+
continue
67+
} else if (childType != AtTypes.COMMENT) {
68+
if (newlineCount >= 2) {
69+
// Align different groups separately, comments are not counted towards any group
70+
entryClassAlignment = Alignment.createAlignment(true)
71+
entryMemberAlignment = Alignment.createAlignment(true)
72+
}
73+
newlineCount = 0
74+
}
75+
}
76+
77+
val alignment = when (childType) {
78+
AtTypes.CLASS_NAME -> entryClassAlignment
79+
AtTypes.FIELD_NAME, AtTypes.FUNCTION, AtTypes.ASTERISK -> entryMemberAlignment
80+
else -> null
81+
}
82+
83+
blocks.add(
84+
AtBlock(
85+
child,
86+
Wrap.createWrap(WrapType.NONE, false),
87+
alignment,
88+
spacingBuilder,
89+
codeStyleSettings,
90+
entryClassAlignment,
91+
entryMemberAlignment
92+
)
93+
)
94+
}
95+
96+
return blocks
97+
}
98+
99+
override fun getIndent(): Indent? = Indent.getNoneIndent()
100+
101+
override fun getSpacing(child1: Block?, child2: Block): Spacing? = spacingBuilder.getSpacing(this, child1, child2)
102+
103+
override fun isLeaf(): Boolean = node.firstChildNode == null
104+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Minecraft Development for IntelliJ
3+
*
4+
* https://mcdev.io/
5+
*
6+
* Copyright (C) 2024 minecraft-dev
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Lesser General Public License as published
10+
* by the Free Software Foundation, version 3.0 only.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public License
18+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
19+
*/
20+
21+
package com.demonwav.mcdev.platform.mcp.at.format
22+
23+
import com.demonwav.mcdev.platform.mcp.at.AtLanguage
24+
import com.intellij.application.options.CodeStyleAbstractConfigurable
25+
import com.intellij.application.options.CodeStyleAbstractPanel
26+
import com.intellij.application.options.TabbedLanguageCodeStylePanel
27+
import com.intellij.lang.Language
28+
import com.intellij.openapi.util.NlsContexts
29+
import com.intellij.psi.codeStyle.CodeStyleConfigurable
30+
import com.intellij.psi.codeStyle.CodeStyleSettings
31+
import com.intellij.psi.codeStyle.CodeStyleSettingsCustomizable
32+
import com.intellij.psi.codeStyle.CodeStyleSettingsProvider
33+
import com.intellij.psi.codeStyle.CustomCodeStyleSettings
34+
import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider
35+
36+
class AtCodeStyleSettings(val settings: CodeStyleSettings) : CustomCodeStyleSettings("AtCodeStyleSettings", settings) {
37+
@JvmField
38+
var SPACE_BEFORE_ENTRY_COMMENT = true
39+
40+
@JvmField
41+
var ALIGN_ENTRY_CLASS_AND_MEMBER = true
42+
}
43+
44+
class AtCodeStyleSettingsProvider : CodeStyleSettingsProvider() {
45+
override fun createCustomSettings(settings: CodeStyleSettings): CustomCodeStyleSettings =
46+
AtCodeStyleSettings(settings)
47+
48+
override fun getConfigurableDisplayName(): @NlsContexts.ConfigurableName String? = AtLanguage.displayName
49+
50+
override fun createConfigurable(
51+
settings: CodeStyleSettings,
52+
modelSettings: CodeStyleSettings
53+
): CodeStyleConfigurable {
54+
return object : CodeStyleAbstractConfigurable(settings, modelSettings, configurableDisplayName) {
55+
override fun createPanel(settings: CodeStyleSettings): CodeStyleAbstractPanel {
56+
return AtCodeStyleSettingsConfigurable(currentSettings, settings)
57+
}
58+
}
59+
}
60+
}
61+
62+
class AtCodeStyleSettingsConfigurable(currentSettings: CodeStyleSettings, settings: CodeStyleSettings) :
63+
TabbedLanguageCodeStylePanel(AtLanguage, currentSettings, settings)
64+
65+
class AtLanguageCodeStyleSettingsProvider : LanguageCodeStyleSettingsProvider() {
66+
67+
override fun getLanguage(): Language = AtLanguage
68+
69+
override fun customizeSettings(consumer: CodeStyleSettingsCustomizable, settingsType: SettingsType) {
70+
if (settingsType == SettingsType.SPACING_SETTINGS) {
71+
consumer.showCustomOption(
72+
AtCodeStyleSettings::class.java,
73+
"SPACE_BEFORE_ENTRY_COMMENT",
74+
"Space before entry comment",
75+
"Spacing and alignment"
76+
)
77+
consumer.showCustomOption(
78+
AtCodeStyleSettings::class.java,
79+
"ALIGN_ENTRY_CLASS_AND_MEMBER",
80+
"Align entry class name and member",
81+
"Spacing and alignment"
82+
)
83+
}
84+
}
85+
86+
override fun getCodeSample(settingsType: SettingsType): String? = """
87+
# Some header comment
88+
89+
public net.minecraft.client.Minecraft pickBlock()V# This is an entry comment
90+
public net.minecraft.client.Minecraft userProperties()Lcom/mojang/authlib/minecraft/UserApiService${'$'}UserProperties;
91+
92+
# Each group can be aligned independently
93+
protected net.minecraft.client.gui.screens.inventory.AbstractContainerScreen clickedSlot
94+
protected-f net.minecraft.client.gui.screens.inventory.AbstractContainerScreen playerInventoryTitle
95+
protected net.minecraft.client.gui.screens.inventory.AbstractContainerScreen findSlot(DD)Lnet/minecraft/world/inventory/Slot;
96+
""".trimIndent()
97+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Minecraft Development for IntelliJ
3+
*
4+
* https://mcdev.io/
5+
*
6+
* Copyright (C) 2024 minecraft-dev
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Lesser General Public License as published
10+
* by the Free Software Foundation, version 3.0 only.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public License
18+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
19+
*/
20+
21+
package com.demonwav.mcdev.platform.mcp.at.format
22+
23+
import com.demonwav.mcdev.platform.mcp.at.AtLanguage
24+
import com.demonwav.mcdev.platform.mcp.at.gen.psi.AtTypes
25+
import com.intellij.formatting.Alignment
26+
import com.intellij.formatting.FormattingContext
27+
import com.intellij.formatting.FormattingModel
28+
import com.intellij.formatting.FormattingModelBuilder
29+
import com.intellij.formatting.FormattingModelProvider
30+
import com.intellij.formatting.SpacingBuilder
31+
import com.intellij.formatting.Wrap
32+
import com.intellij.formatting.WrapType
33+
import com.intellij.psi.codeStyle.CodeStyleSettings
34+
35+
class AtFormattingModelBuilder : FormattingModelBuilder {
36+
37+
private fun createSpaceBuilder(settings: CodeStyleSettings): SpacingBuilder {
38+
val atSettings = settings.getCustomSettings(AtCodeStyleSettings::class.java)
39+
return SpacingBuilder(settings, AtLanguage)
40+
.between(AtTypes.ENTRY, AtTypes.COMMENT).spaceIf(atSettings.SPACE_BEFORE_ENTRY_COMMENT)
41+
// Removes alignment spaces if it is disabled
42+
.between(AtTypes.KEYWORD, AtTypes.CLASS_NAME).spaces(1)
43+
.between(AtTypes.CLASS_NAME, AtTypes.FIELD_NAME).spaces(1)
44+
.between(AtTypes.CLASS_NAME, AtTypes.FUNCTION).spaces(1)
45+
.between(AtTypes.CLASS_NAME, AtTypes.ASTERISK).spaces(1)
46+
}
47+
48+
override fun createModel(formattingContext: FormattingContext): FormattingModel {
49+
val codeStyleSettings = formattingContext.codeStyleSettings
50+
val rootBlock = AtBlock(
51+
formattingContext.node,
52+
Wrap.createWrap(WrapType.NONE, false),
53+
Alignment.createAlignment(),
54+
createSpaceBuilder(codeStyleSettings),
55+
codeStyleSettings,
56+
Alignment.createAlignment(true),
57+
Alignment.createAlignment(true),
58+
)
59+
return FormattingModelProvider.createFormattingModelForPsiFile(
60+
formattingContext.containingFile,
61+
rootBlock,
62+
codeStyleSettings
63+
)
64+
}
65+
}

src/main/resources/META-INF/plugin.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -626,10 +626,12 @@
626626
<colorSettingsPage implementation="com.demonwav.mcdev.platform.mcp.at.AtColorSettingsPage"/>
627627
<lang.commenter language="Access Transformers"
628628
implementationClass="com.demonwav.mcdev.platform.mcp.at.AtCommenter"/>
629+
<codeStyleSettingsProvider implementation="com.demonwav.mcdev.platform.mcp.at.format.AtCodeStyleSettingsProvider"/>
630+
<langCodeStyleSettingsProvider implementation="com.demonwav.mcdev.platform.mcp.at.format.AtLanguageCodeStyleSettingsProvider"/>
631+
<lang.formatter language="Access Transformers" implementationClass="com.demonwav.mcdev.platform.mcp.at.format.AtFormattingModelBuilder"/>
629632
<typedHandler implementation="com.demonwav.mcdev.platform.mcp.at.completion.AtTypedHandlerDelegate"/>
630633
<completion.contributor language="Access Transformers"
631634
implementationClass="com.demonwav.mcdev.platform.mcp.at.completion.AtCompletionContributor"/>
632-
633635
<annotator language="Access Transformers" implementationClass="com.demonwav.mcdev.platform.mcp.at.AtAnnotator"/>
634636
<lang.inspectionSuppressor language="Access Transformers" implementationClass="com.demonwav.mcdev.platform.mcp.at.AtInspectionSuppressor"/>
635637
<psi.referenceContributor language="Access Transformers" implementation="com.demonwav.mcdev.platform.mcp.at.AtReferenceContributor"/>
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Minecraft Development for IntelliJ
3+
*
4+
* https://mcdev.io/
5+
*
6+
* Copyright (C) 2024 minecraft-dev
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Lesser General Public License as published
10+
* by the Free Software Foundation, version 3.0 only.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public License
18+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
19+
*/
20+
21+
package com.demonwav.mcdev.platform.mcp.at
22+
23+
import com.demonwav.mcdev.framework.BaseMinecraftTest
24+
import com.intellij.openapi.command.WriteCommandAction
25+
import com.intellij.psi.codeStyle.CodeStyleManager
26+
import org.intellij.lang.annotations.Language
27+
import org.junit.jupiter.api.DisplayName
28+
import org.junit.jupiter.api.Test
29+
30+
@DisplayName("Access Transformer Tests")
31+
class AtFormatterTest : BaseMinecraftTest() {
32+
33+
private fun doTest(
34+
@Language("Access Transformers") before: String,
35+
@Language("Access Transformers") after: String,
36+
) {
37+
38+
fixture.configureByText(AtFileType, before)
39+
WriteCommandAction.runWriteCommandAction(fixture.project) {
40+
CodeStyleManager.getInstance(project).reformat(fixture.file)
41+
}
42+
43+
fixture.checkResult(after)
44+
}
45+
46+
@Test
47+
@DisplayName("Entry Comment Spacing")
48+
fun entryCommentSpacing() {
49+
doTest("public Test field# A comment", "public Test field # A comment")
50+
}
51+
52+
@Test
53+
@DisplayName("Single Group Alignment")
54+
fun singleGroupAlignment() {
55+
doTest(
56+
"""
57+
public Test field # A comment
58+
public+f AnotherTest method()V
59+
""".trimIndent(),
60+
"""
61+
public Test field # A comment
62+
public+f AnotherTest method()V
63+
""".trimIndent()
64+
)
65+
}
66+
67+
@Test
68+
@DisplayName("Multiple Groups Alignments")
69+
fun multipleGroupsAlignments() {
70+
doTest(
71+
"""
72+
public net.minecraft.Group1A field
73+
protected net.minecraft.Group1BCD method()V
74+
75+
public net.minecraft.server.Group2A anotherField
76+
public-f net.minecraft.server.Group2BCD someMethod()V
77+
# A comment in the middle should not join the two groups
78+
protected net.minecraft.world.Group3A anotherField
79+
protected-f net.minecraft.world.Group2BCD someMethod()V
80+
""".trimIndent(),
81+
"""
82+
public net.minecraft.Group1A field
83+
protected net.minecraft.Group1BCD method()V
84+
85+
public net.minecraft.server.Group2A anotherField
86+
public-f net.minecraft.server.Group2BCD someMethod()V
87+
# A comment in the middle should not join the two groups
88+
protected net.minecraft.world.Group3A anotherField
89+
protected-f net.minecraft.world.Group2BCD someMethod()V
90+
""".trimIndent()
91+
)
92+
}
93+
}

0 commit comments

Comments
 (0)