Skip to content

Commit cc560c6

Browse files
committed
Support indentation and line-breaking for CTEs, and refactor related logic.
1 parent 062c45a commit cc560c6

35 files changed

+466
-334
lines changed

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

Lines changed: 41 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -26,32 +26,27 @@ 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 com.intellij.psi.util.PsiTreeUtil
30-
import org.domaframework.doma.intellij.common.util.TypeUtil.isExpectedClassType
3129
import org.domaframework.doma.intellij.formatter.block.comment.SqlBlockCommentBlock
3230
import org.domaframework.doma.intellij.formatter.block.comment.SqlCommentBlock
3331
import org.domaframework.doma.intellij.formatter.block.comment.SqlLineCommentBlock
34-
import org.domaframework.doma.intellij.formatter.block.conflict.SqlConflictClauseBlock
35-
import org.domaframework.doma.intellij.formatter.block.conflict.SqlDoGroupBlock
3632
import org.domaframework.doma.intellij.formatter.block.expr.SqlElBlockCommentBlock
3733
import org.domaframework.doma.intellij.formatter.block.expr.SqlElConditionLoopCommentBlock
3834
import org.domaframework.doma.intellij.formatter.block.expr.SqlElSymbolBlock
3935
import org.domaframework.doma.intellij.formatter.block.group.SqlNewGroupBlock
4036
import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnBlock
4137
import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnDefinitionRawGroupBlock
4238
import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnRawGroupBlock
43-
import org.domaframework.doma.intellij.formatter.block.group.column.SqlDataTypeBlock
4439
import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlInlineGroupBlock
4540
import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlInlineSecondGroupBlock
4641
import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock
47-
import org.domaframework.doma.intellij.formatter.block.group.keyword.create.SqlCreateTableColumnDefinitionGroupBlock
48-
import org.domaframework.doma.intellij.formatter.block.group.keyword.insert.SqlInsertColumnGroupBlock
4942
import org.domaframework.doma.intellij.formatter.block.group.keyword.update.SqlUpdateColumnAssignmentSymbolBlock
5043
import org.domaframework.doma.intellij.formatter.block.group.keyword.update.SqlUpdateSetGroupBlock
44+
import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithColumnGroupBlock
45+
import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithCommonTableGroupBlock
46+
import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithQueryGroupBlock
5147
import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlDataTypeParamBlock
5248
import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlFunctionParamBlock
53-
import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlParallelListBlock
54-
import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlRightPatternBlock
49+
import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGroupBlock
5550
import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubQueryGroupBlock
5651
import org.domaframework.doma.intellij.formatter.builder.SqlBlockBuilder
5752
import org.domaframework.doma.intellij.formatter.builder.SqlCustomSpacingBuilder
@@ -93,6 +88,7 @@ open class SqlBlock(
9388
val blocks = mutableListOf<AbstractBlock>()
9489
open var parentBlock: SqlBlock? = null
9590
open val childBlocks = mutableListOf<SqlBlock>()
91+
9692
open val indent: ElementIndent =
9793
ElementIndent(
9894
IndentType.FILE,
@@ -118,8 +114,6 @@ open class SqlBlock(
118114
// This method can be overridden to set additional properties on the parent block if needed.
119115
}
120116

121-
open val isNeedWhiteSpace: Boolean = true
122-
123117
open fun addChildBlock(childBlock: SqlBlock) {
124118
childBlocks.add(childBlock)
125119
}
@@ -137,10 +131,10 @@ open class SqlBlock(
137131
val lastGroup = blockBuilder.getLastGroupTopNodeIndexHistory()
138132
if (child !is PsiWhiteSpace) {
139133
val childBlock = getBlock(child)
140-
updateWhiteSpaceInclude(lastBlock, childBlock, lastGroup)
141134
prevNonWhiteSpaceNode = child
142135
updateCommentParentAndIdent(childBlock)
143136
updateBlockParentAndLAddGroup(childBlock)
137+
updateWhiteSpaceInclude(lastBlock, childBlock, lastGroup)
144138
blocks.add(childBlock)
145139
} else {
146140
if (lastBlock !is SqlLineCommentBlock) {
@@ -195,88 +189,15 @@ open class SqlBlock(
195189
}
196190
}
197191

192+
open fun isSaveSpace(lastGroup: SqlBlock?): Boolean = false
193+
198194
/**
199195
* Determines whether to retain the space (newline) based on the last registered group or the class of the currently checked element.
200196
*/
201197
private fun isSaveWhiteSpace(
202198
childBlock: SqlBlock,
203199
lastGroup: SqlBlock?,
204-
): Boolean {
205-
val child = childBlock.node
206-
207-
if (!childBlock.isNeedWhiteSpace) return false
208-
209-
val expectedClassTypes =
210-
listOf(
211-
SqlElConditionLoopCommentBlock::class,
212-
SqlInsertColumnGroupBlock::class,
213-
SqlColumnDefinitionRawGroupBlock::class,
214-
SqlCreateTableColumnDefinitionGroupBlock::class,
215-
SqlUpdateColumnAssignmentSymbolBlock::class,
216-
SqlDoGroupBlock::class,
217-
)
218-
219-
if (isExpectedClassType(expectedClassTypes, childBlock)) return true
220-
221-
if (isNewLineSqlComment(child, childBlock)) return true
222-
223-
if (lastGroup is SqlConflictClauseBlock) return false
224-
if (childBlock is SqlColumnRawGroupBlock) {
225-
return !childBlock.isFirstColumnGroup
226-
}
227-
228-
return (
229-
isNewLineGroupBlockAfterRegistrationChild(childBlock, lastGroup) ||
230-
(childBlock is SqlRightPatternBlock && childBlock.isNewLine(lastGroup))
231-
)
232-
}
233-
234-
/**
235-
* Retains the block only for comments that are broken down.
236-
*/
237-
private fun isNewLineSqlComment(
238-
child: ASTNode,
239-
childBlock: SqlBlock,
240-
): Boolean {
241-
val commentBlockType =
242-
listOf(
243-
SqlLineCommentBlock::class,
244-
SqlBlockCommentBlock::class,
245-
)
246-
val prevSpace = PsiTreeUtil.prevLeaf(child.psi)
247-
return isExpectedClassType(
248-
commentBlockType,
249-
childBlock,
250-
) &&
251-
prevSpace?.text?.contains("\n") == true
252-
}
253-
254-
/**
255-
* Determines whether a newline is required after registering itself as a child of the parent block.
256-
*/
257-
private fun isNewLineGroupBlockAfterRegistrationChild(
258-
childBlock: SqlBlock,
259-
lastGroup: SqlBlock?,
260-
): Boolean {
261-
fun isParallelListRawChild(): Boolean =
262-
childBlock is SqlCommaBlock &&
263-
(
264-
lastGroup is SqlParallelListBlock ||
265-
lastGroup?.parentBlock is SqlParallelListBlock
266-
)
267-
268-
if (isParallelListRawChild()) return false
269-
270-
if (parentSetProcessor.isNewGroupAndNotSetLineKeywords(childBlock, lastGroup)) {
271-
return if (lastGroup is SqlSubQueryGroupBlock) {
272-
val lastGroupChildren = lastGroup.childBlocks
273-
(lastGroupChildren.isNotEmpty() && lastGroupChildren.drop(1).isNotEmpty())
274-
} else {
275-
true
276-
}
277-
}
278-
return false
279-
}
200+
): Boolean = childBlock.isSaveSpace(lastGroup)
280201

281202
/**
282203
* Updates the parent block or registers itself as a new group block based on the class of the target block.
@@ -349,8 +270,8 @@ open class SqlBlock(
349270
)
350271
}
351272

352-
is SqlSubQueryGroupBlock -> {
353-
parentSetProcessor.updateGroupBlockParentAndAddGroup(
273+
is SqlSubGroupBlock -> {
274+
parentSetProcessor.updateSubGroupBlockParent(
354275
childBlock,
355276
)
356277
}
@@ -395,6 +316,8 @@ open class SqlBlock(
395316
*/
396317
open fun createBlockIndentLen(): Int = 0
397318

319+
open fun createGroupIndentLen(): Int = 0
320+
398321
/**
399322
* Creates a block for the given child AST node.
400323
*/
@@ -449,13 +372,27 @@ open class SqlBlock(
449372
)
450373

451374
SqlTypes.COMMA -> {
452-
return blockUtil.getCommaGroupBlock(lastGroup, child)
375+
return if (lastGroup is SqlWithQueryGroupBlock) {
376+
SqlWithCommonTableGroupBlock(child, defaultFormatCtx)
377+
} else {
378+
blockUtil.getCommaGroupBlock(lastGroup, child)
379+
}
453380
}
454381

455-
SqlTypes.WORD -> return blockUtil.getWordBlock(lastGroup, child)
382+
SqlTypes.WORD -> {
383+
return if (lastGroup is SqlWithQueryGroupBlock) {
384+
SqlWithCommonTableGroupBlock(child, defaultFormatCtx)
385+
} else {
386+
blockUtil.getWordBlock(lastGroup, child)
387+
}
388+
}
456389

457390
SqlTypes.BLOCK_COMMENT -> {
458-
return blockUtil.getBlockCommentBlock(child, createBlockCommentSpacingBuilder())
391+
return if (lastGroup is SqlWithCommonTableGroupBlock) {
392+
SqlWithCommonTableGroupBlock(child, defaultFormatCtx)
393+
} else {
394+
blockUtil.getBlockCommentBlock(child, createBlockCommentSpacingBuilder())
395+
}
459396
}
460397

461398
SqlTypes.LINE_COMMENT ->
@@ -555,6 +492,14 @@ open class SqlBlock(
555492
return SqlCustomSpacingBuilder().getSpacing(child2)
556493
}
557494

495+
if (child2 is SqlWithColumnGroupBlock) {
496+
return SqlCustomSpacingBuilder.normalSpacing
497+
}
498+
499+
if (child1 is SqlSubGroupBlock && child2 is SqlSubGroupBlock) {
500+
return SqlCustomSpacingBuilder.nonSpacing
501+
}
502+
558503
// Do not leave a space after the comment block of the bind variable
559504
if (child1 is SqlElBlockCommentBlock && child1 !is SqlElConditionLoopCommentBlock && child2 !is SqlCommentBlock) {
560505
return SqlCustomSpacingBuilder.nonSpacing
@@ -587,6 +532,9 @@ open class SqlBlock(
587532
}
588533

589534
if (child2 is SqlNewGroupBlock) {
535+
if (child1 is SqlSubGroupBlock && child2.indent.indentLevel == IndentType.ATTACHED) {
536+
return SqlCustomSpacingBuilder.nonSpacing
537+
}
590538
when (child2) {
591539
is SqlSubQueryGroupBlock -> {
592540
if (child1 is SqlNewGroupBlock) {

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

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,15 @@ import com.intellij.lang.ASTNode
1919
import com.intellij.psi.formatter.common.AbstractBlock
2020
import org.domaframework.doma.intellij.common.util.TypeUtil
2121
import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnRawGroupBlock
22+
import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock
2223
import org.domaframework.doma.intellij.formatter.block.group.keyword.create.SqlCreateKeywordGroupBlock
2324
import org.domaframework.doma.intellij.formatter.block.group.keyword.insert.SqlInsertColumnGroupBlock
25+
import org.domaframework.doma.intellij.formatter.block.group.keyword.insert.SqlInsertValueGroupBlock
2426
import org.domaframework.doma.intellij.formatter.block.group.keyword.update.SqlUpdateColumnGroupBlock
27+
import org.domaframework.doma.intellij.formatter.block.group.keyword.update.SqlUpdateSetGroupBlock
2528
import org.domaframework.doma.intellij.formatter.block.group.keyword.update.SqlUpdateValueGroupBlock
29+
import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithColumnGroupBlock
30+
import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlFunctionParamBlock
2631
import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlParallelListBlock
2732
import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGroupBlock
2833
import org.domaframework.doma.intellij.formatter.util.IndentType
@@ -67,27 +72,38 @@ open class SqlCommaBlock(
6772
val parentIndentSyncBlockTypes =
6873
listOf(
6974
SqlUpdateColumnGroupBlock::class,
70-
SqlUpdateValueGroupBlock::class,
7175
SqlInsertColumnGroupBlock::class,
76+
SqlWithColumnGroupBlock::class,
7277
)
7378
val parentIndentLen = parent.indent.groupIndentLen
7479
if (TypeUtil.isExpectedClassType(parentIndentSyncBlockTypes, parent)) {
7580
return parentIndentLen
7681
}
7782

83+
// TODO Indent each comma in a value group so that it aligns with the position of the first value row.
84+
val parentIndentSingleSpaceTypes =
85+
listOf(
86+
SqlInsertValueGroupBlock::class,
87+
SqlUpdateValueGroupBlock::class,
88+
)
89+
if (TypeUtil.isExpectedClassType(parentIndentSingleSpaceTypes, parent)) {
90+
return parentIndentLen.plus(1)
91+
}
92+
7893
val grand = parent.parentBlock
7994
grand?.let { grand ->
8095
if (grand is SqlCreateKeywordGroupBlock) {
8196
val grandIndentLen = grand.indent.groupIndentLen
8297
return grandIndentLen.plus(parentIndentLen).minus(1)
8398
}
8499

100+
val grandIndent = grand.indent.indentLen
101+
val groupIndent = parentBlock?.indent?.groupIndentLen ?: 0
102+
85103
if (grand is SqlColumnRawGroupBlock) {
86-
val grandIndentLen = grand.indent.groupIndentLen
87-
var prevTextLen = 1
88-
parent.prevChildren?.dropLast(1)?.forEach { prev -> prevTextLen = prevTextLen.plus(prev.getNodeText().length) }
89-
return grandIndentLen.plus(prevTextLen).plus(1)
104+
return groupIndent.plus(grandIndent)
90105
}
106+
return groupIndent.plus(grandIndent).minus(1)
91107
}
92108
return parentIndentLen.plus(1)
93109
} else {
@@ -104,10 +120,24 @@ open class SqlCommaBlock(
104120
)
105121
}
106122
return parent.indent.groupIndentLen
107-
.plus(prevLen)
108123
.plus(1)
109124
}
110125
}
111126
return 1
112127
}
128+
129+
override fun isSaveSpace(lastGroup: SqlBlock?): Boolean {
130+
val exceptionTypes =
131+
listOf(
132+
SqlInsertColumnGroupBlock::class,
133+
SqlInsertValueGroupBlock::class,
134+
SqlUpdateSetGroupBlock::class,
135+
SqlUpdateColumnGroupBlock::class,
136+
SqlUpdateValueGroupBlock::class,
137+
SqlFunctionParamBlock::class,
138+
SqlWithColumnGroupBlock::class,
139+
SqlKeywordGroupBlock::class,
140+
)
141+
return TypeUtil.isExpectedClassType(exceptionTypes, parentBlock)
142+
}
113143
}

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ import com.intellij.lang.ASTNode
2020
import com.intellij.psi.formatter.common.AbstractBlock
2121
import org.domaframework.doma.intellij.formatter.block.conflict.SqlDoGroupBlock
2222
import org.domaframework.doma.intellij.formatter.block.group.SqlNewGroupBlock
23+
import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock
24+
import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithCommonTableGroupBlock
25+
import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithQueryGroupBlock
2326
import org.domaframework.doma.intellij.formatter.util.IndentType
2427
import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext
2528

@@ -37,16 +40,29 @@ open class SqlKeywordBlock(
3740

3841
override fun setParentGroupBlock(lastGroup: SqlBlock?) {
3942
super.setParentGroupBlock(lastGroup)
40-
4143
indent.indentLevel = indentLevel
4244
indent.indentLen = createBlockIndentLen()
4345
indent.groupIndentLen = indent.indentLen.plus(getNodeText().length)
4446
}
4547

4648
override fun setParentPropertyBlock(lastGroup: SqlBlock?) {
49+
if (lastGroup is SqlKeywordGroupBlock) {
50+
lastGroup.updateTopKeywordBlocks(this)
51+
}
52+
4753
if (getNodeText() == "nothing" && lastGroup is SqlDoGroupBlock) {
4854
lastGroup.doQueryBlock = this
4955
}
56+
57+
if (lastGroup is SqlWithQueryGroupBlock) {
58+
when (getNodeText()) {
59+
"recursive" -> lastGroup.recursiveBlock = this
60+
}
61+
}
62+
63+
if (lastGroup is SqlWithCommonTableGroupBlock) {
64+
lastGroup.optionKeywordBlocks.add(this)
65+
}
5066
}
5167

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

0 commit comments

Comments
 (0)