Skip to content

Commit 7b39e02

Browse files
authored
Merge pull request #408 from domaframework/fix/sql-format-spacing-comparison-operators
Fix SQL formatting rules for JSON columns
2 parents 49b7af8 + b58e9bd commit 7b39e02

File tree

18 files changed

+546
-261
lines changed

18 files changed

+546
-261
lines changed

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

Lines changed: 81 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -67,21 +67,28 @@ open class SqlBlock(
6767
open val childBlocks = mutableListOf<SqlBlock>()
6868
open var prevBlocks = emptyList<SqlBlock>()
6969

70+
companion object {
71+
private const val DEFAULT_INDENT_SIZE = 4
72+
private const val DEFAULT_TEXT_LENGTH_INCREMENT = 1
73+
private val EXCLUDED_FROM_TEXT_LENGTH = setOf(SqlTypes.DOT, SqlTypes.RIGHT_PAREN)
74+
private val SPACING_ONE = Spacing.createSpacing(1, 1, 0, true, 0)
75+
private val SPACING_ZERO = Spacing.createSpacing(0, 0, 0, true, 0)
76+
private val SPACING_ONE_NO_KEEP = Spacing.createSpacing(1, 1, 0, false, 0)
77+
}
78+
7079
fun getChildrenTextLen(): Int = childBlocks.sumOf { child -> calculateChildTextLength(child) }
7180

7281
private fun calculateChildTextLength(child: SqlBlock): Int {
7382
val nonCommentChildren = child.childBlocks.filterNot { it is SqlDefaultCommentBlock }
7483

75-
if (nonCommentChildren.isNotEmpty()) {
76-
return child.getChildrenTextLen() + child.getNodeText().length
84+
return when {
85+
nonCommentChildren.isNotEmpty() -> child.getChildrenTextLen() + child.getNodeText().length
86+
isExcludedFromTextLength(child) -> 0
87+
else -> child.getNodeText().length + DEFAULT_TEXT_LENGTH_INCREMENT
7788
}
78-
if (isExcludedFromTextLength(child)) {
79-
return 0
80-
}
81-
return child.getNodeText().length + 1
8289
}
8390

84-
private fun isExcludedFromTextLength(block: SqlBlock): Boolean = block.node.elementType in setOf(SqlTypes.DOT, SqlTypes.RIGHT_PAREN)
91+
private fun isExcludedFromTextLength(block: SqlBlock): Boolean = block.node.elementType in EXCLUDED_FROM_TEXT_LENGTH
8592

8693
/**
8794
* Checks if a conditional loop directive is registered before the parent block.
@@ -123,13 +130,19 @@ open class SqlBlock(
123130
(parent.parentBlock is SqlNewGroupBlock || parent.parentBlock is SqlElConditionLoopCommentBlock)
124131
} == true
125132

126-
protected fun isElementAfterConditionLoopEnd(): Boolean =
127-
(
133+
protected fun isElementAfterConditionLoopEnd(): Boolean {
134+
val prevChildren =
128135
prevBlocks
129-
.lastOrNull()
136+
.firstOrNull()
130137
?.childBlocks
131-
?.firstOrNull() as? SqlElConditionLoopCommentBlock
132-
)?.conditionEnd != null
138+
139+
val firstConditionBlock = (prevChildren?.firstOrNull() as? SqlElConditionLoopCommentBlock)
140+
val endBlock = firstConditionBlock?.conditionEnd
141+
if (endBlock == null) return false
142+
val lastBlock = prevChildren.lastOrNull()
143+
144+
return endBlock.node.startOffset > (lastBlock?.node?.startOffset ?: 0)
145+
}
133146

134147
protected fun isFirstChildConditionLoopDirective(): Boolean = childBlocks.firstOrNull() is SqlElConditionLoopCommentBlock
135148

@@ -179,10 +192,8 @@ open class SqlBlock(
179192
open fun isSaveSpace(lastGroup: SqlBlock?): Boolean =
180193
when (lastGroup) {
181194
is SqlNewGroupBlock -> shouldSaveSpaceForNewGroup(lastGroup)
182-
else -> {
183-
shouldSaveSpaceForConditionLoop()
184-
}
185-
} == true
195+
else -> shouldSaveSpaceForConditionLoop()
196+
}
186197

187198
private fun shouldSaveSpaceForConditionLoop(): Boolean =
188199
isConditionLoopDirectiveRegisteredBeforeParent() ||
@@ -197,15 +208,22 @@ open class SqlBlock(
197208
return false
198209
}
199210

200-
return isFollowedByConditionLoop() || isPrecededByConditionLoop(parent)
211+
return hasConditionLoopAround(parent)
201212
}
202213

203214
private fun isNonBreakingKeywordCombination(
204215
parent: SqlNewGroupBlock,
205216
prevWord: SqlBlock?,
206-
): Boolean =
207-
SqlKeywordUtil.isSetLineKeyword(getNodeText(), parent.getNodeText()) ||
208-
SqlKeywordUtil.isSetLineKeyword(getNodeText(), prevWord?.getNodeText() ?: "")
217+
): Boolean {
218+
val currentText = getNodeText()
219+
val parentText = parent.getNodeText()
220+
val prevText = prevWord?.getNodeText() ?: ""
221+
222+
return SqlKeywordUtil.isSetLineKeyword(currentText, parentText) ||
223+
SqlKeywordUtil.isSetLineKeyword(currentText, prevText)
224+
}
225+
226+
private fun hasConditionLoopAround(parent: SqlNewGroupBlock): Boolean = isFollowedByConditionLoop() || isPrecededByConditionLoop(parent)
209227

210228
private fun isFollowedByConditionLoop(): Boolean = childBlocks.lastOrNull() is SqlElConditionLoopCommentBlock
211229

@@ -265,158 +283,67 @@ open class SqlBlock(
265283
/**
266284
* Creates a spacing builder specifically for directive block comments.
267285
*/
268-
protected fun createBlockDirectiveCommentSpacingBuilder(): SqlCustomSpacingBuilder =
269-
SqlCustomSpacingBuilder()
270-
.withSpacing(
271-
SqlTypes.BLOCK_COMMENT_START,
272-
SqlTypes.EL_ID_EXPR,
273-
Spacing.createSpacing(1, 1, 0, true, 0),
274-
).withSpacing(
275-
SqlTypes.BLOCK_COMMENT_START,
276-
SqlTypes.EL_PRIMARY_EXPR,
277-
Spacing.createSpacing(1, 1, 0, true, 0),
278-
).withSpacing(
279-
SqlTypes.BLOCK_COMMENT_START,
280-
SqlTypes.EL_STRING,
281-
Spacing.createSpacing(1, 1, 0, true, 0),
282-
).withSpacing(
283-
SqlTypes.BLOCK_COMMENT_START,
284-
SqlTypes.EL_NUMBER,
285-
Spacing.createSpacing(1, 1, 0, true, 0),
286-
).withSpacing(
287-
SqlTypes.BLOCK_COMMENT_START,
288-
SqlTypes.BOOLEAN,
289-
Spacing.createSpacing(1, 1, 0, true, 0),
290-
).withSpacing(
291-
SqlTypes.BLOCK_COMMENT_START,
292-
SqlTypes.EL_NULL,
293-
Spacing.createSpacing(1, 1, 0, true, 0),
294-
).withSpacing(
295-
SqlTypes.BLOCK_COMMENT_START,
296-
SqlTypes.EL_FIELD_ACCESS_EXPR,
297-
Spacing.createSpacing(1, 1, 0, true, 0),
298-
).withSpacing(
299-
SqlTypes.BLOCK_COMMENT_START,
300-
SqlTypes.EL_STATIC_FIELD_ACCESS_EXPR,
301-
Spacing.createSpacing(1, 1, 0, true, 0),
302-
).withSpacing(
303-
SqlTypes.BLOCK_COMMENT_START,
304-
SqlTypes.HASH,
305-
Spacing.createSpacing(0, 0, 0, true, 0),
306-
).withSpacing(
307-
SqlTypes.HASH,
308-
SqlTypes.EL_ID_EXPR,
309-
Spacing.createSpacing(1, 1, 0, true, 0),
310-
).withSpacing(
311-
SqlTypes.HASH,
312-
SqlTypes.EL_PRIMARY_EXPR,
313-
Spacing.createSpacing(1, 1, 0, true, 0),
314-
).withSpacing(
315-
SqlTypes.HASH,
316-
SqlTypes.EL_STRING,
317-
Spacing.createSpacing(1, 1, 0, true, 0),
318-
).withSpacing(
319-
SqlTypes.HASH,
320-
SqlTypes.EL_NUMBER,
321-
Spacing.createSpacing(1, 1, 0, true, 0),
322-
).withSpacing(
323-
SqlTypes.HASH,
324-
SqlTypes.BOOLEAN,
325-
Spacing.createSpacing(1, 1, 0, true, 0),
326-
).withSpacing(
327-
SqlTypes.HASH,
328-
SqlTypes.EL_NULL,
329-
Spacing.createSpacing(1, 1, 0, true, 0),
330-
).withSpacing(
331-
SqlTypes.HASH,
332-
SqlTypes.EL_FIELD_ACCESS_EXPR,
333-
Spacing.createSpacing(1, 1, 0, true, 0),
334-
).withSpacing(
335-
SqlTypes.HASH,
336-
SqlTypes.EL_STATIC_FIELD_ACCESS_EXPR,
337-
Spacing.createSpacing(1, 1, 0, true, 0),
338-
).withSpacing(
339-
SqlTypes.BLOCK_COMMENT_START,
340-
SqlTypes.CARET,
341-
Spacing.createSpacing(0, 0, 0, true, 0),
342-
).withSpacing(
343-
SqlTypes.CARET,
286+
protected open fun createBlockDirectiveCommentSpacingBuilder(): SqlCustomSpacingBuilder {
287+
val builder = SqlCustomSpacingBuilder()
288+
289+
// Types that need spacing after BLOCK_COMMENT_START
290+
val typesNeedingSpaceAfterStart =
291+
listOf(
344292
SqlTypes.EL_ID_EXPR,
345-
Spacing.createSpacing(1, 1, 0, true, 0),
346-
).withSpacing(
347-
SqlTypes.CARET,
348293
SqlTypes.EL_PRIMARY_EXPR,
349-
Spacing.createSpacing(1, 1, 0, true, 0),
350-
).withSpacing(
351-
SqlTypes.CARET,
352294
SqlTypes.EL_STRING,
353-
Spacing.createSpacing(1, 1, 0, true, 0),
354-
).withSpacing(
355-
SqlTypes.CARET,
356295
SqlTypes.EL_NUMBER,
357-
Spacing.createSpacing(1, 1, 0, true, 0),
358-
).withSpacing(
359-
SqlTypes.CARET,
360296
SqlTypes.BOOLEAN,
361-
Spacing.createSpacing(1, 1, 0, true, 0),
362-
).withSpacing(
363-
SqlTypes.CARET,
364297
SqlTypes.EL_NULL,
365-
Spacing.createSpacing(1, 1, 0, true, 0),
366-
).withSpacing(
367-
SqlTypes.CARET,
368-
SqlTypes.EL_FIELD_ACCESS_EXPR,
369-
Spacing.createSpacing(1, 1, 0, true, 0),
370-
).withSpacing(
371-
SqlTypes.CARET,
372-
SqlTypes.EL_STATIC_FIELD_ACCESS_EXPR,
373-
Spacing.createSpacing(1, 1, 0, true, 0),
374-
).withSpacing(
375-
SqlTypes.BLOCK_COMMENT_CONTENT,
376-
SqlTypes.BLOCK_COMMENT_END,
377-
Spacing.createSpacing(0, 0, 0, true, 0),
378-
).withSpacing(
379298
SqlTypes.EL_FIELD_ACCESS_EXPR,
380-
SqlTypes.OTHER,
381-
Spacing.createSpacing(1, 1, 0, false, 0),
382-
).withSpacing(
383299
SqlTypes.EL_STATIC_FIELD_ACCESS_EXPR,
384-
SqlTypes.OTHER,
385-
Spacing.createSpacing(1, 1, 0, false, 0),
386-
).withSpacing(
300+
)
301+
302+
// Types that need spacing before BLOCK_COMMENT_END
303+
val typesNeedingSpaceBeforeEnd =
304+
listOf(
387305
SqlTypes.EL_ID_EXPR,
388-
SqlTypes.BLOCK_COMMENT_END,
389-
Spacing.createSpacing(1, 1, 0, true, 0),
390-
).withSpacing(
391306
SqlTypes.EL_PRIMARY_EXPR,
392-
SqlTypes.BLOCK_COMMENT_END,
393-
Spacing.createSpacing(1, 1, 0, true, 0),
394-
).withSpacing(
395307
SqlTypes.STRING,
396-
SqlTypes.BLOCK_COMMENT_END,
397-
Spacing.createSpacing(1, 1, 0, true, 0),
398-
).withSpacing(
399308
SqlTypes.EL_NUMBER,
400-
SqlTypes.BLOCK_COMMENT_END,
401-
Spacing.createSpacing(1, 1, 0, true, 0),
402-
).withSpacing(
403309
SqlTypes.EL_NULL,
404-
SqlTypes.BLOCK_COMMENT_END,
405-
Spacing.createSpacing(1, 1, 0, true, 0),
406-
).withSpacing(
407310
SqlTypes.BOOLEAN,
408-
SqlTypes.BLOCK_COMMENT_END,
409-
Spacing.createSpacing(1, 1, 0, true, 0),
410-
).withSpacing(
411311
SqlTypes.EL_FIELD_ACCESS_EXPR,
412-
SqlTypes.BLOCK_COMMENT_END,
413-
Spacing.createSpacing(1, 1, 0, true, 0),
414-
).withSpacing(
415312
SqlTypes.EL_STATIC_FIELD_ACCESS_EXPR,
416-
SqlTypes.BLOCK_COMMENT_END,
417-
Spacing.createSpacing(1, 1, 0, true, 0),
418313
)
419314

315+
// Add spacing rules for BLOCK_COMMENT_START
316+
typesNeedingSpaceAfterStart.forEach { type ->
317+
builder.withSpacing(SqlTypes.BLOCK_COMMENT_START, type, SPACING_ONE)
318+
}
319+
320+
// Special cases for BLOCK_COMMENT_START
321+
builder.withSpacing(SqlTypes.BLOCK_COMMENT_START, SqlTypes.HASH, SPACING_ZERO)
322+
builder.withSpacing(SqlTypes.BLOCK_COMMENT_START, SqlTypes.CARET, SPACING_ZERO)
323+
324+
// Add spacing rules for HASH
325+
typesNeedingSpaceAfterStart.forEach { type ->
326+
builder.withSpacing(SqlTypes.HASH, type, SPACING_ONE)
327+
}
328+
329+
// Add spacing rules for CARET
330+
typesNeedingSpaceAfterStart.forEach { type ->
331+
builder.withSpacing(SqlTypes.CARET, type, SPACING_ONE)
332+
}
333+
334+
// Special spacing rules
335+
builder.withSpacing(SqlTypes.BLOCK_COMMENT_CONTENT, SqlTypes.BLOCK_COMMENT_END, SPACING_ZERO)
336+
builder.withSpacing(SqlTypes.EL_FIELD_ACCESS_EXPR, SqlTypes.OTHER, SPACING_ONE_NO_KEEP)
337+
builder.withSpacing(SqlTypes.EL_STATIC_FIELD_ACCESS_EXPR, SqlTypes.OTHER, SPACING_ONE_NO_KEEP)
338+
339+
// Add spacing rules before BLOCK_COMMENT_END
340+
typesNeedingSpaceBeforeEnd.forEach { type ->
341+
builder.withSpacing(type, SqlTypes.BLOCK_COMMENT_END, SPACING_ONE)
342+
}
343+
344+
return builder
345+
}
346+
420347
/**
421348
* Returns the child indentation for the block.
422349
*
@@ -429,10 +356,6 @@ open class SqlBlock(
429356
Indent.getSpaceIndent(0)
430357
}
431358

432-
companion object {
433-
private const val DEFAULT_INDENT_SIZE = 4
434-
}
435-
436359
/**
437360
* Determines whether the block is a leaf node.
438361
*

0 commit comments

Comments
 (0)