Skip to content

Commit

Permalink
Merge pull request #35 from dirkgroot/add-auto-formatting
Browse files Browse the repository at this point in the history
Add support for IntelliJ's "Reformat Code" command
  • Loading branch information
dirkgroot authored Mar 31, 2023
2 parents 0093f04 + d7b9a69 commit 926f328
Show file tree
Hide file tree
Showing 18 changed files with 430 additions and 18 deletions.
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

0 comments on commit 926f328

Please sign in to comment.