Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0472fb9
Add block class for IN clause and update test data to prevent uninten…
xterao Aug 20, 2025
63aa0c4
Add SQL files for conditional clauses and format them for improved re…
xterao Aug 20, 2025
6b18951
Fix SQL formatting logic for condition loop comments and improve inde…
xterao Aug 20, 2025
2487440
Adjust keyword group indentation within conditional directives
xterao Aug 20, 2025
69eedd0
Remove duplicate return
xterao Aug 21, 2025
4de78cd
ix subquery formatting under conditional
xterao Aug 21, 2025
cac0623
Refactor isSaveSpace method for improved readability and conciseness
xterao Aug 21, 2025
ab0a522
Refactor SQL indentation logic for sub-group blocks and clean up form…
xterao Aug 21, 2025
b6ad4c1
Merge pull request #404 from domaframework/fix/sql-format-in-group-li…
xterao Aug 21, 2025
08ff1fe
Fix indent calculation logic for combined IN clause and subquery
xterao Aug 21, 2025
a661681
Refactor SQL indentation logic for condition loop blocks to improve c…
xterao Aug 21, 2025
60b4203
Support for EXISTS clause format
xterao Aug 21, 2025
e91b3ac
Merge pull request #405 from domaframework/fix/sql-format-sub-query-line
xterao Aug 21, 2025
9ba4ebe
Fix detection and indentation logic for EXISTS clauses within conditi…
xterao Aug 21, 2025
b25b329
Add SqlKeywordBlockFactory for creating SQL keyword blocks based on c…
xterao Aug 21, 2025
49b7af8
Merge pull request #406 from domaframework/fix/sql-format-exists-group
xterao Aug 21, 2025
f8ba20a
Fix to prevent spaces from being inserted in comparison operator symbols
xterao Aug 21, 2025
2c55788
Implement formatting rules for syntax handling array-type parameters
xterao Aug 21, 2025
b58e9bd
Refactor indentation logic in SqlSubGroupBlock and SqlEscapeBlock, Sq…
xterao Aug 21, 2025
7b39e02
Merge pull request #408 from domaframework/fix/sql-format-spacing-com…
xterao Aug 22, 2025
6fc5fe2
Move SqlCommaBlock
xterao Aug 22, 2025
624d806
Enhance SQL formatting by refining indentation logic and preventing u…
xterao Aug 22, 2025
1aa62ee
Add tests and formatting for function keyword in condition directive
xterao Aug 22, 2025
10ea283
Remove commented-out code and simplify child text length calculation …
xterao Aug 22, 2025
638527b
Remove duplicate SqlFunctionParamBlock from block class list in SqlCo…
xterao Aug 22, 2025
6a26654
Merge pull request #409 from domaframework/fix/sql-formatter-incorrec…
xterao Aug 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,28 @@ open class SqlBlock(
open val childBlocks = mutableListOf<SqlBlock>()
open var prevBlocks = emptyList<SqlBlock>()

companion object {
private const val DEFAULT_INDENT_SIZE = 4
private const val DEFAULT_TEXT_LENGTH_INCREMENT = 1
private val EXCLUDED_FROM_TEXT_LENGTH = setOf(SqlTypes.DOT, SqlTypes.RIGHT_PAREN)
private val SPACING_ONE = Spacing.createSpacing(1, 1, 0, true, 0)
private val SPACING_ZERO = Spacing.createSpacing(0, 0, 0, true, 0)
private val SPACING_ONE_NO_KEEP = Spacing.createSpacing(1, 1, 0, false, 0)
}

fun getChildrenTextLen(): Int = childBlocks.sumOf { child -> calculateChildTextLength(child) }

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

if (nonCommentChildren.isNotEmpty()) {
return child.getChildrenTextLen() + child.getNodeText().length
return when {
nonCommentChildren.isNotEmpty() -> child.getChildrenTextLen() + child.getNodeText().length
isExcludedFromTextLength(child) -> 0
else -> child.getNodeText().length + DEFAULT_TEXT_LENGTH_INCREMENT
}
if (isExcludedFromTextLength(child)) {
return 0
}
return child.getNodeText().length + 1
}

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

/**
* Checks if a conditional loop directive is registered before the parent block.
Expand Down Expand Up @@ -112,9 +119,9 @@ open class SqlBlock(
*
* @example
* ```sql
* WHERE
* /*%if status == "pending" */
* status = 'pending'
* WHERE -- grand
* /*%if status == "pending" */ -- parent
* status = 'pending' -- child
* ```
*/
protected fun isElementAfterConditionLoopDirective(): Boolean =
Expand All @@ -123,15 +130,33 @@ open class SqlBlock(
(parent.parentBlock is SqlNewGroupBlock || parent.parentBlock is SqlElConditionLoopCommentBlock)
} == true

protected fun isElementAfterConditionLoopEnd(): Boolean {
val prevChildren =
prevBlocks
.firstOrNull()
?.childBlocks

val firstConditionBlock = (prevChildren?.firstOrNull() as? SqlElConditionLoopCommentBlock)
val endBlock = firstConditionBlock?.conditionEnd
if (endBlock == null) return false
val lastBlock = prevBlocks.lastOrNull()

return endBlock.node.startOffset > (lastBlock?.node?.startOffset ?: 0)
}

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

fun getChildBlocksDropLast(
dropIndex: Int = 1,
skipCommentBlock: Boolean = true,
skipConditionLoopCommentBlock: Boolean = true,
): List<SqlBlock> {
val children = childBlocks.dropLast(dropIndex)
var children = childBlocks.dropLast(dropIndex)
if (skipCommentBlock) {
return children.filter { it !is SqlDefaultCommentBlock }
children = children.filter { it !is SqlDefaultCommentBlock }
}
if (skipConditionLoopCommentBlock) {
children = children.filter { it !is SqlElConditionLoopCommentBlock }
}
return children
}
Expand Down Expand Up @@ -167,15 +192,14 @@ open class SqlBlock(
open fun isSaveSpace(lastGroup: SqlBlock?): Boolean =
when (lastGroup) {
is SqlNewGroupBlock -> shouldSaveSpaceForNewGroup(lastGroup)
else -> {
shouldSaveSpaceForConditionLoop()
}
} == true
else -> shouldSaveSpaceForConditionLoop()
}

private fun shouldSaveSpaceForConditionLoop(): Boolean =
isConditionLoopDirectiveRegisteredBeforeParent() ||
isElementAfterConditionLoopDirective() ||
isFirstChildConditionLoopDirective()
isFirstChildConditionLoopDirective() ||
isElementAfterConditionLoopEnd()

private fun shouldSaveSpaceForNewGroup(parent: SqlNewGroupBlock): Boolean {
val prevWord = prevBlocks.lastOrNull { it !is SqlCommentBlock }
Expand All @@ -184,15 +208,22 @@ open class SqlBlock(
return false
}

return isFollowedByConditionLoop() || isPrecededByConditionLoop(parent)
return hasConditionLoopAround(parent)
}

private fun isNonBreakingKeywordCombination(
parent: SqlNewGroupBlock,
prevWord: SqlBlock?,
): Boolean =
SqlKeywordUtil.isSetLineKeyword(getNodeText(), parent.getNodeText()) ||
SqlKeywordUtil.isSetLineKeyword(getNodeText(), prevWord?.getNodeText() ?: "")
): Boolean {
val currentText = getNodeText()
val parentText = parent.getNodeText()
val prevText = prevWord?.getNodeText() ?: ""

return SqlKeywordUtil.isSetLineKeyword(currentText, parentText) ||
SqlKeywordUtil.isSetLineKeyword(currentText, prevText)
}

private fun hasConditionLoopAround(parent: SqlNewGroupBlock): Boolean = isFollowedByConditionLoop() || isPrecededByConditionLoop(parent)

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

Expand All @@ -202,6 +233,9 @@ open class SqlBlock(
lastPrevBlock.node.psi.startOffset > parent.node.psi.startOffset
}

protected fun getLastBlockHasConditionLoopDirective(): SqlElConditionLoopCommentBlock? =
(prevBlocks.lastOrNull()?.childBlocks?.firstOrNull() as? SqlElConditionLoopCommentBlock)

/**
* Creates the indentation length for the block.
*
Expand Down Expand Up @@ -249,158 +283,67 @@ open class SqlBlock(
/**
* Creates a spacing builder specifically for directive block comments.
*/
protected fun createBlockDirectiveCommentSpacingBuilder(): SqlCustomSpacingBuilder =
SqlCustomSpacingBuilder()
.withSpacing(
SqlTypes.BLOCK_COMMENT_START,
SqlTypes.EL_ID_EXPR,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.BLOCK_COMMENT_START,
SqlTypes.EL_PRIMARY_EXPR,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.BLOCK_COMMENT_START,
SqlTypes.EL_STRING,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.BLOCK_COMMENT_START,
SqlTypes.EL_NUMBER,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.BLOCK_COMMENT_START,
SqlTypes.BOOLEAN,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.BLOCK_COMMENT_START,
SqlTypes.EL_NULL,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.BLOCK_COMMENT_START,
SqlTypes.EL_FIELD_ACCESS_EXPR,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.BLOCK_COMMENT_START,
SqlTypes.EL_STATIC_FIELD_ACCESS_EXPR,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.BLOCK_COMMENT_START,
SqlTypes.HASH,
Spacing.createSpacing(0, 0, 0, true, 0),
).withSpacing(
SqlTypes.HASH,
SqlTypes.EL_ID_EXPR,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.HASH,
SqlTypes.EL_PRIMARY_EXPR,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.HASH,
SqlTypes.EL_STRING,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.HASH,
SqlTypes.EL_NUMBER,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.HASH,
SqlTypes.BOOLEAN,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.HASH,
SqlTypes.EL_NULL,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.HASH,
SqlTypes.EL_FIELD_ACCESS_EXPR,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.HASH,
SqlTypes.EL_STATIC_FIELD_ACCESS_EXPR,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.BLOCK_COMMENT_START,
SqlTypes.CARET,
Spacing.createSpacing(0, 0, 0, true, 0),
).withSpacing(
SqlTypes.CARET,
protected open fun createBlockDirectiveCommentSpacingBuilder(): SqlCustomSpacingBuilder {
val builder = SqlCustomSpacingBuilder()

// Types that need spacing after BLOCK_COMMENT_START
val typesNeedingSpaceAfterStart =
listOf(
SqlTypes.EL_ID_EXPR,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.CARET,
SqlTypes.EL_PRIMARY_EXPR,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.CARET,
SqlTypes.EL_STRING,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.CARET,
SqlTypes.EL_NUMBER,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.CARET,
SqlTypes.BOOLEAN,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.CARET,
SqlTypes.EL_NULL,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.CARET,
SqlTypes.EL_FIELD_ACCESS_EXPR,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.CARET,
SqlTypes.EL_STATIC_FIELD_ACCESS_EXPR,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.BLOCK_COMMENT_CONTENT,
SqlTypes.BLOCK_COMMENT_END,
Spacing.createSpacing(0, 0, 0, true, 0),
).withSpacing(
SqlTypes.EL_FIELD_ACCESS_EXPR,
SqlTypes.OTHER,
Spacing.createSpacing(1, 1, 0, false, 0),
).withSpacing(
SqlTypes.EL_STATIC_FIELD_ACCESS_EXPR,
SqlTypes.OTHER,
Spacing.createSpacing(1, 1, 0, false, 0),
).withSpacing(
)

// Types that need spacing before BLOCK_COMMENT_END
val typesNeedingSpaceBeforeEnd =
listOf(
SqlTypes.EL_ID_EXPR,
SqlTypes.BLOCK_COMMENT_END,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.EL_PRIMARY_EXPR,
SqlTypes.BLOCK_COMMENT_END,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.STRING,
SqlTypes.BLOCK_COMMENT_END,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.EL_NUMBER,
SqlTypes.BLOCK_COMMENT_END,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.EL_NULL,
SqlTypes.BLOCK_COMMENT_END,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.BOOLEAN,
SqlTypes.BLOCK_COMMENT_END,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.EL_FIELD_ACCESS_EXPR,
SqlTypes.BLOCK_COMMENT_END,
Spacing.createSpacing(1, 1, 0, true, 0),
).withSpacing(
SqlTypes.EL_STATIC_FIELD_ACCESS_EXPR,
SqlTypes.BLOCK_COMMENT_END,
Spacing.createSpacing(1, 1, 0, true, 0),
)

// Add spacing rules for BLOCK_COMMENT_START
typesNeedingSpaceAfterStart.forEach { type ->
builder.withSpacing(SqlTypes.BLOCK_COMMENT_START, type, SPACING_ONE)
}

// Special cases for BLOCK_COMMENT_START
builder.withSpacing(SqlTypes.BLOCK_COMMENT_START, SqlTypes.HASH, SPACING_ZERO)
builder.withSpacing(SqlTypes.BLOCK_COMMENT_START, SqlTypes.CARET, SPACING_ZERO)

// Add spacing rules for HASH
typesNeedingSpaceAfterStart.forEach { type ->
builder.withSpacing(SqlTypes.HASH, type, SPACING_ONE)
}

// Add spacing rules for CARET
typesNeedingSpaceAfterStart.forEach { type ->
builder.withSpacing(SqlTypes.CARET, type, SPACING_ONE)
}

// Special spacing rules
builder.withSpacing(SqlTypes.BLOCK_COMMENT_CONTENT, SqlTypes.BLOCK_COMMENT_END, SPACING_ZERO)
builder.withSpacing(SqlTypes.EL_FIELD_ACCESS_EXPR, SqlTypes.OTHER, SPACING_ONE_NO_KEEP)
builder.withSpacing(SqlTypes.EL_STATIC_FIELD_ACCESS_EXPR, SqlTypes.OTHER, SPACING_ONE_NO_KEEP)

// Add spacing rules before BLOCK_COMMENT_END
typesNeedingSpaceBeforeEnd.forEach { type ->
builder.withSpacing(type, SqlTypes.BLOCK_COMMENT_END, SPACING_ONE)
}

return builder
}

/**
* Returns the child indentation for the block.
*
Expand All @@ -413,10 +356,6 @@ open class SqlBlock(
Indent.getSpaceIndent(0)
}

companion object {
private const val DEFAULT_INDENT_SIZE = 4
}

/**
* Determines whether the block is a leaf node.
*
Expand Down
Loading
Loading