Skip to content

Commit e4f0c80

Browse files
authored
Merge pull request #357 from domaframework/fix/sql-format-multiple-case-support
Fix SQL formatting for multiple CASE expressions support
2 parents 315fa06 + b7ad1ff commit e4f0c80

File tree

55 files changed

+639
-232
lines changed

Some content is hidden

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

55 files changed

+639
-232
lines changed

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

Lines changed: 64 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,13 @@ open class SqlBlock(
5050
var indentLevel: IndentType,
5151
/**
5252
* The number of indentation spaces for this element.
53+
*
5354
* Returns `0` if there is no line break.
5455
*/
5556
var indentLen: Int,
5657
/**
5758
* Indentation baseline applied to the group itself.
59+
*
5860
* Even if the group does not start on a new line,
5961
* it determines and applies indentation to the group based on factors such as the number of preceding characters.
6062
*/
@@ -81,6 +83,48 @@ open class SqlBlock(
8183

8284
private fun isExcludedFromTextLength(block: SqlBlock): Boolean = block.node.elementType in setOf(SqlTypes.DOT, SqlTypes.RIGHT_PAREN)
8385

86+
/**
87+
* Checks if a conditional loop directive is registered before the parent block.
88+
*
89+
* @note
90+
* If the next element after a conditional directive is not a conditional directive block,
91+
* the directive becomes a child of the next element block.
92+
* Therefore, if the first element in [childBlocks] is a conditional directive,
93+
* it can be determined that—syntactically—the conditional directive was placed immediately before the current block.
94+
*/
95+
protected fun isConditionLoopDirectiveRegisteredBeforeParent(): Boolean {
96+
val firstPrevBlock = (prevBlocks.lastOrNull() as? SqlElConditionLoopCommentBlock)
97+
parentBlock?.let { parent ->
98+
return firstPrevBlock != null &&
99+
firstPrevBlock.conditionEnd != null &&
100+
firstPrevBlock.node.startOffset > parent.node.startOffset
101+
}
102+
return false
103+
}
104+
105+
/**
106+
* Determines if this is the element immediately after a conditional loop directive.
107+
*
108+
* @note
109+
* The parent conditional loop directive becomes a child of the element immediately after the conditional loop directive.
110+
* In the following case, "%if" is a child of "status", and the following "=" and "'pending'" are children of "%if".
111+
* Therefore, set the condition to break line only when the parent of the conditional loop directive is a group block.
112+
*
113+
* @example
114+
* ```sql
115+
* WHERE
116+
* /*%if status == "pending" */
117+
* status = 'pending'
118+
* ```
119+
*/
120+
protected fun isElementAfterConditionLoopDirective(): Boolean =
121+
(parentBlock as? SqlElConditionLoopCommentBlock)?.let { parent ->
122+
parent.childBlocks.firstOrNull() == this &&
123+
(parent.parentBlock is SqlNewGroupBlock || parent.parentBlock is SqlElConditionLoopCommentBlock)
124+
} == true
125+
126+
protected fun isFirstChildConditionLoopDirective(): Boolean = childBlocks.firstOrNull() is SqlElConditionLoopCommentBlock
127+
84128
fun getChildBlocksDropLast(
85129
dropIndex: Int = 1,
86130
skipCommentBlock: Boolean = true,
@@ -121,31 +165,17 @@ open class SqlBlock(
121165
fun isEnableFormat(): Boolean = enableFormat
122166

123167
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
168+
when (lastGroup) {
169+
is SqlNewGroupBlock -> shouldSaveSpaceForNewGroup(lastGroup)
170+
else -> {
171+
shouldSaveSpaceForConditionLoop()
129172
}
130173
} == true
131174

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-
if (parent.conditionType.isElse()) {
139-
return prevBlocks.isEmpty()
140-
}
141-
142-
val isConditionDirectiveParentGroup =
143-
parent.parentBlock?.let { grand ->
144-
grand is SqlNewGroupBlock
145-
} == true
146-
147-
return isPrevBlockElseOrEnd || (hasNoChildrenExceptLast && isConditionDirectiveParentGroup)
148-
}
175+
private fun shouldSaveSpaceForConditionLoop(): Boolean =
176+
isConditionLoopDirectiveRegisteredBeforeParent() ||
177+
isElementAfterConditionLoopDirective() ||
178+
isFirstChildConditionLoopDirective()
149179

150180
private fun shouldSaveSpaceForNewGroup(parent: SqlNewGroupBlock): Boolean {
151181
val prevWord = prevBlocks.lastOrNull { it !is SqlCommentBlock }
@@ -174,6 +204,8 @@ open class SqlBlock(
174204

175205
/**
176206
* Creates the indentation length for the block.
207+
*
208+
* @return The number of spaces to use for indentation
177209
*/
178210
open fun createBlockIndentLen(): Int = 0
179211

@@ -183,18 +215,24 @@ open class SqlBlock(
183215

184216
/**
185217
* Creates a spacing builder for custom spacing rules.
218+
*
219+
* @return A new instance of SqlCustomSpacingBuilder
186220
*/
187221
protected open fun createSpacingBuilder(): SqlCustomSpacingBuilder = SqlCustomSpacingBuilder()
188222

189223
override fun buildChildren(): List<Block?>? = emptyList()
190224

191225
/**
192226
* Determines whether to adjust the indentation on pressing Enter.
227+
*
228+
* @return true if indentation should be adjusted on Enter, false otherwise
193229
*/
194230
fun isAdjustIndentOnEnter(): Boolean = formatMode == FormattingMode.ADJUST_INDENT_ON_ENTER && !isEnableFormat()
195231

196232
/**
197233
* Returns the indentation for the block.
234+
*
235+
* @return The indent to apply to this block, or null if no indentation should be applied
198236
*/
199237
override fun getIndent(): Indent? =
200238
if (isAdjustIndentOnEnter()) {
@@ -210,6 +248,8 @@ open class SqlBlock(
210248

211249
/**
212250
* Returns the child indentation for the block.
251+
*
252+
* @return The indent to apply to child blocks
213253
*/
214254
override fun getChildIndent(): Indent? =
215255
if (isEnableFormat()) {
@@ -224,6 +264,8 @@ open class SqlBlock(
224264

225265
/**
226266
* Determines whether the block is a leaf node.
267+
*
268+
* @return true if this block has no child nodes, false otherwise
227269
*/
228270
override fun isLeaf(): Boolean = myNode.firstChildNode == null
229271
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ open class SqlCommaBlock(
8282
super.setParentGroupBlock(lastGroup)
8383
indent.indentLevel = IndentType.COMMA
8484
indent.indentLen = createBlockIndentLen()
85-
indent.groupIndentLen = indent.indentLen.plus(getNodeText().length)
85+
indent.groupIndentLen = createGroupIndentLen()
8686
}
8787

8888
override fun setParentPropertyBlock(lastGroup: SqlBlock?) {
@@ -142,6 +142,8 @@ open class SqlCommaBlock(
142142
return 1
143143
}
144144

145+
override fun createGroupIndentLen(): Int = indent.indentLen.plus(1)
146+
145147
override fun isSaveSpace(lastGroup: SqlBlock?): Boolean {
146148
if (parentBlock is SqlConditionalExpressionGroupBlock) return false
147149
return TypeUtil.isExpectedClassType(EXPECTED_TYPES, parentBlock)

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

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ import org.domaframework.doma.intellij.formatter.block.word.SqlFunctionGroupBloc
5858
import org.domaframework.doma.intellij.formatter.block.word.SqlTableBlock
5959
import org.domaframework.doma.intellij.formatter.block.word.SqlWordBlock
6060
import org.domaframework.doma.intellij.formatter.builder.SqlBlockBuilder
61+
import org.domaframework.doma.intellij.formatter.builder.SqlBlockRelationBuilder
6162
import org.domaframework.doma.intellij.formatter.builder.SqlCustomSpacingBuilder
6263
import org.domaframework.doma.intellij.formatter.handler.CreateClauseHandler
63-
import org.domaframework.doma.intellij.formatter.processor.SqlSetParentGroupProcessor
6464
import org.domaframework.doma.intellij.formatter.util.IndentType
6565
import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext
6666
import org.domaframework.doma.intellij.formatter.util.SqlBlockGenerator
@@ -96,7 +96,7 @@ open class SqlFileBlock(
9696
private val blocks = mutableListOf<AbstractBlock>()
9797

9898
private val blockBuilder = SqlBlockBuilder()
99-
private val parentSetProcessor = SqlSetParentGroupProcessor(blockBuilder)
99+
private val blockRelationBuilder = SqlBlockRelationBuilder(blockBuilder)
100100
private val blockUtil = SqlBlockGenerator(this, isEnableFormat(), formatMode)
101101

102102
private val pendingCommentBlocks = mutableListOf<SqlBlock>()
@@ -150,6 +150,7 @@ open class SqlFileBlock(
150150
formatMode,
151151
)
152152
val lastGroup = blockBuilder.getLastGroupTopNodeIndexHistory()
153+
val lastGroupFilteredDirective = blockBuilder.getLastGroupFilterDirective()
153154
return when (child.elementType) {
154155
SqlTypes.KEYWORD -> {
155156
return blockUtil.getKeywordBlock(
@@ -218,10 +219,20 @@ open class SqlFileBlock(
218219
}
219220

220221
SqlTypes.BLOCK_COMMENT -> {
222+
val tempBlock =
223+
blockUtil.getBlockCommentBlock(
224+
child,
225+
createBlockDirectiveCommentSpacingBuilder(),
226+
)
227+
if (tempBlock !is SqlElConditionLoopCommentBlock) {
228+
if (lastGroup is SqlWithQueryGroupBlock || lastGroupFilteredDirective is SqlWithQueryGroupBlock) {
229+
return SqlWithCommonTableGroupBlock(child, defaultFormatCtx)
230+
}
231+
}
221232
return if (lastGroup is SqlWithCommonTableGroupBlock) {
222233
SqlWithCommonTableGroupBlock(child, defaultFormatCtx)
223234
} else {
224-
blockUtil.getBlockCommentBlock(child, createBlockDirectiveCommentSpacingBuilder())
235+
tempBlock
225236
}
226237
}
227238

@@ -309,7 +320,7 @@ open class SqlFileBlock(
309320
val lastGroupBlock = blockBuilder.getLastGroupTopNodeIndexHistory()
310321
val lastIndentLevel = lastGroupBlock?.indent?.indentLevel
311322
if (lastGroupBlock == null || lastIndentLevel == null) {
312-
parentSetProcessor.updateGroupBlockAddGroup(
323+
blockRelationBuilder.updateGroupBlockAddGroup(
313324
childBlock,
314325
)
315326
return
@@ -319,81 +330,81 @@ open class SqlFileBlock(
319330

320331
when (childBlock) {
321332
is SqlKeywordGroupBlock -> {
322-
parentSetProcessor.updateKeywordGroupBlockParentAndAddGroup(
333+
blockRelationBuilder.updateKeywordGroupBlockParentAndAddGroup(
323334
lastGroupBlock,
324335
lastIndentLevel,
325336
childBlock,
326337
)
327338
}
328339

329340
is SqlColumnDefinitionRawGroupBlock -> {
330-
parentSetProcessor.updateColumnDefinitionRawGroupBlockParentAndAddGroup(
341+
blockRelationBuilder.updateColumnDefinitionRawGroupBlockParentAndAddGroup(
331342
lastGroupBlock,
332343
lastIndentLevel,
333344
childBlock,
334345
)
335346
}
336347

337348
is SqlColumnRawGroupBlock -> {
338-
parentSetProcessor.updateColumnRawGroupBlockParentAndAddGroup(
349+
blockRelationBuilder.updateColumnRawGroupBlockParentAndAddGroup(
339350
lastGroupBlock,
340351
childBlock,
341352
)
342353
}
343354

344355
is SqlInlineGroupBlock -> {
345356
// case-end
346-
parentSetProcessor.updateGroupBlockParentAndAddGroup(
357+
blockRelationBuilder.updateGroupBlockParentAndAddGroup(
347358
childBlock,
348359
)
349360
}
350361

351362
is SqlInlineSecondGroupBlock -> {
352-
parentSetProcessor.updateInlineSecondGroupBlockParentAndAddGroup(
363+
blockRelationBuilder.updateInlineSecondGroupBlockParentAndAddGroup(
353364
childBlock,
354365
)
355366
}
356367

357368
is SqlColumnBlock -> {
358-
parentSetProcessor.updateGroupBlockParentAndAddGroup(
369+
blockRelationBuilder.updateGroupBlockParentAndAddGroup(
359370
childBlock,
360371
)
361372
}
362373

363374
is SqlElConditionLoopCommentBlock -> {
364-
parentSetProcessor.updateConditionLoopCommentBlockParent(
375+
blockRelationBuilder.updateConditionLoopCommentBlockParent(
365376
lastGroupBlock,
366377
childBlock,
367378
)
368379
}
369380

370381
is SqlWordBlock, is SqlOtherBlock -> {
371-
parentSetProcessor.updateGroupBlockParentAndAddGroup(
382+
blockRelationBuilder.updateGroupBlockParentAndAddGroup(
372383
childBlock,
373384
)
374385
}
375386

376387
is SqlSubGroupBlock -> {
377-
parentSetProcessor.updateSubGroupBlockParent(
388+
blockRelationBuilder.updateSubGroupBlockParent(
378389
lastGroupBlock,
379390
childBlock,
380391
)
381392
}
382393

383394
is SqlRightPatternBlock -> {
384-
parentSetProcessor.updateSqlRightPatternBlockParent(
395+
blockRelationBuilder.updateSqlRightPatternBlockParent(
385396
childBlock,
386397
)
387398
}
388399

389400
is SqlElSymbolBlock -> {
390-
parentSetProcessor.updateGroupBlockParentAndAddGroup(
401+
blockRelationBuilder.updateGroupBlockParentAndAddGroup(
391402
childBlock,
392403
)
393404
}
394405

395406
is SqlDataTypeBlock -> {
396-
parentSetProcessor.updateGroupBlockParentAndAddGroup(
407+
blockRelationBuilder.updateGroupBlockParentAndAddGroup(
397408
childBlock,
398409
)
399410
}
@@ -402,13 +413,13 @@ open class SqlFileBlock(
402413
if (lastGroupBlock is SqlCommaBlock) {
403414
blockBuilder.removeLastGroupTopNodeIndexHistory()
404415
}
405-
parentSetProcessor.updateGroupBlockParentAndAddGroup(
416+
blockRelationBuilder.updateGroupBlockParentAndAddGroup(
406417
childBlock,
407418
)
408419
}
409420

410421
else -> {
411-
parentSetProcessor.updateGroupBlockParentAndAddGroup(
422+
blockRelationBuilder.updateGroupBlockParentAndAddGroup(
412423
childBlock,
413424
)
414425
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlDataTyp
3434
import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlFunctionParamBlock
3535
import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGroupBlock
3636
import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubQueryGroupBlock
37+
import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlValuesParamGroupBlock
3738
import org.domaframework.doma.intellij.formatter.util.IndentType
3839
import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext
3940

@@ -136,7 +137,7 @@ open class SqlRightPatternBlock(
136137
}
137138

138139
// Default case
139-
preSpaceRight = false
140+
preSpaceRight = parentBlock is SqlValuesParamGroupBlock
140141
}
141142

142143
override val indent =

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,5 @@ abstract class SqlCommentBlock(
6262
return 0
6363
}
6464

65-
override fun createGroupIndentLen(): Int = 0
65+
override fun createGroupIndentLen(): Int = indent.indentLen
6666
}

0 commit comments

Comments
 (0)