Skip to content

Commit 28c32fd

Browse files
authored
Merge pull request #323 from domaframework/feature/sql-format-nest-directive
Indentation Adjustment for Nested Conditional / Loop Directives
2 parents f3f3b7f + 3821fe7 commit 28c32fd

File tree

51 files changed

+1465
-636
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1465
-636
lines changed

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

Lines changed: 75 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,15 @@ import com.intellij.formatting.SpacingBuilder
2424
import com.intellij.formatting.Wrap
2525
import com.intellij.lang.ASTNode
2626
import com.intellij.psi.formatter.common.AbstractBlock
27-
import org.domaframework.doma.intellij.formatter.block.comment.SqlBlockCommentBlock
28-
import org.domaframework.doma.intellij.formatter.block.comment.SqlLineCommentBlock
27+
import org.domaframework.doma.intellij.formatter.block.comment.SqlCommentBlock
28+
import org.domaframework.doma.intellij.formatter.block.comment.SqlDefaultCommentBlock
29+
import org.domaframework.doma.intellij.formatter.block.comment.SqlElConditionLoopCommentBlock
30+
import org.domaframework.doma.intellij.formatter.block.group.SqlNewGroupBlock
2931
import org.domaframework.doma.intellij.formatter.builder.SqlCustomSpacingBuilder
3032
import org.domaframework.doma.intellij.formatter.util.IndentType
33+
import org.domaframework.doma.intellij.formatter.util.SqlKeywordUtil
3134
import org.domaframework.doma.intellij.psi.SqlTypes
35+
import org.jetbrains.kotlin.psi.psiUtil.startOffset
3236

3337
open class SqlBlock(
3438
node: ASTNode,
@@ -59,32 +63,31 @@ open class SqlBlock(
5963

6064
open var parentBlock: SqlBlock? = null
6165
open val childBlocks = mutableListOf<SqlBlock>()
66+
open var prevBlocks = emptyList<SqlBlock>()
6267

63-
fun getChildrenTextLen(): Int =
64-
childBlocks.sumOf { child ->
65-
val children =
66-
child.childBlocks.filter { it !is SqlLineCommentBlock && it !is SqlBlockCommentBlock }
67-
if (children.isNotEmpty()) {
68-
child
69-
.getChildrenTextLen()
70-
.plus(child.getNodeText().length)
71-
} else if (child.node.elementType == SqlTypes.DOT ||
72-
child.node.elementType == SqlTypes.RIGHT_PAREN
73-
) {
74-
// Since elements do not include surrounding spaces, they should be excluded from the character count.
75-
0
76-
} else {
77-
child.getNodeText().length.plus(1)
78-
}
68+
fun getChildrenTextLen(): Int = childBlocks.sumOf { child -> calculateChildTextLength(child) }
69+
70+
private fun calculateChildTextLength(child: SqlBlock): Int {
71+
val nonCommentChildren = child.childBlocks.filterNot { it is SqlDefaultCommentBlock }
72+
73+
if (nonCommentChildren.isNotEmpty()) {
74+
return child.getChildrenTextLen() + child.getNodeText().length
7975
}
76+
if (isExcludedFromTextLength(child)) {
77+
return 0
78+
}
79+
return child.getNodeText().length + 1
80+
}
81+
82+
private fun isExcludedFromTextLength(block: SqlBlock): Boolean = block.node.elementType in setOf(SqlTypes.DOT, SqlTypes.RIGHT_PAREN)
8083

8184
fun getChildBlocksDropLast(
8285
dropIndex: Int = 1,
83-
skipCommentBlock: Boolean = false,
86+
skipCommentBlock: Boolean = true,
8487
): List<SqlBlock> {
8588
val children = childBlocks.dropLast(dropIndex)
8689
if (skipCommentBlock) {
87-
return children.filter { it !is SqlLineCommentBlock && it !is SqlBlockCommentBlock }
90+
return children.filter { it !is SqlDefaultCommentBlock }
8891
}
8992
return children
9093
}
@@ -98,6 +101,7 @@ open class SqlBlock(
98101

99102
open fun setParentGroupBlock(lastGroup: SqlBlock?) {
100103
parentBlock = lastGroup
104+
prevBlocks = parentBlock?.childBlocks?.toList() ?: emptyList()
101105
parentBlock?.addChildBlock(this)
102106
setParentPropertyBlock(lastGroup)
103107
}
@@ -107,14 +111,58 @@ open class SqlBlock(
107111
}
108112

109113
open fun addChildBlock(childBlock: SqlBlock) {
110-
childBlocks.add(childBlock)
114+
if (!childBlocks.contains(childBlock)) {
115+
childBlocks.add(childBlock)
116+
}
111117
}
112118

113119
fun getNodeText() = node.text.lowercase()
114120

115121
fun isEnableFormat(): Boolean = enableFormat
116122

117-
open fun isSaveSpace(lastGroup: SqlBlock?): Boolean = false
123+
open fun isSaveSpace(lastGroup: SqlBlock?): Boolean =
124+
parentBlock?.let { parent ->
125+
when (parent) {
126+
is SqlElConditionLoopCommentBlock -> shouldSaveSpaceForConditionLoop(parent)
127+
is SqlNewGroupBlock -> shouldSaveSpaceForNewGroup(parent)
128+
else -> false
129+
}
130+
} == true
131+
132+
private fun shouldSaveSpaceForConditionLoop(parent: SqlElConditionLoopCommentBlock): Boolean {
133+
val prevBlock = prevBlocks.lastOrNull { it !is SqlDefaultCommentBlock }
134+
val isPrevBlockElseOrEnd =
135+
prevBlock is SqlElConditionLoopCommentBlock &&
136+
(prevBlock.conditionType.isElse() || prevBlock.conditionType.isEnd())
137+
val hasNoChildrenExceptLast = parent.childBlocks.dropLast(1).isEmpty()
138+
139+
return isPrevBlockElseOrEnd || hasNoChildrenExceptLast
140+
}
141+
142+
private fun shouldSaveSpaceForNewGroup(parent: SqlNewGroupBlock): Boolean {
143+
val prevWord = prevBlocks.lastOrNull { it !is SqlCommentBlock }
144+
145+
if (isNonBreakingKeywordCombination(parent, prevWord)) {
146+
return false
147+
}
148+
149+
return isFollowedByConditionLoop() || isPrecededByConditionLoop(parent)
150+
}
151+
152+
private fun isNonBreakingKeywordCombination(
153+
parent: SqlNewGroupBlock,
154+
prevWord: SqlBlock?,
155+
): Boolean =
156+
SqlKeywordUtil.isSetLineKeyword(getNodeText(), parent.getNodeText()) ||
157+
SqlKeywordUtil.isSetLineKeyword(getNodeText(), prevWord?.getNodeText() ?: "")
158+
159+
private fun isFollowedByConditionLoop(): Boolean = childBlocks.lastOrNull() is SqlElConditionLoopCommentBlock
160+
161+
private fun isPrecededByConditionLoop(parent: SqlNewGroupBlock): Boolean {
162+
val lastPrevBlock = prevBlocks.lastOrNull()
163+
return lastPrevBlock is SqlElConditionLoopCommentBlock &&
164+
lastPrevBlock.node.psi.startOffset > parent.node.psi.startOffset
165+
}
118166

119167
/**
120168
* Creates the indentation length for the block.
@@ -157,11 +205,15 @@ open class SqlBlock(
157205
*/
158206
override fun getChildIndent(): Indent? =
159207
if (isEnableFormat()) {
160-
Indent.getSpaceIndent(4)
208+
Indent.getSpaceIndent(DEFAULT_INDENT_SIZE)
161209
} else {
162210
Indent.getSpaceIndent(0)
163211
}
164212

213+
companion object {
214+
private const val DEFAULT_INDENT_SIZE = 4
215+
}
216+
165217
/**
166218
* Determines whether the block is a leaf node.
167219
*/

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

Lines changed: 2 additions & 0 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.common.util.TypeUtil
21+
import org.domaframework.doma.intellij.formatter.block.comment.SqlElConditionLoopCommentBlock
2122
import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnRawGroupBlock
2223
import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock
2324
import org.domaframework.doma.intellij.formatter.block.group.keyword.condition.SqlConditionalExpressionGroupBlock
@@ -58,6 +59,7 @@ open class SqlCommaBlock(
5859
SqlFunctionParamBlock::class,
5960
SqlWithColumnGroupBlock::class,
6061
SqlKeywordGroupBlock::class,
62+
SqlElConditionLoopCommentBlock::class,
6163
)
6264

6365
private val PARENT_INDENT_SYNC_TYPES =

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

Lines changed: 78 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@ import com.intellij.formatting.Wrap
2626
import com.intellij.lang.ASTNode
2727
import com.intellij.psi.PsiWhiteSpace
2828
import com.intellij.psi.formatter.common.AbstractBlock
29-
import org.domaframework.doma.intellij.formatter.block.comment.SqlBlockCommentBlock
29+
import org.domaframework.doma.intellij.common.util.TypeUtil
3030
import org.domaframework.doma.intellij.formatter.block.comment.SqlCommentBlock
31+
import org.domaframework.doma.intellij.formatter.block.comment.SqlDefaultCommentBlock
32+
import org.domaframework.doma.intellij.formatter.block.comment.SqlElBlockCommentBlock
33+
import org.domaframework.doma.intellij.formatter.block.comment.SqlElConditionLoopCommentBlock
3134
import org.domaframework.doma.intellij.formatter.block.comment.SqlLineCommentBlock
32-
import org.domaframework.doma.intellij.formatter.block.expr.SqlElBlockCommentBlock
33-
import org.domaframework.doma.intellij.formatter.block.expr.SqlElConditionLoopCommentBlock
3435
import org.domaframework.doma.intellij.formatter.block.expr.SqlElSymbolBlock
3536
import org.domaframework.doma.intellij.formatter.block.group.SqlNewGroupBlock
3637
import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnBlock
@@ -50,15 +51,17 @@ import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGrou
5051
import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubQueryGroupBlock
5152
import org.domaframework.doma.intellij.formatter.block.other.SqlEscapeBlock
5253
import org.domaframework.doma.intellij.formatter.block.other.SqlOtherBlock
54+
import org.domaframework.doma.intellij.formatter.block.word.SqlAliasBlock
5355
import org.domaframework.doma.intellij.formatter.block.word.SqlFunctionGroupBlock
56+
import org.domaframework.doma.intellij.formatter.block.word.SqlTableBlock
5457
import org.domaframework.doma.intellij.formatter.block.word.SqlWordBlock
5558
import org.domaframework.doma.intellij.formatter.builder.SqlBlockBuilder
5659
import org.domaframework.doma.intellij.formatter.builder.SqlCustomSpacingBuilder
60+
import org.domaframework.doma.intellij.formatter.handler.CreateClauseHandler
5761
import org.domaframework.doma.intellij.formatter.processor.SqlSetParentGroupProcessor
58-
import org.domaframework.doma.intellij.formatter.util.CreateTableUtil
5962
import org.domaframework.doma.intellij.formatter.util.IndentType
6063
import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext
61-
import org.domaframework.doma.intellij.formatter.util.SqlBlockUtil
64+
import org.domaframework.doma.intellij.formatter.util.SqlBlockGenerator
6265
import org.domaframework.doma.intellij.psi.SqlTypes
6366

6467
class SqlFileBlock(
@@ -77,11 +80,22 @@ class SqlFileBlock(
7780
enableFormat,
7881
formatMode,
7982
) {
83+
override val indent =
84+
ElementIndent(
85+
IndentType.FILE,
86+
0,
87+
0,
88+
)
89+
90+
override fun setParentGroupBlock(lastGroup: SqlBlock?) {
91+
super.setParentGroupBlock(null)
92+
}
93+
8094
private val blocks = mutableListOf<AbstractBlock>()
8195

8296
private val blockBuilder = SqlBlockBuilder()
8397
private val parentSetProcessor = SqlSetParentGroupProcessor(blockBuilder)
84-
private val blockUtil = SqlBlockUtil(this, isEnableFormat(), formatMode)
98+
private val blockUtil = SqlBlockGenerator(this, isEnableFormat(), formatMode)
8599

86100
private val pendingCommentBlocks = mutableListOf<SqlBlock>()
87101

@@ -251,7 +265,11 @@ class SqlFileBlock(
251265
commentBlock,
252266
)
253267
} else {
254-
blockBuilder.addCommentBlock(commentBlock)
268+
(commentBlock as? SqlDefaultCommentBlock)?.let {
269+
blockBuilder.addCommentBlock(
270+
commentBlock,
271+
)
272+
}
255273
}
256274
}
257275

@@ -295,6 +313,8 @@ class SqlFileBlock(
295313
return
296314
}
297315

316+
if (childBlock is SqlDefaultCommentBlock) return
317+
298318
when (childBlock) {
299319
is SqlKeywordGroupBlock -> {
300320
parentSetProcessor.updateKeywordGroupBlockParentAndAddGroup(
@@ -345,7 +365,7 @@ class SqlFileBlock(
345365
)
346366
}
347367

348-
is SqlWordBlock, is SqlOtherBlock, is SqlLineCommentBlock, is SqlBlockCommentBlock -> {
368+
is SqlWordBlock, is SqlOtherBlock -> {
349369
parentSetProcessor.updateGroupBlockParentAndAddGroup(
350370
childBlock,
351371
)
@@ -440,6 +460,25 @@ class SqlFileBlock(
440460
return SqlCustomSpacingBuilder().getSpacing(childBlock2)
441461
}
442462

463+
if (childBlock1 is SqlWhitespaceBlock && childBlock2.parentBlock is SqlElConditionLoopCommentBlock) {
464+
val child1 = childBlock2.parentBlock as SqlElConditionLoopCommentBlock
465+
SqlCustomSpacingBuilder()
466+
.getSpacingElDirectiveComment(child1, childBlock2)
467+
?.let { return it }
468+
}
469+
470+
if (childBlock1 is SqlElBlockCommentBlock && childBlock2 !is SqlRightPatternBlock) {
471+
SqlCustomSpacingBuilder()
472+
.getSpacingElDirectiveComment(childBlock1, childBlock2)
473+
?.let { return it }
474+
}
475+
476+
if (childBlock2 is SqlRightPatternBlock) {
477+
return SqlCustomSpacingBuilder().getSpacingRightPattern(
478+
childBlock2,
479+
)
480+
}
481+
443482
if (childBlock2 is SqlWithColumnGroupBlock) {
444483
return SqlCustomSpacingBuilder.normalSpacing
445484
}
@@ -454,8 +493,15 @@ class SqlFileBlock(
454493
}
455494

456495
if (childBlock2 is SqlElBlockCommentBlock) {
496+
if (TypeUtil.isExpectedClassType(
497+
SqlRightPatternBlock.NOT_INDENT_EXPECTED_TYPES,
498+
childBlock1,
499+
)
500+
) {
501+
return SqlCustomSpacingBuilder.nonSpacing
502+
}
457503
return when (childBlock1) {
458-
is SqlElBlockCommentBlock, is SqlWhitespaceBlock -> {
504+
is SqlWhitespaceBlock -> {
459505
SqlCustomSpacingBuilder().getSpacing(childBlock2)
460506
}
461507

@@ -488,14 +534,6 @@ class SqlFileBlock(
488534
return SqlCustomSpacingBuilder().getSpacing(childBlock2)
489535
}
490536

491-
if (childBlock1 is SqlWhitespaceBlock) {
492-
when (childBlock2) {
493-
is SqlBlockCommentBlock, is SqlLineCommentBlock, is SqlNewGroupBlock -> {
494-
return SqlCustomSpacingBuilder().getSpacing(childBlock2)
495-
}
496-
}
497-
}
498-
499537
if (childBlock2 is SqlNewGroupBlock) {
500538
if (childBlock1 is SqlSubGroupBlock && childBlock2.indent.indentLevel == IndentType.ATTACHED) {
501539
return SqlCustomSpacingBuilder.nonSpacing
@@ -509,12 +547,14 @@ class SqlFileBlock(
509547

510548
is SqlDataTypeParamBlock, is SqlFunctionParamBlock -> return SqlCustomSpacingBuilder.nonSpacing
511549

512-
else -> return SqlCustomSpacingBuilder.normalSpacing
550+
// else -> return SqlCustomSpacingBuilder.normalSpacing
513551
}
514552
}
515553

516554
// Create Table Column Definition Raw Group Block
517-
CreateTableUtil.getColumnDefinitionRawGroupSpacing(childBlock1, childBlock2)?.let { return it }
555+
CreateClauseHandler
556+
.getColumnDefinitionRawGroupSpacing(childBlock1, childBlock2)
557+
?.let { return it }
518558

519559
when (childBlock2) {
520560
is SqlColumnDefinitionRawGroupBlock ->
@@ -523,18 +563,32 @@ class SqlFileBlock(
523563
childBlock2,
524564
)?.let { return it }
525565

526-
is SqlRightPatternBlock -> return SqlCustomSpacingBuilder().getSpacingRightPattern(
527-
childBlock2,
528-
)
529-
530566
is SqlColumnBlock ->
531567
SqlCustomSpacingBuilder()
532568
.getSpacingColumnDefinition(childBlock2)
533569
?.let { return it }
534570
}
535571

536572
if (childBlock1 is SqlBlock && (childBlock2 is SqlCommaBlock || childBlock2 is SqlColumnRawGroupBlock)) {
537-
SqlCustomSpacingBuilder().getSpacingWithIndentComma(childBlock1, childBlock2)?.let { return it }
573+
SqlCustomSpacingBuilder()
574+
.getSpacingWithIndentComma(childBlock1, childBlock2)
575+
?.let { return it }
576+
}
577+
578+
// First apply spacing logic for blocks under specific conditions,
579+
// then execute the general spacing logic for post-line-break blocks at the end.
580+
if (childBlock1 is SqlWhitespaceBlock) {
581+
return when (childBlock2) {
582+
is SqlDefaultCommentBlock, is SqlNewGroupBlock -> {
583+
SqlCustomSpacingBuilder().getSpacing(childBlock2)
584+
}
585+
586+
else -> SqlCustomSpacingBuilder().getSpacing(childBlock2)
587+
}
588+
}
589+
590+
if (childBlock1 is SqlTableBlock || childBlock1 is SqlAliasBlock) {
591+
return SqlCustomSpacingBuilder.normalSpacing
538592
}
539593

540594
val spacing: Spacing? = customSpacingBuilder?.getCustomSpacing(childBlock1, childBlock2)

0 commit comments

Comments
 (0)