Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for IntelliJ's "Reformat Code" command #35

Merged
merged 4 commits into from
Mar 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@ indent_size = 4
indent_style = space
insert_final_newline = true

[*.md]
indent_size = 2

[*.yml]
indent_size = 2
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

- Very simple parser, which should provide just enough of a basis to add basic editor functionality like code
formatting and auto indentation.
- Support for IntelliJ's "Reformat Code" command. Currently, this does the following:
- Indent blocks
- Normalize spacing between tokens on 1 line

## [0.1.3-alpha] - 2023-03-30

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ using [Simon Brown](https://twitter.com/simonbrown)'s [C4 model](https://c4model

* Basic syntax highlighting for the Structurizr DSL language. Files with the `.dsl` extension are considered to be
Structurizr DSL files.
* Automatically indent and normalize spacing with IntelliJ's "Reformat Code" command.
* More to come! Please refer to the [TODO section](https://github.com/dirkgroot/structurizr-dsl-intellij-plugin#todo) of
the README for a list of features that will likely be added.

Expand Down Expand Up @@ -73,7 +74,7 @@ using [Simon Brown](https://twitter.com/simonbrown)'s [C4 model](https://c4model
- ☑️ syntax highlighting
- ✅ token based
- 🔳 annotator based
- 🔳 reformat code
- reformat code
- 🔳 auto indentation
- 🔳 go to definition
- 🔳 rename refactoring for identifiers
Expand Down
54 changes: 54 additions & 0 deletions src/main/kotlin/nl/dirkgroot/structurizr/dsl/lang/SDBlock.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package nl.dirkgroot.structurizr.dsl.lang

import com.intellij.formatting.*
import com.intellij.lang.ASTNode
import com.intellij.psi.TokenType.WHITE_SPACE
import com.intellij.psi.formatter.common.AbstractBlock
import com.intellij.psi.tree.TokenSet
import nl.dirkgroot.structurizr.dsl.psi.SDTypes.*

class SDBlock(
node: ASTNode,
private val spacingBuilder: SpacingBuilder,
private val indent: Indent = Indent.getNoneIndent(),
wrap: Wrap = Wrap.createWrap(WrapType.NONE, false),
alignment: Alignment = Alignment.createAlignment()
) : AbstractBlock(node, wrap, alignment) {
override fun buildChildren(): MutableList<Block> {
val result = mutableListOf<Block>()
var child = myNode.firstChildNode
while (child != null) {
if (child.elementType != WHITE_SPACE && child.elementType != CRLF) {
val indent = if (child.elementType in INDENT_TOKEN_TYPES && myNode.treeParent != null)
Indent.getNormalIndent(true)
else
Indent.getNoneIndent()
val block = SDBlock(child, spacingBuilder, indent)
result.add(block)
}
child = child.treeNext
}
return result
}

override fun getIndent() = indent

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

override fun isLeaf() = myNode.firstChildNode == null

companion object {
private val INDENT_TOKEN_TYPES = TokenSet.create(
BLOCK_COMMENT,
BLOCK_STATEMENT,
EXPLICIT_RELATIONSHIP,
IDENTIFIER_REFERENCES,
IMPLICIT_RELATIONSHIP,
KEY_VALUE_PAIR,
LINE_COMMENT,
PROPERTY_BLOCK_STATEMENT,
SCRIPT_BLOCK,
SINGLE_LINE_STATEMENT,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package nl.dirkgroot.structurizr.dsl.lang

import com.intellij.formatting.*
import com.intellij.psi.codeStyle.CodeStyleSettings
import nl.dirkgroot.structurizr.dsl.StructurizrDSLLanguage
import nl.dirkgroot.structurizr.dsl.psi.SDTypes

class SDFormattingModelBuilder : FormattingModelBuilder {
override fun createModel(formattingContext: FormattingContext): FormattingModel =
FormattingModelProvider.createFormattingModelForPsiFile(
formattingContext.containingFile,
SDBlock(formattingContext.node, createSpacingBuilder(formattingContext.codeStyleSettings)),
formattingContext.codeStyleSettings
)

private fun createSpacingBuilder(codeStyleSettings: CodeStyleSettings) =
SpacingBuilder(codeStyleSettings, StructurizrDSLLanguage)
.around(SDTypes.EQUALS).spaces(1)
.around(SDTypes.IDENTIFIER_NAME).spaces(1)
.before(SDTypes.ARGUMENT).spaces(1)
.before(SDTypes.BRACE1).spaces(1)
.between(SDTypes.KEY, SDTypes.VALUE).spaces(1)
}
2 changes: 2 additions & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
implementationClass="nl.dirkgroot.structurizr.dsl.syntax.StructurizrDSLSyntaxHighlighterFactory"/>
<lang.parserDefinition language="StructurizrDSL"
implementationClass="nl.dirkgroot.structurizr.dsl.lang.StructurizrDSLParserDefinition"/>
<lang.formatter language="StructurizrDSL"
implementationClass="nl.dirkgroot.structurizr.dsl.lang.SDFormattingModelBuilder"/>
</extensions>

<actions>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package nl.dirkgroot.structurizr.dsl.editing.formatter

import com.intellij.openapi.command.WriteCommandAction
import com.intellij.psi.codeStyle.CodeStyleManager
import nl.dirkgroot.structurizr.dsl.support.StructurizrDSLCodeInsightTest

abstract class FormatterTest : StructurizrDSLCodeInsightTest() {
protected fun assertFormattingResult(code: String, expectedResult: String) {
val file = fixture.addFileToProject("test.dsl", code)
fixture.openFileInEditor(file.virtualFile)

WriteCommandAction.writeCommandAction(project).run<Exception> {
CodeStyleManager.getInstance(project)
.reformatText(fixture.file, listOf(fixture.file.textRange))
}
fixture.checkResult(expectedResult)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
package nl.dirkgroot.structurizr.dsl.editing.formatter

import org.junit.jupiter.api.Test

class IndentationTest : FormatterTest() {
@Test
fun `block with single line element`() {
assertFormattingResult(
"""
softwareSystem name description {
container Backend
}
""".trimIndent(),
"""
softwareSystem name description {
container Backend
}
""".trimIndent(),
)
}

@Test
fun `nested blocks`() {
assertFormattingResult(
"""
softwareSystem name description {
container Backend {
component RestAPI
}
}
""".trimIndent(),
"""
softwareSystem name description {
container Backend {
component RestAPI
}
}
""".trimIndent(),
)
}

@Test
fun `property block`() {
assertFormattingResult(
"""
properties {
a b
c d
}
""".trimIndent(),
"""
properties {
a b
c d
}
""".trimIndent(),
)
}

@Test
fun `nested property block`() {
assertFormattingResult(
"""
softwareSystem system {
properties {
a b
c d
}
}
""".trimIndent(),
"""
softwareSystem system {
properties {
a b
c d
}
}
""".trimIndent(),
)
}

@Test
fun `block with assigned identifier`() {
assertFormattingResult(
"""
systemIdentifier = softwareSystem system {
container backend
}
""".trimIndent(),
"""
systemIdentifier = softwareSystem system {
container backend
}
""".trimIndent(),
)
}

@Test
fun `script block`() {
assertFormattingResult(
"""
!script kotlin {
println("Hello World")
}
""".trimIndent(),
"""
!script kotlin {
println("Hello World")
}
""".trimIndent(),
)
}

@Test
fun `nested script block`() {
assertFormattingResult(
"""
workspace {
!script kotlin {
println("Hello World")
}
}
""".trimIndent(),
"""
workspace {
!script kotlin {
println("Hello World")
}
}
""".trimIndent(),
)
}

@Test
fun `explicit relationships`() {
assertFormattingResult(
"""
model {
a = softwareSystem A
b = softwareSystem B
a -> b Uses
}
""".trimIndent(),
"""
model {
a = softwareSystem A
b = softwareSystem B
a -> b Uses
}
""".trimIndent()
)
}

@Test
fun `implicit relationships`() {
assertFormattingResult(
"""
model {
a = softwareSystem A
b = softwareSystem B {
-> a Uses
}
}
""".trimIndent(),
"""
model {
a = softwareSystem A
b = softwareSystem B {
-> a Uses
}
}
""".trimIndent()
)
}

@Test
fun `line comments`() {
assertFormattingResult(
"""
model {
# comment
}
""".trimIndent(),
"""
model {
# comment
}
""".trimIndent()
)
}

@Test
fun `block comments`() {
assertFormattingResult(
"""
model {
/*
comment
*/
}
""".trimIndent(),
"""
model {
/*
comment
*/
}
""".trimIndent()
)
}

@Test
fun `identifier references`() {
assertFormattingResult(
"""
animation {
a b c
d
}
""".trimIndent(),
"""
animation {
a b c
d
}
""".trimIndent()
)
}
}
Loading