Skip to content

Commit 3e3fce3

Browse files
committed
Implement formatting support for nested structures using the else directive block.
1 parent 3037f58 commit 3e3fce3

File tree

12 files changed

+222
-71
lines changed

12 files changed

+222
-71
lines changed

src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlBlock.kt

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ open class SqlBlock(
6060

6161
open var parentBlock: SqlBlock? = null
6262
open val childBlocks = mutableListOf<SqlBlock>()
63+
open var prevBlocks = emptyList<SqlBlock>()
6364

6465
fun getChildrenTextLen(): Int =
6566
childBlocks.sumOf { child ->
@@ -99,6 +100,7 @@ open class SqlBlock(
99100

100101
open fun setParentGroupBlock(lastGroup: SqlBlock?) {
101102
parentBlock = lastGroup
103+
prevBlocks = parentBlock?.childBlocks?.toList() ?: emptyList()
102104
parentBlock?.addChildBlock(this)
103105
setParentPropertyBlock(lastGroup)
104106
}
@@ -108,21 +110,27 @@ open class SqlBlock(
108110
}
109111

110112
open fun addChildBlock(childBlock: SqlBlock) {
111-
childBlocks.add(childBlock)
113+
if (!childBlocks.contains(childBlock)) {
114+
childBlocks.add(childBlock)
115+
}
112116
}
113117

114118
fun getNodeText() = node.text.lowercase()
115119

116120
fun isEnableFormat(): Boolean = enableFormat
117121

118-
open fun isSaveSpace(lastGroup: SqlBlock?): Boolean =
122+
open fun isSaveSpace(lastGroup: SqlBlock?): Boolean {
119123
parentBlock?.let { parent ->
120124
if (parent is SqlElConditionLoopCommentBlock) {
121-
parent.childBlocks.dropLast(1).isEmpty()
122-
} else {
123-
false
125+
val prevBlock =
126+
prevBlocks.lastOrNull { it !is SqlLineCommentBlock && it !is SqlBlockCommentBlock }
127+
return prevBlock is SqlElConditionLoopCommentBlock &&
128+
(prevBlock.conditionType.isElse() || prevBlock.conditionType.isEnd()) ||
129+
parent.childBlocks.dropLast(1).isEmpty()
124130
}
125-
} == true
131+
}
132+
return false
133+
}
126134

127135
/**
128136
* Creates the indentation length for the block.

src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlFileBlock.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ class SqlFileBlock(
101101
updateCommentParentAndIdent(childBlock)
102102
updateBlockParentAndLAddGroup(childBlock)
103103
updateWhiteSpaceInclude(lastBlock, childBlock, lastGroup)
104+
// TODO After processing the END directive block,
105+
// if there is only one element (with two or fewer spaces),
106+
// remove the line breaks within the if~end block and consolidate it into a single line.
104107
blocks.add(childBlock)
105108
} else {
106109
if (lastBlock !is SqlLineCommentBlock) {

src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlKeywordBlock.kt

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package org.domaframework.doma.intellij.formatter.block
1818
import com.intellij.lang.ASTNode
1919
import com.intellij.psi.formatter.common.AbstractBlock
2020
import org.domaframework.doma.intellij.formatter.block.conflict.SqlDoGroupBlock
21+
import org.domaframework.doma.intellij.formatter.block.expr.SqlElConditionLoopCommentBlock
2122
import org.domaframework.doma.intellij.formatter.block.group.SqlNewGroupBlock
2223
import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock
2324
import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithCommonTableGroupBlock
@@ -69,31 +70,39 @@ open class SqlKeywordBlock(
6970
override fun createBlockIndentLen(): Int =
7071
when (indentLevel) {
7172
IndentType.TOP -> {
72-
parentBlock?.let {
73-
if (it.indent.indentLevel == IndentType.SUB) {
74-
it.indent.groupIndentLen.plus(1)
73+
parentBlock?.let { parent ->
74+
if (parent.indent.indentLevel == IndentType.SUB) {
75+
parent.indent.groupIndentLen.plus(1)
7576
} else {
7677
0
7778
}
7879
} ?: 0
7980
}
8081

8182
IndentType.SECOND -> {
82-
parentBlock?.let {
83-
it.indent.groupIndentLen
84-
.plus(it.getNodeText().length)
85-
.minus(this.getNodeText().length)
83+
parentBlock?.let { parent ->
84+
parent.indent.groupIndentLen
85+
.plus(parent.getNodeText().length)
86+
.minus(getNodeText().length)
8687
} ?: 1
8788
}
8889

8990
IndentType.INLINE_SECOND -> {
90-
parentBlock?.let {
91-
it.indent.groupIndentLen
92-
.plus(it.getNodeText().length)
91+
parentBlock?.let { parent ->
92+
parent.indent.groupIndentLen
93+
.plus(parent.getNodeText().length)
9394
.plus(1)
9495
} ?: 1
9596
}
9697

97-
else -> 1
98+
else -> {
99+
parentBlock?.let { parent ->
100+
if (parent is SqlElConditionLoopCommentBlock) {
101+
parent.indent.groupIndentLen
102+
} else {
103+
1
104+
}
105+
} ?: 1
106+
}
98107
}
99108
}

src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElConditionLoopCommentBlock.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ class SqlElConditionLoopCommentBlock(
6868
fun isElse(): Boolean = this == ELSE
6969
}
7070

71+
var tempParentBlock: SqlBlock? = null
7172
val conditionType: SqlConditionLoopCommentBlockType = initConditionOrLoopType(node)
7273
var conditionStart: SqlElConditionLoopCommentBlock? = null
7374
var conditionEnd: SqlElConditionLoopCommentBlock? = null
@@ -114,7 +115,7 @@ class SqlElConditionLoopCommentBlock(
114115
indent.groupIndentLen = createGroupIndentLen()
115116

116117
childBlocks.forEach { child ->
117-
if (child is SqlElConditionLoopCommentBlock) {
118+
if (child is SqlElConditionLoopCommentBlock && child.conditionType.isStartDirective()) {
118119
// If the child is a condition loop directive, align its indentation with the parent directive
119120
child.indent.indentLen = indent.indentLen.plus(2)
120121
} else if (child is SqlLineCommentBlock) {

src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlSubGroupBlock.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ abstract class SqlSubGroupBlock(
8080
if (childBlocks.isEmpty()) {
8181
isFirstLineComment = childBlock is SqlCommentBlock
8282
}
83-
childBlocks.add(childBlock)
83+
super.addChildBlock(childBlock)
8484
}
8585

8686
override fun buildChildren(): MutableList<AbstractBlock> = mutableListOf()

src/main/kotlin/org/domaframework/doma/intellij/formatter/builder/SqlBlockBuilder.kt

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,6 @@ open class SqlBlockBuilder {
5555
commentBlocks.add(block)
5656
}
5757

58-
/**
59-
* For condition/loop directive blocks
60-
*
61-
* determine the parent based on the type of block immediately below.
62-
*/
6358
fun updateCommentBlockIndent(baseIndent: SqlBlock) {
6459
if (commentBlocks.isNotEmpty()) {
6560
var index = 0
@@ -84,8 +79,15 @@ open class SqlBlockBuilder {
8479
}
8580
}
8681

87-
fun updateConditionLoopBlockIndent(baseIndent: SqlBlock) {
88-
if (!isExpectedClassType(updateDirectiveParentTypes, baseIndent)) {
82+
/**
83+
* For condition/loop directive blocks
84+
* determine the parent based on the type of block immediately below.
85+
*
86+
* When this process is invoked, a directive block has already been added to [groupTopNodeIndexHistory],
87+
* and the parent of the block passed as [nextBlock] has already been determined.
88+
*/
89+
fun updateConditionLoopBlockIndent(nextBlock: SqlBlock) {
90+
if (!isExpectedClassType(updateDirectiveParentTypes, nextBlock)) {
8991
if (conditionOrLoopBlocks.isNotEmpty()) {
9092
val lastGroup = groupTopNodeIndexHistory.lastOrNull()
9193
conditionOrLoopBlocks
@@ -100,35 +102,36 @@ open class SqlBlockBuilder {
100102
null
101103
}
102104
// Prioritize previous condition loop block over keyword group
103-
if (prevConditionBlockGroup is SqlElConditionLoopCommentBlock) {
104-
setParentBlock = prevConditionBlockGroup
105-
} else if (prevConditionBlockGroup?.parentBlock is SqlElConditionLoopCommentBlock) {
106-
setParentBlock = prevConditionBlockGroup.parentBlock
107-
} else if (lastGroup == baseIndent) {
105+
// if (prevConditionBlockGroup is SqlElConditionLoopCommentBlock) {
106+
// setParentBlock = prevConditionBlockGroup
107+
// } else if (prevConditionBlockGroup?.parentBlock is SqlElConditionLoopCommentBlock) {
108+
// setParentBlock = prevConditionBlockGroup.parentBlock
109+
// } else
110+
if (lastGroup == nextBlock) {
108111
setParentBlock =
109112
if (isExpectedClassType(
110113
originalConditionLoopDirectiveParentType,
111-
baseIndent,
114+
nextBlock,
112115
)
113116
) {
114-
baseIndent
117+
nextBlock
115118
} else {
116-
prevConditionBlockGroup
119+
null
117120
}
118121
} else {
119122
setParentBlock =
120123
if (isExpectedClassType(
121124
originalConditionLoopDirectiveParentType,
122-
baseIndent,
125+
nextBlock,
123126
)
124127
) {
125-
baseIndent
128+
nextBlock
126129
} else {
127130
prevConditionBlockGroup
128131
}
129132
}
130133

131-
if (block != baseIndent) {
134+
if (block != nextBlock) {
132135
block.setParentGroupBlock(setParentBlock)
133136
} else if (setParentBlock is SqlElConditionLoopCommentBlock) {
134137
block.setParentGroupBlock(setParentBlock)
@@ -159,10 +162,8 @@ open class SqlBlockBuilder {
159162
condition(it)
160163
}
161164

162-
fun getConditionOrLoopBlocksLast(): SqlElConditionLoopCommentBlock? = conditionOrLoopBlocks.lastOrNull()
163-
164165
fun addConditionOrLoopBlock(block: SqlElConditionLoopCommentBlock) {
165-
if (block.conditionType.isStartDirective()) {
166+
if (block.conditionType.isStartDirective() || block.conditionType.isElse()) {
166167
conditionOrLoopBlocks.add(block)
167168
}
168169
}

src/main/kotlin/org/domaframework/doma/intellij/formatter/processor/SqlSetParentGroupProcessor.kt

Lines changed: 67 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,36 @@ class SqlSetParentGroupProcessor(
117117
blockBuilder,
118118
)
119119

120-
if (lastGroupBlock is SqlElConditionLoopCommentBlock && lastGroupBlock.parentBlock != null) {
121-
setParentGroups(context) { history ->
122-
return@setParentGroups lastGroupBlock
120+
if (lastGroupBlock is SqlElConditionLoopCommentBlock) {
121+
if (lastGroupBlock.parentBlock != null) {
122+
setParentGroups(context) { history ->
123+
return@setParentGroups lastGroupBlock
124+
}
125+
} else {
126+
val history = blockBuilder.getGroupTopNodeIndexHistory()
127+
val findParent =
128+
history
129+
.lastOrNull {
130+
it.indent.indentLevel < childBlock.indent.indentLevel ||
131+
(it is SqlElConditionLoopCommentBlock && it.parentBlock != null)
132+
}
133+
// Search for keyword groups with a level lower than or equal to the current level,
134+
// or conditional directives that already have a parent assigned.
135+
if (findParent is SqlElConditionLoopCommentBlock) {
136+
// Set the parent of the most recent conditional directive to the current node.
137+
lastGroupBlock.setParentGroupBlock(findParent)
138+
setParentGroups(context) { history ->
139+
return@setParentGroups lastGroupBlock
140+
}
141+
}
142+
if (findParent !is SqlElConditionLoopCommentBlock) {
143+
// If a keyword group with a level lower than or equal to the current level is found,
144+
// set that keyword group as the parent, and set the parent of the most recent conditional directive to the current node.
145+
setParentGroups(context) { history ->
146+
return@setParentGroups findParent
147+
}
148+
lastGroupBlock.setParentGroupBlock(childBlock)
149+
}
123150
}
124151
return
125152
}
@@ -223,7 +250,9 @@ class SqlSetParentGroupProcessor(
223250
} else {
224251
setParentGroups(context) { history ->
225252
return@setParentGroups history
226-
.lastOrNull { it.indent.indentLevel < childBlock.indent.indentLevel }
253+
.lastOrNull {
254+
it.indent.indentLevel < childBlock.indent.indentLevel
255+
}
227256
}
228257
}
229258
}
@@ -307,36 +336,45 @@ class SqlSetParentGroupProcessor(
307336
lastGroupBlock: SqlBlock,
308337
childBlock: SqlElConditionLoopCommentBlock,
309338
) {
310-
if (lastGroupBlock is SqlCommaBlock) {
311-
blockBuilder.removeLastGroupTopNodeIndexHistory()
312-
}
313-
setParentGroups(
339+
val context =
314340
SetParentContext(
315341
childBlock,
316342
blockBuilder,
317-
),
318-
) { history ->
319-
if (childBlock.conditionType.isElse()) {
320-
val lastConditionLoopCommentBlock =
321-
blockBuilder.getConditionOrLoopBlocksLast()
322-
return@setParentGroups lastConditionLoopCommentBlock
323-
}
324-
if (childBlock.conditionType.isEnd()) {
325-
val lastConditionLoopCommentBlock =
326-
blockBuilder.getConditionOrLoopBlocksLast()
343+
)
344+
345+
if (lastGroupBlock is SqlCommaBlock) {
346+
blockBuilder.removeLastGroupTopNodeIndexHistory()
347+
}
348+
setParentGroups(context) { history ->
349+
if (childBlock.conditionType.isEnd() || childBlock.conditionType.isElse()) {
350+
// remove self and previous conditional directive block
351+
blockBuilder.removeConditionOrLoopBlockLast()
327352
blockBuilder.removeConditionOrLoopBlockLast()
328353

329354
val directiveIndex =
330355
blockBuilder
331-
.getGroupTopNodeIndex { it is SqlElConditionLoopCommentBlock && it.conditionType.isStartDirective() }
356+
.getGroupTopNodeIndex { it is SqlElConditionLoopCommentBlock && it != childBlock }
332357
if (directiveIndex >= 0) {
358+
val lastConditionLoopCommentBlock =
359+
blockBuilder.getGroupTopNodeIndexHistory()[directiveIndex]
333360
blockBuilder.clearSubListGroupTopNodeIndexHistory(directiveIndex)
361+
return@setParentGroups lastConditionLoopCommentBlock
334362
}
335-
336-
return@setParentGroups lastConditionLoopCommentBlock
363+
return@setParentGroups null
337364
}
338365

339-
return@setParentGroups null
366+
// If the most recent block is a conditional directive, set it as the parent block.
367+
if (lastGroupBlock is SqlElConditionLoopCommentBlock) {
368+
val prevGroupIndex = blockBuilder.getGroupTopNodeIndex { it == lastGroupBlock }
369+
if (prevGroupIndex > 0 && lastGroupBlock.parentBlock == null) {
370+
// Determine the parent of the most recent conditional directive.
371+
val prevGroup = blockBuilder.getGroupTopNodeIndexHistory()[prevGroupIndex - 1]
372+
lastGroupBlock.setParentGroupBlock(prevGroup)
373+
}
374+
return@setParentGroups lastGroupBlock
375+
}
376+
// Temporary Parent Block
377+
return@setParentGroups history.lastOrNull()
340378
}
341379
}
342380

@@ -405,9 +443,12 @@ class SqlSetParentGroupProcessor(
405443
val targetChildBlock = context.childBlock
406444

407445
// The parent block for SqlElConditionLoopCommentBlock will be set later
408-
if (targetChildBlock !is SqlElConditionLoopCommentBlock ||
409-
!targetChildBlock.conditionType.isStartDirective()
410-
) {
446+
if (targetChildBlock is SqlElConditionLoopCommentBlock && targetChildBlock.conditionType.isStartDirective()) {
447+
targetChildBlock.tempParentBlock = parentGroup
448+
if (parentGroup is SqlElConditionLoopCommentBlock && parentGroup.parentBlock != null) {
449+
targetChildBlock.setParentGroupBlock(parentGroup)
450+
}
451+
} else {
411452
targetChildBlock.setParentGroupBlock(parentGroup)
412453
}
413454

@@ -433,7 +474,7 @@ class SqlSetParentGroupProcessor(
433474
blockBuilder: SqlBlockBuilder,
434475
): Boolean {
435476
if (childBlock is SqlElConditionLoopCommentBlock) {
436-
if (childBlock.conditionType.isStartDirective()) {
477+
if (childBlock.conditionType.isStartDirective() || childBlock.conditionType.isElse()) {
437478
return true
438479
}
439480
}

src/test/kotlin/org/domaframework/doma/intellij/formatter/SqlFormatterTest.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,10 @@ class SqlFormatterTest : BasePlatformTestCase() {
166166
formatSqlFile("WithDelete.sql", "WithDelete$formatDataPrefix.sql")
167167
}
168168

169+
fun testNestedDirectivesFormatter() {
170+
formatSqlFile("NestedDirectives.sql", "NestedDirectives$formatDataPrefix.sql")
171+
}
172+
169173
private fun formatSqlFile(
170174
beforeFile: String,
171175
afterFile: String,

0 commit comments

Comments
 (0)