@@ -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.
@@ -112,9 +119,9 @@ open class SqlBlock(
112119 *
113120 * @example
114121 * ```sql
115- * WHERE
116- * /*%if status == "pending" */
117- * status = 'pending'
122+ * WHERE -- grand
123+ * /*%if status == "pending" */ -- parent
124+ * status = 'pending' -- child
118125 * ```
119126 */
120127 protected fun isElementAfterConditionLoopDirective (): Boolean =
@@ -123,15 +130,33 @@ open class SqlBlock(
123130 (parent.parentBlock is SqlNewGroupBlock || parent.parentBlock is SqlElConditionLoopCommentBlock )
124131 } == true
125132
133+ protected fun isElementAfterConditionLoopEnd (): Boolean {
134+ val prevChildren =
135+ prevBlocks
136+ .firstOrNull()
137+ ?.childBlocks
138+
139+ val firstConditionBlock = (prevChildren?.firstOrNull() as ? SqlElConditionLoopCommentBlock )
140+ val endBlock = firstConditionBlock?.conditionEnd
141+ if (endBlock == null ) return false
142+ val lastBlock = prevBlocks.lastOrNull()
143+
144+ return endBlock.node.startOffset > (lastBlock?.node?.startOffset ? : 0 )
145+ }
146+
126147 protected fun isFirstChildConditionLoopDirective (): Boolean = childBlocks.firstOrNull() is SqlElConditionLoopCommentBlock
127148
128149 fun getChildBlocksDropLast (
129150 dropIndex : Int = 1,
130151 skipCommentBlock : Boolean = true,
152+ skipConditionLoopCommentBlock : Boolean = true,
131153 ): List <SqlBlock > {
132- val children = childBlocks.dropLast(dropIndex)
154+ var children = childBlocks.dropLast(dropIndex)
133155 if (skipCommentBlock) {
134- return children.filter { it !is SqlDefaultCommentBlock }
156+ children = children.filter { it !is SqlDefaultCommentBlock }
157+ }
158+ if (skipConditionLoopCommentBlock) {
159+ children = children.filter { it !is SqlElConditionLoopCommentBlock }
135160 }
136161 return children
137162 }
@@ -167,15 +192,14 @@ open class SqlBlock(
167192 open fun isSaveSpace (lastGroup : SqlBlock ? ): Boolean =
168193 when (lastGroup) {
169194 is SqlNewGroupBlock -> shouldSaveSpaceForNewGroup(lastGroup)
170- else -> {
171- shouldSaveSpaceForConditionLoop()
172- }
173- } == true
195+ else -> shouldSaveSpaceForConditionLoop()
196+ }
174197
175198 private fun shouldSaveSpaceForConditionLoop (): Boolean =
176199 isConditionLoopDirectiveRegisteredBeforeParent() ||
177200 isElementAfterConditionLoopDirective() ||
178- isFirstChildConditionLoopDirective()
201+ isFirstChildConditionLoopDirective() ||
202+ isElementAfterConditionLoopEnd()
179203
180204 private fun shouldSaveSpaceForNewGroup (parent : SqlNewGroupBlock ): Boolean {
181205 val prevWord = prevBlocks.lastOrNull { it !is SqlCommentBlock }
@@ -184,15 +208,22 @@ open class SqlBlock(
184208 return false
185209 }
186210
187- return isFollowedByConditionLoop() || isPrecededByConditionLoop (parent)
211+ return hasConditionLoopAround (parent)
188212 }
189213
190214 private fun isNonBreakingKeywordCombination (
191215 parent : SqlNewGroupBlock ,
192216 prevWord : SqlBlock ? ,
193- ): Boolean =
194- SqlKeywordUtil .isSetLineKeyword(getNodeText(), parent.getNodeText()) ||
195- 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)
196227
197228 private fun isFollowedByConditionLoop (): Boolean = childBlocks.lastOrNull() is SqlElConditionLoopCommentBlock
198229
@@ -202,6 +233,9 @@ open class SqlBlock(
202233 lastPrevBlock.node.psi.startOffset > parent.node.psi.startOffset
203234 }
204235
236+ protected fun getLastBlockHasConditionLoopDirective (): SqlElConditionLoopCommentBlock ? =
237+ (prevBlocks.lastOrNull()?.childBlocks?.firstOrNull() as ? SqlElConditionLoopCommentBlock )
238+
205239 /* *
206240 * Creates the indentation length for the block.
207241 *
@@ -249,158 +283,67 @@ open class SqlBlock(
249283 /* *
250284 * Creates a spacing builder specifically for directive block comments.
251285 */
252- protected fun createBlockDirectiveCommentSpacingBuilder (): SqlCustomSpacingBuilder =
253- SqlCustomSpacingBuilder ()
254- .withSpacing(
255- SqlTypes .BLOCK_COMMENT_START ,
256- SqlTypes .EL_ID_EXPR ,
257- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
258- ).withSpacing(
259- SqlTypes .BLOCK_COMMENT_START ,
260- SqlTypes .EL_PRIMARY_EXPR ,
261- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
262- ).withSpacing(
263- SqlTypes .BLOCK_COMMENT_START ,
264- SqlTypes .EL_STRING ,
265- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
266- ).withSpacing(
267- SqlTypes .BLOCK_COMMENT_START ,
268- SqlTypes .EL_NUMBER ,
269- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
270- ).withSpacing(
271- SqlTypes .BLOCK_COMMENT_START ,
272- SqlTypes .BOOLEAN ,
273- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
274- ).withSpacing(
275- SqlTypes .BLOCK_COMMENT_START ,
276- SqlTypes .EL_NULL ,
277- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
278- ).withSpacing(
279- SqlTypes .BLOCK_COMMENT_START ,
280- SqlTypes .EL_FIELD_ACCESS_EXPR ,
281- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
282- ).withSpacing(
283- SqlTypes .BLOCK_COMMENT_START ,
284- SqlTypes .EL_STATIC_FIELD_ACCESS_EXPR ,
285- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
286- ).withSpacing(
287- SqlTypes .BLOCK_COMMENT_START ,
288- SqlTypes .HASH ,
289- Spacing .createSpacing(0 , 0 , 0 , true , 0 ),
290- ).withSpacing(
291- SqlTypes .HASH ,
292- SqlTypes .EL_ID_EXPR ,
293- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
294- ).withSpacing(
295- SqlTypes .HASH ,
296- SqlTypes .EL_PRIMARY_EXPR ,
297- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
298- ).withSpacing(
299- SqlTypes .HASH ,
300- SqlTypes .EL_STRING ,
301- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
302- ).withSpacing(
303- SqlTypes .HASH ,
304- SqlTypes .EL_NUMBER ,
305- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
306- ).withSpacing(
307- SqlTypes .HASH ,
308- SqlTypes .BOOLEAN ,
309- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
310- ).withSpacing(
311- SqlTypes .HASH ,
312- SqlTypes .EL_NULL ,
313- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
314- ).withSpacing(
315- SqlTypes .HASH ,
316- SqlTypes .EL_FIELD_ACCESS_EXPR ,
317- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
318- ).withSpacing(
319- SqlTypes .HASH ,
320- SqlTypes .EL_STATIC_FIELD_ACCESS_EXPR ,
321- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
322- ).withSpacing(
323- SqlTypes .BLOCK_COMMENT_START ,
324- SqlTypes .CARET ,
325- Spacing .createSpacing(0 , 0 , 0 , true , 0 ),
326- ).withSpacing(
327- 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 (
328292 SqlTypes .EL_ID_EXPR ,
329- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
330- ).withSpacing(
331- SqlTypes .CARET ,
332293 SqlTypes .EL_PRIMARY_EXPR ,
333- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
334- ).withSpacing(
335- SqlTypes .CARET ,
336294 SqlTypes .EL_STRING ,
337- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
338- ).withSpacing(
339- SqlTypes .CARET ,
340295 SqlTypes .EL_NUMBER ,
341- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
342- ).withSpacing(
343- SqlTypes .CARET ,
344296 SqlTypes .BOOLEAN ,
345- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
346- ).withSpacing(
347- SqlTypes .CARET ,
348297 SqlTypes .EL_NULL ,
349- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
350- ).withSpacing(
351- SqlTypes .CARET ,
352298 SqlTypes .EL_FIELD_ACCESS_EXPR ,
353- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
354- ).withSpacing(
355- SqlTypes .CARET ,
356299 SqlTypes .EL_STATIC_FIELD_ACCESS_EXPR ,
357- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
358- ).withSpacing(
359- SqlTypes .BLOCK_COMMENT_CONTENT ,
360- SqlTypes .BLOCK_COMMENT_END ,
361- Spacing .createSpacing(0 , 0 , 0 , true , 0 ),
362- ).withSpacing(
363- SqlTypes .EL_FIELD_ACCESS_EXPR ,
364- SqlTypes .OTHER ,
365- Spacing .createSpacing(1 , 1 , 0 , false , 0 ),
366- ).withSpacing(
367- SqlTypes .EL_STATIC_FIELD_ACCESS_EXPR ,
368- SqlTypes .OTHER ,
369- Spacing .createSpacing(1 , 1 , 0 , false , 0 ),
370- ).withSpacing(
300+ )
301+
302+ // Types that need spacing before BLOCK_COMMENT_END
303+ val typesNeedingSpaceBeforeEnd =
304+ listOf (
371305 SqlTypes .EL_ID_EXPR ,
372- SqlTypes .BLOCK_COMMENT_END ,
373- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
374- ).withSpacing(
375306 SqlTypes .EL_PRIMARY_EXPR ,
376- SqlTypes .BLOCK_COMMENT_END ,
377- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
378- ).withSpacing(
379307 SqlTypes .STRING ,
380- SqlTypes .BLOCK_COMMENT_END ,
381- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
382- ).withSpacing(
383308 SqlTypes .EL_NUMBER ,
384- SqlTypes .BLOCK_COMMENT_END ,
385- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
386- ).withSpacing(
387309 SqlTypes .EL_NULL ,
388- SqlTypes .BLOCK_COMMENT_END ,
389- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
390- ).withSpacing(
391310 SqlTypes .BOOLEAN ,
392- SqlTypes .BLOCK_COMMENT_END ,
393- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
394- ).withSpacing(
395311 SqlTypes .EL_FIELD_ACCESS_EXPR ,
396- SqlTypes .BLOCK_COMMENT_END ,
397- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
398- ).withSpacing(
399312 SqlTypes .EL_STATIC_FIELD_ACCESS_EXPR ,
400- SqlTypes .BLOCK_COMMENT_END ,
401- Spacing .createSpacing(1 , 1 , 0 , true , 0 ),
402313 )
403314
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+
404347 /* *
405348 * Returns the child indentation for the block.
406349 *
@@ -413,10 +356,6 @@ open class SqlBlock(
413356 Indent .getSpaceIndent(0 )
414357 }
415358
416- companion object {
417- private const val DEFAULT_INDENT_SIZE = 4
418- }
419-
420359 /* *
421360 * Determines whether the block is a leaf node.
422361 *
0 commit comments