Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -17,6 +17,7 @@ package org.domaframework.doma.intellij.formatter.block

import com.intellij.lang.ASTNode
import com.intellij.psi.formatter.common.AbstractBlock
import org.domaframework.doma.intellij.formatter.block.comment.SqlElConditionLoopCommentBlock
import org.domaframework.doma.intellij.formatter.block.conflict.SqlDoGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.SqlNewGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock
Expand Down Expand Up @@ -95,7 +96,11 @@ open class SqlKeywordBlock(
}

else -> {
parentBlock?.indent?.groupIndentLen ?: 1
if (parentBlock is SqlElConditionLoopCommentBlock) {
parentBlock?.indent?.groupIndentLen ?: 1
} else {
parentBlock?.indent?.groupIndentLen?.plus(1) ?: 1
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright Doma Tools Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.domaframework.doma.intellij.formatter.block.group.keyword.option

import com.intellij.lang.ASTNode
import org.domaframework.doma.intellij.formatter.block.SqlBlock
import org.domaframework.doma.intellij.formatter.block.comment.SqlElConditionLoopCommentBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.create.SqlCreateTableColumnDefinitionRawGroupBlock
import org.domaframework.doma.intellij.formatter.util.IndentType
import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext

class SqlExistsGroupBlock(
node: ASTNode,
context: SqlBlockFormattingContext,
) : SqlKeywordGroupBlock(node, IndentType.OPTIONS, context) {
override fun setParentGroupBlock(lastGroup: SqlBlock?) {
super.setParentGroupBlock(lastGroup)
indent.indentLen = createBlockIndentLen()
indent.groupIndentLen = createGroupIndentLen()
}

override fun createBlockIndentLen(): Int {
parentBlock?.let { parent ->
if (parent.parentBlock is SqlElConditionLoopCommentBlock) {
return parent.indent.groupIndentLen
}
}
return parentBlock?.indent?.groupIndentLen?.plus(1) ?: 1
}

override fun createGroupIndentLen(): Int {
val parentGroupIndent = parentBlock?.indent?.groupIndentLen ?: 0
return topKeywordBlocks.sumOf { it.getNodeText().length.plus(1) }.plus(parentGroupIndent).minus(1)
}

override fun isSaveSpace(lastGroup: SqlBlock?): Boolean {
if (lastGroup is SqlCreateTableColumnDefinitionRawGroupBlock) {
return false
}
return super.isSaveSpace(lastGroup)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,7 @@ open class SqlSubQueryGroupBlock(
is SqlJoinQueriesGroupBlock -> return parent.indent.indentLen
is SqlJoinGroupBlock -> return parent.indent.groupIndentLen.plus(1)
else -> {
val children =
prevChildren?.filter {
it !is SqlDefaultCommentBlock &&
(parent as? SqlKeywordGroupBlock)?.topKeywordBlocks?.contains(it) == false
}
val children = prevChildren?.filter { shouldIncludeChildBlock(it, parent) }
// Retrieve the list of child blocks excluding the conditional directive that appears immediately before this block,
// as it is already included as a child block.
val sumChildren =
Expand All @@ -90,6 +86,13 @@ open class SqlSubQueryGroupBlock(
}
} ?: offset

private fun shouldIncludeChildBlock(
block: SqlBlock,
parent: SqlBlock,
): Boolean =
block !is SqlDefaultCommentBlock &&
(parent as? SqlKeywordGroupBlock)?.topKeywordBlocks?.contains(block) == false

override fun createGroupIndentLen(): Int {
parentBlock?.let { parent ->
if (parent is SqlJoinQueriesGroupBlock) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordG
import org.domaframework.doma.intellij.formatter.block.group.keyword.create.SqlCreateViewGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.inline.SqlInlineGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.inline.SqlInlineSecondGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.option.SqlExistsGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.option.SqlInGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.option.SqlLateralGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.second.SqlReturningGroupBlock
Expand Down Expand Up @@ -64,6 +65,7 @@ class SqlBlockRelationBuilder(
SqlColumnDefinitionRawGroupBlock::class,
SqlLateralGroupBlock::class,
SqlInGroupBlock::class,
SqlExistsGroupBlock::class,
)

private val TOP_LEVEL_EXPECTED_TYPES =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ object JoinClauseHandler {
child: ASTNode,
sqlBlockFormattingCtx: SqlBlockFormattingContext,
): SqlBlock =
if (SqlKeywordUtil.Companion.isJoinKeyword(keywordText)) {
if (SqlKeywordUtil.isJoinKeyword(keywordText)) {
SqlJoinGroupBlock(
child,
sqlBlockFormattingCtx,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import org.domaframework.doma.intellij.formatter.block.comment.SqlBlockCommentBl
import org.domaframework.doma.intellij.formatter.block.comment.SqlCommentBlock
import org.domaframework.doma.intellij.formatter.block.comment.SqlElBlockCommentBlock
import org.domaframework.doma.intellij.formatter.block.comment.SqlElConditionLoopCommentBlock
import org.domaframework.doma.intellij.formatter.block.conflict.OnConflictKeywordType
import org.domaframework.doma.intellij.formatter.block.conflict.SqlConflictClauseBlock
import org.domaframework.doma.intellij.formatter.block.conflict.SqlDoGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnBlock
Expand All @@ -41,12 +40,8 @@ import org.domaframework.doma.intellij.formatter.block.group.keyword.condition.S
import org.domaframework.doma.intellij.formatter.block.group.keyword.create.SqlCreateKeywordGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.create.SqlCreateTableColumnDefinitionGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.create.SqlCreateTableColumnDefinitionRawGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.create.SqlCreateViewGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.inline.SqlInlineGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.inline.SqlInlineSecondGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.insert.SqlInsertQueryGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.option.SqlInGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.option.SqlLateralGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.option.SqlSecondOptionKeywordGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.second.SqlFromGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.second.SqlSecondKeywordBlock
Expand Down Expand Up @@ -99,69 +94,26 @@ class SqlBlockGenerator(
formatMode,
)

private val keywordBlockFactory = SqlKeywordBlockFactory(sqlBlockFormattingCtx)

fun getKeywordBlock(
child: ASTNode,
lastGroupBlock: SqlBlock?,
): SqlBlock {
// Because we haven't yet set the parent-child relationship of the block,
// the parent group references groupTopNodeIndexHistory.
val keywordText = child.text.lowercase()
val indentLevel = SqlKeywordUtil.getIndentType(keywordText)

if (indentLevel.isNewLineGroup()) {
return getKeywordGroupBlock(indentLevel, keywordText, child, lastGroupBlock)
}

when (indentLevel) {
IndentType.INLINE -> {
if (!SqlKeywordUtil.isSetLineKeyword(
child.text,
lastGroupBlock?.getNodeText() ?: "",
)
) {
return SqlInlineGroupBlock(child, sqlBlockFormattingCtx)
}
}

IndentType.ATTACHED -> {
if (lastGroupBlock is SqlCreateKeywordGroupBlock) {
lastGroupBlock.setCreateQueryType(child.text)
return SqlKeywordBlock(child, indentLevel, sqlBlockFormattingCtx)
}
}

IndentType.OPTIONS -> {
if (keywordText == "as") {
val parentCreateBlock =
lastGroupBlock as? SqlCreateKeywordGroupBlock
?: lastGroupBlock?.parentBlock as? SqlCreateKeywordGroupBlock
if (parentCreateBlock != null && parentCreateBlock.createType == CreateQueryType.VIEW) {
return SqlCreateViewGroupBlock(child, sqlBlockFormattingCtx)
}
}
if (keywordText == "lateral") {
return SqlLateralGroupBlock(child, sqlBlockFormattingCtx)
}
if (keywordText == "in") {
return SqlInGroupBlock(
child,
sqlBlockFormattingCtx,
)
}
}

IndentType.CONFLICT -> {
if (lastGroupBlock is SqlConflictClauseBlock) {
lastGroupBlock.conflictType = OnConflictKeywordType.of(keywordText)
return SqlKeywordBlock(child, indentLevel, sqlBlockFormattingCtx)
} else {
return SqlConflictClauseBlock(child, sqlBlockFormattingCtx)
}
}

else -> return SqlKeywordBlock(child, indentLevel, sqlBlockFormattingCtx)
return when (indentLevel) {
IndentType.INLINE -> keywordBlockFactory.createInlineBlock(child, lastGroupBlock)
IndentType.ATTACHED -> keywordBlockFactory.createAttachedBlock(child, lastGroupBlock)
IndentType.OPTIONS -> keywordBlockFactory.createOptionsBlock(keywordText, child, lastGroupBlock)
IndentType.CONFLICT -> keywordBlockFactory.createConflictBlock(keywordText, child, lastGroupBlock)
else -> SqlKeywordBlock(child, indentLevel, sqlBlockFormattingCtx)
}
return SqlKeywordBlock(child, indentLevel, sqlBlockFormattingCtx)
}

private fun getKeywordGroupBlock(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* Copyright Doma Tools Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.domaframework.doma.intellij.formatter.util

import com.intellij.lang.ASTNode
import org.domaframework.doma.intellij.formatter.block.SqlBlock
import org.domaframework.doma.intellij.formatter.block.SqlKeywordBlock
import org.domaframework.doma.intellij.formatter.block.comment.SqlElConditionLoopCommentBlock
import org.domaframework.doma.intellij.formatter.block.conflict.OnConflictKeywordType
import org.domaframework.doma.intellij.formatter.block.conflict.SqlConflictClauseBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.condition.SqlConditionKeywordGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.create.SqlCreateKeywordGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.create.SqlCreateViewGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.inline.SqlInlineGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.option.SqlExistsGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.option.SqlInGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.option.SqlLateralGroupBlock
import org.domaframework.doma.intellij.formatter.block.group.keyword.second.SqlWhereGroupBlock

/**
* Factory class for creating SQL keyword blocks based on indent type and context
*/
class SqlKeywordBlockFactory(
private val sqlBlockFormattingCtx: SqlBlockFormattingContext,
) {
fun createInlineBlock(
child: ASTNode,
lastGroupBlock: SqlBlock?,
): SqlBlock =
if (!SqlKeywordUtil.isSetLineKeyword(
child.text,
lastGroupBlock?.getNodeText() ?: "",
)
) {
SqlInlineGroupBlock(child, sqlBlockFormattingCtx)
} else {
SqlKeywordBlock(child, IndentType.INLINE, sqlBlockFormattingCtx)
}

fun createAttachedBlock(
child: ASTNode,
lastGroupBlock: SqlBlock?,
): SqlBlock {
if (lastGroupBlock is SqlCreateKeywordGroupBlock) {
lastGroupBlock.setCreateQueryType(child.text)
}
return SqlKeywordBlock(child, IndentType.ATTACHED, sqlBlockFormattingCtx)
}

fun createOptionsBlock(
keywordText: String,
child: ASTNode,
lastGroupBlock: SqlBlock?,
): SqlBlock {
// Handle AS keyword for CREATE VIEW
if (keywordText == "as") {
val parentCreateBlock = findParentCreateBlock(lastGroupBlock)
if (parentCreateBlock?.createType == CreateQueryType.VIEW) {
return SqlCreateViewGroupBlock(child, sqlBlockFormattingCtx)
}
}

// Handle LATERAL keyword
if (keywordText == "lateral") {
return SqlLateralGroupBlock(child, sqlBlockFormattingCtx)
}

// Handle IN keyword
if (keywordText == "in") {
return SqlInGroupBlock(child, sqlBlockFormattingCtx)
}

// Handle EXISTS/NOT EXISTS keywords
if (SqlKeywordUtil.isExistsKeyword(keywordText)) {
return createExistsBlock(keywordText, child, lastGroupBlock)
}

return SqlKeywordBlock(child, IndentType.OPTIONS, sqlBlockFormattingCtx)
}

fun createConflictBlock(
keywordText: String,
child: ASTNode,
lastGroupBlock: SqlBlock?,
): SqlBlock =
if (lastGroupBlock is SqlConflictClauseBlock) {
lastGroupBlock.conflictType = OnConflictKeywordType.of(keywordText)
SqlKeywordBlock(child, IndentType.CONFLICT, sqlBlockFormattingCtx)
} else {
SqlConflictClauseBlock(child, sqlBlockFormattingCtx)
}

private fun findParentCreateBlock(block: SqlBlock?): SqlCreateKeywordGroupBlock? =
when (block) {
is SqlCreateKeywordGroupBlock -> block
else -> block?.parentBlock as? SqlCreateKeywordGroupBlock
}

private fun createExistsBlock(
keywordText: String,
child: ASTNode,
lastGroupBlock: SqlBlock?,
): SqlBlock {
// If already in EXISTS group, just return a keyword block
if (lastGroupBlock is SqlExistsGroupBlock) {
return SqlKeywordBlock(child, IndentType.OPTIONS, sqlBlockFormattingCtx)
}

// Check for ELSE condition or NOT keyword outside WHERE/condition context
val shouldCreateExistsGroup =
when {
lastGroupBlock is SqlElConditionLoopCommentBlock && lastGroupBlock.conditionType.isElse() -> true
keywordText == "not" && !isInConditionContext(lastGroupBlock) -> true
else -> false
}

return if (shouldCreateExistsGroup) {
SqlExistsGroupBlock(child, sqlBlockFormattingCtx)
} else {
SqlKeywordBlock(child, IndentType.OPTIONS, sqlBlockFormattingCtx)
}
}

private fun isInConditionContext(block: SqlBlock?): Boolean = block is SqlConditionKeywordGroupBlock || block is SqlWhereGroupBlock
}
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,15 @@ class SqlKeywordUtil {

fun isWithOptionKeyword(keyword: String): Boolean = WITH_OPTION_KEYWORDS.contains(keyword.lowercase())

private val EXISTS_KEYWORDS =
setOf(
"if",
"exists",
"not",
)

fun isExistsKeyword(keyword: String): Boolean = EXISTS_KEYWORDS.contains(keyword.lowercase())

private val SET_LINE_KEYWORDS =
mapOf(
"into" to setOf("insert"),
Expand All @@ -307,6 +316,7 @@ class SqlKeywordUtil {
"by" to setOf("group", "order", "first"),
"and" to setOf("between"),
"if" to setOf("table", "create"),
"exists" to setOf("if", "where"),
"conflict" to setOf("on"),
"nothing" to setOf("do"),
"constraint" to setOf("on"),
Expand Down
Loading
Loading