@@ -63,9 +63,17 @@ open class SqlBlock(
6363 var groupIndentLen : Int ,
6464 )
6565
66+ // Maintain the conditional loop directive block associated with itself.
67+ var conditionLoopDirective: SqlElConditionLoopCommentBlock ? = null
68+
69+ // A flag that exists within a conditional loop directive but is not associated with the directive
70+ // (representing the top element of multiple lines).
71+ var multipleInlineDirective = false
72+
6673 open var parentBlock: SqlBlock ? = null
6774 open val childBlocks = mutableListOf<SqlBlock >()
6875 open var prevBlocks = emptyList<SqlBlock >()
76+ open val offset = 0
6977
7078 companion object {
7179 private const val DEFAULT_INDENT_SIZE = 4
@@ -93,76 +101,6 @@ open class SqlBlock(
93101
94102 private fun isExcludedFromTextLength (block : SqlBlock ): Boolean = block.node.elementType in EXCLUDED_FROM_TEXT_LENGTH
95103
96- /* *
97- * Checks if a conditional loop directive is registered before the parent block.
98- *
99- * @note
100- * If the next element after a conditional directive is not a conditional directive block,
101- * the directive becomes a child of the next element block.
102- * Therefore, if the first element in [childBlocks] is a conditional directive,
103- * it can be determined that—syntactically—the conditional directive was placed immediately before the current block.
104- */
105- protected fun isConditionLoopDirectiveRegisteredBeforeParent (): Boolean {
106- val firstPrevBlock = (prevBlocks.lastOrNull() as ? SqlElConditionLoopCommentBlock )
107- parentBlock?.let { parent ->
108- return (childBlocks.firstOrNull() as ? SqlElConditionLoopCommentBlock )?.isBeforeParentBlock() == true ||
109- firstPrevBlock != null &&
110- firstPrevBlock.conditionEnd != null &&
111- firstPrevBlock.node.startOffset > parent.node.startOffset
112- }
113- return false
114- }
115-
116- /* *
117- * Determines if this is the element immediately after a conditional loop directive.
118- *
119- * @note
120- * The parent conditional loop directive becomes a child of the element immediately after the conditional loop directive.
121- * In the following case, "%if" is a child of "status", and the following "=" and "'pending'" are children of "%if".
122- * Therefore, set the condition to break line only when the parent of the conditional loop directive is a group block.
123- *
124- * @example
125- * ```sql
126- * WHERE -- grand
127- * /*%if status == "pending" */ -- parent
128- * status = 'pending' -- child
129- * ```
130- */
131- protected fun isElementAfterConditionLoopDirective (): Boolean =
132- (parentBlock as ? SqlElConditionLoopCommentBlock )?.let { parent ->
133- parent.childBlocks.firstOrNull() == this &&
134- (parent.parentBlock is SqlNewGroupBlock || parent.isParentConditionLoopDirective())
135- } == true
136-
137- protected fun isElementAfterConditionLoopEnd (): Boolean {
138- val prevChildren =
139- prevBlocks
140- .firstOrNull()
141- ?.childBlocks
142-
143- val firstConditionBlock = prevChildren?.firstOrNull { it is SqlElConditionLoopCommentBlock } as ? SqlElConditionLoopCommentBlock
144- val endBlock =
145- findConditionEndBlock(firstConditionBlock)
146- if (endBlock == null ) return false
147- val lastBlock = prevBlocks.lastOrNull()
148-
149- return endBlock.node.startOffset > (lastBlock?.node?.startOffset ? : 0 )
150- }
151-
152- private fun findConditionEndBlock (firstConditionBlock : SqlElConditionLoopCommentBlock ? ): SqlElConditionLoopCommentBlock ? =
153- (
154- firstConditionBlock?.conditionEnd
155- ? : (
156- firstConditionBlock?.childBlocks?.lastOrNull {
157- it is SqlElConditionLoopCommentBlock
158- } as ? SqlElConditionLoopCommentBlock
159- )?.conditionEnd
160- )
161-
162- fun isParentConditionLoopDirective (): Boolean = parentBlock is SqlElConditionLoopCommentBlock
163-
164- protected fun isFirstChildConditionLoopDirective (): Boolean = childBlocks.firstOrNull() is SqlElConditionLoopCommentBlock
165-
166104 fun getChildBlocksDropLast (
167105 dropIndex : Int = 1,
168106 skipCommentBlock : Boolean = true,
@@ -185,11 +123,103 @@ open class SqlBlock(
185123 0 ,
186124 )
187125
126+ /* *
127+ * Calculate indentation and line breaks based on the parent block and conditional loop directives with no dependency target set.
128+ * @param lastGroup The last group block
129+ */
188130 open fun setParentGroupBlock (lastGroup : SqlBlock ? ) {
189131 parentBlock = lastGroup
190- prevBlocks = parentBlock?.childBlocks?.toList() ? : emptyList ()
132+ setPrevBlocks ()
191133 parentBlock?.addChildBlock(this )
192134 setParentPropertyBlock(lastGroup)
135+ setIndentLen()
136+ }
137+
138+ fun setPrevBlocks (parent : SqlBlock ? = parentBlock) {
139+ parent?.let { p ->
140+ // Retrieve the first conditional loop directive and the closing tag of the last conditional loop directive.
141+ val firstConditionStart = p.childBlocks.firstOrNull { it.conditionLoopDirective != null }?.conditionLoopDirective
142+ val lastConditionEnd =
143+ p.childBlocks
144+ .lastOrNull {
145+ it.conditionLoopDirective != null &&
146+ it.conditionLoopDirective?.conditionEnd != null
147+ }?.conditionLoopDirective
148+ ?.conditionEnd
149+ var openDirective: SqlElConditionLoopCommentBlock ? = null
150+
151+ val filterBlockInlineOpenDirectives =
152+ if (firstConditionStart != null && lastConditionEnd != null ) {
153+ p.childBlocks.filterNot {
154+ it.node.startOffset in
155+ (firstConditionStart.node.startOffset until lastConditionEnd.node.startOffset)
156+ }
157+ } else if (firstConditionStart != null ) {
158+ openDirective = firstConditionStart
159+ p.childBlocks.filter { it.node.startOffset >= firstConditionStart.node.startOffset }
160+ } else {
161+ p.childBlocks
162+ }
163+
164+ prevBlocks = filterBlockInlineOpenDirectives.filter { it != this }
165+ }
166+ }
167+
168+ /* *
169+ * Indentation calculation
170+ *
171+ * * When `multipleInlineDirective` is **true**: sibling blocks whose parent is **outside** the conditional loop directive.
172+ * * When `multipleInlineDirective` is **false**: sibling blocks whose parent is **inside** the conditional loop directive, or the block body that the conditional loop directive depends on.
173+ *
174+ */
175+ protected fun setIndentLen (baseDirective : SqlElConditionLoopCommentBlock ? = conditionLoopDirective): Int {
176+ indent.indentLen =
177+ if (multipleInlineDirective) {
178+ baseDirective?.indent?.indentLen ? : indent.indentLen
179+ } else {
180+ if (baseDirective?.getDependsOnBlock() == this ) {
181+ baseDirective.indent.indentLen // The block body that the conditional loop directive depends on.
182+ } else {
183+ createBlockIndentLen() // Sibling blocks whose parent is within a conditional loop directive.
184+ }
185+ }
186+ return indent.indentLen
187+ }
188+
189+ fun createBlockIndentLenDirective (
190+ parent : SqlBlock ? ,
191+ dependDirective : SqlElConditionLoopCommentBlock ? ,
192+ ) {
193+ val dependDirectiveOnBlock = dependDirective?.getDependsOnBlock()
194+ if (dependDirectiveOnBlock == null ) {
195+ conditionLoopDirective = dependDirective
196+ conditionLoopDirective?.setDependsOnBlock(this )
197+ conditionLoopDirective?.createBlockIndentLenFromDependOn(this )
198+ }
199+ setPrevBlocks(parent)
200+ // Check its own parent block and the parent of the block that `notDependDirective` depends on,
201+ // and adjust the indentation as needed so that it fits within the conditional loop directive block.
202+ val directiveDependent = conditionLoopDirective?.getDependsOnBlock()
203+ if (directiveDependent == null || parent == directiveDependent.parentBlock) {
204+ // Search among sibling blocks for those associated with a conditional loop directive.
205+ // Even if a sibling block is associated with a conditional loop directive,
206+ // there are cases where the indentation is aligned with the parent rather than the conditional loop directive.
207+ val inlineDirectiveParentBlock =
208+ parent?.let { p -> p.node.startOffset >= (dependDirective?.node?.startOffset ? : 0 ) } == true
209+ multipleInlineDirective = dependDirective?.getDependsOnBlock() != this && ! inlineDirectiveParentBlock
210+ setIndentLen(dependDirective)
211+ }
212+ }
213+
214+ /* *
215+ * Trace back from the associated directive and recalculate the indentation of the nested directives.
216+ */
217+ fun recalculateDirectiveIndent () {
218+ conditionLoopDirective?.let { directive ->
219+ directive.recalculateIndentLen(createBlockIndentLen())
220+ indent.indentLen = directive.indent.indentLen
221+ indent.groupIndentLen = createGroupIndentLen()
222+ }
193223 }
194224
195225 open fun setParentPropertyBlock (lastGroup : SqlBlock ? ) {
@@ -206,18 +236,15 @@ open class SqlBlock(
206236
207237 fun isEnableFormat (): Boolean = enableFormat
208238
239+ /* *
240+ * Block-specific line break determination.
241+ */
209242 open fun isSaveSpace (lastGroup : SqlBlock ? ): Boolean =
210243 when (lastGroup) {
211244 is SqlNewGroupBlock -> shouldSaveSpaceForNewGroup(lastGroup)
212- else -> shouldSaveSpaceForConditionLoop()
245+ else -> false
213246 }
214247
215- private fun shouldSaveSpaceForConditionLoop (): Boolean =
216- isConditionLoopDirectiveRegisteredBeforeParent() ||
217- isElementAfterConditionLoopDirective() ||
218- isFirstChildConditionLoopDirective() ||
219- isElementAfterConditionLoopEnd()
220-
221248 private fun shouldSaveSpaceForNewGroup (parent : SqlNewGroupBlock ): Boolean {
222249 val prevWord = prevBlocks.lastOrNull { it !is SqlCommentBlock }
223250
@@ -250,16 +277,16 @@ open class SqlBlock(
250277 lastPrevBlock.node.psi.startOffset > parent.node.psi.startOffset
251278 }
252279
253- protected fun getLastBlockHasConditionLoopDirective (): SqlElConditionLoopCommentBlock ? =
254- (prevBlocks.lastOrNull()?.childBlocks?.firstOrNull() as ? SqlElConditionLoopCommentBlock )
255-
256280 /* *
257- * Creates the indentation length for the block.
258- *
281+ * Set the indentation for line breaks caused by conditional loop directives based on the parent block and the conditional loop directive.
259282 * @return The number of spaces to use for indentation
260283 */
261284 open fun createBlockIndentLen (): Int = 0
262285
286+ /* *
287+ * Calculate the indentation to apply to its own child blocks.
288+ * @return The number of spaces to use for child block indentation
289+ */
263290 open fun createGroupIndentLen (): Int = 0
264291
265292 open fun getBlock (child : ASTNode ): SqlBlock = this
@@ -329,31 +356,25 @@ open class SqlBlock(
329356 SqlTypes .EL_STATIC_FIELD_ACCESS_EXPR ,
330357 )
331358
332- // Add spacing rules for BLOCK_COMMENT_START
333359 typesNeedingSpaceAfterStart.forEach { type ->
334360 builder.withSpacing(SqlTypes .BLOCK_COMMENT_START , type, SPACING_ONE )
335361 }
336362
337- // Special cases for BLOCK_COMMENT_START
338363 builder.withSpacing(SqlTypes .BLOCK_COMMENT_START , SqlTypes .HASH , SPACING_ZERO )
339364 builder.withSpacing(SqlTypes .BLOCK_COMMENT_START , SqlTypes .CARET , SPACING_ZERO )
340365
341- // Add spacing rules for HASH
342366 typesNeedingSpaceAfterStart.forEach { type ->
343367 builder.withSpacing(SqlTypes .HASH , type, SPACING_ONE )
344368 }
345369
346- // Add spacing rules for CARET
347370 typesNeedingSpaceAfterStart.forEach { type ->
348371 builder.withSpacing(SqlTypes .CARET , type, SPACING_ONE )
349372 }
350373
351- // Special spacing rules
352374 builder.withSpacing(SqlTypes .BLOCK_COMMENT_CONTENT , SqlTypes .BLOCK_COMMENT_END , SPACING_ZERO )
353375 builder.withSpacing(SqlTypes .EL_FIELD_ACCESS_EXPR , SqlTypes .OTHER , SPACING_ONE_NO_KEEP )
354376 builder.withSpacing(SqlTypes .EL_STATIC_FIELD_ACCESS_EXPR , SqlTypes .OTHER , SPACING_ONE_NO_KEEP )
355377
356- // Add spacing rules before BLOCK_COMMENT_END
357378 typesNeedingSpaceBeforeEnd.forEach { type ->
358379 builder.withSpacing(type, SqlTypes .BLOCK_COMMENT_END , SPACING_ONE )
359380 }
@@ -365,20 +386,13 @@ open class SqlBlock(
365386 children : List <SqlBlock >,
366387 parent : SqlBlock ,
367388 ): Int {
368- // Add the parent's text length to the indentation if the parent is a conditional loop directive.
369- val directiveParentIndent =
370- if (parent is SqlElConditionLoopCommentBlock ) {
371- parent.parentBlock
372- ?.getNodeText()
373- ?.length ? : 0
374- } else {
375- 0
376- }
377-
378389 var prevBlock: SqlBlock ? = null
379- return children
380- .filter { it !is SqlDefaultCommentBlock && it !is SqlElConditionLoopCommentBlock }
381- .sumOf { prev ->
390+ val prevChildren =
391+ children
392+ .filter { it !is SqlDefaultCommentBlock }
393+
394+ val prevSumLength =
395+ prevChildren.sumOf { prev ->
382396 val sum =
383397 prev
384398 .getChildrenTextLen()
@@ -397,8 +411,8 @@ open class SqlBlock(
397411 )
398412 prevBlock = prev
399413 return @sumOf sum
400- }.plus(parent.indent.groupIndentLen)
401- .plus(directiveParentIndent )
414+ }
415+ return prevSumLength .plus(parent.indent.groupIndentLen )
402416 }
403417
404418 fun isOperationSymbol (): Boolean =
0 commit comments