diff --git a/src/main/java/org/domaframework/doma/intellij/Sql.flex b/src/main/java/org/domaframework/doma/intellij/Sql.flex index 95b48860..e3975b30 100644 --- a/src/main/java/org/domaframework/doma/intellij/Sql.flex +++ b/src/main/java/org/domaframework/doma/intellij/Sql.flex @@ -29,19 +29,25 @@ import org.domaframework.doma.intellij.psi.SqlTypes; "as", "asc", "between", + "breadth", "by", "case", "change", "check", + "conflict", + "constraint", "column", + "collate", "comment", "create", "cross", + "cycle", "database", "default", "delete", "desc", "distinct", + "do", "drop", "else", "end", @@ -67,7 +73,9 @@ import org.domaframework.doma.intellij.psi.SqlTypes; "like", "limit", "not", + "nothing", "null", + "materialized", "modify", "offset", "on", @@ -77,7 +85,10 @@ import org.domaframework.doma.intellij.psi.SqlTypes; "primary", "references", "rename", + "returning", + "recursive", "right", + "search", "select", "set", "table", @@ -88,10 +99,12 @@ import org.domaframework.doma.intellij.psi.SqlTypes; "union", "unique", "update", + "using", "values", "view", "when", - "where" + "where", + "with" ); // COLUMN DataTypes @@ -124,6 +137,7 @@ import org.domaframework.doma.intellij.psi.SqlTypes; ); private static boolean isKeyword(CharSequence word) { + // TODO Reads plugin settings and allows users to register arbitrary keywords return KEYWORDS.contains(word.toString().toLowerCase()); } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/util/TypeUtil.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/util/TypeUtil.kt index b418f280..57867f0c 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/util/TypeUtil.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/util/TypeUtil.kt @@ -24,6 +24,8 @@ import org.domaframework.doma.intellij.extension.getJavaClazz import org.domaframework.doma.intellij.extension.psi.getClassAnnotation import org.domaframework.doma.intellij.extension.psi.isDomain import org.domaframework.doma.intellij.extension.psi.isEntity +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import kotlin.reflect.KClass object TypeUtil { /** @@ -94,4 +96,15 @@ object TypeUtil { if (type == null) return false return PsiTypeChecker.isBaseClassType(type) || DomaClassName.isOptionalWrapperType(type.canonicalText) } + + /** + * Determines whether the specified class instance matches. + */ + fun isExpectedClassType( + expectedClasses: List>, + childBlock: SqlBlock?, + ): Boolean = + expectedClasses.any { clazz -> + clazz.isInstance(childBlock) + } } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlBlockUtil.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlBlockUtil.kt deleted file mode 100644 index 94980c4b..00000000 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlBlockUtil.kt +++ /dev/null @@ -1,303 +0,0 @@ -/* - * 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 - -import com.intellij.formatting.FormattingMode -import com.intellij.lang.ASTNode -import com.intellij.psi.PsiComment -import com.intellij.psi.util.PsiTreeUtil -import org.domaframework.doma.intellij.extension.expr.isConditionOrLoopDirective -import org.domaframework.doma.intellij.formatter.block.SqlBlock -import org.domaframework.doma.intellij.formatter.block.SqlBlockCommentBlock -import org.domaframework.doma.intellij.formatter.block.SqlColumnBlock -import org.domaframework.doma.intellij.formatter.block.SqlCommaBlock -import org.domaframework.doma.intellij.formatter.block.SqlCommentBlock -import org.domaframework.doma.intellij.formatter.block.SqlKeywordBlock -import org.domaframework.doma.intellij.formatter.block.SqlTableBlock -import org.domaframework.doma.intellij.formatter.block.SqlWordBlock -import org.domaframework.doma.intellij.formatter.block.expr.SqlElBlockCommentBlock -import org.domaframework.doma.intellij.formatter.block.expr.SqlElConditionLoopCommentBlock -import org.domaframework.doma.intellij.formatter.block.group.SqlColumnDefinitionRawGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlCreateKeywordGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlInlineGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlInlineSecondGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlInsertKeywordGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlJoinGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlUpdateKeywordGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlColumnDefinitionGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlColumnGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlDataTypeParamBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlFunctionParamBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlInsertColumnGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlParallelListBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubQueryGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlUpdateColumnGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlUpdateValueGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlViewGroupBlock -import org.domaframework.doma.intellij.psi.SqlCustomElCommentExpr -import org.domaframework.doma.intellij.psi.SqlTypes - -class SqlBlockUtil( - sqlBlock: SqlBlock, - private val enableFormat: Boolean, - private val formatMode: FormattingMode, -) { - val wrap = sqlBlock.wrap - val alignment = sqlBlock.alignment - val spacingBuilder = sqlBlock.spacingBuilder - - 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 indentLevel = SqlKeywordUtil.getIndentType(child.text) - val keywordText = child.text.lowercase() - if (indentLevel.isNewLineGroup()) { - when (indentLevel) { - IndentType.JOIN -> { - return if (SqlKeywordUtil.isJoinKeyword(child.text)) { - SqlJoinGroupBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } else if (lastGroupBlock is SqlJoinGroupBlock) { - SqlKeywordBlock(child, IndentType.ATTACHED, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } else { - SqlJoinGroupBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - } - - IndentType.INLINE_SECOND -> { - return SqlInlineSecondGroupBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - - IndentType.TOP -> { - if (keywordText == "create") { - return SqlCreateKeywordGroupBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - if (keywordText == "insert") { - return SqlInsertKeywordGroupBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - - return SqlKeywordGroupBlock(child, indentLevel, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - - IndentType.SECOND -> { - return if (keywordText == "set") { - SqlUpdateKeywordGroupBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } else { - SqlKeywordGroupBlock(child, indentLevel, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - } - - else -> { - return SqlKeywordGroupBlock(child, indentLevel, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - } - } - - when (indentLevel) { - IndentType.INLINE -> { - if (!SqlKeywordUtil.isSetLineKeyword( - child.text, - lastGroupBlock?.getNodeText() ?: "", - ) - ) { - return SqlInlineGroupBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - } - - IndentType.ATTACHED -> { - if (lastGroupBlock is SqlCreateKeywordGroupBlock) { - lastGroupBlock.setCreateQueryType(child.text) - return SqlKeywordBlock(child, indentLevel, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - } - - IndentType.OPTIONS -> { - if (child.text.lowercase() == "as") { - val parentCreateBlock = - lastGroupBlock as? SqlCreateKeywordGroupBlock - ?: lastGroupBlock?.parentBlock as? SqlCreateKeywordGroupBlock - if (parentCreateBlock != null && parentCreateBlock.createType == CreateQueryType.VIEW) { - return SqlViewGroupBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - } - } - - else -> return SqlKeywordBlock(child, indentLevel, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - return SqlKeywordBlock(child, indentLevel, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - - fun getSubGroupBlock( - lastGroup: SqlBlock?, - child: ASTNode, - ): SqlBlock { - if (child.treePrev.elementType == SqlTypes.WORD) { - return SqlFunctionParamBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - - when (lastGroup) { - is SqlKeywordGroupBlock -> { - val lastKeyword = - lastGroup.childBlocks - .lastOrNull { SqlKeywordUtil.isOptionSqlKeyword(it.getNodeText()) } - if (lastKeyword != null && lastKeyword.getNodeText().lowercase() == "in") { - return SqlParallelListBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - if (lastGroup is SqlCreateKeywordGroupBlock) { - return SqlColumnDefinitionGroupBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - if (lastGroup is SqlInsertKeywordGroupBlock) { - return SqlInsertColumnGroupBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - if (lastGroup is SqlUpdateKeywordGroupBlock) { - return if (lastGroup.childBlocks.firstOrNull { it is SqlUpdateColumnGroupBlock } == null) { - SqlUpdateColumnGroupBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } else if (lastGroup.childBlocks.lastOrNull { it is SqlUpdateColumnGroupBlock } != null) { - SqlUpdateValueGroupBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } else { - SqlSubQueryGroupBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - } - return SqlSubQueryGroupBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - - is SqlColumnDefinitionRawGroupBlock -> - return SqlDataTypeParamBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - - else -> - return SqlSubQueryGroupBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - } - - fun getCommaGroupBlock( - lastGroup: SqlBlock?, - child: ASTNode, - ): SqlBlock = - when (lastGroup) { - is SqlColumnDefinitionGroupBlock, is SqlColumnDefinitionRawGroupBlock -> - SqlColumnDefinitionRawGroupBlock( - child, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, - ) - - is SqlColumnGroupBlock, is SqlKeywordGroupBlock -> { - if (lastGroup.indent.indentLevel == IndentType.SECOND) { - SqlCommaBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } else { - SqlColumnGroupBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - } - - else -> SqlCommaBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - - fun getWordBlock( - lastGroup: SqlBlock?, - child: ASTNode, - ): SqlBlock = - when (lastGroup) { - is SqlKeywordGroupBlock -> { - when { - SqlKeywordUtil.isBeforeTableKeyword(lastGroup.getNodeText()) -> - SqlTableBlock( - child, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, - ) - - else -> SqlWordBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - } - - is SqlColumnDefinitionGroupBlock -> { - lastGroup.alignmentColumnName = child.text - SqlColumnDefinitionRawGroupBlock( - child, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, - ) - } - - is SqlColumnDefinitionRawGroupBlock -> { - if (lastGroup.childBlocks.isEmpty()) { - lastGroup.columnName = child.text - SqlColumnBlock( - child, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, - ) - } else { - SqlWordBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - } - - else -> SqlWordBlock(child, wrap, alignment, spacingBuilder, enableFormat, formatMode) - } - - fun getBlockCommentBlock( - child: ASTNode, - blockCommentSpacingBuilder: SqlCustomSpacingBuilder?, - ): SqlCommentBlock { - if (PsiTreeUtil.getChildOfType(child.psi, PsiComment::class.java) != null) { - return SqlBlockCommentBlock( - child, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, - ) - } - if (child.psi is SqlCustomElCommentExpr && - (child.psi as SqlCustomElCommentExpr).isConditionOrLoopDirective() - ) { - return SqlElConditionLoopCommentBlock( - child, - wrap, - alignment, - blockCommentSpacingBuilder, - spacingBuilder, - enableFormat, - formatMode, - ) - } - return SqlElBlockCommentBlock( - child, - wrap, - alignment, - blockCommentSpacingBuilder, - spacingBuilder, - enableFormat, - formatMode, - ) - } -} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlFormatPreProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlFormatPreProcessor.kt deleted file mode 100644 index 1a72a49a..00000000 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlFormatPreProcessor.kt +++ /dev/null @@ -1,386 +0,0 @@ -/* - * 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 - -import com.intellij.lang.ASTNode -import com.intellij.openapi.editor.Document -import com.intellij.openapi.util.TextRange -import com.intellij.psi.PsiDocumentManager -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiFile -import com.intellij.psi.PsiRecursiveElementVisitor -import com.intellij.psi.PsiWhiteSpace -import com.intellij.psi.TokenType -import com.intellij.psi.impl.source.codeStyle.PreFormatProcessor -import com.intellij.psi.util.PsiTreeUtil -import com.intellij.psi.util.elementType -import com.intellij.psi.util.prevLeafs -import org.domaframework.doma.intellij.common.util.PluginLoggerUtil -import org.domaframework.doma.intellij.extension.expr.isConditionOrLoopDirective -import org.domaframework.doma.intellij.psi.SqlBlockComment -import org.domaframework.doma.intellij.psi.SqlCustomElCommentExpr -import org.domaframework.doma.intellij.psi.SqlTypes -import org.domaframework.doma.intellij.setting.SqlLanguage -import org.jetbrains.kotlin.psi.psiUtil.startOffset - -class SqlFormatPreProcessor : PreFormatProcessor { - override fun process( - node: ASTNode, - rangeToReformat: TextRange, - ): TextRange = processText(node.psi.containingFile, rangeToReformat) - - private fun processText( - source: PsiFile, - rangeToReformat: TextRange, - ): TextRange { - // Turn on by default the code formatter that only runs when explicitly invoked by the user. - if (source.language != SqlLanguage.INSTANCE) return rangeToReformat - - logging() - - val visitor = SqlFormatVisitor() - source.accept(visitor) - - val docManager = PsiDocumentManager.getInstance(source.project) - val document = docManager.getDocument(source) ?: return rangeToReformat - - val keywordList = visitor.replaces.filter { it.elementType != TokenType.WHITE_SPACE } - val replaceKeywordList = visitor.replaces.filter { it.elementType == SqlTypes.KEYWORD } - var index = keywordList.size - var keywordIndex = replaceKeywordList.size - - visitor.replaces.asReversed().forEach { - val createQueryType = getCreateQueryGroup(keywordList, index) - val textRangeStart = it.startOffset - val textRangeEnd = textRangeStart + it.text.length - if (it.elementType != TokenType.WHITE_SPACE) { - // Add a newline before any element that needs a newline+indent, without overlapping if there is already a newline - index-- - var newKeyword = getUpperText(it) - when (it.elementType) { - SqlTypes.KEYWORD -> { - keywordIndex-- - newKeyword = getKeywordNewText(index, it, createQueryType, keywordList) - } - - SqlTypes.LEFT_PAREN -> { - newKeyword = - if (createQueryType == CreateQueryType.TABLE) { - getNewLineString(it.prevSibling, getUpperText(it)) - } else if (keywordIndex > 0) { - if (replaceKeywordList[keywordIndex - 1].text.lowercase() == "insert" || - replaceKeywordList[keywordIndex - 1].text.lowercase() == "into" - ) { - getNewLineString(it.prevSibling, getUpperText(it)) - } else { - getUpperText(it) - } - } else { - getUpperText(it) - } - } - - SqlTypes.RIGHT_PAREN -> { - newKeyword = - getRightPatternNewText( - it, - newKeyword, - replaceKeywordList[keywordIndex - 1], - createQueryType, - ) - } - - SqlTypes.WORD -> { - newKeyword = getWordNewText(it, newKeyword, createQueryType) - } - - SqlTypes.COMMA, SqlTypes.BLOCK_COMMENT, SqlTypes.OTHER -> { - newKeyword = getNewLineString(it.prevSibling, getUpperText(it)) - } - } - document.deleteString(textRangeStart, textRangeEnd) - document.insertString(textRangeStart, newKeyword) - } else { - // Remove spaces after newlines to reset indentation - val nextSibling = it.nextSibling - if (nextSibling.elementType == SqlTypes.BLOCK_COMMENT) { - removeSpacesAroundNewline(document, it.textRange) - } else if (keywordIndex < replaceKeywordList.size) { - val nextElement = replaceKeywordList[keywordIndex] - if (isNewLineOnlyCreateTable(nextSibling) && createQueryType == CreateQueryType.TABLE) { - removeSpacesAroundNewline(document, it.textRange) - } else if (isSubGroupFirstElement(nextElement)) { - document.deleteString(textRangeStart, textRangeEnd) - } else if (isCreateViewAs(replaceKeywordList[keywordIndex], createQueryType)) { - removeSpacesAroundNewline(document, it.textRange) - } else { - val isNewLineGroup = - SqlKeywordUtil.getIndentType(nextElement.text ?: "").isNewLineGroup() - val isSetLineKeyword = - if (keywordIndex > 0) { - SqlKeywordUtil.isSetLineKeyword( - nextElement.text, - replaceKeywordList[keywordIndex - 1].text, - ) - } else { - false - } - - if (isNewLineGroup && !isSetLineKeyword || keywordList[index].elementType == SqlTypes.COMMA) { - removeSpacesAroundNewline(document, it.textRange) - } else { - document.replaceString(textRangeStart, textRangeEnd, " ") - } - } - } else { - removeSpacesAroundNewline(document, it.textRange) - } - } - } - - docManager.commitDocument(document) - - return rangeToReformat.grown(visitor.replaces.size) - } - - private fun removeSpacesAroundNewline( - document: Document, - range: TextRange, - ) { - val originalText = document.getText(range) - val newText = originalText.replace(Regex("\\s*\\n\\s*"), "\n") - document.replaceString(range.startOffset, range.endOffset, newText) - } - - /** - * Checks for special case keyword elements and specific combinations of keywords with line breaks and capitalization only - */ - private fun getKeywordNewText( - index: Int, - element: PsiElement, - createQueryType: CreateQueryType, - keywordList: List, - ): String = - if (element.text.lowercase() == "end") { - getNewLineString(element.prevSibling, getUpperText(element)) - } else if (isCreateViewAs(element, createQueryType)) { - getNewLineString(element.prevSibling, getUpperText(element)) - } else if (isSubGroupFirstElement(element)) { - getUpperText(element) - } else if (SqlKeywordUtil.getIndentType(element.text).isNewLineGroup()) { - if (index > 0 && - SqlKeywordUtil.isSetLineKeyword( - element.text, - keywordList[index - 1].text, - ) - ) { - getUpperText(element) - } else { - getNewLineString(element.prevSibling, getUpperText(element)) - } - } else { - getUpperText(element) - } - - private fun getRightPatternNewText( - element: PsiElement, - keyword: String, - nextKeyword: PsiElement, - createQueryType: CreateQueryType, - ): String { - var newKeyword = keyword - val elementText = element.text - if (createQueryType == CreateQueryType.TABLE) { - val prefixElements = - getElementsBeforeKeyword(element.prevLeafs.toList()) { it.elementType == SqlTypes.LEFT_PAREN } - val containsColumnRaw = - prefixElements.findLast { isColumnDefinedRawElementType(it) } != null - newKeyword = - if (containsColumnRaw) { - getNewLineString(element.prevSibling, elementText) - } else { - elementText - } - } else if (nextKeyword.text.lowercase() == "set") { - newKeyword = getNewLineString(element.prevSibling, elementText) - } else { - newKeyword = elementText - } - return newKeyword - } - - private fun getWordNewText( - element: PsiElement, - newKeyword: String, - createQueryType: CreateQueryType, - ): String { - newKeyword - var prev = element.prevSibling - var isColumnName = true - while (prev != null && prev.elementType != SqlTypes.LEFT_PAREN && prev.elementType != SqlTypes.COMMA) { - if (prev !is PsiWhiteSpace && - prev.elementType != SqlTypes.LINE_COMMENT && - prev.elementType != SqlTypes.BLOCK_COMMENT - ) { - isColumnName = - prev.elementType == SqlTypes.COMMA || - prev.elementType == SqlTypes.LEFT_PAREN - break - } - prev = prev.prevSibling - } - - return if (createQueryType == CreateQueryType.TABLE && isColumnName) { - getNewLineString(element.prevSibling, getUpperText(element)) - } else { - getUpperText(element) - } - } - - private fun isCreateViewAs( - element: PsiElement, - createQueryType: CreateQueryType, - ): Boolean = - element.text.lowercase() == "as" && - createQueryType == CreateQueryType.VIEW - - private fun isColumnDefinedRawElementType(element: PsiElement): Boolean = - element.elementType == SqlTypes.WORD || - element.elementType == SqlTypes.KEYWORD || - element.elementType == SqlTypes.COMMA - - private fun getCreateQueryGroup( - keywordList: List, - index: Int, - ): CreateQueryType { - var topLastKeyWord: PsiElement? = null - var attachmentKeywordType = CreateQueryType.NONE - keywordList - .dropLast(keywordList.size.minus(index)) - .filter { - it.elementType == SqlTypes.KEYWORD - }.asReversed() - .forEach { key -> - if (SqlKeywordUtil.isTopKeyword(key.text)) { - topLastKeyWord = key - return@forEach - } - if (SqlKeywordUtil.isAttachedKeyword(key.text)) { - attachmentKeywordType = CreateQueryType.getCreateTableType(key.text) - } - } - val prevKeywordText = topLastKeyWord?.text?.lowercase() - val isCreateGroup = prevKeywordText == "create" - if (!isCreateGroup) return CreateQueryType.NONE - return attachmentKeywordType - } - - /** - * The column definition elements of Create Table, "(", "WORD", and ")" must be line breaks - */ - private fun isNewLineOnlyCreateTable(nextElement: PsiElement): Boolean = - nextElement.elementType == SqlTypes.LEFT_PAREN || - nextElement.elementType == SqlTypes.RIGHT_PAREN || - nextElement.elementType == SqlTypes.WORD - - fun getElementsBeforeKeyword( - elements: List, - isLeft: (T) -> Boolean, - ): List = elements.takeWhile { element -> !isLeft(element) } - - private fun getNewLineString( - prevElement: PsiElement?, - text: String, - ): String = - if (prevElement?.text?.contains("\n") == false) { - "\n$text" - } else { - text - } - - private fun getUpperText(element: PsiElement): String = - if (element.elementType == SqlTypes.KEYWORD) { - element.text.uppercase() - } else { - element.text - } - - private fun isSubGroupFirstElement(element: PsiElement): Boolean = - getElementsBeforeKeyword(element.prevLeafs.toList()) { it.elementType == SqlTypes.LEFT_PAREN } - .findLast { it !is PsiWhiteSpace } == null - - private fun logging() { - PluginLoggerUtil.countLogging( - this::class.java.simpleName, - "SqlFormat", - "Format", - System.nanoTime(), - ) - } -} - -private class SqlFormatVisitor : PsiRecursiveElementVisitor() { - val replaces = mutableListOf() - var lastElement: PsiElement? = null - - override fun visitElement(element: PsiElement) { - super.visitElement(element) - if (element !is PsiFile && element.nextSibling == null) { - lastElement = element - } - - if (PsiTreeUtil.getParentOfType(element, SqlBlockComment::class.java) == null) { - when (element.elementType) { - SqlTypes.KEYWORD, SqlTypes.COMMA, SqlTypes.LEFT_PAREN, SqlTypes.RIGHT_PAREN, SqlTypes.WORD -> { - replaces.add(element) - } - - SqlTypes.OTHER -> { - if (element.text == "=") { - val updateSetKeyword = - replaces - .lastOrNull { it.elementType == SqlTypes.KEYWORD } - if (updateSetKeyword?.text?.lowercase() == "set") { - replaces.add(element) - } - } - } - - SqlTypes.BLOCK_COMMENT -> - if ( - element is SqlCustomElCommentExpr && - element.isConditionOrLoopDirective() - ) { - replaces.add(element) - } - } - } - } - - override fun visitWhiteSpace(space: PsiWhiteSpace) { - super.visitWhiteSpace(space) - val nextElement = space.nextSibling - if (nextElement != null && - ( - space.text.contains("\n") || - nextElement.elementType == SqlTypes.LINE_COMMENT || - nextElement.elementType == SqlTypes.BLOCK_COMMENT - ) - ) { - replaces.add(space) - } - } -} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlBlock.kt index c77cb0d8..46c9b5ec 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlBlock.kt @@ -26,28 +26,35 @@ import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.PsiWhiteSpace import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.IndentType -import org.domaframework.doma.intellij.formatter.SqlBlockBuilder -import org.domaframework.doma.intellij.formatter.SqlBlockUtil -import org.domaframework.doma.intellij.formatter.SqlCustomSpacingBuilder -import org.domaframework.doma.intellij.formatter.SqlKeywordUtil +import org.domaframework.doma.intellij.formatter.block.comment.SqlBlockCommentBlock +import org.domaframework.doma.intellij.formatter.block.comment.SqlCommentBlock +import org.domaframework.doma.intellij.formatter.block.comment.SqlLineCommentBlock import org.domaframework.doma.intellij.formatter.block.expr.SqlElBlockCommentBlock import org.domaframework.doma.intellij.formatter.block.expr.SqlElConditionLoopCommentBlock import org.domaframework.doma.intellij.formatter.block.expr.SqlElSymbolBlock -import org.domaframework.doma.intellij.formatter.block.group.SqlColumnDefinitionRawGroupBlock import org.domaframework.doma.intellij.formatter.block.group.SqlNewGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnBlock +import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnDefinitionRawGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnRawGroupBlock import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlInlineGroupBlock import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlInlineSecondGroupBlock import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlColumnDefinitionGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlColumnGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.update.SqlUpdateColumnAssignmentSymbolBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.update.SqlUpdateSetGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithColumnGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithCommonTableGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithQueryGroupBlock import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlDataTypeParamBlock import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlFunctionParamBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlInsertColumnGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlParallelListBlock import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGroupBlock import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubQueryGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlViewGroupBlock +import org.domaframework.doma.intellij.formatter.builder.SqlBlockBuilder +import org.domaframework.doma.intellij.formatter.builder.SqlCustomSpacingBuilder +import org.domaframework.doma.intellij.formatter.processor.SqlSetParentGroupProcessor +import org.domaframework.doma.intellij.formatter.util.CreateTableUtil +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext +import org.domaframework.doma.intellij.formatter.util.SqlBlockUtil import org.domaframework.doma.intellij.psi.SqlTypes open class SqlBlock( @@ -65,13 +72,25 @@ open class SqlBlock( ) { data class ElementIndent( var indentLevel: IndentType, + /** + * The number of indentation spaces for this element. + * Returns `0` if there is no line break. + */ var indentLen: Int, + /** + * Indentation baseline applied to the group itself. + * Even if the group does not start on a new line, + * it determines and applies indentation to the group based on factors such as the number of preceding characters. + */ var groupIndentLen: Int, ) val blocks = mutableListOf() open var parentBlock: SqlBlock? = null open val childBlocks = mutableListOf() + + fun getChildBlocksDropLast(dropIndex: Int = 1): List = childBlocks.dropLast(dropIndex) + open val indent: ElementIndent = ElementIndent( IndentType.FILE, @@ -80,15 +99,21 @@ open class SqlBlock( ) private val blockBuilder = SqlBlockBuilder() + private val parentSetProcessor = SqlSetParentGroupProcessor(blockBuilder) protected val blockUtil = SqlBlockUtil(this, isEnableFormat(), formatMode) protected open val pendingCommentBlocks = mutableListOf() fun isEnableFormat(): Boolean = enableFormat - open fun setParentGroupBlock(block: SqlBlock?) { - parentBlock = block + open fun setParentGroupBlock(lastGroup: SqlBlock?) { + parentBlock = lastGroup parentBlock?.addChildBlock(this) + setParentPropertyBlock(lastGroup) + } + + open fun setParentPropertyBlock(lastGroup: SqlBlock?) { + // This method can be overridden to set additional properties on the parent block if needed. } open fun addChildBlock(childBlock: SqlBlock) { @@ -102,33 +127,16 @@ open class SqlBlock( var child = node.firstChildNode var prevNonWhiteSpaceNode: ASTNode? = null - blockBuilder.addGroupTopNodeIndexHistory(Pair(0, this)) + blockBuilder.addGroupTopNodeIndexHistory(this) while (child != null) { val lastBlock = blocks.lastOrNull() - val lastGroup = blockBuilder.getLastGroupTopNodeIndexHistory()?.second + val lastGroup = blockBuilder.getLastGroupTopNodeIndexHistory() if (child !is PsiWhiteSpace) { val childBlock = getBlock(child) - if (blocks.isNotEmpty() && lastBlock is SqlWhitespaceBlock) { - if (isSaveWhiteSpace(childBlock, child, lastGroup)) { - val whiteBlock = lastBlock as SqlBlock - whiteBlock.parentBlock = lastGroup - } else { - // Ignore space blocks for non-breaking elements - blocks.removeLast() - } - } prevNonWhiteSpaceNode = child - if (childBlock is SqlCommentBlock) { - when (childBlock) { - is SqlElConditionLoopCommentBlock -> - blockBuilder.addConditionOrLoopBlock( - childBlock, - ) - - else -> blockBuilder.addCommentBlock(childBlock) - } - } - updateSearchKeywordLevelHistory(childBlock, child) + updateCommentParentAndIdent(childBlock) + updateBlockParentAndLAddGroup(childBlock) + updateWhiteSpaceInclude(lastBlock, childBlock, lastGroup) blocks.add(childBlock) } else { if (lastBlock !is SqlLineCommentBlock) { @@ -150,459 +158,285 @@ open class SqlBlock( return blocks } - private fun isSaveWhiteSpace( - childBlock: SqlBlock, - child: ASTNode, - lastGroup: SqlBlock?, - ): Boolean = - isNewLineGroupBlock(childBlock, child, lastGroup) || - childBlock is SqlInsertColumnGroupBlock || - childBlock is SqlColumnDefinitionRawGroupBlock || - childBlock is SqlColumnDefinitionGroupBlock || - (childBlock is SqlOtherBlock && childBlock.isUpdateColumnSubstitutions) || - (childBlock is SqlRightPatternBlock && childBlock.isNewLine(lastGroup)) || - ( - ( - childBlock is SqlLineCommentBlock || - childBlock is SqlBlockCommentBlock - ) && - child.treePrev.text.contains("\n") - ) || - (childBlock is SqlElConditionLoopCommentBlock) - - private fun isNewGroup(childBlock: SqlBlock): Boolean { - val isNewGroupType = childBlock.indent.indentLevel.isNewLineGroup() - val lastGroup = blockBuilder.getLastGroupTopNodeIndexHistory()?.second - val lastKeywordText = - if (lastGroup?.indent?.indentLevel == IndentType.JOIN) { - lastGroup.getNodeText() - } else { - getLastGroupKeywordText(lastGroup) - } - - val isSetLineGroup = - SqlKeywordUtil.isSetLineKeyword( - childBlock.getNodeText(), - lastKeywordText, + /** + * Sets the parent and indentation for the comment element based on the element that was registered earlier. + */ + private fun updateCommentParentAndIdent(commentBlock: SqlBlock) { + if (commentBlock !is SqlCommentBlock) return + if (commentBlock is SqlElConditionLoopCommentBlock) { + blockBuilder.addConditionOrLoopBlock( + commentBlock, ) - - return isNewGroupType && !isSetLineGroup + } else { + blockBuilder.addCommentBlock(commentBlock) + } } - private fun isNewLineGroupBlock( + /** + * Determines whether to retain the preceding newline (space) as a formatting target block based on the currently checked element. + */ + private fun updateWhiteSpaceInclude( + lastBlock: AbstractBlock?, childBlock: SqlBlock, - child: ASTNode, lastGroup: SqlBlock?, - ): Boolean { - if (childBlock is SqlCommaBlock && - ( - lastGroup is SqlParallelListBlock || - lastGroup?.parentBlock is SqlParallelListBlock - ) - ) { - return false - } - - val isNewGroupType = childBlock.indent.indentLevel.isNewLineGroup() - val lastKeywordText = - if (lastGroup?.indent?.indentLevel == IndentType.JOIN) { - lastGroup.getNodeText() + ) { + if (blocks.isNotEmpty() && lastBlock is SqlWhitespaceBlock) { + if (isSaveWhiteSpace(childBlock, lastGroup)) { + val whiteBlock = lastBlock as SqlBlock + whiteBlock.parentBlock = lastGroup } else { - getLastGroupKeywordText(lastGroup) - } - - val isSetLineGroup = - SqlKeywordUtil.isSetLineKeyword( - child.text, - lastKeywordText, - ) - if (isNewGroupType && !isSetLineGroup) { - if (lastGroup is SqlSubQueryGroupBlock) { - return (lastGroup.childBlocks.size > 1) + // Ignore space blocks for non-breaking elements + blocks.removeLast() } - return true } - return false } + open fun isSaveSpace(lastGroup: SqlBlock?): Boolean = false + /** - * Searches for a keyword element in the most recent group block and returns its text. - * If not found, returns the text of the group block itself. + * Determines whether to retain the space (newline) based on the last registered group or the class of the currently checked element. */ - private fun getLastGroupKeywordText(lastGroup: SqlBlock?): String = - lastGroup - ?.childBlocks - ?.lastOrNull { it.node.elementType == SqlTypes.KEYWORD } - ?.node - ?.text ?: lastGroup?.getNodeText() ?: "" - - protected open fun updateSearchKeywordLevelHistory( + private fun isSaveWhiteSpace( childBlock: SqlBlock, - child: ASTNode, - ) { - val lastGroupBlock = blockBuilder.getLastGroupTopNodeIndexHistory()?.second + lastGroup: SqlBlock?, + ): Boolean = childBlock.isSaveSpace(lastGroup) + + /** + * Updates the parent block or registers itself as a new group block based on the class of the target block. + */ + private fun updateBlockParentAndLAddGroup(childBlock: SqlBlock) { + val lastGroupBlock = blockBuilder.getLastGroupTopNodeIndexHistory() val lastIndentLevel = lastGroupBlock?.indent?.indentLevel if (lastGroupBlock == null || lastIndentLevel == null) { - setParentGroups( + parentSetProcessor.updateGroupBlockAddGroup( childBlock, - ) { history -> - return@setParentGroups null - } + ) return } when (childBlock) { is SqlKeywordGroupBlock -> { - if (lastGroupBlock.indent.indentLevel == IndentType.SUB) { - setParentGroups( - childBlock, - ) { history -> - return@setParentGroups lastGroupBlock - } - } else if (lastIndentLevel == childBlock.indent.indentLevel) { - // The AND following an OR will be a child of OR unless surrounded by a subgroup - if (childBlock.getNodeText() == "and" && lastGroupBlock.getNodeText() == "or") { - setParentGroups( - childBlock, - ) { history -> - return@setParentGroups lastGroupBlock - } - } else { - if (childBlock.getNodeText() == "or" && - lastGroupBlock.getNodeText() == "and" && - lastGroupBlock.parentBlock?.getNodeText() == "or" - ) { - val orParentIndex = - blockBuilder.getGroupTopNodeIndex { block -> - block is SqlKeywordGroupBlock && block.getNodeText() == "or" - } - blockBuilder.clearSubListGroupTopNodeIndexHistory(orParentIndex) - setParentGroups( - childBlock, - ) { history -> - return@setParentGroups history.lastOrNull()?.second - } - } else { - blockBuilder.removeLastGroupTopNodeIndexHistory() - setParentGroups( - childBlock, - ) { history -> - return@setParentGroups lastGroupBlock.parentBlock - } - } - } - } else if (lastIndentLevel < childBlock.indent.indentLevel) { - setParentGroups( - childBlock, - ) { history -> - return@setParentGroups history.last().second - } - } else { - if (lastIndentLevel == IndentType.JOIN && - SqlKeywordUtil.isSecondOptionKeyword(child.text) - ) { - // left,right < inner,outer < join - setParentGroups( - childBlock, - ) { history -> - return@setParentGroups history.last().second - } - return - } - - setParentGroups( - childBlock, - ) { history -> - return@setParentGroups history - .lastOrNull { it.second.indent.indentLevel < childBlock.indent.indentLevel } - ?.second - } - } + parentSetProcessor.updateKeywordGroupBlockParentAndAddGroup( + lastGroupBlock, + lastIndentLevel, + childBlock, + ) } - is SqlColumnGroupBlock -> { - when (lastIndentLevel) { - childBlock.indent.indentLevel -> { - blockBuilder.removeLastGroupTopNodeIndexHistory() - setParentGroups( - childBlock, - ) { history -> - return@setParentGroups lastGroupBlock.parentBlock - } - } + is SqlColumnDefinitionRawGroupBlock -> { + parentSetProcessor.updateColumnDefinitionRawGroupBlockParentAndAddGroup( + lastGroupBlock, + lastIndentLevel, + childBlock, + ) + } - else -> { - setParentGroups( - childBlock, - ) { history -> - return@setParentGroups history.last().second - } - } - } + is SqlColumnRawGroupBlock -> { + parentSetProcessor.updateColumnRawGroupBlockParentAndAddGroup( + lastGroupBlock, + childBlock, + ) } is SqlInlineGroupBlock -> { // case-end - setParentGroups( + parentSetProcessor.updateGroupBlockParentAndAddGroup( childBlock, - ) { history -> - return@setParentGroups history.last().second - } + ) } is SqlInlineSecondGroupBlock -> { - if (childBlock.isEndCase) { - val inlineIndex = - blockBuilder.getGroupTopNodeIndex { block -> - block.indent.indentLevel == IndentType.INLINE - } - if (inlineIndex >= 0) { - setParentGroups( - childBlock, - ) { history -> - return@setParentGroups history[inlineIndex].second - } - blockBuilder.clearSubListGroupTopNodeIndexHistory(inlineIndex) - } - return - } - if (lastIndentLevel == IndentType.INLINE_SECOND) { - blockBuilder.removeLastGroupTopNodeIndexHistory() - setParentGroups( - childBlock, - ) { history -> - return@setParentGroups lastGroupBlock.parentBlock - } - return - } - setParentGroups( + parentSetProcessor.updateInlineSecondGroupBlockParentAndAddGroup( + lastGroupBlock, + lastIndentLevel, childBlock, - ) { history -> - return@setParentGroups history.last().second - } + ) } is SqlColumnBlock -> { - setParentGroups( - childBlock, - ) { history -> - val parentGroupBlock = history.last().second - if (parentGroupBlock is SqlColumnDefinitionRawGroupBlock && - parentGroupBlock.columnName != "," - ) { - parentGroupBlock.columnName = childBlock.getNodeText() - val columnDefinition = - parentGroupBlock.parentBlock as? SqlColumnDefinitionGroupBlock - if (columnDefinition != null && columnDefinition.alignmentColumnName.length < parentGroupBlock.columnName.length) { - columnDefinition.alignmentColumnName = parentGroupBlock.columnName - } - } - return@setParentGroups history.last().second - } - } - - is SqlColumnDefinitionRawGroupBlock -> { - if (lastGroupBlock is SqlColumnDefinitionRawGroupBlock) { - blockBuilder.removeLastGroupTopNodeIndexHistory() - } - setParentGroups( + parentSetProcessor.updateGroupBlockParentAndAddGroup( childBlock, - ) { history -> - return@setParentGroups history.last().second - } + ) } is SqlElConditionLoopCommentBlock -> { - if (lastGroupBlock is SqlCommaBlock || lastGroupBlock is SqlElConditionLoopCommentBlock) { - blockBuilder.removeLastGroupTopNodeIndexHistory() - } - setParentGroups( + parentSetProcessor.updateConditionLoopCommentBlockParent( + lastGroupBlock, childBlock, - ) { history -> - if (childBlock.conditionType.isEnd()) { - val lastConditionLoopCommentBlock = blockBuilder.getConditionOrLoopBlocksLast() - blockBuilder.removeConditionOrLoopBlockLast() - return@setParentGroups lastConditionLoopCommentBlock - } - return@setParentGroups null - } + ) } is SqlWordBlock, is SqlOtherBlock, is SqlLineCommentBlock, is SqlBlockCommentBlock -> { - setParentGroups( + parentSetProcessor.updateGroupBlockParentAndAddGroup( childBlock, - ) { history -> - return@setParentGroups history.last().second - } + ) } - is SqlSubQueryGroupBlock -> { - setParentGroups( + is SqlSubGroupBlock -> { + parentSetProcessor.updateSubGroupBlockParent( childBlock, - ) { history -> - return@setParentGroups history.last().second - } + ) } is SqlRightPatternBlock -> { - val paramIndex = - blockBuilder.getGroupTopNodeIndex { block -> - block.indent.indentLevel == IndentType.PARAM - } - if (paramIndex >= 0) { - setParentGroups( - childBlock, - ) { history -> - return@setParentGroups history[paramIndex].second - } - blockBuilder.clearSubListGroupTopNodeIndexHistory(paramIndex) - return - } - - val leftIndex = - blockBuilder.getGroupTopNodeIndex { block -> - block.indent.indentLevel == IndentType.SUB - } - if (leftIndex >= 0) { - setParentGroups( - childBlock, - ) { history -> - return@setParentGroups history[leftIndex].second - } - blockBuilder.clearSubListGroupTopNodeIndexHistory(leftIndex) - return - } + parentSetProcessor.updateSqlRightPatternBlockParent( + childBlock, + ) } is SqlElSymbolBlock -> { - setParentGroups( + parentSetProcessor.updateGroupBlockParentAndAddGroup( childBlock, - ) { history -> - return@setParentGroups history.last().second - } + ) } is SqlDataTypeBlock -> { - setParentGroups( + parentSetProcessor.updateGroupBlockParentAndAddGroup( childBlock, - ) { history -> - return@setParentGroups history.last().second - } + ) } is SqlCommaBlock -> { if (lastGroupBlock is SqlCommaBlock) { blockBuilder.removeLastGroupTopNodeIndexHistory() } - setParentGroups( + parentSetProcessor.updateGroupBlockParentAndAddGroup( childBlock, - ) { history -> - return@setParentGroups history.last().second - } + ) } else -> { - setParentGroups( + parentSetProcessor.updateGroupBlockParentAndAddGroup( childBlock, - ) { history -> - return@setParentGroups history.last().second - } + ) } } } - private fun setParentGroups( - childBlock: SqlBlock, - getParentGroup: (MutableList>) -> SqlBlock?, - ) { - val parentGroup = - getParentGroup(blockBuilder.getGroupTopNodeIndexHistory() as MutableList>) - - // // The parent block for SqlElConditionLoopCommentBlock will be set later - if (childBlock !is SqlElConditionLoopCommentBlock || - childBlock.conditionType.isEnd() - ) { - childBlock.setParentGroupBlock(parentGroup) - } - - if (isNewGroup(childBlock) || - (childBlock is SqlSubGroupBlock) || - childBlock is SqlViewGroupBlock || - childBlock is SqlInlineGroupBlock || - childBlock is SqlInlineSecondGroupBlock || - childBlock is SqlColumnDefinitionRawGroupBlock - ) { - blockBuilder.addGroupTopNodeIndexHistory(Pair(blocks.size - 1, childBlock)) - // Set parent-child relationship and indent for preceding comment at beginning of block group - blockBuilder.updateCommentBlockIndent(childBlock) - } - } - + /** + * Creates the indentation length for the block. + */ open fun createBlockIndentLen(): Int = 0 + open fun createGroupIndentLen(): Int = 0 + + /** + * Creates a block for the given child AST node. + */ open fun getBlock(child: ASTNode): SqlBlock { - val lastGroup = blockBuilder.getLastGroupTopNodeIndexHistory()?.second + val defaultFormatCtx = + SqlBlockFormattingContext( + wrap, + alignment, + spacingBuilder, + isEnableFormat(), + formatMode, + ) + val lastGroup = blockBuilder.getLastGroupTopNodeIndexHistory() return when (child.elementType) { SqlTypes.KEYWORD -> { return blockUtil.getKeywordBlock( child, - blockBuilder.getLastGroupTopNodeIndexHistory()?.second, + blockBuilder.getLastGroupTopNodeIndexHistory(), ) } - SqlTypes.DATATYPE -> SqlDataTypeBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + SqlTypes.DATATYPE -> { + SqlDataTypeBlock( + child, + defaultFormatCtx, + ) + } SqlTypes.LEFT_PAREN -> { return blockUtil.getSubGroupBlock(lastGroup, child) } - SqlTypes.OTHER -> return SqlOtherBlock( - child, - wrap, - alignment, - spacingBuilder, - blockBuilder.getLastGroup(), - isEnableFormat(), - formatMode, - ) + SqlTypes.OTHER -> { + return if (lastGroup is SqlUpdateSetGroupBlock && + lastGroup.columnDefinitionGroupBlock != null + ) { + SqlUpdateColumnAssignmentSymbolBlock( + child, + defaultFormatCtx, + ) + } else { + SqlOtherBlock( + child, + defaultFormatCtx, + ) + } + } SqlTypes.RIGHT_PAREN -> return SqlRightPatternBlock( child, - wrap, - alignment, - spacingBuilder, - isEnableFormat(), - formatMode, + defaultFormatCtx, ) SqlTypes.COMMA -> { - return blockUtil.getCommaGroupBlock(lastGroup, child) + return if (lastGroup is SqlWithQueryGroupBlock) { + SqlWithCommonTableGroupBlock(child, defaultFormatCtx) + } else { + blockUtil.getCommaGroupBlock(lastGroup, child) + } } - SqlTypes.WORD -> return blockUtil.getWordBlock(lastGroup, child) + SqlTypes.WORD -> { + return if (lastGroup is SqlWithQueryGroupBlock) { + SqlWithCommonTableGroupBlock(child, defaultFormatCtx) + } else { + blockUtil.getWordBlock(lastGroup, child) + } + } SqlTypes.BLOCK_COMMENT -> { - return blockUtil.getBlockCommentBlock(child, createBlockCommentSpacingBuilder()) + return if (lastGroup is SqlWithCommonTableGroupBlock) { + SqlWithCommonTableGroupBlock(child, defaultFormatCtx) + } else { + blockUtil.getBlockCommentBlock(child, createBlockCommentSpacingBuilder()) + } } SqlTypes.LINE_COMMENT -> - return SqlLineCommentBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + return SqlLineCommentBlock( + child, + defaultFormatCtx, + ) SqlTypes.PLUS, SqlTypes.MINUS, SqlTypes.ASTERISK, SqlTypes.SLASH -> - return SqlElSymbolBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + return SqlElSymbolBlock( + child, + defaultFormatCtx, + ) SqlTypes.LE, SqlTypes.LT, SqlTypes.EL_EQ, SqlTypes.EL_NE, SqlTypes.GE, SqlTypes.GT -> - return SqlElSymbolBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + return SqlElSymbolBlock( + child, + defaultFormatCtx, + ) SqlTypes.STRING, SqlTypes.NUMBER, SqlTypes.BOOLEAN -> - return SqlLiteralBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + return SqlLiteralBlock( + child, + defaultFormatCtx, + ) - else -> SqlUnknownBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + else -> + SqlUnknownBlock( + child, + defaultFormatCtx, + ) } } + /** + * Creates a spacing builder for custom spacing rules. + */ protected open fun createSpacingBuilder(): SqlCustomSpacingBuilder = SqlCustomSpacingBuilder() + /** + * Creates a spacing builder specifically for block comments. + */ protected fun createBlockCommentSpacingBuilder(): SqlCustomSpacingBuilder = SqlCustomSpacingBuilder() .withSpacing( @@ -631,6 +465,9 @@ open class SqlBlock( Spacing.createSpacing(0, 0, 0, true, 0), ) + /** + * Returns the indentation for the block. + */ override fun getIndent(): Indent? = if (isAdjustIndentOnEnter()) { null @@ -638,8 +475,14 @@ open class SqlBlock( Indent.getSpaceIndent(indent.indentLen) } - protected fun isAdjustIndentOnEnter(): Boolean = formatMode == FormattingMode.ADJUST_INDENT_ON_ENTER && !isEnableFormat() + /** + * Determines whether to adjust the indentation on pressing Enter. + */ + fun isAdjustIndentOnEnter(): Boolean = formatMode == FormattingMode.ADJUST_INDENT_ON_ENTER && !isEnableFormat() + /** + * Returns the spacing between two child blocks. + */ override fun getSpacing( child1: Block?, child2: Block, @@ -651,6 +494,14 @@ open class SqlBlock( return SqlCustomSpacingBuilder().getSpacing(child2) } + if (child2 is SqlWithColumnGroupBlock) { + return SqlCustomSpacingBuilder.normalSpacing + } + + if (child1 is SqlSubGroupBlock && child2 is SqlSubGroupBlock) { + return SqlCustomSpacingBuilder.nonSpacing + } + // Do not leave a space after the comment block of the bind variable if (child1 is SqlElBlockCommentBlock && child1 !is SqlElConditionLoopCommentBlock && child2 !is SqlCommentBlock) { return SqlCustomSpacingBuilder.nonSpacing @@ -658,11 +509,7 @@ open class SqlBlock( if (child2 is SqlElBlockCommentBlock) { return when (child1) { - is SqlElBlockCommentBlock -> { - SqlCustomSpacingBuilder().getSpacing(child2) - } - - is SqlWhitespaceBlock -> { + is SqlElBlockCommentBlock, is SqlWhitespaceBlock -> { SqlCustomSpacingBuilder().getSpacing(child2) } @@ -680,17 +527,16 @@ open class SqlBlock( if (child1 is SqlWhitespaceBlock) { when (child2) { - is SqlBlockCommentBlock, is SqlLineCommentBlock -> { - return SqlCustomSpacingBuilder().getSpacing(child2) - } - - is SqlNewGroupBlock -> { + is SqlBlockCommentBlock, is SqlLineCommentBlock, is SqlNewGroupBlock -> { return SqlCustomSpacingBuilder().getSpacing(child2) } } } if (child2 is SqlNewGroupBlock) { + if (child1 is SqlSubGroupBlock && child2.indent.indentLevel == IndentType.ATTACHED) { + return SqlCustomSpacingBuilder.nonSpacing + } when (child2) { is SqlSubQueryGroupBlock -> { if (child1 is SqlNewGroupBlock) { @@ -704,30 +550,37 @@ open class SqlBlock( } } - if (child2 is SqlColumnDefinitionRawGroupBlock) { - SqlCustomSpacingBuilder().getSpacingColumnDefinitionRaw(child2)?.let { return it } - } + // Create Table Column Definition Raw Group Block + CreateTableUtil.getColumnDefinitionRawGroupSpacing(child1, child2)?.let { return it } - if (child2 is SqlRightPatternBlock) { - return SqlCustomSpacingBuilder().getSpacingRightPattern(child2) - } + when (child2) { + is SqlColumnDefinitionRawGroupBlock -> + SqlCustomSpacingBuilder() + .getSpacingColumnDefinitionRaw( + child2, + )?.let { return it } - if (child1 is SqlBlock && (child2 is SqlCommaBlock || child2 is SqlColumnGroupBlock)) { - SqlCustomSpacingBuilder().getSpacingWithIndentComma(child1, child2)?.let { return it } - } + is SqlRightPatternBlock -> return SqlCustomSpacingBuilder().getSpacingRightPattern( + child2, + ) - if (child2 is SqlDataTypeParamBlock) { - return SqlCustomSpacingBuilder.nonSpacing + is SqlColumnBlock -> + SqlCustomSpacingBuilder() + .getSpacingColumnDefinition(child2) + ?.let { return it } } - if (child2 is SqlColumnBlock) { - SqlCustomSpacingBuilder().getSpacingColumnDefinition(child2)?.let { return it } + if (child1 is SqlBlock && (child2 is SqlCommaBlock || child2 is SqlColumnRawGroupBlock)) { + SqlCustomSpacingBuilder().getSpacingWithIndentComma(child1, child2)?.let { return it } } val spacing: Spacing? = customSpacingBuilder?.getCustomSpacing(child1, child2) return spacing ?: spacingBuilder.getSpacing(this, child1, child2) } + /** + * Returns the child attributes for a new child at the specified index. + */ override fun getChildAttributes(newChildIndex: Int): ChildAttributes { if (!isEnableFormat()) return ChildAttributes(Indent.getNoneIndent(), null) @@ -744,6 +597,9 @@ open class SqlBlock( return ChildAttributes(Indent.getNoneIndent(), null) } + /** + * Returns the child indentation for the block. + */ override fun getChildIndent(): Indent? = if (isEnableFormat()) { Indent.getSpaceIndent(4) @@ -751,5 +607,8 @@ open class SqlBlock( Indent.getSpaceIndent(0) } + /** + * Determines whether the block is a leaf node. + */ override fun isLeaf(): Boolean = myNode.firstChildNode == null } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlCommaBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlCommaBlock.kt index cfe8afbf..16ad4360 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlCommaBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlCommaBlock.kt @@ -15,37 +15,36 @@ */ package org.domaframework.doma.intellij.formatter.block -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.IndentType -import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlCreateKeywordGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlInsertKeywordGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlColumnGroupBlock +import org.domaframework.doma.intellij.common.util.TypeUtil +import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnRawGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.create.SqlCreateKeywordGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.insert.SqlInsertColumnGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.insert.SqlInsertValueGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.update.SqlUpdateColumnGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.update.SqlUpdateSetGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.update.SqlUpdateValueGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithColumnGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlFunctionParamBlock import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlParallelListBlock import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlUpdateColumnGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlUpdateValueGroupBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext import org.domaframework.doma.intellij.psi.SqlTypes open class SqlCommaBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlBlock( node, - wrap, - alignment, + context.wrap, + context.alignment, null, - spacingBuilder, - enableFormat, - formatMode, + context.spacingBuilder, + context.enableFormat, + context.formatMode, ) { override val indent = ElementIndent( @@ -54,8 +53,8 @@ open class SqlCommaBlock( 0, ) - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) indent.indentLevel = IndentType.COMMA indent.indentLen = createBlockIndentLen() indent.groupIndentLen = indent.indentLen.plus(getNodeText().length) @@ -70,28 +69,43 @@ open class SqlCommaBlock( return 0 } + val parentIndentSyncBlockTypes = + listOf( + SqlUpdateColumnGroupBlock::class, + SqlInsertColumnGroupBlock::class, + SqlWithColumnGroupBlock::class, + ) val parentIndentLen = parent.indent.groupIndentLen - if (parent is SqlUpdateColumnGroupBlock || parent is SqlUpdateValueGroupBlock) { + if (TypeUtil.isExpectedClassType(parentIndentSyncBlockTypes, parent)) { return parentIndentLen } + // TODO Indent each comma in a value group so that it aligns with the position of the first value row. + val parentIndentSingleSpaceTypes = + listOf( + SqlInsertValueGroupBlock::class, + SqlUpdateValueGroupBlock::class, + ) + if (TypeUtil.isExpectedClassType(parentIndentSingleSpaceTypes, parent)) { + return parentIndentLen.plus(1) + } + val grand = parent.parentBlock grand?.let { grand -> if (grand is SqlCreateKeywordGroupBlock) { val grandIndentLen = grand.indent.groupIndentLen return grandIndentLen.plus(parentIndentLen).minus(1) } - if (grand is SqlInsertKeywordGroupBlock) { - return parentIndentLen - } - if (grand is SqlColumnGroupBlock) { - val grandIndentLen = grand.indent.groupIndentLen - var prevTextLen = 1 - parent.prevChildren?.dropLast(1)?.forEach { prev -> prevTextLen = prevTextLen.plus(prev.getNodeText().length) } - return grandIndentLen.plus(prevTextLen).plus(1) + + val grandIndent = grand.indent.indentLen + val groupIndent = parentBlock?.indent?.groupIndentLen ?: 0 + + if (grand is SqlColumnRawGroupBlock) { + return groupIndent.plus(grandIndent) } + return groupIndent.plus(grandIndent).minus(1) } - return parentIndentLen + return parentIndentLen.plus(1) } else { var prevLen = 0 parent.childBlocks @@ -106,10 +120,24 @@ open class SqlCommaBlock( ) } return parent.indent.groupIndentLen - .plus(prevLen) .plus(1) } } return 1 } + + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean { + val exceptionTypes = + listOf( + SqlInsertColumnGroupBlock::class, + SqlInsertValueGroupBlock::class, + SqlUpdateSetGroupBlock::class, + SqlUpdateColumnGroupBlock::class, + SqlUpdateValueGroupBlock::class, + SqlFunctionParamBlock::class, + SqlWithColumnGroupBlock::class, + SqlKeywordGroupBlock::class, + ) + return TypeUtil.isExpectedClassType(exceptionTypes, parentBlock) + } } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlDataTypeBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlDataTypeBlock.kt index fb58e872..31898b95 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlDataTypeBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlDataTypeBlock.kt @@ -15,29 +15,23 @@ */ package org.domaframework.doma.intellij.formatter.block -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.IndentType +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 open class SqlDataTypeBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlBlock( node, - wrap, - alignment, + context.wrap, + context.alignment, null, - spacingBuilder, - enableFormat, - formatMode, + context.spacingBuilder, + context.enableFormat, + context.formatMode, ) { override val indent = ElementIndent( @@ -46,11 +40,15 @@ open class SqlDataTypeBlock( 0, ) - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + override fun setParentGroupBlock(lastGroupBlock: SqlBlock?) { + super.setParentGroupBlock(lastGroupBlock) indent.indentLevel = IndentType.NONE indent.indentLen = 1 indent.groupIndentLen = indent.indentLen + + if (lastGroupBlock is SqlCreateTableColumnDefinitionRawGroupBlock) { + lastGroupBlock.columnDataTypeBlock = this + } } override fun buildChildren(): MutableList = mutableListOf() diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlKeywordBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlKeywordBlock.kt index 82fa7653..fd160482 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlKeywordBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlKeywordBlock.kt @@ -15,32 +15,22 @@ */ package org.domaframework.doma.intellij.formatter.block -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode import com.intellij.formatting.Indent -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.IndentType +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 +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithCommonTableGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithQueryGroupBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext open class SqlKeywordBlock( node: ASTNode, val indentLevel: IndentType = IndentType.ATTACHED, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, -) : SqlNewGroupBlock( - node, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, - ) { + context: SqlBlockFormattingContext, +) : SqlNewGroupBlock(node, context) { override val indent = ElementIndent( indentLevel, @@ -48,14 +38,33 @@ open class SqlKeywordBlock( 0, ) - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) - + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) indent.indentLevel = indentLevel indent.indentLen = createBlockIndentLen() indent.groupIndentLen = indent.indentLen.plus(getNodeText().length) } + override fun setParentPropertyBlock(lastGroup: SqlBlock?) { + if (lastGroup is SqlKeywordGroupBlock) { + lastGroup.updateTopKeywordBlocks(this) + } + + if (getNodeText() == "nothing" && lastGroup is SqlDoGroupBlock) { + lastGroup.doQueryBlock = this + } + + if (lastGroup is SqlWithQueryGroupBlock) { + when (getNodeText()) { + "recursive" -> lastGroup.recursiveBlock = this + } + } + + if (lastGroup is SqlWithCommonTableGroupBlock) { + lastGroup.optionKeywordBlocks.add(this) + } + } + override fun buildChildren(): MutableList = mutableListOf() override fun getIndent(): Indent? { diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlLiteralBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlLiteralBlock.kt index 37c61eae..1b73c78e 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlLiteralBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlLiteralBlock.kt @@ -15,29 +15,22 @@ */ package org.domaframework.doma.intellij.formatter.block -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.IndentType +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext open class SqlLiteralBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlBlock( node, - wrap, - alignment, + context.wrap, + context.alignment, null, - spacingBuilder, - enableFormat, - formatMode, + context.spacingBuilder, + context.enableFormat, + context.formatMode, ) { override val indent = ElementIndent( @@ -46,8 +39,8 @@ open class SqlLiteralBlock( 0, ) - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) indent.indentLevel = IndentType.NONE indent.indentLen = 0 indent.groupIndentLen = 0 diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlOperationBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlOperationBlock.kt index 95a75756..703ca9ae 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlOperationBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlOperationBlock.kt @@ -15,29 +15,18 @@ */ package org.domaframework.doma.intellij.formatter.block -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.IndentType import org.domaframework.doma.intellij.formatter.block.expr.SqlElSymbolBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext class SqlOperationBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlElSymbolBlock( node, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, + context, ) { override val indent = ElementIndent( @@ -46,8 +35,8 @@ class SqlOperationBlock( 0, ) - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) indent.indentLevel = IndentType.NONE indent.indentLen = 0 indent.groupIndentLen = 0 diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlOtherBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlOtherBlock.kt index ab4c1ba1..0433119e 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlOtherBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlOtherBlock.kt @@ -15,34 +15,23 @@ */ package org.domaframework.doma.intellij.formatter.block -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.IndentType -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlUpdateColumnGroupBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext open class SqlOtherBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - lastGroup: SqlBlock? = null, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlBlock( node, - wrap, - alignment, + context.wrap, + context.alignment, null, - spacingBuilder, - enableFormat, - formatMode, + context.spacingBuilder, + context.enableFormat, + context.formatMode, ) { - var isUpdateColumnSubstitutions = isBeforeUpdateValuesBlock(lastGroup) - override val indent = ElementIndent( IndentType.NONE, @@ -50,8 +39,8 @@ open class SqlOtherBlock( 0, ) - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) indent.indentLevel = IndentType.NONE indent.indentLen = createIndentLen() indent.groupIndentLen = 0 @@ -59,17 +48,7 @@ open class SqlOtherBlock( override fun buildChildren(): MutableList = mutableListOf() - private fun createIndentLen(): Int { - if (isUpdateColumnSubstitutions) { - parentBlock?.let { return it.indent.groupIndentLen.plus(1) } - ?: return indent.indentLen - } else { - return 1 - } - } - - fun isBeforeUpdateValuesBlock(lastGroupBlock: SqlBlock?): Boolean = - lastGroupBlock?.childBlocks?.lastOrNull() is SqlUpdateColumnGroupBlock + private fun createIndentLen(): Int = 1 override fun isLeaf(): Boolean = true } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlRightPatternBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlRightPatternBlock.kt index 85b2c086..ad2a8a1e 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlRightPatternBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlRightPatternBlock.kt @@ -15,53 +15,59 @@ */ package org.domaframework.doma.intellij.formatter.block -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.IndentType -import org.domaframework.doma.intellij.formatter.block.group.SqlColumnDefinitionRawGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlInsertKeywordGroupBlock +import org.domaframework.doma.intellij.common.util.TypeUtil.isExpectedClassType +import org.domaframework.doma.intellij.formatter.block.conflict.SqlConflictClauseBlock +import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnDefinitionRawGroupBlock import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlUpdateKeywordGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlColumnDefinitionGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.create.SqlCreateTableColumnDefinitionGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.insert.SqlInsertColumnGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.update.SqlUpdateColumnGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.update.SqlUpdateSetGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.update.SqlUpdateValueGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithQuerySubGroupBlock import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlFunctionParamBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlInsertColumnGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGroupBlock import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubQueryGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlUpdateColumnGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlUpdateValueGroupBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext /** * Parent is always a subclass of a subgroup */ open class SqlRightPatternBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlBlock( node, - wrap, - alignment, + context.wrap, + context.alignment, null, - spacingBuilder, - enableFormat, - formatMode, + context.spacingBuilder, + context.enableFormat, + context.formatMode, ) { var preSpaceRight = false + /** + * Configures whether to add a space to the right side when the group ends. + */ fun enableLastRight() { parentBlock?.let { parent -> - // TODO:Customize indentation - if (parent is SqlFunctionParamBlock) { + // TODO:Customize spacing + val notInsertSpaceClassList = + listOf( + SqlFunctionParamBlock::class, + SqlInsertColumnGroupBlock::class, + SqlWithQuerySubGroupBlock::class, + ) + if (isExpectedClassType(notInsertSpaceClassList, parent)) { preSpaceRight = false return } - if (parent is SqlInsertColumnGroupBlock) { + + if (parent.parentBlock is SqlConflictClauseBlock) { preSpaceRight = false return } @@ -78,11 +84,7 @@ open class SqlRightPatternBlock( } parent.parentBlock?.let { grand -> - preSpaceRight = ( - grand.indent.indentLevel <= IndentType.SECOND && - grand.parentBlock !is SqlInsertKeywordGroupBlock - ) || - grand.indent.indentLevel == IndentType.JOIN + preSpaceRight = grand.childBlocks.find { it is SqlKeywordGroupBlock } != null return } } @@ -96,31 +98,50 @@ open class SqlRightPatternBlock( 0, ) - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) indent.indentLevel = IndentType.NONE indent.indentLen = createBlockIndentLen() indent.groupIndentLen = indent.indentLen enableLastRight() + (lastGroup as? SqlSubGroupBlock)?.endPatternBlock = this } override fun buildChildren(): MutableList = mutableListOf() - override fun createBlockIndentLen(): Int = - if (parentBlock is SqlUpdateColumnGroupBlock || parentBlock is SqlUpdateValueGroupBlock) { - parentBlock?.indent?.indentLen ?: 1 - } else { - parentBlock?.indent?.groupIndentLen ?: 1 - } + override fun createBlockIndentLen(): Int { + parentBlock?.let { parent -> + if (parent is SqlWithQuerySubGroupBlock) return 0 + val exceptionalTypes = + listOf( + SqlUpdateColumnGroupBlock::class, + SqlUpdateValueGroupBlock::class, + SqlCreateTableColumnDefinitionGroupBlock::class, + ) + if (isExpectedClassType(exceptionalTypes, parent)) return parent.indent.indentLen + return parent.indent.groupIndentLen + } ?: return 0 + } override fun isLeaf(): Boolean = true - fun isNewLine(lastGroup: SqlBlock?): Boolean = - lastGroup is SqlColumnDefinitionGroupBlock || - lastGroup is SqlColumnDefinitionRawGroupBlock || - lastGroup?.parentBlock is SqlUpdateKeywordGroupBlock || - lastGroup?.parentBlock is SqlUpdateColumnGroupBlock || - lastGroup is SqlUpdateColumnGroupBlock || - lastGroup is SqlUpdateValueGroupBlock || - lastGroup?.parentBlock is SqlUpdateValueGroupBlock + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean { + val exceptionalTypes = + listOf( + SqlCreateTableColumnDefinitionGroupBlock::class, + SqlColumnDefinitionRawGroupBlock::class, + SqlUpdateColumnGroupBlock::class, + SqlUpdateValueGroupBlock::class, + SqlWithQuerySubGroupBlock::class, + ) + if (isExpectedClassType(exceptionalTypes, parentBlock)) return true + + val parentExceptionalTypes = + listOf( + SqlUpdateSetGroupBlock::class, + SqlUpdateColumnGroupBlock::class, + SqlUpdateValueGroupBlock::class, + ) + return isExpectedClassType(parentExceptionalTypes, parentBlock?.parentBlock) + } } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlTableBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlTableBlock.kt index df223871..4ad08d06 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlTableBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlTableBlock.kt @@ -15,27 +15,28 @@ */ package org.domaframework.doma.intellij.formatter.block -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.create.SqlCreateKeywordGroupBlock +import org.domaframework.doma.intellij.formatter.util.CreateQueryType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext class SqlTableBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlWordBlock( node, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, + context, ) { + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + } + + override fun setParentPropertyBlock(lastGroup: SqlBlock?) { + if (lastGroup is SqlCreateKeywordGroupBlock && lastGroup.createType == CreateQueryType.TABLE) { + lastGroup.tableBlock = this + } + } + override fun buildChildren(): MutableList = mutableListOf() } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlUnknownBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlUnknownBlock.kt index 5f477cda..0836dc9b 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlUnknownBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlUnknownBlock.kt @@ -15,32 +15,25 @@ */ package org.domaframework.doma.intellij.formatter.block -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.IndentType +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext class SqlUnknownBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlBlock( node, - wrap, - alignment, + context.wrap, + context.alignment, null, - spacingBuilder, - enableFormat, - formatMode, + context.spacingBuilder, + context.enableFormat, + context.formatMode, ) { - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) indent.indentLevel = IndentType.NONE indent.indentLen = 0 indent.groupIndentLen = 0 diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlWordBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlWordBlock.kt index d6e6ab4e..d4faabe4 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlWordBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlWordBlock.kt @@ -15,30 +15,24 @@ */ package org.domaframework.doma.intellij.formatter.block -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.IndentType +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithCommonTableGroupBlock import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubQueryGroupBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext open class SqlWordBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlBlock( node, - wrap, - alignment, + context.wrap, + context.alignment, null, - spacingBuilder, - enableFormat, - formatMode, + context.spacingBuilder, + context.enableFormat, + context.formatMode, ) { override val indent = ElementIndent( @@ -47,12 +41,18 @@ open class SqlWordBlock( 0, ) - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) indent.indentLen = createBlockIndentLen() indent.groupIndentLen = indent.indentLen } + override fun setParentPropertyBlock(lastGroup: SqlBlock?) { + if (lastGroup is SqlWithCommonTableGroupBlock) { + lastGroup.commonTableNameBlock = this + } + } + override fun buildChildren(): MutableList = mutableListOf() override fun createBlockIndentLen(): Int { diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlBlockCommentBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/comment/SqlBlockCommentBlock.kt similarity index 60% rename from src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlBlockCommentBlock.kt rename to src/main/kotlin/org/domaframework/doma/intellij/formatter/block/comment/SqlBlockCommentBlock.kt index edb94215..04b11ab0 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlBlockCommentBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/comment/SqlBlockCommentBlock.kt @@ -13,26 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.domaframework.doma.intellij.formatter.block +package org.domaframework.doma.intellij.formatter.block.comment -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode +import com.intellij.psi.util.PsiTreeUtil +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext open class SqlBlockCommentBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlCommentBlock( node, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, - ) + context, + ) { + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean = PsiTreeUtil.prevLeaf(node.psi)?.text?.contains("\n") == true +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlCommentBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/comment/SqlCommentBlock.kt similarity index 70% rename from src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlCommentBlock.kt rename to src/main/kotlin/org/domaframework/doma/intellij/formatter/block/comment/SqlCommentBlock.kt index 1401ecbb..8764ae11 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlCommentBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/comment/SqlCommentBlock.kt @@ -13,32 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.domaframework.doma.intellij.formatter.block +package org.domaframework.doma.intellij.formatter.block.comment -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.IndentType +import org.domaframework.doma.intellij.formatter.block.SqlBlock import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGroupBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext abstract class SqlCommentBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlBlock( node, - wrap, - alignment, + context.wrap, + context.alignment, null, - spacingBuilder, - enableFormat, - formatMode, + context.spacingBuilder, + context.enableFormat, + context.formatMode, ) { override val indent = ElementIndent( @@ -47,8 +41,8 @@ abstract class SqlCommentBlock( 0, ) - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) indent.indentLevel = IndentType.NONE indent.indentLen = createBlockIndentLen() indent.groupIndentLen = 0 @@ -66,6 +60,6 @@ abstract class SqlCommentBlock( return parent.indent.indentLen } } - return 1 + return 0 } } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlLineCommentBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/comment/SqlLineCommentBlock.kt similarity index 56% rename from src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlLineCommentBlock.kt rename to src/main/kotlin/org/domaframework/doma/intellij/formatter/block/comment/SqlLineCommentBlock.kt index 718cf03c..adbcc527 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlLineCommentBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/comment/SqlLineCommentBlock.kt @@ -13,33 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.domaframework.doma.intellij.formatter.block +package org.domaframework.doma.intellij.formatter.block.comment -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock +import com.intellij.psi.util.PsiTreeUtil +import org.domaframework.doma.intellij.formatter.block.SqlBlock import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubQueryGroupBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext open class SqlLineCommentBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlCommentBlock( node, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, + context, ) { - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) indent.indentLen = createBlockIndentLen() } @@ -48,17 +39,19 @@ open class SqlLineCommentBlock( override fun isLeaf(): Boolean = true override fun createBlockIndentLen(): Int { - parentBlock?.let { - if (it is SqlSubQueryGroupBlock) { - if (it.childBlocks.dropLast(1).isEmpty()) { - return 1 + parentBlock?.let { parent -> + if (parent is SqlSubQueryGroupBlock) { + if (parent.getChildBlocksDropLast().isEmpty()) { + return 0 } - if (it.isFirstLineComment) { - return it.indent.groupIndentLen.minus(2) + if (parent.isFirstLineComment) { + return parent.indent.groupIndentLen.minus(2) } } - return it.indent.indentLen + return parent.indent.indentLen } - return 1 + return 0 } + + override fun isSaveSpace(lastGroup: SqlBlock?) = PsiTreeUtil.prevLeaf(node.psi)?.text?.contains("\n") == true } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/conflict/OnConflictKeywordType.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/conflict/OnConflictKeywordType.kt new file mode 100644 index 00000000..651925c0 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/conflict/OnConflictKeywordType.kt @@ -0,0 +1,22 @@ +/* + * 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.conflict + +enum class OnConflictKeywordType { + CONFLICT, + CONSTRAINT, + UNKNOWN, +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/conflict/SqlConflictClauseBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/conflict/SqlConflictClauseBlock.kt new file mode 100644 index 00000000..877cd91a --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/conflict/SqlConflictClauseBlock.kt @@ -0,0 +1,43 @@ +/* + * 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.conflict + +import com.intellij.lang.ASTNode +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +class SqlConflictClauseBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlKeywordGroupBlock( + node, + IndentType.TOP, + context, + ) { + var conflictType: OnConflictKeywordType = OnConflictKeywordType.CONFLICT + var doBlock: SqlDoGroupBlock? = null + + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + indent.indentLevel = IndentType.CONFLICT + indent.indentLen = createBlockIndentLen() + indent.groupIndentLen = indent.indentLen.plus(getNodeText().length) + } + + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean = lastGroup !is SqlConflictClauseBlock +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/conflict/SqlDoGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/conflict/SqlDoGroupBlock.kt new file mode 100644 index 00000000..7341c1ae --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/conflict/SqlDoGroupBlock.kt @@ -0,0 +1,50 @@ +/* + * 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.conflict + +import com.intellij.lang.ASTNode +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +class SqlDoGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlKeywordGroupBlock( + node, + IndentType.CONFLICT, + context, + ) { + /** + * **NOTHING** or **UPDATE** + */ + var doQueryBlock: SqlBlock? = null + + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + indent.indentLen = 0 + indent.groupIndentLen = getNodeText().length + } + + override fun setParentPropertyBlock(lastGroup: SqlBlock?) { + if (lastGroup is SqlConflictClauseBlock) { + lastGroup.doBlock = this + } + } + + override fun isSaveSpace(lastGroup: SqlBlock?) = true +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElAtSignBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElAtSignBlock.kt index d13912f2..c6b868c2 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElAtSignBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElAtSignBlock.kt @@ -15,33 +15,20 @@ */ package org.domaframework.doma.intellij.formatter.block.expr -import com.intellij.formatting.Alignment import com.intellij.formatting.Block -import com.intellij.formatting.FormattingMode import com.intellij.formatting.Spacing -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.SqlCustomSpacingBuilder -import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.builder.SqlCustomSpacingBuilder +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext class SqlElAtSignBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, + context: SqlBlockFormattingContext, private val customSpacingBuilder: SqlCustomSpacingBuilder?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, -) : SqlBlock( +) : SqlExprBlock( node, - wrap, - alignment, - null, - spacingBuilder, - enableFormat, - formatMode, + context, ) { override fun buildChildren(): MutableList = mutableListOf() diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElBlockCommentBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElBlockCommentBlock.kt index ebc60d5c..28b38130 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElBlockCommentBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElBlockCommentBlock.kt @@ -15,41 +15,27 @@ */ package org.domaframework.doma.intellij.formatter.block.expr -import com.intellij.formatting.Alignment import com.intellij.formatting.Block -import com.intellij.formatting.FormattingMode import com.intellij.formatting.Spacing -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.PsiWhiteSpace import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.IndentType -import org.domaframework.doma.intellij.formatter.SqlCustomSpacingBuilder import org.domaframework.doma.intellij.formatter.block.SqlBlock -import org.domaframework.doma.intellij.formatter.block.SqlBlockCommentBlock -import org.domaframework.doma.intellij.formatter.block.SqlCommentBlock import org.domaframework.doma.intellij.formatter.block.SqlOperationBlock import org.domaframework.doma.intellij.formatter.block.SqlUnknownBlock +import org.domaframework.doma.intellij.formatter.block.comment.SqlBlockCommentBlock +import org.domaframework.doma.intellij.formatter.block.comment.SqlCommentBlock import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubQueryGroupBlock +import org.domaframework.doma.intellij.formatter.builder.SqlCustomSpacingBuilder +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext import org.domaframework.doma.intellij.psi.SqlTypes open class SqlElBlockCommentBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, + private val context: SqlBlockFormattingContext, open val customSpacingBuilder: SqlCustomSpacingBuilder?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - private val formatMode: FormattingMode, -) : SqlCommentBlock( - node, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, - ) { +) : SqlCommentBlock(node, context) { override val indent = ElementIndent( IndentType.NONE, @@ -57,8 +43,8 @@ open class SqlElBlockCommentBlock( 0, ) - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) indent.indentLevel = IndentType.NONE indent.indentLen = createBlockIndentLen() indent.groupIndentLen = 0 @@ -82,45 +68,31 @@ open class SqlElBlockCommentBlock( SqlTypes.GE, SqlTypes.LE, SqlTypes.GT, SqlTypes.LT, SqlTypes.EL_EQ, SqlTypes.EL_NE, SqlTypes.PLUS, SqlTypes.MINUS, SqlTypes.ASTERISK, SqlTypes.SLASH, SqlTypes.AT_SIGN, -> - SqlOperationBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + SqlOperationBlock(child, context) SqlTypes.EL_FIELD_ACCESS_EXPR -> SqlElFieldAccessBlock( child, - wrap, - alignment, + context, createFieldAccessSpacingBuilder(), - spacingBuilder, - isEnableFormat(), - formatMode, ) SqlTypes.EL_STATIC_FIELD_ACCESS_EXPR -> SqlElStaticFieldAccessBlock( child, - wrap, - alignment, - createStaticFieldSpacingBuilder(), - spacingBuilder, - isEnableFormat(), - formatMode, + context, ) SqlTypes.EL_FUNCTION_CALL_EXPR -> SqlElFunctionCallBlock( child, - wrap, - alignment, - createSpacingBuilder(), - spacingBuilder, - isEnableFormat(), - formatMode, + context, ) SqlTypes.BLOCK_COMMENT_CONTENT -> - SqlBlockCommentBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + SqlBlockCommentBlock(child, context) - else -> SqlUnknownBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + else -> SqlUnknownBlock(child, context) } private fun createFieldAccessSpacingBuilder(): SqlCustomSpacingBuilder = @@ -143,26 +115,6 @@ open class SqlElBlockCommentBlock( Spacing.createSpacing(0, 0, 0, false, 0), ) - private fun createStaticFieldSpacingBuilder(): SqlCustomSpacingBuilder = - SqlCustomSpacingBuilder() - .withSpacing( - SqlTypes.AT_SIGN, - SqlTypes.EL_CLASS, - Spacing.createSpacing(0, 0, 0, false, 0), - ).withSpacing( - SqlTypes.AT_SIGN, - SqlTypes.EL_IDENTIFIER, - Spacing.createSpacing(0, 0, 0, false, 0), - ).withSpacing( - SqlTypes.EL_IDENTIFIER, - SqlTypes.DOT, - Spacing.createSpacing(0, 0, 0, false, 0), - ).withSpacing( - SqlTypes.EL_IDENTIFIER, - SqlTypes.EL_PARAMETERS, - Spacing.createSpacing(0, 0, 0, false, 0), - ) - override fun getSpacing( child1: Block?, child2: Block, @@ -178,14 +130,14 @@ open class SqlElBlockCommentBlock( override fun createBlockIndentLen(): Int { parentBlock?.let { if (it is SqlSubQueryGroupBlock) { - if (it.childBlocks.dropLast(1).isEmpty()) { - return 1 + if (it.getChildBlocksDropLast().isEmpty()) { + return 0 } if (it.isFirstLineComment) { return it.indent.groupIndentLen.minus(2) } } } - return 1 + return 0 } } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElClassBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElClassBlock.kt index 07f1efd8..d4660e01 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElClassBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElClassBlock.kt @@ -15,36 +15,24 @@ */ package org.domaframework.doma.intellij.formatter.block.expr -import com.intellij.formatting.Alignment import com.intellij.formatting.Block -import com.intellij.formatting.FormattingMode import com.intellij.formatting.Spacing -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.PsiWhiteSpace import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.SqlCustomSpacingBuilder import org.domaframework.doma.intellij.formatter.block.SqlBlock import org.domaframework.doma.intellij.formatter.block.SqlUnknownBlock +import org.domaframework.doma.intellij.formatter.builder.SqlCustomSpacingBuilder +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext import org.domaframework.doma.intellij.psi.SqlTypes class SqlElClassBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, + private val context: SqlBlockFormattingContext, private val customSpacingBuilder: SqlCustomSpacingBuilder?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - private val formatMode: FormattingMode, -) : SqlBlock( +) : SqlExprBlock( node, - wrap, - alignment, - customSpacingBuilder, - spacingBuilder, - enableFormat, - formatMode, + context, ) { override fun buildChildren(): MutableList { val blocks = mutableListOf() @@ -61,11 +49,11 @@ class SqlElClassBlock( override fun getBlock(child: ASTNode): SqlBlock = when (child.elementType) { - SqlTypes.EL_IDENTIFIER -> SqlElIdentifierBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + SqlTypes.EL_IDENTIFIER -> SqlElIdentifierBlock(child, context) - SqlTypes.DOT -> SqlElDotBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + SqlTypes.DOT -> SqlElDotBlock(child, context) - else -> SqlUnknownBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + else -> SqlUnknownBlock(child, context) } override fun getSpacing( diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElClassRightBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElClassRightBlock.kt index a2fbc639..a27d727a 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElClassRightBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElClassRightBlock.kt @@ -15,31 +15,18 @@ */ package org.domaframework.doma.intellij.formatter.block.expr -import com.intellij.formatting.Alignment import com.intellij.formatting.Block -import com.intellij.formatting.FormattingMode import com.intellij.formatting.Spacing -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext class SqlElClassRightBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, -) : SqlBlock( + context: SqlBlockFormattingContext, +) : SqlExprBlock( node, - wrap, - alignment, - null, - spacingBuilder, - enableFormat, - formatMode, + context, ) { override fun buildChildren(): MutableList = mutableListOf() diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElCommaBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElCommaBlock.kt index c8b71e6d..3b79ee3e 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElCommaBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElCommaBlock.kt @@ -15,27 +15,16 @@ */ package org.domaframework.doma.intellij.formatter.block.expr -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext open class SqlElCommaBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, -) : SqlElSymbolBlock( + context: SqlBlockFormattingContext, +) : SqlExprBlock( node, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, + context, ) { override fun buildChildren(): MutableList = mutableListOf() } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElConditionLoopCommentBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElConditionLoopCommentBlock.kt index 9d9d4b81..931231ef 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElConditionLoopCommentBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElConditionLoopCommentBlock.kt @@ -15,30 +15,27 @@ */ package org.domaframework.doma.intellij.formatter.block.expr -import com.intellij.formatting.Alignment import com.intellij.formatting.Block -import com.intellij.formatting.FormattingMode import com.intellij.formatting.Spacing -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.PsiWhiteSpace import com.intellij.psi.formatter.common.AbstractBlock import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.elementType import org.domaframework.doma.intellij.extension.expr.isConditionOrLoopDirective -import org.domaframework.doma.intellij.formatter.IndentType -import org.domaframework.doma.intellij.formatter.SqlCustomSpacingBuilder import org.domaframework.doma.intellij.formatter.block.SqlBlock -import org.domaframework.doma.intellij.formatter.block.SqlBlockCommentBlock import org.domaframework.doma.intellij.formatter.block.SqlCommaBlock import org.domaframework.doma.intellij.formatter.block.SqlOperationBlock import org.domaframework.doma.intellij.formatter.block.SqlUnknownBlock -import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlCreateKeywordGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlInsertKeywordGroupBlock +import org.domaframework.doma.intellij.formatter.block.comment.SqlBlockCommentBlock +import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnRawGroupBlock import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlColumnGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.create.SqlCreateKeywordGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.insert.SqlInsertQueryGroupBlock import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGroupBlock +import org.domaframework.doma.intellij.formatter.builder.SqlCustomSpacingBuilder +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext import org.domaframework.doma.intellij.psi.SqlCustomElCommentExpr import org.domaframework.doma.intellij.psi.SqlElForDirective import org.domaframework.doma.intellij.psi.SqlElIfDirective @@ -46,20 +43,12 @@ import org.domaframework.doma.intellij.psi.SqlTypes class SqlElConditionLoopCommentBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, + private val context: SqlBlockFormattingContext, override val customSpacingBuilder: SqlCustomSpacingBuilder?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - private val formatMode: FormattingMode, ) : SqlElBlockCommentBlock( node, - wrap, - alignment, + context, customSpacingBuilder, - spacingBuilder, - enableFormat, - formatMode, ) { enum class SqlConditionLoopCommentBlockType { CONDITION, @@ -100,8 +89,8 @@ class SqlElConditionLoopCommentBlock( 0, ) - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) indent.indentLevel = IndentType.NONE indent.indentLen = createBlockIndentLen() indent.groupIndentLen = 0 @@ -125,45 +114,31 @@ class SqlElConditionLoopCommentBlock( SqlTypes.GE, SqlTypes.LE, SqlTypes.GT, SqlTypes.LT, SqlTypes.EL_EQ, SqlTypes.EL_NE, SqlTypes.PLUS, SqlTypes.MINUS, SqlTypes.ASTERISK, SqlTypes.SLASH, SqlTypes.AT_SIGN, -> - SqlOperationBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + SqlOperationBlock(child, context) SqlTypes.EL_FIELD_ACCESS_EXPR -> SqlElFieldAccessBlock( child, - wrap, - alignment, + context, createFieldAccessSpacingBuilder(), - spacingBuilder, - isEnableFormat(), - formatMode, ) SqlTypes.EL_STATIC_FIELD_ACCESS_EXPR -> SqlElStaticFieldAccessBlock( child, - wrap, - alignment, - createStaticFieldSpacingBuilder(), - spacingBuilder, - isEnableFormat(), - formatMode, + context, ) SqlTypes.EL_FUNCTION_CALL_EXPR -> SqlElFunctionCallBlock( child, - wrap, - alignment, - createSpacingBuilder(), - spacingBuilder, - isEnableFormat(), - formatMode, + context, ) SqlTypes.BLOCK_COMMENT_CONTENT -> - SqlBlockCommentBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + SqlBlockCommentBlock(child, context) - else -> SqlUnknownBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + else -> SqlUnknownBlock(child, context) } private fun createFieldAccessSpacingBuilder(): SqlCustomSpacingBuilder = @@ -186,26 +161,6 @@ class SqlElConditionLoopCommentBlock( Spacing.createSpacing(0, 0, 0, false, 0), ) - private fun createStaticFieldSpacingBuilder(): SqlCustomSpacingBuilder = - SqlCustomSpacingBuilder() - .withSpacing( - SqlTypes.AT_SIGN, - SqlTypes.EL_CLASS, - Spacing.createSpacing(0, 0, 0, false, 0), - ).withSpacing( - SqlTypes.AT_SIGN, - SqlTypes.EL_IDENTIFIER, - Spacing.createSpacing(0, 0, 0, false, 0), - ).withSpacing( - SqlTypes.EL_IDENTIFIER, - SqlTypes.DOT, - Spacing.createSpacing(0, 0, 0, false, 0), - ).withSpacing( - SqlTypes.EL_IDENTIFIER, - SqlTypes.EL_PARAMETERS, - Spacing.createSpacing(0, 0, 0, false, 0), - ) - override fun getSpacing( child1: Block?, child2: Block, @@ -218,6 +173,8 @@ class SqlElConditionLoopCommentBlock( override fun isLeaf(): Boolean = false + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean = true + override fun createBlockIndentLen(): Int { parentBlock?.let { parent -> when (parent) { @@ -229,10 +186,10 @@ class SqlElConditionLoopCommentBlock( val grandIndentLen = grand.indent.groupIndentLen return grandIndentLen.plus(parentGroupIndentLen).minus(1) } - if (grand is SqlInsertKeywordGroupBlock) { + if (grand is SqlInsertQueryGroupBlock) { return parentGroupIndentLen } - if (grand is SqlColumnGroupBlock) { + if (grand is SqlColumnRawGroupBlock) { val grandIndentLen = grand.indent.groupIndentLen var prevTextLen = 1 parent.prevChildren?.dropLast(1)?.forEach { prev -> @@ -254,6 +211,6 @@ class SqlElConditionLoopCommentBlock( } } } - return 1 + return 0 } } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElDotBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElDotBlock.kt index 6f49dd8b..25d4f928 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElDotBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElDotBlock.kt @@ -15,29 +15,18 @@ */ package org.domaframework.doma.intellij.formatter.block.expr -import com.intellij.formatting.Alignment import com.intellij.formatting.Block -import com.intellij.formatting.FormattingMode import com.intellij.formatting.Spacing -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext open class SqlElDotBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, -) : SqlElSymbolBlock( + context: SqlBlockFormattingContext, +) : SqlExprBlock( node, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, + context, ) { override fun buildChildren(): MutableList = mutableListOf() diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElFieldAccessBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElFieldAccessBlock.kt index a630c054..47a56628 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElFieldAccessBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElFieldAccessBlock.kt @@ -15,35 +15,23 @@ */ package org.domaframework.doma.intellij.formatter.block.expr -import com.intellij.formatting.Alignment import com.intellij.formatting.Block -import com.intellij.formatting.FormattingMode import com.intellij.formatting.Spacing -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.PsiWhiteSpace import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.SqlCustomSpacingBuilder import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.builder.SqlCustomSpacingBuilder +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext import org.domaframework.doma.intellij.psi.SqlTypes class SqlElFieldAccessBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, + private val context: SqlBlockFormattingContext, private val customSpacingBuilder: SqlCustomSpacingBuilder?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - private val formatMode: FormattingMode, -) : SqlBlock( +) : SqlExprBlock( node, - wrap, - alignment, - customSpacingBuilder, - spacingBuilder, - enableFormat, - formatMode, + context, ) { override fun buildChildren(): MutableList { val blocks = mutableListOf() @@ -61,19 +49,28 @@ class SqlElFieldAccessBlock( override fun getBlock(child: ASTNode): SqlBlock = when (child.elementType) { SqlTypes.EL_PRIMARY_EXPR -> { - SqlElPrimaryBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + SqlElPrimaryBlock(child, context) } SqlTypes.DOT -> - SqlElDotBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + SqlElDotBlock(child, context) SqlTypes.EL_IDENTIFIER -> - SqlElIdentifierBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + SqlElIdentifierBlock(child, context) SqlTypes.EL_PARAMETERS -> - SqlElParametersBlock(child, wrap, alignment, customSpacingBuilder, spacingBuilder, isEnableFormat(), formatMode) + SqlElParametersBlock(child, context) - else -> SqlBlock(child, wrap, alignment, customSpacingBuilder, spacingBuilder, isEnableFormat(), formatMode) + else -> + SqlBlock( + child, + context.wrap, + context.alignment, + customSpacingBuilder, + context.spacingBuilder, + context.enableFormat, + context.formatMode, + ) } override fun getSpacing( diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElFunctionCallBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElFunctionCallBlock.kt index 43b78a5c..c931eaf0 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElFunctionCallBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElFunctionCallBlock.kt @@ -15,33 +15,19 @@ */ package org.domaframework.doma.intellij.formatter.block.expr -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.PsiWhiteSpace import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.SqlCustomSpacingBuilder import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext import org.domaframework.doma.intellij.psi.SqlTypes class SqlElFunctionCallBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - customSpacingBuilder: SqlCustomSpacingBuilder?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - private val formatMode: FormattingMode, -) : SqlBlock( + private val context: SqlBlockFormattingContext, +) : SqlExprBlock( node, - wrap, - alignment, - customSpacingBuilder, - spacingBuilder, - enableFormat, - formatMode, + context, ) { override fun buildChildren(): MutableList { val blocks = mutableListOf() @@ -59,15 +45,24 @@ class SqlElFunctionCallBlock( override fun getBlock(child: ASTNode): SqlBlock = when (child.elementType) { SqlTypes.AT_SIGN -> - SqlElAtSignBlock(child, wrap, alignment, createSpacingBuilder(), spacingBuilder, isEnableFormat(), formatMode) + SqlElAtSignBlock(child, context, createSpacingBuilder()) SqlTypes.EL_IDENTIFIER -> - SqlElIdentifierBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + SqlElIdentifierBlock(child, context) SqlTypes.EL_PARAMETERS -> - SqlElParametersBlock(child, wrap, alignment, createSpacingBuilder(), spacingBuilder, isEnableFormat(), formatMode) + SqlElParametersBlock(child, context) - else -> SqlBlock(child, wrap, alignment, createSpacingBuilder(), spacingBuilder, isEnableFormat(), formatMode) + else -> + SqlBlock( + child, + wrap, + alignment, + createSpacingBuilder(), + spacingBuilder, + context.enableFormat, + context.formatMode, + ) } override fun isLeaf(): Boolean = false diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElIdentifierBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElIdentifierBlock.kt index f61f5845..86d41cb8 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElIdentifierBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElIdentifierBlock.kt @@ -15,29 +15,16 @@ */ package org.domaframework.doma.intellij.formatter.block.expr -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext class SqlElIdentifierBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, -) : SqlBlock( + context: SqlBlockFormattingContext, +) : SqlExprBlock( node, - wrap, - alignment, - null, - spacingBuilder, - enableFormat, - formatMode, + context, ) { override fun buildChildren(): MutableList = mutableListOf() diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElParametersBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElParametersBlock.kt index 3c58861d..092a8bb5 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElParametersBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElParametersBlock.kt @@ -15,45 +15,31 @@ */ package org.domaframework.doma.intellij.formatter.block.expr -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode -import org.domaframework.doma.intellij.formatter.SqlCustomSpacingBuilder import org.domaframework.doma.intellij.formatter.block.SqlBlock import org.domaframework.doma.intellij.formatter.block.SqlUnknownBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext import org.domaframework.doma.intellij.psi.SqlTypes class SqlElParametersBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - customSpacingBuilder: SqlCustomSpacingBuilder?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - private val formatMode: FormattingMode, -) : SqlBlock( + private val context: SqlBlockFormattingContext, +) : SqlExprBlock( node, - wrap, - alignment, - customSpacingBuilder, - spacingBuilder, - enableFormat, - formatMode, + context, ) { override fun getBlock(child: ASTNode): SqlBlock = when (child.elementType) { SqlTypes.LEFT_PAREN, SqlTypes.RIGHT_PAREN -> - SqlElSymbolBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + SqlElSymbolBlock(child, context) SqlTypes.EL_PRIMARY_EXPR -> - SqlElPrimaryBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + SqlElPrimaryBlock(child, context) SqlTypes.COMMA -> - SqlElCommaBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + SqlElCommaBlock(child, context) - else -> SqlUnknownBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + else -> SqlUnknownBlock(child, context) } override fun isLeaf(): Boolean = false diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElPrimaryBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElPrimaryBlock.kt index 1d5e84b3..16b81e10 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElPrimaryBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElPrimaryBlock.kt @@ -15,30 +15,17 @@ */ package org.domaframework.doma.intellij.formatter.block.expr -import com.intellij.formatting.Alignment import com.intellij.formatting.Block -import com.intellij.formatting.FormattingMode import com.intellij.formatting.Spacing -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode -import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext class SqlElPrimaryBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, -) : SqlBlock( + context: SqlBlockFormattingContext, +) : SqlExprBlock( node, - wrap, - alignment, - null, - spacingBuilder, - enableFormat, - formatMode, + context, ) { override fun getSpacing( child1: Block?, diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElStaticFieldAccessBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElStaticFieldAccessBlock.kt index bf9bf98e..b8efbe12 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElStaticFieldAccessBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElStaticFieldAccessBlock.kt @@ -15,36 +15,23 @@ */ package org.domaframework.doma.intellij.formatter.block.expr -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode import com.intellij.formatting.Spacing -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.PsiWhiteSpace import com.intellij.psi.util.PsiTreeUtil -import org.domaframework.doma.intellij.formatter.SqlCustomSpacingBuilder import org.domaframework.doma.intellij.formatter.block.SqlBlock import org.domaframework.doma.intellij.formatter.block.SqlUnknownBlock +import org.domaframework.doma.intellij.formatter.builder.SqlCustomSpacingBuilder +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext import org.domaframework.doma.intellij.psi.SqlElClass import org.domaframework.doma.intellij.psi.SqlTypes class SqlElStaticFieldAccessBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - customSpacingBuilder: SqlCustomSpacingBuilder?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - private val formatMode: FormattingMode, -) : SqlBlock( + private val context: SqlBlockFormattingContext, +) : SqlExprBlock( node, - wrap, - alignment, - customSpacingBuilder, - spacingBuilder, - enableFormat, - formatMode, + context, ) { override fun getBlock(child: ASTNode): SqlBlock = when (child.elementType) { @@ -54,19 +41,19 @@ class SqlElStaticFieldAccessBlock( PsiWhiteSpace::class.java, ) is SqlElClass ) { - SqlElClassRightBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + SqlElClassRightBlock(child, context) } else { - SqlElAtSignBlock(child, wrap, alignment, null, spacingBuilder, isEnableFormat(), formatMode) + SqlElAtSignBlock(child, context, null) } } SqlTypes.EL_CLASS -> - SqlElClassBlock(child, wrap, alignment, null, spacingBuilder, isEnableFormat(), formatMode) + SqlElClassBlock(child, context, null) SqlTypes.EL_IDENTIFIER -> - SqlElIdentifierBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + SqlElIdentifierBlock(child, context) - else -> SqlUnknownBlock(child, wrap, alignment, spacingBuilder, isEnableFormat(), formatMode) + else -> SqlUnknownBlock(child, context) } override fun createSpacingBuilder(): SqlCustomSpacingBuilder = diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElSymbolBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElSymbolBlock.kt index 2e3cc8a1..cb9f7052 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElSymbolBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlElSymbolBlock.kt @@ -15,30 +15,18 @@ */ package org.domaframework.doma.intellij.formatter.block.expr -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.IndentType import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext open class SqlElSymbolBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, -) : SqlBlock( + context: SqlBlockFormattingContext, +) : SqlExprBlock( node, - wrap, - alignment, - null, - spacingBuilder, - enableFormat, - formatMode, + context, ) { override val indent = ElementIndent( @@ -47,8 +35,8 @@ open class SqlElSymbolBlock( 0, ) - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) indent.indentLevel = IndentType.NONE indent.indentLen = 0 indent.groupIndentLen = 0 diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlExprBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlExprBlock.kt new file mode 100644 index 00000000..89f76817 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/expr/SqlExprBlock.kt @@ -0,0 +1,40 @@ +/* + * 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.expr + +import com.intellij.formatting.Block +import com.intellij.formatting.Spacing +import com.intellij.lang.ASTNode +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +abstract class SqlExprBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlBlock( + node, + context.wrap, + context.alignment, + null, + context.spacingBuilder, + context.enableFormat, + context.formatMode, + ) { + override fun getSpacing( + child1: Block?, + child2: Block, + ): Spacing? = null +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/SqlColumnDefinitionRawGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/SqlColumnDefinitionRawGroupBlock.kt deleted file mode 100644 index 745c72f7..00000000 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/SqlColumnDefinitionRawGroupBlock.kt +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 - -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap -import com.intellij.lang.ASTNode -import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.block.SqlBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlColumnDefinitionGroupBlock -import org.domaframework.doma.intellij.psi.SqlTypes - -/** - * Column definition group block in the column list group attached to Create Table - * The parent must be SqlColumnDefinitionGroupBlock - */ -class SqlColumnDefinitionRawGroupBlock( - node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, -) : SqlBlock( - node, - wrap, - alignment, - null, - spacingBuilder, - enableFormat, - formatMode, - ) { - // TODO:Customize indentation within an inline group - val defaultOffset = 5 - val isFirstColumnRaw = node.elementType != SqlTypes.COMMA - - var columnName = getNodeText() - - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) - indent.indentLen = createBlockIndentLen() - indent.groupIndentLen = indent.indentLen - } - - override fun buildChildren(): MutableList = mutableListOf() - - /** - * Right-justify the longest column name in the column definition. - */ - override fun createBlockIndentLen(): Int { - if (!isFirstColumnRaw) return defaultOffset - - parentBlock?.let { - return when (it) { - is SqlColumnDefinitionGroupBlock -> { - getColumnRawNewIndent(it) - } - - else -> { - 1 - } - } - } - return 1 - } - - private fun getColumnRawNewIndent(groupRawBlock: SqlColumnDefinitionGroupBlock): Int { - val groupMaxAlimentLen = groupRawBlock.alignmentColumnName.length - val diffColumnName = groupMaxAlimentLen.minus(columnName.length) - val newSpaces = defaultOffset.plus(diffColumnName) - return newSpaces.plus(2) - } -} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/SqlNewGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/SqlNewGroupBlock.kt index 713207e9..32eaf2b9 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/SqlNewGroupBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/SqlNewGroupBlock.kt @@ -15,26 +15,35 @@ */ package org.domaframework.doma.intellij.formatter.block.group -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext +import org.domaframework.doma.intellij.psi.SqlTypes abstract class SqlNewGroupBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlBlock( node, - wrap, - alignment, + context.wrap, + context.alignment, null, - spacingBuilder, - enableFormat, - formatMode, - ) + context.spacingBuilder, + context.enableFormat, + context.formatMode, + ) { + protected fun getKeywordNameLength( + blocks: List, + dropLast: Int = 0, + ): Int { + val keywords = + blocks + .dropLast(dropLast) + .takeWhile { it.node.elementType == SqlTypes.KEYWORD } + val parentLen = + keywords.sumOf { keyword -> + keyword.getNodeText().length.plus(1) + } + return parentLen + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlColumnBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/column/SqlColumnBlock.kt similarity index 60% rename from src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlColumnBlock.kt rename to src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/column/SqlColumnBlock.kt index d89152c8..492a58b6 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/SqlColumnBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/column/SqlColumnBlock.kt @@ -13,31 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.domaframework.doma.intellij.formatter.block +package org.domaframework.doma.intellij.formatter.block.group.column -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.IndentType -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlColumnDefinitionGroupBlock +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.SqlWordBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.create.SqlCreateTableColumnDefinitionGroupBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext class SqlColumnBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlWordBlock( node, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, + context, ) { override val indent = ElementIndent( @@ -46,22 +37,26 @@ class SqlColumnBlock( 0, ) - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) indent.indentLevel = IndentType.NONE // Calculate right justification space during indentation after getting all column rows indent.indentLen = 1 indent.groupIndentLen = 0 } + override fun setParentPropertyBlock(lastGroup: SqlBlock?) { + (lastGroup as? SqlColumnDefinitionRawGroupBlock)?.columnBlock = this + } + override fun buildChildren(): MutableList = mutableListOf() override fun createBlockIndentLen(): Int { parentBlock?.let { - val parentGroupDefinition = it.parentBlock as? SqlColumnDefinitionGroupBlock + val parentGroupDefinition = it.parentBlock as? SqlCreateTableColumnDefinitionGroupBlock if (parentGroupDefinition == null) return 1 - val groupMaxAlimentLen = parentGroupDefinition.alignmentColumnName.length + val groupMaxAlimentLen = parentGroupDefinition.getMaxColumnNameLength() val diffColumnName = groupMaxAlimentLen.minus(getNodeText().length) return diffColumnName.plus(1) } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/column/SqlColumnDefinitionRawGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/column/SqlColumnDefinitionRawGroupBlock.kt new file mode 100644 index 00000000..477a2754 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/column/SqlColumnDefinitionRawGroupBlock.kt @@ -0,0 +1,55 @@ +/* + * 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.column + +import com.intellij.lang.ASTNode +import com.intellij.psi.formatter.common.AbstractBlock +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext +import org.domaframework.doma.intellij.psi.SqlTypes + +/** + * Column definition group block in the column list group attached to Create Table + * The parent must be SqlCreateTableColumnDefinitionGroupBlock + */ +open class SqlColumnDefinitionRawGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlRawGroupBlock( + node, + context, + ) { + // TODO:Customize indentation within an inline group + open val defaultOffset = 0 + val isFirstColumnRaw = node.elementType != SqlTypes.COMMA + + open var columnBlock: SqlBlock? = if (isFirstColumnRaw) this else null + + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + indent.indentLen = createBlockIndentLen() + indent.groupIndentLen = indent.indentLen + } + + override fun buildChildren(): MutableList = mutableListOf() + + /** + * Right-justify the longest column name in the column definition. + */ + override fun createBlockIndentLen(): Int = if (isFirstColumnRaw) 1 else defaultOffset + + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean = true +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/column/SqlColumnRawGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/column/SqlColumnRawGroupBlock.kt new file mode 100644 index 00000000..b6b0e30e --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/column/SqlColumnRawGroupBlock.kt @@ -0,0 +1,71 @@ +/* + * 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.column + +import com.intellij.lang.ASTNode +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.top.SqlSelectQueryGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.update.SqlUpdateColumnGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithQueryGroupBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +/** + * Group blocks when generating columns with subqueries. + * Represents a group consisting of a single column row. + * For Example: + * SELECT id -- "id" is [SqlColumnRawGroupBlock](isFirstColumnGroup = true) + * , name -- "," is [SqlColumnRawGroupBlock] + */ +class SqlColumnRawGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlRawGroupBlock( + node, + context, + ) { + private val offset = 1 + + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + indent.indentLevel = IndentType.COLUMN + indent.indentLen = createBlockIndentLen() + indent.groupIndentLen = + if (isFirstColumnGroup) indent.indentLen else indent.indentLen.plus(1) + } + + override fun setParentPropertyBlock(lastGroup: SqlBlock?) { + when (lastGroup) { + is SqlSelectQueryGroupBlock -> lastGroup.selectionColumns.add(this) + is SqlUpdateColumnGroupBlock -> lastGroup.columnRawGroupBlocks.add(this) + } + (lastGroup as? SqlSelectQueryGroupBlock)?.selectionColumns?.add(this) + } + + override fun createBlockIndentLen(): Int = + if (parentBlock is SqlWithQueryGroupBlock) { + parentBlock + ?.childBlocks + ?.dropLast(1) + ?.lastOrNull() + ?.indent + ?.indentLen ?: offset + } else { + parentBlock?.indent?.groupIndentLen?.plus(1) ?: offset + } + + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean = !isFirstColumnGroup +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/column/SqlColumnSelectionGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/column/SqlColumnSelectionGroupBlock.kt new file mode 100644 index 00000000..78b5e006 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/column/SqlColumnSelectionGroupBlock.kt @@ -0,0 +1,31 @@ +/* + * 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.column + +import com.intellij.lang.ASTNode +import com.intellij.psi.formatter.common.AbstractBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGroupBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +abstract class SqlColumnSelectionGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlSubGroupBlock( + node, + context, + ) { + override fun buildChildren(): MutableList = mutableListOf() +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/column/SqlRawGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/column/SqlRawGroupBlock.kt new file mode 100644 index 00000000..82de996c --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/column/SqlRawGroupBlock.kt @@ -0,0 +1,55 @@ +/* + * 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.column + +import com.intellij.formatting.Block +import com.intellij.formatting.Spacing +import com.intellij.lang.ASTNode +import com.intellij.psi.formatter.common.AbstractBlock +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +abstract class SqlRawGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlBlock( + node, + context.wrap, + context.alignment, + null, + context.spacingBuilder, + context.enableFormat, + context.formatMode, + ) { + var isFirstColumnGroup = getNodeText() != "," + + override val indent = + ElementIndent( + IndentType.COLUMN, + 0, + 0, + ) + + override fun getSpacing( + p0: Block?, + p1: Block, + ): Spacing? = null + + override fun buildChildren(): MutableList = mutableListOf() + + override fun isLeaf(): Boolean = true +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlInlineGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlInlineGroupBlock.kt index 524c860b..aea65d2e 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlInlineGroupBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlInlineGroupBlock.kt @@ -15,31 +15,20 @@ */ package org.domaframework.doma.intellij.formatter.block.group.keyword -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode import com.intellij.formatting.Indent -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.IndentType import org.domaframework.doma.intellij.formatter.block.SqlBlock import org.domaframework.doma.intellij.formatter.block.group.SqlNewGroupBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext open class SqlInlineGroupBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlNewGroupBlock( node, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, + context, ) { override val indent = ElementIndent( @@ -48,8 +37,8 @@ open class SqlInlineGroupBlock( 0, ) - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) indent.indentLevel = IndentType.INLINE indent.indentLen = createBlockIndentLen() indent.groupIndentLen = indent.indentLen.plus(getNodeText().length) diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlInlineSecondGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlInlineSecondGroupBlock.kt index 6e9df359..237c5353 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlInlineSecondGroupBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlInlineSecondGroupBlock.kt @@ -15,31 +15,20 @@ */ package org.domaframework.doma.intellij.formatter.block.group.keyword -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode import com.intellij.formatting.Indent -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.IndentType import org.domaframework.doma.intellij.formatter.block.SqlBlock import org.domaframework.doma.intellij.formatter.block.group.SqlNewGroupBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext open class SqlInlineSecondGroupBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlNewGroupBlock( node, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, + context, ) { val isEndCase = getNodeText().lowercase() == "end" @@ -50,8 +39,8 @@ open class SqlInlineSecondGroupBlock( 0, ) - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) indent.indentLevel = IndentType.INLINE_SECOND indent.indentLen = createBlockIndentLen() indent.groupIndentLen = indent.indentLen diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlInsertKeywordGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlInsertKeywordGroupBlock.kt deleted file mode 100644 index 3aafafea..00000000 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlInsertKeywordGroupBlock.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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 - -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.Indent -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap -import com.intellij.lang.ASTNode -import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.IndentType -import org.domaframework.doma.intellij.formatter.block.SqlBlock - -open class SqlInsertKeywordGroupBlock( - node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, -) : SqlKeywordGroupBlock( - node, - IndentType.TOP, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, - ) { - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) - indent.indentLevel = IndentType.TOP - indent.indentLen = createBlockIndentLen() - indent.groupIndentLen = indent.indentLen.plus(getNodeText().length) - } - - override fun buildChildren(): MutableList = mutableListOf() - - override fun getIndent(): Indent? = Indent.getSpaceIndent(indent.indentLen) - - override fun createBlockIndentLen(): Int = - if (!isAdjustIndentOnEnter()) { - parentBlock?.let { - if (it.indent.indentLevel == IndentType.SUB) { - it.indent.groupIndentLen.plus(1) - } else { - 0 - } - } ?: 0 - } else { - 0 - } -} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlJoinGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlJoinGroupBlock.kt index 7fbfa4d8..cb2336ea 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlJoinGroupBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlJoinGroupBlock.kt @@ -15,31 +15,20 @@ */ package org.domaframework.doma.intellij.formatter.block.group.keyword -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode import com.intellij.formatting.Indent -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.IndentType import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext open class SqlJoinGroupBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlKeywordGroupBlock( node, IndentType.JOIN, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, + context, ) { override val indent = ElementIndent( @@ -48,12 +37,12 @@ open class SqlJoinGroupBlock( 0, ) - override fun setParentGroupBlock(block: SqlBlock?) { - parentBlock = block + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + parentBlock = lastGroup parentBlock?.childBlocks?.add(this) indent.indentLevel = IndentType.JOIN - indent.indentLen = createBlockIndentLen() - indent.groupIndentLen = indent.indentLen.plus(getNodeText().length) + indent.indentLen = createBlockIndentLen(null) + indent.groupIndentLen = createGroupIndentLen() } override fun buildChildren(): MutableList = mutableListOf() @@ -65,9 +54,11 @@ open class SqlJoinGroupBlock( Indent.getSpaceIndent(indent.indentLen) } - override fun createBlockIndentLen(): Int = + override fun createBlockIndentLen(preChildBlock: SqlBlock?): Int = parentBlock ?.indent ?.groupIndentLen ?.plus(1) ?: 1 + + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean = true } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlKeywordGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlKeywordGroupBlock.kt index 432b59aa..c9ea64b1 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlKeywordGroupBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlKeywordGroupBlock.kt @@ -15,38 +15,30 @@ */ package org.domaframework.doma.intellij.formatter.block.group.keyword -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode import com.intellij.formatting.Indent -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.IndentType -import org.domaframework.doma.intellij.formatter.SqlKeywordUtil import org.domaframework.doma.intellij.formatter.block.SqlBlock import org.domaframework.doma.intellij.formatter.block.group.SqlNewGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.top.SqlSelectQueryGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithCommonTableGroupBlock import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubQueryGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlViewGroupBlock -import org.domaframework.doma.intellij.psi.SqlTypes +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext +import org.domaframework.doma.intellij.formatter.util.SqlKeywordUtil open class SqlKeywordGroupBlock( node: ASTNode, val indentLevel: IndentType = IndentType.TOP, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, -) : SqlNewGroupBlock( - node, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, - ) { + context: SqlBlockFormattingContext, +) : SqlNewGroupBlock(node, context) { + val topKeywordBlocks: MutableList = mutableListOf(this) + + fun updateTopKeywordBlocks(block: SqlBlock) { + topKeywordBlocks.add(block) + indent.groupIndentLen = createGroupIndentLen() + } + override val indent = ElementIndent( indentLevel, @@ -54,27 +46,44 @@ open class SqlKeywordGroupBlock( 0, ) - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) - val preChildBlock = block?.childBlocks?.dropLast(1)?.lastOrNull() + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + val preChildBlock = + if (lastGroup?.indent?.indentLevel == IndentType.FILE) { + null + } else { + lastGroup?.childBlocks?.dropLast(1)?.lastOrNull() + } indent.indentLevel = indentLevel - val baseIndentLen = getBaseIndentLen(preChildBlock, block) - indent.groupIndentLen = baseIndentLen.plus(getNodeText().length) + val baseIndentLen = getBaseIndentLen(preChildBlock, lastGroup) + indent.groupIndentLen = createGroupIndentLen() indent.indentLen = adjustIndentIfFirstChildIsLineComment(baseIndentLen) - createGroupIndentLen() } - private fun getBaseIndentLen( + override fun setParentPropertyBlock(lastGroup: SqlBlock?) { + if (lastGroup is SqlSelectQueryGroupBlock && + SqlKeywordUtil.isSelectSecondOptionKeyword(getNodeText()) + ) { + lastGroup.secondGroupBlocks.add(this) + } + + if (lastGroup is SqlWithCommonTableGroupBlock) { + lastGroup.queryGroupBlock.add(this) + } + } + + open fun getBaseIndentLen( preChildBlock: SqlBlock?, - block: SqlBlock?, + lastGroup: SqlBlock?, ): Int { - if (block == null) { - return createBlockIndentLen() + if (lastGroup == null) { + return createBlockIndentLen(preChildBlock) } - if (preChildBlock != null && - preChildBlock.indent.indentLevel == this.indent.indentLevel && - !SqlKeywordUtil.isSetLineKeyword(preChildBlock.getNodeText(), block.getNodeText()) + if (preChildBlock == null) return createBlockIndentLen(preChildBlock) + + if (preChildBlock.indent.indentLevel == this.indent.indentLevel && + !SqlKeywordUtil.isSetLineKeyword(getNodeText(), preChildBlock.getNodeText()) ) { if (indent.indentLevel == IndentType.SECOND) { val diffPreBlockTextLen = getNodeText().length.minus(preChildBlock.getNodeText().length) @@ -84,7 +93,7 @@ open class SqlKeywordGroupBlock( return preChildBlock.indent.indentLen.minus(diffPretextLen) } } else { - return createBlockIndentLen() + return createBlockIndentLen(preChildBlock) } } @@ -100,12 +109,12 @@ open class SqlKeywordGroupBlock( /** * Adjust the indent position of the subgroup block element itself if it has a comment */ - private fun adjustIndentIfFirstChildIsLineComment(baseIndent: Int): Int { - parentBlock?.let { + open fun adjustIndentIfFirstChildIsLineComment(baseIndent: Int): Int { + parentBlock?.let { parent -> if (indent.indentLevel == IndentType.TOP) { - return if (it is SqlSubGroupBlock) { - return if (it.isFirstLineComment) { - it.indent.groupIndentLen.minus(it.getNodeText().length) + return if (parent is SqlSubGroupBlock) { + return if (parent.isFirstLineComment) { + parent.indent.groupIndentLen.minus(parent.getNodeText().length) } else { val newIndentLen = baseIndent.minus(1) return if (newIndentLen >= 0) newIndentLen else 0 @@ -118,77 +127,23 @@ open class SqlKeywordGroupBlock( return baseIndent } - override fun createBlockIndentLen(): Int { + open fun createBlockIndentLen(preChildBlock: SqlBlock?): Int { when (indentLevel) { IndentType.TOP -> { - parentBlock?.let { - val groupLen = it.indent.groupIndentLen - return if (it.indent.indentLevel == IndentType.FILE) { + parentBlock?.let { parent -> + if (SqlKeywordUtil.isSetLineKeyword(getNodeText(), preChildBlock?.getNodeText() ?: "")) { + val prevBlockIndent = preChildBlock?.indent?.indentLen ?: 0 + val prevBlockLen = preChildBlock?.getNodeText()?.length ?: 0 + return prevBlockIndent.plus(prevBlockLen).plus(1) + } + return if (parent.indent.indentLevel == IndentType.FILE) { 0 } else { - groupLen + parent.indent.groupIndentLen } } ?: return 0 } - IndentType.SECOND -> { - parentBlock?.let { parent -> - val groupLen = parent.indent.groupIndentLen - - if (parent.indent.indentLevel == IndentType.FILE) { - return 0 - } else { - parent.parentBlock?.let { grand -> - return if (grand is SqlViewGroupBlock) { - groupLen.minus(this.getNodeText().length) - } else if (grand is SqlSubGroupBlock) { - groupLen.minus(getNodeText().length).plus(1) - } else { - groupLen.minus(this.getNodeText().length) - } - } ?: return groupLen.minus(this.getNodeText().length) - } - } ?: return 1 - } - - IndentType.SECOND_OPTION -> { - parentBlock?.let { parent -> - val groupLen = parent.indent.groupIndentLen - if (parent.indent.indentLevel == IndentType.FILE) { - return 0 - } - val subGroupBlock = parent.parentBlock as? SqlSubGroupBlock - val newIndent = - if (parent is SqlSubQueryGroupBlock) { - return if (getNodeText() == "and") { - groupLen - } else { - groupLen.plus(1) - } - } else if (getNodeText() == "and" && parent.getNodeText() == "or") { - return groupLen.plus(1) - } else if (parent is SqlKeywordGroupBlock && subGroupBlock != null && subGroupBlock.isFirstLineComment) { - groupLen - } else { - var parentLen = 0 - val removeStartOffsetLess = - parent.childBlocks.dropLast(1).filter { - it.node.startOffset > - parent.node.startOffset - } - val keywords = - removeStartOffsetLess - .takeWhile { it.node.elementType == SqlTypes.KEYWORD } - keywords.forEach { keyword -> - parentLen = parentLen.plus(keyword.getNodeText().length).plus(1) - } - val parentTextLen = parent.indent.groupIndentLen.plus(parentLen) - return parentTextLen.minus(getNodeText().length) - } - return newIndent - } ?: 1 - } - IndentType.INLINE_SECOND -> { parentBlock?.let { if (it.indent.indentLevel == IndentType.FILE) 0 @@ -202,19 +157,10 @@ open class SqlKeywordGroupBlock( return 1 } - private fun createGroupIndentLen(): Int { - parentBlock?.let { - if (indent.indentLevel == IndentType.SECOND_OPTION) { - var parentLen = 0 - val keywords = it.childBlocks.dropLast(1).filter { it.node.elementType == SqlTypes.KEYWORD } - keywords.forEach { keyword -> - parentLen = parentLen.plus(keyword.getNodeText().length).plus(1) - } - it.indent.groupIndentLen - .plus(parentLen) - .minus(getNodeText().length) - } - } ?: 1 - return 1 - } + override fun createGroupIndentLen(): Int = + indent.indentLen + .plus(topKeywordBlocks.drop(1).sumOf { it.getNodeText().length.plus(1) }) + .plus(getNodeText().length) + + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean = true } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlSecondOptionKeywordGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlSecondOptionKeywordGroupBlock.kt new file mode 100644 index 00000000..b8f2f6b4 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlSecondOptionKeywordGroupBlock.kt @@ -0,0 +1,61 @@ +/* + * 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 + +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.group.subgroup.SqlSubGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubQueryGroupBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext +import org.domaframework.doma.intellij.formatter.util.SqlKeywordUtil + +open class SqlSecondOptionKeywordGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlKeywordGroupBlock(node, IndentType.SECOND_OPTION, context) { + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + indent.indentLen = createBlockIndentLen() + } + + override fun createBlockIndentLen(): Int { + parentBlock?.let { parent -> + val groupLen = parent.indent.groupIndentLen + if (parent.indent.indentLevel == IndentType.FILE) { + return 0 + } + val subGroupBlock = parent.parentBlock as? SqlSubGroupBlock + val newIndent = + if (parent is SqlSubQueryGroupBlock) { + groupLen.plus(1) + } else if (parent is SqlKeywordGroupBlock && subGroupBlock != null && subGroupBlock.isFirstLineComment) { + groupLen + } else { + return parent.indent.groupIndentLen + .minus(getNodeText().length) + } + return newIndent + } ?: 1 + return 1 + } + + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean { + val prevKeyword = lastGroup?.childBlocks?.dropLast(1)?.findLast { it is SqlKeywordBlock } + return !SqlKeywordUtil.isSetLineKeyword(getNodeText(), prevKeyword?.getNodeText() ?: "") + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlUpdateKeywordGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlUpdateKeywordGroupBlock.kt deleted file mode 100644 index a96d7b77..00000000 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlUpdateKeywordGroupBlock.kt +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 - -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.Indent -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap -import com.intellij.lang.ASTNode -import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.IndentType -import org.domaframework.doma.intellij.formatter.block.SqlBlock - -open class SqlUpdateKeywordGroupBlock( - node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, -) : SqlKeywordGroupBlock( - node, - IndentType.SECOND, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, - ) { - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) - indent.indentLevel = IndentType.SECOND - indent.indentLen = createBlockIndentLen() - indent.groupIndentLen = indent.indentLen.plus(getNodeText().length) - } - - override fun buildChildren(): MutableList = mutableListOf() - - override fun getIndent(): Indent? = Indent.getSpaceIndent(indent.indentLen) - - override fun createBlockIndentLen(): Int = - if (!isAdjustIndentOnEnter()) { - parentBlock?.let { - if (it.indent.indentLevel == IndentType.SUB) { - it.indent.groupIndentLen.plus(1) - } else { - val parentTextLen = it.getNodeText().length - val diffTextLen = parentTextLen.minus(getNodeText().length) - it.indent.indentLen.plus(diffTextLen) - } - } ?: 0 - } else { - 0 - } -} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/condition/SqlConditionKeywordGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/condition/SqlConditionKeywordGroupBlock.kt new file mode 100644 index 00000000..2218fe21 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/condition/SqlConditionKeywordGroupBlock.kt @@ -0,0 +1,72 @@ +/* + * 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.condition + +import com.intellij.lang.ASTNode +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlSecondOptionKeywordGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGroupBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +/** + * Keywords representing conditions such as AND or OR + */ +class SqlConditionKeywordGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlSecondOptionKeywordGroupBlock( + node, + context, + ) { + var conditionalExpressionGroupBlock: SqlConditionalExpressionGroupBlock? = null + + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + indent.indentLen = createBlockIndentLen() + indent.groupIndentLen = createGroupIndentLen() + } + + override fun setParentPropertyBlock(lastGroup: SqlBlock?) { + if (lastGroup is SqlConditionalExpressionGroupBlock) { + lastGroup.conditionKeywordGroupBlocks.add(this) + } + } + + // TODO If AND appears after OR, change it so that it is right-justified. + override fun createBlockIndentLen(): Int { + parentBlock?.let { parent -> + val groupLen = parent.indent.groupIndentLen + return if (parent is SqlSubGroupBlock) { + if (getNodeText() == "and") { + groupLen + } else { + groupLen.plus(1) + } + } else { + val orBlock = + parentBlock + ?.childBlocks + ?.dropLast(1) + ?.findLast { it is SqlConditionKeywordGroupBlock && it.getNodeText() == "or" } + if (getNodeText() == "and" && orBlock != null) { + groupLen.plus(1) + } else { + return parent.indent.groupIndentLen.minus(getNodeText().length) + } + } + } ?: return 1 + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/condition/SqlConditionalExpressionGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/condition/SqlConditionalExpressionGroupBlock.kt new file mode 100644 index 00000000..815bb6fb --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/condition/SqlConditionalExpressionGroupBlock.kt @@ -0,0 +1,51 @@ +/* + * 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.condition + +import com.intellij.lang.ASTNode +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGroupBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +/** + * A grouped conditional expression following keywords such as AND or OR + */ +class SqlConditionalExpressionGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlSubGroupBlock( + node, + context, + ) { + val conditionKeywordGroupBlocks: MutableList = mutableListOf() + + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + indent.indentLen = createBlockIndentLen() + indent.groupIndentLen = createGroupIndentLen() + } + + override fun setParentPropertyBlock(lastGroup: SqlBlock?) { + if (lastGroup is SqlConditionKeywordGroupBlock) { + lastGroup.conditionalExpressionGroupBlock = this + } + } + + override fun createBlockIndentLen(): Int = parentBlock?.indent?.groupIndentLen?.plus(1) ?: 1 + + override fun createGroupIndentLen(): Int = indent.indentLen.plus(1) +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlCreateKeywordGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/create/SqlCreateKeywordGroupBlock.kt similarity index 70% rename from src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlCreateKeywordGroupBlock.kt rename to src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/create/SqlCreateKeywordGroupBlock.kt index 5f6fb5ef..4bf8bc45 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/SqlCreateKeywordGroupBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/create/SqlCreateKeywordGroupBlock.kt @@ -13,39 +13,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.domaframework.doma.intellij.formatter.block.group.keyword +package org.domaframework.doma.intellij.formatter.block.group.keyword.create -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode import com.intellij.formatting.Indent -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.CreateQueryType -import org.domaframework.doma.intellij.formatter.IndentType import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.SqlTableBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock +import org.domaframework.doma.intellij.formatter.util.CreateQueryType +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext open class SqlCreateKeywordGroupBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlKeywordGroupBlock( node, IndentType.TOP, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, + context, ) { var createType: CreateQueryType = CreateQueryType.NONE + var tableBlock: SqlTableBlock? = null - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) indent.indentLevel = IndentType.TOP indent.indentLen = createBlockIndentLen() indent.groupIndentLen = indent.indentLen.plus(getNodeText().length) @@ -60,7 +52,7 @@ open class SqlCreateKeywordGroupBlock( return Indent.getNoneIndent() } - override fun createBlockIndentLen(): Int = + override fun createBlockIndentLen(preChildBlock: SqlBlock?): Int = parentBlock?.let { if (it.indent.indentLevel == IndentType.SUB) { it.indent.groupIndentLen.plus(1) @@ -70,6 +62,6 @@ open class SqlCreateKeywordGroupBlock( } ?: 0 fun setCreateQueryType(nextKeyword: String) { - createType = CreateQueryType.getCreateTableType(nextKeyword) + createType = CreateQueryType.Companion.getCreateTableType(nextKeyword) } } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/create/SqlCreateTableColumnDefinitionGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/create/SqlCreateTableColumnDefinitionGroupBlock.kt new file mode 100644 index 00000000..ed84aad7 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/create/SqlCreateTableColumnDefinitionGroupBlock.kt @@ -0,0 +1,65 @@ +/* + * 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.create + +import com.intellij.lang.ASTNode +import com.intellij.psi.formatter.common.AbstractBlock +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnDefinitionRawGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGroupBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +/** + * Column List Group Block attached to Create Table + * example : + * ``` + * CREATE TABLE departments + * ( -- [SqlCreateTableColumnDefinitionGroupBlock] + * id INT PRIMARY KEY -- [SqlCreateTableColumnDefinitionRawGroupBlock] + * , name VARCHAR(100) -- [SqlCreateTableColumnDefinitionRawGroupBlock] + * , loc INT NOT NULL -- [SqlCreateTableColumnDefinitionRawGroupBlock] + * ) + * ``` + */ +class SqlCreateTableColumnDefinitionGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlSubGroupBlock( + node, + context, + ) { + // TODO:Customize indentation + override val offset = 2 + private val groupOffset = 5 + val columnRawGroupBlocks = mutableListOf() + + fun getMaxColumnNameLength(): Int = + columnRawGroupBlocks.maxOfOrNull { raw -> + raw.columnBlock?.getNodeText()?.length ?: 0 + } ?: 0 + + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + indent.indentLen = createBlockIndentLen() + indent.groupIndentLen = indent.indentLen.plus(groupOffset) + } + + override fun buildChildren(): MutableList = mutableListOf() + + override fun createBlockIndentLen(): Int = offset + + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean = true +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/create/SqlCreateTableColumnDefinitionRawGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/create/SqlCreateTableColumnDefinitionRawGroupBlock.kt new file mode 100644 index 00000000..bd58573c --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/create/SqlCreateTableColumnDefinitionRawGroupBlock.kt @@ -0,0 +1,81 @@ +/* + * 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.create + +import com.intellij.lang.ASTNode +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.SqlDataTypeBlock +import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnDefinitionRawGroupBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext +import org.domaframework.doma.intellij.psi.SqlTypes + +class SqlCreateTableColumnDefinitionRawGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlColumnDefinitionRawGroupBlock( + node, + context, + ) { + var columnDataTypeBlock: SqlDataTypeBlock? = null + + override var parentBlock: SqlBlock? + get() = super.parentBlock as? SqlCreateTableColumnDefinitionGroupBlock + set(value) { + if (value is SqlCreateTableColumnDefinitionGroupBlock) { + super.parentBlock = value + } + } + + override var columnBlock: SqlBlock? + get() = super.columnBlock + set(value) { + if (value?.node?.elementType == SqlTypes.COMMA) { + super.columnBlock = null + } else { + super.columnBlock = value + } + } + + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + indent.indentLen = createBlockIndentLen() + indent.groupIndentLen = indent.indentLen + } + + override fun setParentPropertyBlock(lastGroup: SqlBlock?) { + (lastGroup as? SqlCreateTableColumnDefinitionGroupBlock) + ?.columnRawGroupBlocks + ?.add( + this, + ) + } + + override fun createBlockIndentLen(): Int = + (parentBlock as? SqlCreateTableColumnDefinitionGroupBlock) + ?.let { parent -> + return getColumnRawNewIndent(parent) + } ?: 1 + + /** + * Aligns each column name to the right based on the longest column name + * within the column definition group of a Create Table query. + */ + private fun getColumnRawNewIndent(groupRawBlock: SqlCreateTableColumnDefinitionGroupBlock): Int { + val groupMaxAlimentLen = groupRawBlock.getMaxColumnNameLength() + val diffColumnName = groupMaxAlimentLen.minus(getNodeText().length) + return diffColumnName.plus(2) + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlViewGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/create/SqlCreateViewGroupBlock.kt similarity index 67% rename from src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlViewGroupBlock.kt rename to src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/create/SqlCreateViewGroupBlock.kt index c22ecd6d..5d5e2de0 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlViewGroupBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/create/SqlCreateViewGroupBlock.kt @@ -13,34 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.domaframework.doma.intellij.formatter.block.group.subgroup +package org.domaframework.doma.intellij.formatter.block.group.keyword.create -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode import com.intellij.formatting.Indent -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.IndentType import org.domaframework.doma.intellij.formatter.block.SqlBlock import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext -open class SqlViewGroupBlock( +open class SqlCreateViewGroupBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlKeywordGroupBlock( node, IndentType.SECOND, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, + context, ) { override val indent = ElementIndent( @@ -49,8 +38,8 @@ open class SqlViewGroupBlock( 0, ) - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) indent.indentLevel = IndentType.SUB indent.indentLen = createBlockIndentLen() indent.groupIndentLen = getNodeText().length @@ -65,7 +54,11 @@ open class SqlViewGroupBlock( Indent.getSpaceIndent(indent.indentLen) } - override fun createBlockIndentLen(): Int = parentBlock?.indent?.indentLen ?: 0 + override fun createBlockIndentLen(preChildBlock: SqlBlock?): Int = 0 + + override fun createGroupIndentLen(): Int = getNodeText().length override fun isLeaf(): Boolean = true + + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean = true } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/insert/SqlInsertColumnGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/insert/SqlInsertColumnGroupBlock.kt new file mode 100644 index 00000000..ea4f22bc --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/insert/SqlInsertColumnGroupBlock.kt @@ -0,0 +1,89 @@ +/* + * 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.insert + +import com.intellij.lang.ASTNode +import com.intellij.psi.formatter.common.AbstractBlock +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGroupBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +/** + * Block of columns to insert + * For Example: + * INSERT INTO users + * (username -- [SqlInsertColumnGroupBlock] + * , email) + * VALUES ('user' + * , 'user@example.com') + */ +class SqlInsertColumnGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlSubGroupBlock( + node, + context, + ) { + // TODO:Customize indentation + override val offset = 2 + + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + indent.indentLen = createBlockIndentLen() + indent.groupIndentLen = indent.indentLen.plus(1) + updateParentGroupIndentLen() + } + + override fun setParentPropertyBlock(lastGroup: SqlBlock?) { + (lastGroup as? SqlInsertQueryGroupBlock)?.columnDefinitionGroupBlock = this + } + + override fun buildChildren(): MutableList = mutableListOf() + + /** + * Set the column line indentation to match the length of the `INSERT` query keyword (including `INSERT INTO`). + */ + override fun createBlockIndentLen(): Int { + parentBlock?.let { parent -> + val parentBaseLen = getParentInsertKeywordsIndentLength(parent) + return parentBaseLen.plus(1) + } ?: return offset + } + + /** + * Retrieve the length of the INSERT query keyword (including INSERT INTO) + * and adjust the indentation baseline for the INSERT group accordingly. + */ + private fun updateParentGroupIndentLen() { + parentBlock?.let { parent -> + // TODO Indentation is adjusted on the parent class side + val parentBaseLen = getParentInsertKeywordsIndentLength(parent) + parent.indent.groupIndentLen = parentBaseLen + } + } + + private fun getParentInsertKeywordsIndentLength(parent: SqlBlock): Int { + if (parent is SqlInsertQueryGroupBlock) { + var parentLen = getKeywordNameLength(parent.childBlocks, 1) + return parentLen + .plus(parent.indent.indentLen) + .plus(parent.getNodeText().length) + } + return parent.indent.groupIndentLen + } + + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean = true +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/insert/SqlInsertQueryGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/insert/SqlInsertQueryGroupBlock.kt new file mode 100644 index 00000000..7ef7454c --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/insert/SqlInsertQueryGroupBlock.kt @@ -0,0 +1,35 @@ +/* + * 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.insert + +import com.intellij.lang.ASTNode +import org.domaframework.doma.intellij.formatter.block.group.keyword.second.SqlValuesGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.top.SqlTopQueryGroupBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +open class SqlInsertQueryGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlTopQueryGroupBlock( + node, + context, + ) { + var columnDefinitionGroupBlock: SqlInsertColumnGroupBlock? = null + var valueKeywordBlock: SqlValuesGroupBlock? = null + var valueGroupBlock: SqlInsertValueGroupBlock? = null + + override fun createGroupIndentLen(): Int = indent.indentLen.plus(getNodeText().length) +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/insert/SqlInsertValueGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/insert/SqlInsertValueGroupBlock.kt new file mode 100644 index 00000000..d60d1db0 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/insert/SqlInsertValueGroupBlock.kt @@ -0,0 +1,44 @@ +/* + * 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.insert + +import com.intellij.lang.ASTNode +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGroupBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +class SqlInsertValueGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlSubGroupBlock( + node, + context, + ) { + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + indent.groupIndentLen = createBlockIndentLen() + } + + override fun setParentPropertyBlock(lastGroup: SqlBlock?) { + (lastGroup as? SqlInsertQueryGroupBlock)?.valueGroupBlock = this + } + + override fun createBlockIndentLen(): Int { + parentBlock?.let { parent -> + return parent.indent.groupIndentLen.plus(1) + } ?: return 1 + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/second/SqlReturningGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/second/SqlReturningGroupBlock.kt new file mode 100644 index 00000000..9094e9a9 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/second/SqlReturningGroupBlock.kt @@ -0,0 +1,31 @@ +/* + * 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.second + +import com.intellij.lang.ASTNode +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +class SqlReturningGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlSecondKeywordBlock(node, context) { + override fun createBlockIndentLen(): Int { + parentBlock?.let { parent -> + return parent.indent.indentLen + } + return 0 + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/second/SqlSecondKeywordBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/second/SqlSecondKeywordBlock.kt new file mode 100644 index 00000000..a71eb196 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/second/SqlSecondKeywordBlock.kt @@ -0,0 +1,60 @@ +/* + * 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.second + +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.group.keyword.SqlKeywordGroupBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext +import org.domaframework.doma.intellij.formatter.util.SqlKeywordUtil + +open class SqlSecondKeywordBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlKeywordGroupBlock(node, IndentType.SECOND, context) { + private val offset = 0 + + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + indent.indentLen = createBlockIndentLen() + indent.groupIndentLen = createGroupIndentLen() + } + + override fun createBlockIndentLen(): Int { + parentBlock?.let { parent -> + val groupLen = parent.indent.groupIndentLen + return if (parent.indent.indentLevel == IndentType.FILE) { + offset + } else { + groupLen.minus(this.getNodeText().length) + } + } + return offset + } + + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean { + lastGroup?.let { last -> + val prevKeyword = last.childBlocks.findLast { it is SqlKeywordBlock } + prevKeyword?.let { prev -> + return !SqlKeywordUtil.isSetLineKeyword(getNodeText(), prev.getNodeText()) + } + return !SqlKeywordUtil.isSetLineKeyword(getNodeText(), last.getNodeText()) + } + return true + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/second/SqlValuesGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/second/SqlValuesGroupBlock.kt new file mode 100644 index 00000000..95664449 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/second/SqlValuesGroupBlock.kt @@ -0,0 +1,34 @@ +/* + * 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.second + +import com.intellij.lang.ASTNode +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.insert.SqlInsertQueryGroupBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +class SqlValuesGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlSecondKeywordBlock(node, context) { + override fun setParentPropertyBlock(lastGroup: SqlBlock?) { + if (lastGroup is SqlInsertQueryGroupBlock) { + lastGroup.valueKeywordBlock = this + } + } + + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean = parentBlock is SqlInsertQueryGroupBlock +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/top/SqlDeleteQueryGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/top/SqlDeleteQueryGroupBlock.kt new file mode 100644 index 00000000..374d6198 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/top/SqlDeleteQueryGroupBlock.kt @@ -0,0 +1,24 @@ +/* + * 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.top + +import com.intellij.lang.ASTNode +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +class SqlDeleteQueryGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlTopQueryGroupBlock(node, context) diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/top/SqlJoinQueriesGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/top/SqlJoinQueriesGroupBlock.kt new file mode 100644 index 00000000..f6e2462b --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/top/SqlJoinQueriesGroupBlock.kt @@ -0,0 +1,58 @@ +/* + * 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.top + +import com.intellij.lang.ASTNode +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithQuerySubGroupBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +/** + * Join Queries Keyword Group Block + * [UNION, INTERSECT, EXCEPT] + */ +class SqlJoinQueriesGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlKeywordGroupBlock(node, IndentType.TOP, context) { + // TODO Customize offset + val offset = 0 + + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + indent.indentLen = createBlockIndentLen() + indent.groupIndentLen = createGroupIndentLen() + } + + override fun createBlockIndentLen(): Int { + parentBlock?.let { parent -> + if (parent is SqlWithQuerySubGroupBlock) { + return parent.indent.groupIndentLen + } + return parent.indent.groupIndentLen.plus(1) + } + return offset + } + + override fun createGroupIndentLen(): Int = + topKeywordBlocks + .sumOf { it.getNodeText().length.plus(1) } + .plus(indent.indentLen) + + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean = true +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/top/SqlSelectQueryGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/top/SqlSelectQueryGroupBlock.kt new file mode 100644 index 00000000..e4676e0a --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/top/SqlSelectQueryGroupBlock.kt @@ -0,0 +1,72 @@ +/* + * 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.top + +import com.intellij.lang.ASTNode +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithQuerySubGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubQueryGroupBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +class SqlSelectQueryGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlTopQueryGroupBlock( + node, + context, + ) { + val secondGroupBlocks: MutableList = mutableListOf() + val selectionColumns: MutableList = mutableListOf() + + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + parentBlock = lastGroup + parentBlock?.addChildBlock(this) + setParentPropertyBlock(lastGroup) + indent.indentLevel = indentLevel + indent.indentLen = createBlockIndentLen() + indent.groupIndentLen = createGroupIndentLen() + } + + override fun getBaseIndentLen( + preChildBlock: SqlBlock?, + lastGroup: SqlBlock?, + ): Int { + if (parentBlock is SqlSubGroupBlock) { + return parentBlock?.indent?.groupIndentLen + ?: createBlockIndentLen(preChildBlock) + } + return createBlockIndentLen(preChildBlock) + } + + override fun createGroupIndentLen(): Int { + parentBlock?.let { parent -> + if (parent is SqlSubQueryGroupBlock) { + return indent.indentLen.plus(getNodeText().length) + } + } + return indent.indentLen.plus(getNodeText().length) + } + + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean { + lastGroup?.let { lastBlock -> + if (lastGroup is SqlWithQuerySubGroupBlock) return true + if (lastBlock is SqlSubGroupBlock) return lastBlock.getChildBlocksDropLast().isNotEmpty() + } + return true + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/top/SqlTopQueryGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/top/SqlTopQueryGroupBlock.kt new file mode 100644 index 00000000..ddb9a17c --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/top/SqlTopQueryGroupBlock.kt @@ -0,0 +1,69 @@ +/* + * 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.top + +import com.intellij.formatting.Indent +import com.intellij.lang.ASTNode +import com.intellij.psi.formatter.common.AbstractBlock +import org.domaframework.doma.intellij.common.util.TypeUtil +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.create.SqlCreateViewGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithQuerySubGroupBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +abstract class SqlTopQueryGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlKeywordGroupBlock( + node, + IndentType.TOP, + context, + ) { + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + indent.indentLevel = indentLevel + indent.indentLen = createBlockIndentLen() + indent.groupIndentLen = createGroupIndentLen() + } + + override fun buildChildren(): MutableList = mutableListOf() + + override fun getIndent(): Indent? = + if (isAdjustIndentOnEnter()) { + Indent.getSpaceIndent(indent.indentLen) + } else { + null + } + + override fun createBlockIndentLen(): Int { + parentBlock?.let { parent -> + if (parent.indent.indentLevel == IndentType.FILE) return 0 + var baseIndent = parent.indent.groupIndentLen + val parentIndentSyncTypes = + listOf( + SqlCreateViewGroupBlock::class, + SqlWithQuerySubGroupBlock::class, + ) + if (!TypeUtil.isExpectedClassType(parentIndentSyncTypes, parent)) { + baseIndent = baseIndent.plus(1) + } + return baseIndent + } + return 0 + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/update/SqlUpdateColumnAssignmentSymbolBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/update/SqlUpdateColumnAssignmentSymbolBlock.kt new file mode 100644 index 00000000..25f5afee --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/update/SqlUpdateColumnAssignmentSymbolBlock.kt @@ -0,0 +1,48 @@ +/* + * 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.update + +import com.intellij.lang.ASTNode +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.SqlOtherBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +/** + * A block representing the `=` that connects column groups and value groups in a bulk update statement. + */ +class SqlUpdateColumnAssignmentSymbolBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlOtherBlock(node, context) { + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + indent.indentLen = createIndentLen() + indent.groupIndentLen = indent.groupIndentLen.plus(1) + } + + override fun setParentPropertyBlock(lastGroup: SqlBlock?) { + if (lastGroup is SqlUpdateSetGroupBlock && lastGroup.columnDefinitionGroupBlock != null) { + lastGroup.assignmentSymbol = this + } + } + + private fun createIndentLen(): Int { + parentBlock?.let { parent -> return parent.indent.groupIndentLen.plus(1) } + ?: return 0 + } + + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean = true +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/update/SqlUpdateColumnGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/update/SqlUpdateColumnGroupBlock.kt new file mode 100644 index 00000000..fd6485b9 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/update/SqlUpdateColumnGroupBlock.kt @@ -0,0 +1,74 @@ +/* + * 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.update + +import com.intellij.lang.ASTNode +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnRawGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnSelectionGroupBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +/** + * In an UPDATE statement using the row value constructor, + * a group representing the column list + */ +class SqlUpdateColumnGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlColumnSelectionGroupBlock( + node, + context, + ) { + // TODO:Customize indentation + override val offset = 2 + val columnRawGroupBlocks: MutableList = mutableListOf() + + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + indent.indentLen = createBlockIndentLen() + indent.groupIndentLen = indent.indentLen.plus(1) + updateParentGroupIndentLen() + } + + override fun setParentPropertyBlock(lastGroup: SqlBlock?) { + (lastGroup as? SqlUpdateSetGroupBlock)?.columnDefinitionGroupBlock = this + } + + override fun createBlockIndentLen(): Int { + parentBlock?.let { parent -> + if (parent is SqlUpdateSetGroupBlock) { + var parentLen = getKeywordNameLength(parent.childBlocks, 1) + return parent.indent.indentLen + .plus(parent.getNodeText().length) + .plus(1) + .plus(parentLen) + } + return offset + } ?: return offset + } + + private fun updateParentGroupIndentLen() { + parentBlock?.let { parent -> + if (parent is SqlUpdateSetGroupBlock) { + val parentLen = getKeywordNameLength(parent.childBlocks, 1) + parent.indent.groupIndentLen = + parent.indent.indentLen + .plus(parent.getNodeText().length) + .plus(parentLen) + } + } + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/update/SqlUpdateQueryGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/update/SqlUpdateQueryGroupBlock.kt new file mode 100644 index 00000000..c329fb65 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/update/SqlUpdateQueryGroupBlock.kt @@ -0,0 +1,61 @@ +/* + * 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.update + +import com.intellij.lang.ASTNode +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.conflict.SqlDoGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.top.SqlTopQueryGroupBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +class SqlUpdateQueryGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlTopQueryGroupBlock( + node, + context, + ) { + var setQueryGroupBlock: SqlUpdateSetGroupBlock? = null + + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + val preChildBlock = lastGroup?.childBlocks?.dropLast(1)?.lastOrNull() + indent.indentLevel = indentLevel + + val baseIndentLen = getBaseIndentLen(preChildBlock, lastGroup) + indent.groupIndentLen = baseIndentLen.plus(getNodeText().length) + indent.indentLen = baseIndentLen + } + + override fun setParentPropertyBlock(lastGroup: SqlBlock?) { + (lastGroup as? SqlDoGroupBlock)?.doQueryBlock = this + } + + override fun getBaseIndentLen( + preChildBlock: SqlBlock?, + lastGroup: SqlBlock?, + ): Int { + if (lastGroup == null) return 0 + + return if (lastGroup is SqlDoGroupBlock) { + lastGroup.getNodeText().length.plus(1) + } else { + createBlockIndentLen(preChildBlock) + } + } + + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean = parentBlock !is SqlDoGroupBlock +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/update/SqlUpdateSetGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/update/SqlUpdateSetGroupBlock.kt new file mode 100644 index 00000000..50587172 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/update/SqlUpdateSetGroupBlock.kt @@ -0,0 +1,68 @@ +/* + * 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.update + +import com.intellij.formatting.Indent +import com.intellij.lang.ASTNode +import com.intellij.psi.formatter.common.AbstractBlock +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +open class SqlUpdateSetGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlKeywordGroupBlock( + node, + IndentType.SECOND, + context, + ) { + // A block used for bulk updates. + // It contains a **column group**, an **equals sign (`=`)**, and a **value group block**. + var columnDefinitionGroupBlock: SqlUpdateColumnGroupBlock? = null + var assignmentSymbol: SqlUpdateColumnAssignmentSymbolBlock? = null + var valueGroupBlock: SqlUpdateValueGroupBlock? = null + + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + indent.indentLevel = IndentType.SECOND + indent.indentLen = createBlockIndentLen(null) + indent.groupIndentLen = indent.indentLen.plus(getNodeText().length) + } + + override fun setParentPropertyBlock(lastGroup: SqlBlock?) { + (lastGroup as? SqlUpdateQueryGroupBlock)?.setQueryGroupBlock = this + } + + override fun buildChildren(): MutableList = mutableListOf() + + override fun getIndent(): Indent? = + if (isAdjustIndentOnEnter()) { + Indent.getSpaceIndent(indent.indentLen) + } else { + Indent.getNoneIndent() + } + + override fun createBlockIndentLen(preChildBlock: SqlBlock?): Int = + if (isAdjustIndentOnEnter()) { + 0 + } else { + parentBlock?.indent?.groupIndentLen?.minus(getNodeText().length) ?: 0 + } + + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean = true +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlUpdateValueGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/update/SqlUpdateValueGroupBlock.kt similarity index 60% rename from src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlUpdateValueGroupBlock.kt rename to src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/update/SqlUpdateValueGroupBlock.kt index 628e6345..2950e457 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlUpdateValueGroupBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/update/SqlUpdateValueGroupBlock.kt @@ -13,16 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.domaframework.doma.intellij.formatter.block.group.subgroup +package org.domaframework.doma.intellij.formatter.block.group.keyword.update -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock import org.domaframework.doma.intellij.formatter.block.SqlBlock -import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlUpdateKeywordGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGroupBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext /** * In an UPDATE statement using the row value constructor, @@ -30,46 +27,44 @@ import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlUpdateKe */ class SqlUpdateValueGroupBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlSubGroupBlock( node, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, + context, ) { - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + // TODO:Customize indentation + override val offset = 2 + + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) indent.indentLen = createBlockIndentLen() indent.groupIndentLen = createGroupIndentLen() } + override fun setParentPropertyBlock(lastGroup: SqlBlock?) { + (lastGroup as? SqlUpdateSetGroupBlock)?.valueGroupBlock = this + } + override fun buildChildren(): MutableList = mutableListOf() override fun createBlockIndentLen(): Int { parentBlock?.let { parent -> - if (parent is SqlUpdateKeywordGroupBlock) { + if (parent is SqlUpdateSetGroupBlock) { return parent.indent.indentLen .plus(parent.getNodeText().length) .plus(3) } - // TODO:Customize indentation - return 2 - } ?: return 2 + return offset + } ?: return offset } - private fun createGroupIndentLen(): Int { + override fun createGroupIndentLen(): Int { parentBlock?.let { parent -> - if (parent is SqlUpdateKeywordGroupBlock) { + if (parent is SqlUpdateSetGroupBlock) { val parentGroupIndent = parent.indent.groupIndentLen - return parentGroupIndent.plus(4) + return parentGroupIndent.plus(3) } - } ?: return 2 - return 2 + } ?: return offset + return offset } } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/with/SqlWithColumnGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/with/SqlWithColumnGroupBlock.kt new file mode 100644 index 00000000..a87f0714 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/with/SqlWithColumnGroupBlock.kt @@ -0,0 +1,53 @@ +/* + * 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.with + +import com.intellij.lang.ASTNode +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGroupBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +class SqlWithColumnGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlSubGroupBlock(node, context) { + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + indent.indentLen = createBlockIndentLen() + indent.groupIndentLen = createGroupIndentLen() + } + + override fun setParentPropertyBlock(lastGroup: SqlBlock?) { + (lastGroup as? SqlWithCommonTableGroupBlock)?.columnGroupBlock = this + } + + override fun createBlockIndentLen(): Int = parentBlock?.indent?.groupIndentLen?.minus(1) ?: 0 + + override fun createGroupIndentLen(): Int { + parentBlock?.let { parent -> + parent.parentBlock?.let { grand -> + val topKeywordLen = grand.getNodeText().length.plus(1) + return grand.childBlocks + .sumOf { it.getNodeText().length.plus(1) } + .plus(topKeywordLen) + .plus(1) + } + } + return 2 + } + + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean = false +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/with/SqlWithCommonTableGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/with/SqlWithCommonTableGroupBlock.kt new file mode 100644 index 00000000..f37d9d9b --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/with/SqlWithCommonTableGroupBlock.kt @@ -0,0 +1,84 @@ +/* + * 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.with + +import com.intellij.lang.ASTNode +import org.domaframework.doma.intellij.common.util.TypeUtil +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.SqlWordBlock +import org.domaframework.doma.intellij.formatter.block.comment.SqlBlockCommentBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGroupBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +class SqlWithCommonTableGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlSubGroupBlock(node, context) { + override val offset = 4 + + var commonTableNameBlock: SqlBlock? = getCommonTableName() + var columnGroupBlock: SqlWithColumnGroupBlock? = null + val optionKeywordBlocks: MutableList = mutableListOf() + val queryGroupBlock: MutableList = mutableListOf() + var isFirstTable = false + + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + indent.indentLen = createBlockIndentLen() + indent.groupIndentLen = createGroupIndentLen() + isFirstTable = findWithQueryChildBlocks() == null + } + + private fun getCommonTableName(): SqlBlock? { + if (getNodeText() == ",") return null + val exceptionalTypes = + listOf( + SqlBlockCommentBlock::class, + SqlWordBlock::class, + ) + if (TypeUtil.isExpectedClassType(exceptionalTypes, this)) return this + return null + } + + private fun findWithQueryChildBlocks(): SqlBlock? { + parentBlock?.let { parent -> + if (parent is SqlWithQueryGroupBlock) { + return parent.getChildBlocksDropLast().find { it is SqlWithCommonTableGroupBlock } + } + } + return null + } + + override fun setParentPropertyBlock(lastGroup: SqlBlock?) { + if (lastGroup is SqlWithQueryGroupBlock) { + lastGroup.commonTableBlocks.add(this) + } + if (lastGroup is SqlWithCommonTableGroupBlock) { + (lastGroup.parentBlock as? SqlWithQueryGroupBlock)?.commonTableBlocks?.add(this) + } + } + + override fun createBlockIndentLen(): Int = 0 + + override fun createGroupIndentLen(): Int { + parentBlock?.let { parent -> + getChildBlocksDropLast().sumOf { it.getNodeText().length.plus(1) } + } + return offset + } + + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean = !isFirstTable +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/with/SqlWithOptionGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/with/SqlWithOptionGroupBlock.kt new file mode 100644 index 00000000..10c05ec1 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/with/SqlWithOptionGroupBlock.kt @@ -0,0 +1,35 @@ +/* + * 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.with + +import com.intellij.lang.ASTNode +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +class SqlWithOptionGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlKeywordGroupBlock(node, IndentType.SECOND, context) { + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + indent.indentLen = 0 + indent.groupIndentLen = getNodeText().length + } + + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean = true +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/with/SqlWithQueryGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/with/SqlWithQueryGroupBlock.kt new file mode 100644 index 00000000..22854fe0 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/with/SqlWithQueryGroupBlock.kt @@ -0,0 +1,55 @@ +/* + * 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.with + +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.group.keyword.top.SqlTopQueryGroupBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +/** + * For Example: + * -- [SqlWithQueryGroupBlock] + * WITH cte_name ([SqlWithColumnGroupBlock]) AS NOT MATERIALIZED ( [SqlWithCommonTableGroupBlock] + * SQL_query or [SqlWithQuerySubGroupBlock] + * ) + * , cte_name ([SqlWithColumnGroupBlock]) AS ( [SqlWithCommonTableGroupBlock] + * ([SqlWithQuerySubGroupBlock]) + * ) + */ +class SqlWithQueryGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlTopQueryGroupBlock( + node, + context, + ) { + var recursiveBlock: SqlKeywordBlock? = null + val commonTableBlocks: MutableList = mutableListOf() + + override fun addChildBlock(childBlock: SqlBlock) { + super.addChildBlock(childBlock) + indent.groupIndentLen = createGroupIndentLen() + } + + override fun createBlockIndentLen(): Int = 0 + + override fun createGroupIndentLen(): Int = + childBlocks + .sumOf { it.getNodeText().length.plus(1) } + .plus(getNodeText().length) +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/with/SqlWithQuerySubGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/with/SqlWithQuerySubGroupBlock.kt new file mode 100644 index 00000000..589e6e90 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/keyword/with/SqlWithQuerySubGroupBlock.kt @@ -0,0 +1,43 @@ +/* + * 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.with + +import com.intellij.lang.ASTNode +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubQueryGroupBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext + +class SqlWithQuerySubGroupBlock( + node: ASTNode, + context: SqlBlockFormattingContext, +) : SqlSubQueryGroupBlock( + node, + context, + ) { + override val offset = 4 + + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) + indent.indentLen = createBlockIndentLen() + indent.groupIndentLen = createGroupIndentLen() + } + + override fun createBlockIndentLen(): Int = offset + + override fun createGroupIndentLen(): Int = offset + + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean = false +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlColumnDefinitionGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlColumnDefinitionGroupBlock.kt deleted file mode 100644 index 30c8d780..00000000 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlColumnDefinitionGroupBlock.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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.subgroup - -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap -import com.intellij.lang.ASTNode -import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.block.SqlBlock - -/** - * Column List Group Block attached to Create Table - */ -class SqlColumnDefinitionGroupBlock( - node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, -) : SqlSubGroupBlock( - node, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, - ) { - var alignmentColumnName = "" - - // TODO:Customize indentation - val offset = 2 - - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) - indent.indentLen = createBlockIndentLen() - indent.groupIndentLen = indent.indentLen - } - - override fun buildChildren(): MutableList = mutableListOf() - - override fun createBlockIndentLen(): Int = offset -} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlColumnGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlColumnGroupBlock.kt deleted file mode 100644 index 55de9b07..00000000 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlColumnGroupBlock.kt +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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.subgroup - -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap -import com.intellij.lang.ASTNode -import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.IndentType -import org.domaframework.doma.intellij.formatter.block.SqlBlock -import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock - -/** - * Group blocks when generating columns with subqueries - */ -class SqlColumnGroupBlock( - node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, -) : SqlSubGroupBlock( - node, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, - ) { - var isFirstColumnGroup = getNodeText() != "," - - override val indent = - ElementIndent( - IndentType.COLUMN, - 0, - 0, - ) - - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) - indent.indentLevel = IndentType.COLUMN - indent.indentLen = createBlockIndentLen() - indent.groupIndentLen = - if (isFirstColumnGroup) indent.indentLen else indent.indentLen.plus(1) - } - - override fun buildChildren(): MutableList = mutableListOf() - - override fun createBlockIndentLen(): Int = - parentBlock?.let { - if (it is SqlKeywordGroupBlock) { - val parentIndentLen = it.indent.indentLen.plus(it.getNodeText().length) - val subGroup = it.parentBlock as? SqlSubGroupBlock - if (subGroup is SqlSubGroupBlock && !subGroup.isFirstLineComment) { - parentIndentLen.plus(3) - } else { - parentIndentLen.plus(1) - } - } else { - it.indent.groupIndentLen.plus(1) - } - } ?: 1 -} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlDataTypeParamBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlDataTypeParamBlock.kt index 2754622c..9f512389 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlDataTypeParamBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlDataTypeParamBlock.kt @@ -15,31 +15,20 @@ */ package org.domaframework.doma.intellij.formatter.block.group.subgroup -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode -import org.domaframework.doma.intellij.formatter.IndentType import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext /** - * The parent must be [org.domaframework.doma.intellij.formatter.block.group.SqlColumnDefinitionRawGroupBlock] + * The parent must be [org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnDefinitionRawGroupBlock] */ class SqlDataTypeParamBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlSubGroupBlock( node, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, + context, ) { override val indent = ElementIndent( @@ -48,8 +37,8 @@ class SqlDataTypeParamBlock( 0, ) - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) indent.indentLevel = IndentType.PARAM indent.indentLen = 0 indent.groupIndentLen = 0 diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlFunctionParamBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlFunctionParamBlock.kt index bc3e83df..e6deefa0 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlFunctionParamBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlFunctionParamBlock.kt @@ -15,28 +15,18 @@ */ package org.domaframework.doma.intellij.formatter.block.group.subgroup -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode -import org.domaframework.doma.intellij.formatter.IndentType import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext +import org.domaframework.doma.intellij.psi.SqlTypes class SqlFunctionParamBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlSubGroupBlock( node, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, + context, ) { override val indent = ElementIndent( @@ -45,11 +35,23 @@ class SqlFunctionParamBlock( 0, ) - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) indent.indentLevel = IndentType.PARAM indent.indentLen = 0 - indent.groupIndentLen = 0 + indent.groupIndentLen = createGroupIndentLen() + } + + override fun createGroupIndentLen(): Int { + prevChildren?.let { prevList -> + return prevList + .dropLast(1) + .sumOf { it.getNodeText().length.plus(1) } + .plus(getNodeText().length) + .minus(prevList.count { it.node.elementType == SqlTypes.DOT } * 2) + .plus(1) + } + return getNodeText().length } override fun createBlockIndentLen(): Int = 0 diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlInsertColumnGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlInsertColumnGroupBlock.kt deleted file mode 100644 index 48e8bb1b..00000000 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlInsertColumnGroupBlock.kt +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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.subgroup - -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap -import com.intellij.lang.ASTNode -import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.block.SqlBlock -import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlInsertKeywordGroupBlock -import org.domaframework.doma.intellij.psi.SqlTypes - -/** - * Block of columns to insert - */ -class SqlInsertColumnGroupBlock( - node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, -) : SqlSubGroupBlock( - node, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, - ) { - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) - indent.indentLen = createBlockIndentLen() - indent.groupIndentLen = indent.indentLen.plus(1) - updateParentGroupIndentLen() - } - - override fun buildChildren(): MutableList = mutableListOf() - - override fun createBlockIndentLen(): Int { - parentBlock?.let { - if (it is SqlInsertKeywordGroupBlock) { - var parentLen = 0 - val keywords = - it.childBlocks.dropLast(1).takeWhile { it.node.elementType == SqlTypes.KEYWORD } - keywords.forEach { keyword -> - parentLen = parentLen.plus(keyword.getNodeText().length).plus(1) - } - return it.indent.indentLen - .plus(it.getNodeText().length) - .plus(1) - .plus(parentLen) - } - // TODO:Customize indentation - return 2 - } ?: return 2 - } - - private fun updateParentGroupIndentLen() { - parentBlock?.let { - if (it is SqlInsertKeywordGroupBlock) { - var parentLen = 0 - val keywords = - it.childBlocks.dropLast(1).takeWhile { it.node.elementType == SqlTypes.KEYWORD } - keywords.forEach { keyword -> - parentLen = parentLen.plus(keyword.getNodeText().length).plus(1) - } - it.indent.groupIndentLen = - it.indent.indentLen - .plus(it.getNodeText().length) - .plus(parentLen) - } - } - } -} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlParallelListBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlParallelListBlock.kt index 8be12c37..531f53b2 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlParallelListBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlParallelListBlock.kt @@ -15,13 +15,11 @@ */ package org.domaframework.doma.intellij.formatter.block.group.subgroup -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode -import org.domaframework.doma.intellij.formatter.IndentType -import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.comment.SqlBlockCommentBlock +import org.domaframework.doma.intellij.formatter.block.comment.SqlLineCommentBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext /** * A class that represents the List type test data after the IN clause. @@ -31,18 +29,10 @@ import org.domaframework.doma.intellij.formatter.block.SqlBlock */ class SqlParallelListBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlSubQueryGroupBlock( node, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, + context, ) { override val indent = ElementIndent( @@ -51,7 +41,18 @@ class SqlParallelListBlock( 0, ) - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + override fun createBlockIndentLen(): Int { + parentBlock?.let { parent -> + return parent + .getChildBlocksDropLast() + .filter { it !is SqlLineCommentBlock && it !is SqlBlockCommentBlock } + .sumOf { it.getNodeText().length.plus(1) } + .plus(parent.indent.indentLen) + .plus(parent.getNodeText().length) + .plus(1) + } + return 0 } + + override fun createGroupIndentLen(): Int = indent.indentLen.plus(1) } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlSubGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlSubGroupBlock.kt index c6f8477f..d5a1d851 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlSubGroupBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlSubGroupBlock.kt @@ -15,34 +15,38 @@ */ package org.domaframework.doma.intellij.formatter.block.group.subgroup -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap +import com.intellij.formatting.Block +import com.intellij.formatting.Spacing import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.IndentType +import org.domaframework.doma.intellij.common.util.TypeUtil import org.domaframework.doma.intellij.formatter.block.SqlBlock -import org.domaframework.doma.intellij.formatter.block.SqlCommentBlock +import org.domaframework.doma.intellij.formatter.block.SqlRightPatternBlock +import org.domaframework.doma.intellij.formatter.block.comment.SqlCommentBlock +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.create.SqlCreateViewGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.top.SqlJoinQueriesGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithColumnGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithCommonTableGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithQueryGroupBlock +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext abstract class SqlSubGroupBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlNewGroupBlock( node, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, + context, ) { + open val offset = 1 + + // TODO Even if the first element of a subgroup is a comment, + // the indentation of subsequent elements is now aligned. var isFirstLineComment = false var prevChildren: List? = emptyList() + var endPatternBlock: SqlRightPatternBlock? = null override val indent = ElementIndent( @@ -51,20 +55,60 @@ abstract class SqlSubGroupBlock( 0, ) - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) prevChildren = parentBlock?.childBlocks?.toList() indent.indentLevel = indent.indentLevel indent.indentLen = createBlockIndentLen() - indent.groupIndentLen = indent.indentLen.plus(getNodeText().length) + indent.groupIndentLen = parentBlock?.let { parent -> + parent.indent.indentLen.plus(parent.getNodeText().length.plus(1)) + } ?: indent.indentLen.plus(getNodeText().length) + } + + override fun setParentPropertyBlock(lastGroup: SqlBlock?) { + (lastGroup as? SqlDoGroupBlock)?.doQueryBlock = this } override fun addChildBlock(childBlock: SqlBlock) { - childBlocks.add(childBlock) - if (!isFirstLineComment) { + if (childBlocks.isEmpty()) { isFirstLineComment = childBlock is SqlCommentBlock } + childBlocks.add(childBlock) } override fun buildChildren(): MutableList = mutableListOf() + + override fun getSpacing( + child1: Block?, + child2: Block, + ): Spacing? = null + + override fun isLeaf(): Boolean = true + + open fun endGroup() {} + + override fun createBlockIndentLen(): Int = offset + + override fun createGroupIndentLen(): Int { + parentBlock?.let { parent -> + // The parent groupIndent includes the number of characters in the group itself. + val baseGroupLen = parent.indent.groupIndentLen + return if (parent is SqlSubGroupBlock) baseGroupLen.plus(2) else baseGroupLen + } ?: return 1 + } + + override fun isSaveSpace(lastGroup: SqlBlock?): Boolean { + lastGroup?.let { lastBlock -> + if (lastBlock is SqlJoinQueriesGroupBlock) return true + val exceptionalParentTypes = + listOf( + SqlWithQueryGroupBlock::class, + SqlWithCommonTableGroupBlock::class, + SqlWithColumnGroupBlock::class, + SqlCreateViewGroupBlock::class, + ) + return TypeUtil.isExpectedClassType(exceptionalParentTypes, lastBlock.parentBlock) + } + return false + } } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlSubQueryGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlSubQueryGroupBlock.kt index e549bf95..1edc80c0 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlSubQueryGroupBlock.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlSubQueryGroupBlock.kt @@ -15,70 +15,56 @@ */ package org.domaframework.doma.intellij.formatter.block.group.subgroup -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap import com.intellij.lang.ASTNode import com.intellij.psi.formatter.common.AbstractBlock import org.domaframework.doma.intellij.formatter.block.SqlBlock -import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlJoinGroupBlock -import org.domaframework.doma.intellij.psi.SqlTypes +import org.domaframework.doma.intellij.formatter.block.group.keyword.condition.SqlConditionalExpressionGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.top.SqlJoinQueriesGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithCommonTableGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithQuerySubGroupBlock +import org.domaframework.doma.intellij.formatter.util.SqlBlockFormattingContext open class SqlSubQueryGroupBlock( node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, + context: SqlBlockFormattingContext, ) : SqlSubGroupBlock( node, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, + context, ) { - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) + override fun setParentGroupBlock(lastGroup: SqlBlock?) { + super.setParentGroupBlock(lastGroup) indent.indentLen = createBlockIndentLen() indent.groupIndentLen = createGroupIndentLen() } - override fun buildChildren(): MutableList = mutableListOf() + override fun setParentPropertyBlock(lastGroup: SqlBlock?) { + if (lastGroup is SqlWithCommonTableGroupBlock) { + lastGroup.queryGroupBlock.add(this) + } + } - override fun createBlockIndentLen(): Int = 1 + override fun buildChildren(): MutableList = mutableListOf() - private fun createGroupIndentLen(): Int { + override fun createBlockIndentLen(): Int = parentBlock?.let { parent -> - if (parent is SqlJoinGroupBlock) { - var parentLen = 0 - val keywords = - parent.childBlocks.dropLast(1).takeWhile { parent.node.elementType == SqlTypes.KEYWORD } - keywords.forEach { keyword -> - parentLen = parentLen.plus(keyword.getNodeText().length).plus(1) - } - return parent.indent.indentLen - .plus(parent.getNodeText().length) - .plus(2) - .plus(parentLen) + return if (parent is SqlJoinQueriesGroupBlock) { + parent.indent.indentLen + } else if (parent is SqlWithQuerySubGroupBlock) { + parent.indent.groupIndentLen } else { - var parentLen = 0 - val prevBlocks = - prevChildren - ?.dropLast(1) - ?.filter { it.node.startOffset > parent.node.startOffset } - prevBlocks - ?.forEach { prev -> - parentLen = parentLen.plus(prev.getNodeText().length).plus(1) - } - return parent.indent.groupIndentLen - .plus(parentLen) - .plus(2) + parent.indent.groupIndentLen.plus(1) + } + } ?: offset + + override fun createGroupIndentLen(): Int { + parentBlock?.let { parent -> + if (parent is SqlJoinQueriesGroupBlock) { + return parent.indent.indentLen.plus(1) + } + if (parent is SqlConditionalExpressionGroupBlock) { + return indent.indentLen } - return parent.indent.groupIndentLen - .plus(2) - } ?: return 1 + } + return indent.indentLen.plus(1) } } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlUpdateColumnGroupBlock.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlUpdateColumnGroupBlock.kt deleted file mode 100644 index ed493a2c..00000000 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/block/group/subgroup/SqlUpdateColumnGroupBlock.kt +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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.subgroup - -import com.intellij.formatting.Alignment -import com.intellij.formatting.FormattingMode -import com.intellij.formatting.SpacingBuilder -import com.intellij.formatting.Wrap -import com.intellij.lang.ASTNode -import com.intellij.psi.formatter.common.AbstractBlock -import org.domaframework.doma.intellij.formatter.block.SqlBlock -import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlUpdateKeywordGroupBlock -import org.domaframework.doma.intellij.psi.SqlTypes - -/** - * In an UPDATE statement using the row value constructor, - * a group representing the column list - */ -class SqlUpdateColumnGroupBlock( - node: ASTNode, - wrap: Wrap?, - alignment: Alignment?, - spacingBuilder: SpacingBuilder, - enableFormat: Boolean, - formatMode: FormattingMode, -) : SqlSubGroupBlock( - node, - wrap, - alignment, - spacingBuilder, - enableFormat, - formatMode, - ) { - override fun setParentGroupBlock(block: SqlBlock?) { - super.setParentGroupBlock(block) - indent.indentLen = createBlockIndentLen() - indent.groupIndentLen = indent.indentLen.plus(1) - updateParentGroupIndentLen() - } - - override fun buildChildren(): MutableList = mutableListOf() - - override fun createBlockIndentLen(): Int { - parentBlock?.let { - if (it is SqlUpdateKeywordGroupBlock) { - var parentLen = 0 - val keywords = - it.childBlocks.dropLast(1).takeWhile { it.node.elementType == SqlTypes.KEYWORD } - keywords.forEach { keyword -> - parentLen = parentLen.plus(keyword.getNodeText().length).plus(1) - } - return it.indent.indentLen - .plus(it.getNodeText().length) - .plus(1) - .plus(parentLen) - } - // TODO:Customize indentation - return 2 - } ?: return 2 - } - - private fun updateParentGroupIndentLen() { - parentBlock?.let { - if (it is SqlUpdateKeywordGroupBlock) { - var parentLen = 0 - val keywords = - it.childBlocks.dropLast(1).takeWhile { it.node.elementType == SqlTypes.KEYWORD } - keywords.forEach { keyword -> - parentLen = parentLen.plus(keyword.getNodeText().length).plus(1) - } - it.indent.groupIndentLen = - it.indent.indentLen - .plus(it.getNodeText().length) - .plus(parentLen) - } - } - } -} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlBlockBuilder.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/builder/SqlBlockBuilder.kt similarity index 85% rename from src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlBlockBuilder.kt rename to src/main/kotlin/org/domaframework/doma/intellij/formatter/builder/SqlBlockBuilder.kt index 8fd90e3a..10379e23 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlBlockBuilder.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/builder/SqlBlockBuilder.kt @@ -13,26 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.domaframework.doma.intellij.formatter +package org.domaframework.doma.intellij.formatter.builder import org.domaframework.doma.intellij.formatter.block.SqlBlock -import org.domaframework.doma.intellij.formatter.block.SqlCommentBlock +import org.domaframework.doma.intellij.formatter.block.comment.SqlCommentBlock import org.domaframework.doma.intellij.formatter.block.expr.SqlElBlockCommentBlock import org.domaframework.doma.intellij.formatter.block.expr.SqlElConditionLoopCommentBlock import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGroupBlock +import org.domaframework.doma.intellij.formatter.util.IndentType open class SqlBlockBuilder { - private val groupTopNodeIndexHistory = mutableListOf>() + private val groupTopNodeIndexHistory = mutableListOf() private val commentBlocks = mutableListOf() private val conditionOrLoopBlocks = mutableListOf() - fun getGroupTopNodeIndexHistory(): List> = groupTopNodeIndexHistory + fun getGroupTopNodeIndexHistory(): List = groupTopNodeIndexHistory - fun getLastGroup(): SqlBlock? = groupTopNodeIndexHistory.lastOrNull()?.second - - fun addGroupTopNodeIndexHistory(block: Pair) { + fun addGroupTopNodeIndexHistory(block: SqlBlock) { groupTopNodeIndexHistory.add(block) } @@ -69,7 +68,7 @@ open class SqlBlockBuilder { } } - fun getLastGroupTopNodeIndexHistory(): Pair? = groupTopNodeIndexHistory.lastOrNull() + fun getLastGroupTopNodeIndexHistory(): SqlBlock? = groupTopNodeIndexHistory.lastOrNull() fun removeLastGroupTopNodeIndexHistory() { if (groupTopNodeIndexHistory.isNotEmpty()) { @@ -87,7 +86,7 @@ open class SqlBlockBuilder { fun getGroupTopNodeIndex(condition: (SqlBlock) -> Boolean): Int = groupTopNodeIndexHistory.indexOfLast { - condition(it.second) + condition(it) } fun getConditionOrLoopBlocksLast(): SqlElConditionLoopCommentBlock? = conditionOrLoopBlocks.lastOrNull() diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlCustomSpacingBuilder.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/builder/SqlCustomSpacingBuilder.kt similarity index 79% rename from src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlCustomSpacingBuilder.kt rename to src/main/kotlin/org/domaframework/doma/intellij/formatter/builder/SqlCustomSpacingBuilder.kt index 825c4b51..8b0526ea 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlCustomSpacingBuilder.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/builder/SqlCustomSpacingBuilder.kt @@ -13,23 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.domaframework.doma.intellij.formatter +package org.domaframework.doma.intellij.formatter.builder import com.intellij.formatting.ASTBlock import com.intellij.formatting.Block import com.intellij.formatting.Spacing import com.intellij.psi.tree.IElementType import org.domaframework.doma.intellij.formatter.block.SqlBlock -import org.domaframework.doma.intellij.formatter.block.SqlColumnBlock import org.domaframework.doma.intellij.formatter.block.SqlRightPatternBlock import org.domaframework.doma.intellij.formatter.block.SqlWhitespaceBlock -import org.domaframework.doma.intellij.formatter.block.group.SqlColumnDefinitionRawGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnBlock +import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnDefinitionRawGroupBlock import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlColumnDefinitionGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.create.SqlCreateTableColumnDefinitionGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.update.SqlUpdateColumnGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.update.SqlUpdateValueGroupBlock import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlDataTypeParamBlock import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlParallelListBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlUpdateColumnGroupBlock -import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlUpdateValueGroupBlock class SqlCustomSpacingBuilder { companion object { @@ -123,25 +123,24 @@ class SqlCustomSpacingBuilder { } fun getSpacingRightPattern(block: SqlRightPatternBlock): Spacing? { + val paramBlock = block.parentBlock return when { - block.parentBlock is SqlColumnDefinitionGroupBlock || - block.parentBlock is SqlUpdateColumnGroupBlock || - block.parentBlock is SqlUpdateValueGroupBlock -> { + paramBlock is SqlCreateTableColumnDefinitionGroupBlock || + paramBlock is SqlUpdateColumnGroupBlock || + (paramBlock is SqlUpdateValueGroupBlock) -> { return getSpacing(block) } - block.parentBlock is SqlParallelListBlock -> { - if (block.parentBlock - ?.childBlocks - ?.dropLast(1) - ?.lastOrNull() is SqlKeywordGroupBlock - ) { - return normalSpacing + paramBlock is SqlParallelListBlock -> { + val lastKeywordGroup = paramBlock.getChildBlocksDropLast().lastOrNull() + return if (lastKeywordGroup is SqlKeywordGroupBlock) { + normalSpacing + } else { + nonSpacing } - return nonSpacing } - block.parentBlock is SqlDataTypeParamBlock -> nonSpacing + paramBlock is SqlDataTypeParamBlock -> nonSpacing block.preSpaceRight -> normalSpacing else -> nonSpacing diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlFormattingModelBuilder.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/builder/SqlFormattingModelBuilder.kt similarity index 98% rename from src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlFormattingModelBuilder.kt rename to src/main/kotlin/org/domaframework/doma/intellij/formatter/builder/SqlFormattingModelBuilder.kt index 050d631c..0155c0db 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlFormattingModelBuilder.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/builder/SqlFormattingModelBuilder.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.domaframework.doma.intellij.formatter +package org.domaframework.doma.intellij.formatter.builder import com.intellij.formatting.Alignment import com.intellij.formatting.FormattingContext diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/processor/SqlFormatPreProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/processor/SqlFormatPreProcessor.kt new file mode 100644 index 00000000..9b5d9b81 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/processor/SqlFormatPreProcessor.kt @@ -0,0 +1,268 @@ +/* + * 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.processor + +import com.intellij.lang.ASTNode +import com.intellij.openapi.editor.Document +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiWhiteSpace +import com.intellij.psi.TokenType +import com.intellij.psi.impl.source.codeStyle.PreFormatProcessor +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.psi.util.elementType +import org.domaframework.doma.intellij.common.util.PluginLoggerUtil +import org.domaframework.doma.intellij.formatter.util.CreateQueryType +import org.domaframework.doma.intellij.formatter.util.SqlKeywordUtil +import org.domaframework.doma.intellij.formatter.visitor.SqlFormatVisitor +import org.domaframework.doma.intellij.psi.SqlTypes +import org.domaframework.doma.intellij.setting.SqlLanguage +import org.jetbrains.kotlin.psi.psiUtil.startOffset + +class SqlFormatPreProcessor : PreFormatProcessor { + private val targetElementTypes = + listOf( + SqlTypes.KEYWORD, + SqlTypes.LEFT_PAREN, + SqlTypes.RIGHT_PAREN, + SqlTypes.BLOCK_COMMENT, + SqlTypes.LINE_COMMENT, + SqlTypes.WORD, + SqlTypes.COMMA, + SqlTypes.OTHER, + ) + + override fun process( + node: ASTNode, + rangeToReformat: TextRange, + ): TextRange = processText(node.psi.containingFile, rangeToReformat) + + private fun processText( + source: PsiFile, + rangeToReformat: TextRange, + ): TextRange { + // Turn on by default the code formatter that only runs when explicitly invoked by the user. + if (source.language != SqlLanguage.INSTANCE) return rangeToReformat + + logging() + + val visitor = SqlFormatVisitor() + source.accept(visitor) + + val docManager = PsiDocumentManager.getInstance(source.project) + val document = docManager.getDocument(source) ?: return rangeToReformat + + val keywordList = visitor.replaces.filter { it.elementType != TokenType.WHITE_SPACE } + val replaceKeywordList = visitor.replaces.filter { it.elementType == SqlTypes.KEYWORD } + var index = keywordList.size + var keywordIndex = replaceKeywordList.size + + visitor.replaces.asReversed().forEach { + val createQueryType = getCreateQueryGroup(keywordList, index) + val textRangeStart = it.startOffset + val textRangeEnd = textRangeStart + it.text.length + if (it.elementType != TokenType.WHITE_SPACE) { + // Add a newline before any element that needs a newline+indent, without overlapping if there is already a newline + index-- + var newKeyword = getUpperText(it) + when (it.elementType) { + SqlTypes.KEYWORD -> { + keywordIndex-- + newKeyword = getKeywordNewText(it) + } + + SqlTypes.LEFT_PAREN -> { + newKeyword = + if (createQueryType == CreateQueryType.TABLE) { + getNewLineString(it.prevSibling, getUpperText(it)) + } else if (keywordIndex > 0) { + if (listOf("insert", "into", "all").contains(replaceKeywordList[keywordIndex - 1].text.lowercase())) { + getNewLineString(it.prevSibling, getUpperText(it)) + } else { + getUpperText(it) + } + } else { + getUpperText(it) + } + } + + SqlTypes.RIGHT_PAREN -> { + newKeyword = + getRightPatternNewText(it) + } + + SqlTypes.WORD -> { + newKeyword = getWordNewText(it, newKeyword, createQueryType) + } + + SqlTypes.COMMA, SqlTypes.BLOCK_COMMENT, SqlTypes.OTHER -> { + newKeyword = getNewLineString(it.prevSibling, getUpperText(it)) + } + } + document.deleteString(textRangeStart, textRangeEnd) + document.insertString(textRangeStart, newKeyword) + } else { + removeSpacesAroundNewline(document, it as PsiWhiteSpace) + } + } + + docManager.commitDocument(document) + + return rangeToReformat.grown(visitor.replaces.size) + } + + private fun removeSpacesAroundNewline( + document: Document, + element: PsiWhiteSpace, + ) { + val singleSpace = " " + val newLine = "\n" + val range = element.textRange + val originalText = document.getText(range) + val nextElement = element.nextSibling + val nextElementText = nextElement?.let { document.getText(it.textRange) } ?: "" + + var newText = "" + if (!targetElementTypes.contains(nextElement?.elementType)) { + newText = originalText.replace(originalText, singleSpace) + } else { + newText = + when (nextElement.elementType) { + SqlTypes.LINE_COMMENT -> { + if (nextElementText.startsWith(newLine)) { + originalText.replace(originalText, singleSpace) + } else if (originalText.contains(newLine)) { + originalText.replace(Regex("\\s*\\n\\s*"), newLine) + } else { + originalText.replace(originalText, singleSpace) + } + } + + else -> { + if (nextElementText.contains(newLine) == true) { + originalText.replace(originalText, singleSpace) + } else if (originalText.contains(newLine)) { + originalText.replace(Regex("\\s*\\n\\s*"), newLine) + } else { + originalText.replace(originalText, newLine) + } + } + } + } + document.replaceString(range.startOffset, range.endOffset, newText) + } + + /** + * Checks for special case keyword elements and specific combinations of keywords with line breaks and capitalization only + */ + private fun getKeywordNewText(element: PsiElement): String { + val keywordText = element.text.lowercase() + val upperText = getUpperText(element) + return if (SqlKeywordUtil.getIndentType(keywordText).isNewLineGroup()) { + val prevElement = PsiTreeUtil.prevLeaf(element) + getNewLineString(prevElement, upperText) + } else { + upperText + } + } + + private fun getRightPatternNewText(element: PsiElement): String { + val elementText = element.text + return getNewLineString(element.prevSibling, elementText) + } + + private fun getWordNewText( + element: PsiElement, + newKeyword: String, + createQueryType: CreateQueryType, + ): String { + newKeyword + var prev = element.prevSibling + var isColumnName = true + while (prev != null && prev.elementType != SqlTypes.LEFT_PAREN && prev.elementType != SqlTypes.COMMA) { + if (prev !is PsiWhiteSpace && + prev.elementType != SqlTypes.LINE_COMMENT && + prev.elementType != SqlTypes.BLOCK_COMMENT + ) { + isColumnName = + prev.elementType == SqlTypes.COMMA || + prev.elementType == SqlTypes.LEFT_PAREN + break + } + prev = prev.prevSibling + } + + return if (createQueryType == CreateQueryType.TABLE && isColumnName) { + getNewLineString(element.prevSibling, getUpperText(element)) + } else { + getUpperText(element) + } + } + + private fun getCreateQueryGroup( + keywordList: List, + index: Int, + ): CreateQueryType { + var topLastKeyWord: PsiElement? = null + var attachmentKeywordType = CreateQueryType.NONE + keywordList + .dropLast(keywordList.size.minus(index)) + .filter { + it.elementType == SqlTypes.KEYWORD + }.asReversed() + .forEach { key -> + if (SqlKeywordUtil.Companion.isTopKeyword(key.text)) { + topLastKeyWord = key + return@forEach + } + if (SqlKeywordUtil.Companion.isAttachedKeyword(key.text)) { + attachmentKeywordType = CreateQueryType.Companion.getCreateTableType(key.text) + } + } + val prevKeywordText = topLastKeyWord?.text?.lowercase() + val isCreateGroup = prevKeywordText == "create" + if (!isCreateGroup) return CreateQueryType.NONE + return attachmentKeywordType + } + + private fun getNewLineString( + prevElement: PsiElement?, + text: String, + ): String = + if (prevElement?.text?.contains("\n") == false) { + "\n$text" + } else { + text + } + + private fun getUpperText(element: PsiElement): String = + if (element.elementType == SqlTypes.KEYWORD) { + element.text.uppercase() + } else { + element.text + } + + private fun logging() { + PluginLoggerUtil.Companion.countLogging( + this::class.java.simpleName, + "SqlFormat", + "Format", + System.nanoTime(), + ) + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlPostProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/processor/SqlPostProcessor.kt similarity index 97% rename from src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlPostProcessor.kt rename to src/main/kotlin/org/domaframework/doma/intellij/formatter/processor/SqlPostProcessor.kt index 2b4b5e27..69eef4c7 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlPostProcessor.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/processor/SqlPostProcessor.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.domaframework.doma.intellij.formatter +package org.domaframework.doma.intellij.formatter.processor import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.command.WriteCommandAction diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/processor/SqlSetParentGroupProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/processor/SqlSetParentGroupProcessor.kt new file mode 100644 index 00000000..c4aeae20 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/processor/SqlSetParentGroupProcessor.kt @@ -0,0 +1,428 @@ +/* + * 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.processor + +import org.domaframework.doma.intellij.common.util.TypeUtil.isExpectedClassType +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.SqlCommaBlock +import org.domaframework.doma.intellij.formatter.block.SqlKeywordBlock +import org.domaframework.doma.intellij.formatter.block.SqlRightPatternBlock +import org.domaframework.doma.intellij.formatter.block.conflict.SqlDoGroupBlock +import org.domaframework.doma.intellij.formatter.block.expr.SqlElConditionLoopCommentBlock +import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnDefinitionRawGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnRawGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlInlineGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlInlineSecondGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.create.SqlCreateViewGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.second.SqlReturningGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.top.SqlTopQueryGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.update.SqlUpdateQueryGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithCommonTableGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithQuerySubGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGroupBlock +import org.domaframework.doma.intellij.formatter.builder.SqlBlockBuilder +import org.domaframework.doma.intellij.formatter.util.IndentType +import org.domaframework.doma.intellij.formatter.util.SqlKeywordUtil +import org.domaframework.doma.intellij.formatter.util.UpdateClauseUtil +import org.domaframework.doma.intellij.psi.SqlTypes + +class SqlSetParentGroupProcessor( + private val blockBuilder: SqlBlockBuilder, +) { + data class SetParentContext( + val childBlock: SqlBlock, + val blockBuilder: SqlBlockBuilder, + ) + + /** + * Sets the parent of the latest group block and registers itself as a child element. + */ + fun updateGroupBlockParentAndAddGroup(childBlock: SqlBlock) { + setParentGroups( + SetParentContext( + childBlock, + blockBuilder, + ), + ) { history -> + return@setParentGroups history.last() + } + } + + /** + * Does not set a parent. + */ + fun updateGroupBlockAddGroup(childBlock: SqlBlock) { + setParentGroups( + SetParentContext( + childBlock, + blockBuilder, + ), + ) { history -> + return@setParentGroups null + } + } + + /** + * Registers itself as a child element in the same block as the parent of the last group at the time of processing. + */ + fun updateGroupBlockLastGroupParentAddGroup( + lastGroupBlock: SqlBlock, + childBlock: SqlBlock, + ) { + setParentGroups( + SetParentContext( + childBlock, + blockBuilder, + ), + ) { history -> + return@setParentGroups lastGroupBlock.parentBlock + } + } + + fun updateKeywordGroupBlockParentAndAddGroup( + lastGroupBlock: SqlBlock, + lastIndentLevel: IndentType, + childBlock: SqlKeywordGroupBlock, + ) { + val context = + SetParentContext( + childBlock, + blockBuilder, + ) + + val currentIndentLevel = childBlock.indent.indentLevel + if (currentIndentLevel == IndentType.TOP) { + var parentBlock: SqlBlock? = null + val exceptionalTypes = + listOf( + SqlSubGroupBlock::class, + SqlCreateViewGroupBlock::class, + ) + if (isExpectedClassType(exceptionalTypes, lastGroupBlock)) { + parentBlock = lastGroupBlock + } else { + when (childBlock) { + is SqlUpdateQueryGroupBlock -> { + UpdateClauseUtil + .getParentGroupBlock(blockBuilder, childBlock) + ?.let { parentBlock = it } + } + + else -> { + val topKeywordIndex = + blockBuilder.getGroupTopNodeIndex { block -> + block.indent.indentLevel == IndentType.TOP + } + val subGroupIndex = + blockBuilder.getGroupTopNodeIndex { block -> + block is SqlSubGroupBlock + } + + var deleteIndex = topKeywordIndex + if (topKeywordIndex >= 0 && subGroupIndex < 0) { + val copyParentBlock = + blockBuilder.getGroupTopNodeIndexHistory()[topKeywordIndex] + parentBlock = copyParentBlock.parentBlock + } else if (topKeywordIndex > subGroupIndex) { + val copyParentBlock = + blockBuilder.getGroupTopNodeIndexHistory()[subGroupIndex] + parentBlock = copyParentBlock + } + if (deleteIndex >= 0) { + blockBuilder.clearSubListGroupTopNodeIndexHistory(deleteIndex) + } + } + } + } + setParentGroups(context) { history -> + return@setParentGroups parentBlock + } + return + } + + if (lastGroupBlock.indent.indentLevel == IndentType.SUB) { + setParentGroups(context) { history -> + return@setParentGroups lastGroupBlock + } + } else if (lastIndentLevel == currentIndentLevel) { + val prevKeyword = lastGroupBlock.childBlocks.findLast { it is SqlKeywordBlock } + prevKeyword?.let { prev -> + if (SqlKeywordUtil.isSetLineKeyword(childBlock.getNodeText(), prev.getNodeText())) { + updateGroupBlockLastGroupParentAddGroup( + lastGroupBlock, + childBlock, + ) + return + } + } + + blockBuilder.removeLastGroupTopNodeIndexHistory() + if (childBlock is SqlReturningGroupBlock) { + // Since `DO UPDATE` does not include a `RETURNING` clause, it should be registered as a child of the parent `INSERT` query. + // The `DO` keyword should align with the `INSERT` query, and therefore it will serve as the **indentation anchor** for the following update block. + setParentGroups(context) { history -> + val lastGroup = history.findLast { it is SqlTopQueryGroupBlock } + return@setParentGroups if (lastGroup is SqlUpdateQueryGroupBlock && lastGroup.parentBlock is SqlDoGroupBlock) { + lastGroup.parentBlock + } else { + lastGroup + } + } + return + } + updateGroupBlockLastGroupParentAddGroup( + lastGroupBlock, + childBlock, + ) + } else if (lastIndentLevel < currentIndentLevel) { + updateGroupBlockParentAndAddGroup( + childBlock, + ) + } else if (lastIndentLevel == IndentType.JOIN && + SqlKeywordUtil.isSecondOptionKeyword(childBlock.getNodeText()) + ) { + // left,right < inner,outer < join + updateGroupBlockParentAndAddGroup( + childBlock, + ) + return + } else { + setParentGroups(context) { history -> + return@setParentGroups history + .lastOrNull { it.indent.indentLevel < childBlock.indent.indentLevel } + } + } + } + + fun updateColumnDefinitionRawGroupBlockParentAndAddGroup( + lastGroupBlock: SqlBlock, + lastIndentLevel: IndentType, + childBlock: SqlColumnDefinitionRawGroupBlock, + ) { + when (lastIndentLevel) { + childBlock.indent.indentLevel -> { + blockBuilder.removeLastGroupTopNodeIndexHistory() + updateGroupBlockLastGroupParentAddGroup( + lastGroupBlock, + childBlock, + ) + } + + else -> { + updateGroupBlockParentAndAddGroup(childBlock) + } + } + } + + fun updateColumnRawGroupBlockParentAndAddGroup( + lastGroupBlock: SqlBlock, + childBlock: SqlColumnRawGroupBlock, + ) { + val exceptionTypes = + listOf( + SqlColumnRawGroupBlock::class, + ) + if (isExpectedClassType(exceptionTypes, lastGroupBlock)) { + blockBuilder.removeLastGroupTopNodeIndexHistory() + } + setParentGroups( + SetParentContext( + childBlock, + blockBuilder, + ), + ) { history -> + return@setParentGroups history + .lastOrNull { it.indent.indentLevel < childBlock.indent.indentLevel } + } + } + + fun updateInlineSecondGroupBlockParentAndAddGroup( + lastGroupBlock: SqlBlock, + lastIndentLevel: IndentType, + childBlock: SqlInlineSecondGroupBlock, + ) { + val context = + SetParentContext( + childBlock, + blockBuilder, + ) + if (childBlock.isEndCase) { + val inlineIndex = + blockBuilder.getGroupTopNodeIndex { block -> + block.indent.indentLevel == IndentType.INLINE + } + if (inlineIndex >= 0) { + setParentGroups( + context, + ) { history -> + return@setParentGroups history[inlineIndex] + } + blockBuilder.clearSubListGroupTopNodeIndexHistory(inlineIndex) + } + return + } + if (lastIndentLevel == IndentType.INLINE_SECOND) { + blockBuilder.removeLastGroupTopNodeIndexHistory() + updateGroupBlockLastGroupParentAddGroup( + lastGroupBlock, + childBlock, + ) + return + } + updateGroupBlockParentAndAddGroup( + childBlock, + ) + } + + fun updateConditionLoopCommentBlockParent( + lastGroupBlock: SqlBlock, + childBlock: SqlElConditionLoopCommentBlock, + ) { + val exceptionalTypes = + listOf( + SqlCommaBlock::class, + SqlElConditionLoopCommentBlock::class, + ) + if (isExpectedClassType(exceptionalTypes, lastGroupBlock)) { + blockBuilder.removeLastGroupTopNodeIndexHistory() + } + setParentGroups( + SetParentContext( + childBlock, + blockBuilder, + ), + ) { history -> + if (childBlock.conditionType.isEnd()) { + val lastConditionLoopCommentBlock = + blockBuilder.getConditionOrLoopBlocksLast() + blockBuilder.removeConditionOrLoopBlockLast() + return@setParentGroups lastConditionLoopCommentBlock + } + return@setParentGroups null + } + } + + fun updateSqlRightPatternBlockParent(childBlock: SqlRightPatternBlock) { + val paramIndex = + blockBuilder.getGroupTopNodeIndex { block -> + block is SqlSubGroupBlock + } + if (paramIndex >= 0) { + val context = + SetParentContext( + childBlock, + blockBuilder, + ) + setParentGroups(context) { history -> + return@setParentGroups history[paramIndex] + } + + if (childBlock.parentBlock is SqlSubGroupBlock) { + (childBlock.parentBlock as SqlSubGroupBlock).endGroup() + } + + if (blockBuilder.getGroupTopNodeIndexHistory()[paramIndex] is SqlWithQuerySubGroupBlock) { + val withCommonBlockIndex = + blockBuilder.getGroupTopNodeIndex { block -> + block is SqlWithCommonTableGroupBlock + } + if (withCommonBlockIndex >= 0) { + blockBuilder.clearSubListGroupTopNodeIndexHistory(withCommonBlockIndex) + } + } else { + blockBuilder.clearSubListGroupTopNodeIndexHistory(paramIndex) + } + } + } + + fun updateSubGroupBlockParent(childBlock: SqlSubGroupBlock) { + updateGroupBlockParentAndAddGroup(childBlock) + } + + /** + * Determines its parent group and, if conditions are met, registers itself as a new group block in the list. + */ + private fun setParentGroups( + context: SetParentContext, + getParentGroup: (MutableList) -> SqlBlock?, + ) { + val parentGroup = + getParentGroup(context.blockBuilder.getGroupTopNodeIndexHistory() as MutableList) + + // The parent block for SqlElConditionLoopCommentBlock will be set later + if (context.childBlock !is SqlElConditionLoopCommentBlock || + context.childBlock.conditionType.isEnd() + ) { + context.childBlock.setParentGroupBlock(parentGroup) + } + + val expectedClassTypes = + listOf( + SqlSubGroupBlock::class, + SqlCreateViewGroupBlock::class, + SqlInlineGroupBlock::class, + SqlInlineSecondGroupBlock::class, + SqlColumnDefinitionRawGroupBlock::class, + ) + if (isNewGroup(context.childBlock, context.blockBuilder) || isExpectedClassType(expectedClassTypes, context.childBlock)) { + context.blockBuilder.addGroupTopNodeIndexHistory(context.childBlock) + // Set parent-child relationship and indent for preceding comment at beginning of block group + context.blockBuilder.updateCommentBlockIndent(context.childBlock) + } + } + + /** + * Determines whether it is a group that requires a line break or a specific combination of keywords that does not require a line break. + */ + private fun isNewGroup( + childBlock: SqlBlock, + blockBuilder: SqlBlockBuilder, + ): Boolean { + val lastGroup = blockBuilder.getLastGroupTopNodeIndexHistory() + return isNewGroupAndNotSetLineKeywords(childBlock, lastGroup) + } + + fun isNewGroupAndNotSetLineKeywords( + childBlock: SqlBlock, + lastGroup: SqlBlock?, + ): Boolean { + val isNewGroupType = childBlock.indent.indentLevel.isNewLineGroup() + val lastKeywordText = + if (lastGroup?.indent?.indentLevel == IndentType.JOIN) { + lastGroup.getNodeText() + } else { + getLastGroupKeywordText(lastGroup) + } + + val isSetLineGroup = + SqlKeywordUtil.Companion.isSetLineKeyword( + childBlock.getNodeText(), + lastKeywordText, + ) + return isNewGroupType && !isSetLineGroup + } + + /** + * Searches for a keyword element in the most recent group block and returns its text. + * If not found, returns the text of the group block itself. + */ + fun getLastGroupKeywordText(lastGroup: SqlBlock?): String = + lastGroup + ?.childBlocks + ?.lastOrNull { it.node.elementType == SqlTypes.KEYWORD } + ?.getNodeText() ?: lastGroup?.getNodeText() ?: "" +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/util/CreateTableUtil.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/util/CreateTableUtil.kt new file mode 100644 index 00000000..22e2fbac --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/util/CreateTableUtil.kt @@ -0,0 +1,103 @@ +/* + * 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.formatting.Block +import com.intellij.formatting.Spacing +import com.intellij.lang.ASTNode +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.SqlWhitespaceBlock +import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnBlock +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.psi.SqlTypes + +object CreateTableUtil { + fun getCreateTableClauseSubGroup( + lastGroup: SqlBlock, + child: ASTNode, + sqlBlockFormattingCtx: SqlBlockFormattingContext, + ): SqlCreateTableColumnDefinitionGroupBlock? { + if (lastGroup is SqlCreateKeywordGroupBlock) { + return SqlCreateTableColumnDefinitionGroupBlock(child, sqlBlockFormattingCtx) + } + return null + } + + fun getColumnRawGroup( + lastGroup: SqlBlock?, + child: ASTNode, + sqlBlockFormattingCtx: SqlBlockFormattingContext, + ): SqlCreateTableColumnDefinitionRawGroupBlock? = + if (lastGroup is SqlCreateTableColumnDefinitionGroupBlock || + lastGroup is SqlCreateTableColumnDefinitionRawGroupBlock + ) { + SqlCreateTableColumnDefinitionRawGroupBlock( + child, + sqlBlockFormattingCtx, + ) + } else { + null + } + + /** + * Right-justify based on the longest column name in the column definition line + */ + fun getColumnDefinitionRawGroupSpacing( + child1: Block?, + child2: Block, + ): Spacing? { + // TODO Customize indentation + val offset = 5 + + // Top Column Definition Group Block + if (child1 is SqlWhitespaceBlock && child2 is SqlCreateTableColumnDefinitionRawGroupBlock) { + val columnDefinitionGroupBlock = + child2.parentBlock as? SqlCreateTableColumnDefinitionGroupBlock ?: return null + + if (child2.node.elementType == SqlTypes.COMMA) { + // If the child2 is a comma, it is not a column definition raw group block. + return Spacing.createSpacing(offset, offset, 0, false, 0, 0) + } + + val maxColumnName = columnDefinitionGroupBlock.getMaxColumnNameLength() + val diffColumnNameLen = + maxColumnName.minus(child2.columnBlock?.getNodeText()?.length ?: 0) + // If the longest column name is not in the top row, add two spaces for the "," to match the row with a comma. + var indentLen = offset.plus(diffColumnNameLen) + val maxColumnNameRaw = + columnDefinitionGroupBlock.columnRawGroupBlocks + .findLast { raw -> raw.columnBlock?.getNodeText()?.length == maxColumnName } + if (maxColumnNameRaw?.isFirstColumnRaw != true) { + indentLen = indentLen.plus(2) + } + + return Spacing.createSpacing(indentLen, indentLen, 0, false, 0, 0) + } + + if (child1 is SqlCreateTableColumnDefinitionRawGroupBlock && child2 is SqlColumnBlock) { + val columnDefinitionGroupBlock = + child1.parentBlock as? SqlCreateTableColumnDefinitionGroupBlock ?: return null + + val maxColumnName = columnDefinitionGroupBlock.getMaxColumnNameLength() + val diffColumnNameLen = maxColumnName.minus(child2.getNodeText().length) + var indentLen = diffColumnNameLen.plus(1) + return Spacing.createSpacing(indentLen, indentLen, 0, false, 0, 0) + } + return null + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/util/InsertClauseUtil.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/util/InsertClauseUtil.kt new file mode 100644 index 00000000..a35d6803 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/util/InsertClauseUtil.kt @@ -0,0 +1,40 @@ +/* + * 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.group.keyword.insert.SqlInsertColumnGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.insert.SqlInsertQueryGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.insert.SqlInsertValueGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.second.SqlValuesGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGroupBlock + +object InsertClauseUtil { + fun getInsertClauseSubGroup( + lastGroup: SqlBlock, + child: ASTNode, + sqlBlockFormattingCtx: SqlBlockFormattingContext, + ): SqlSubGroupBlock? { + if (lastGroup is SqlInsertQueryGroupBlock) { + return SqlInsertColumnGroupBlock(child, sqlBlockFormattingCtx) + } + if (lastGroup is SqlValuesGroupBlock) { + return SqlInsertValueGroupBlock(child, sqlBlockFormattingCtx) + } + return null + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/util/JoinGroupUtil.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/util/JoinGroupUtil.kt new file mode 100644 index 00000000..f4ec5bb6 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/util/JoinGroupUtil.kt @@ -0,0 +1,49 @@ +/* + * 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.group.keyword.SqlJoinGroupBlock + +object JoinGroupUtil { + fun getJoinKeywordGroupBlock( + lastGroupBlock: SqlBlock?, + keywordText: String, + child: ASTNode, + sqlBlockFormattingCtx: SqlBlockFormattingContext, + ): SqlBlock = + if (SqlKeywordUtil.isJoinKeyword(keywordText)) { + SqlJoinGroupBlock( + child, + sqlBlockFormattingCtx, + ) + } else if (lastGroupBlock is SqlJoinGroupBlock) { + // JOIN_ATTACHED_KEYWORD Keywords + SqlKeywordBlock( + child, + IndentType.ATTACHED, + sqlBlockFormattingCtx, + ) + } else { + // Catch any unregistered JOIN keywords here if they are not already listed in JOIN_KEYWORD. + SqlJoinGroupBlock( + child, + sqlBlockFormattingCtx, + ) + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/util/NotQueryGroupUtil.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/util/NotQueryGroupUtil.kt new file mode 100644 index 00000000..6d5b0f53 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/util/NotQueryGroupUtil.kt @@ -0,0 +1,66 @@ +/* + * 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 com.intellij.psi.util.PsiTreeUtil +import com.intellij.psi.util.elementType +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.condition.SqlConditionKeywordGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.condition.SqlConditionalExpressionGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.second.SqlReturningGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlFunctionParamBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlParallelListBlock +import org.domaframework.doma.intellij.psi.SqlTypes + +object NotQueryGroupUtil { + fun getSubGroup( + lastGroup: SqlBlock, + child: ASTNode, + sqlBlockFormattingCtx: SqlBlockFormattingContext, + ): SqlBlock? { + val lastKeyword = + lastGroup.childBlocks + .lastOrNull { SqlKeywordUtil.isOptionSqlKeyword(it.getNodeText()) } + if (lastKeyword != null && lastKeyword.getNodeText().lowercase() == "in") { + return SqlParallelListBlock(child, sqlBlockFormattingCtx) + } + + if (PsiTreeUtil.prevLeaf(child.psi)?.elementType == SqlTypes.WORD) { + return SqlFunctionParamBlock(child, sqlBlockFormattingCtx) + } + + if (lastGroup is SqlConditionKeywordGroupBlock) { + return SqlConditionalExpressionGroupBlock( + child, + sqlBlockFormattingCtx, + ) + } + + return null + } + + fun getKeywordGroup( + child: ASTNode, + sqlBlockFormattingCtx: SqlBlockFormattingContext, + ): SqlBlock? { + val keyword = child.text.lowercase() + if (keyword == "returning") { + return SqlReturningGroupBlock(child, sqlBlockFormattingCtx) + } + return null + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/util/SqlBlockUtil.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/util/SqlBlockUtil.kt new file mode 100644 index 00000000..d9cc7079 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/util/SqlBlockUtil.kt @@ -0,0 +1,462 @@ +/* + * 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.formatting.Alignment +import com.intellij.formatting.FormattingMode +import com.intellij.formatting.SpacingBuilder +import com.intellij.formatting.Wrap +import com.intellij.lang.ASTNode +import com.intellij.psi.PsiComment +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.psi.util.elementType +import org.domaframework.doma.intellij.extension.expr.isConditionOrLoopDirective +import org.domaframework.doma.intellij.formatter.block.SqlBlock +import org.domaframework.doma.intellij.formatter.block.SqlCommaBlock +import org.domaframework.doma.intellij.formatter.block.SqlKeywordBlock +import org.domaframework.doma.intellij.formatter.block.SqlTableBlock +import org.domaframework.doma.intellij.formatter.block.SqlWordBlock +import org.domaframework.doma.intellij.formatter.block.comment.SqlBlockCommentBlock +import org.domaframework.doma.intellij.formatter.block.comment.SqlCommentBlock +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.expr.SqlElBlockCommentBlock +import org.domaframework.doma.intellij.formatter.block.expr.SqlElConditionLoopCommentBlock +import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnBlock +import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnDefinitionRawGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.column.SqlColumnRawGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlInlineGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlInlineSecondGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlJoinGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlKeywordGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.SqlSecondOptionKeywordGroupBlock +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.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.insert.SqlInsertQueryGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.second.SqlSecondKeywordBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.second.SqlValuesGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.top.SqlDeleteQueryGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.top.SqlJoinQueriesGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.top.SqlSelectQueryGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.update.SqlUpdateQueryGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.update.SqlUpdateSetGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithCommonTableGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithQueryGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlDataTypeParamBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlFunctionParamBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubQueryGroupBlock +import org.domaframework.doma.intellij.formatter.builder.SqlCustomSpacingBuilder +import org.domaframework.doma.intellij.psi.SqlCustomElCommentExpr +import org.domaframework.doma.intellij.psi.SqlTypes + +data class SqlBlockFormattingContext( + val wrap: Wrap?, + val alignment: Alignment?, + val spacingBuilder: SpacingBuilder, + val enableFormat: Boolean, + val formatMode: FormattingMode, +) + +class SqlBlockUtil( + sqlBlock: SqlBlock, + enableFormat: Boolean, + formatMode: FormattingMode, +) { + val sqlBlockFormattingCtx = + SqlBlockFormattingContext( + sqlBlock.wrap, + sqlBlock.alignment, + sqlBlock.spacingBuilder, + enableFormat, + formatMode, + ) + + 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) + } + } + } + + IndentType.CONFLICT -> { + if (lastGroupBlock is SqlConflictClauseBlock) { + lastGroupBlock.conflictType = + when (keywordText) { + "conflict" -> OnConflictKeywordType.CONFLICT + "constraint" -> OnConflictKeywordType.CONSTRAINT + else -> OnConflictKeywordType.UNKNOWN + } + return SqlKeywordBlock(child, indentLevel, sqlBlockFormattingCtx) + } else { + return SqlConflictClauseBlock(child, sqlBlockFormattingCtx) + } + } + + else -> return SqlKeywordBlock(child, indentLevel, sqlBlockFormattingCtx) + } + return SqlKeywordBlock(child, indentLevel, sqlBlockFormattingCtx) + } + + private fun getKeywordGroupBlock( + indentLevel: IndentType, + keywordText: String, + child: ASTNode, + lastGroupBlock: SqlBlock?, + ): SqlBlock { + when (indentLevel) { + IndentType.JOIN -> { + return JoinGroupUtil.getJoinKeywordGroupBlock( + lastGroupBlock, + keywordText, + child, + sqlBlockFormattingCtx, + ) + } + + IndentType.INLINE_SECOND -> { + return SqlInlineSecondGroupBlock( + child, + sqlBlockFormattingCtx, + ) + } + + IndentType.TOP -> { + return when (keywordText) { + "with" -> + SqlWithQueryGroupBlock( + child, + sqlBlockFormattingCtx, + ) + + "select" -> + SqlSelectQueryGroupBlock( + child, + sqlBlockFormattingCtx, + ) + + "create" -> + SqlCreateKeywordGroupBlock( + child, + sqlBlockFormattingCtx, + ) + + "insert" -> + SqlInsertQueryGroupBlock( + child, + sqlBlockFormattingCtx, + ) + + "do" -> + SqlDoGroupBlock( + child, + sqlBlockFormattingCtx, + ) + + "update" -> + SqlUpdateQueryGroupBlock( + child, + sqlBlockFormattingCtx, + ) + + "delete" -> + SqlDeleteQueryGroupBlock( + child, + sqlBlockFormattingCtx, + ) + "union", "intersect", "except" -> + SqlJoinQueriesGroupBlock( + child, + sqlBlockFormattingCtx, + ) + + else -> + SqlKeywordGroupBlock( + child, + indentLevel, + sqlBlockFormattingCtx, + ) + } + } + + IndentType.SECOND -> { + return when (keywordText) { + "set" -> { + if (lastGroupBlock is SqlUpdateQueryGroupBlock) { + SqlUpdateSetGroupBlock( + child, + sqlBlockFormattingCtx, + ) + } else { + WithClauseUtil + .getWithClauseKeywordGroup(lastGroupBlock, child, sqlBlockFormattingCtx) + ?.let { return it } + return SqlSecondKeywordBlock( + child, + sqlBlockFormattingCtx, + ) + } + } + + "values" -> + SqlValuesGroupBlock( + child, + sqlBlockFormattingCtx, + ) + + else -> { + WithClauseUtil + .getWithClauseKeywordGroup(lastGroupBlock, child, sqlBlockFormattingCtx) + ?.let { return it } + + NotQueryGroupUtil + .getKeywordGroup( + child, + sqlBlockFormattingCtx, + )?.let { return it } + + SqlSecondKeywordBlock( + child, + sqlBlockFormattingCtx, + ) + } + } + } + + IndentType.SECOND_OPTION -> { + return if (keywordText == "on" && lastGroupBlock !is SqlJoinGroupBlock) { + SqlConflictClauseBlock( + child, + sqlBlockFormattingCtx, + ) + } else if (SqlKeywordUtil.isConditionKeyword(keywordText)) { + SqlConditionKeywordGroupBlock( + child, + sqlBlockFormattingCtx, + ) + } else { + SqlSecondOptionKeywordGroupBlock( + child, + sqlBlockFormattingCtx, + ) + } + } + + IndentType.CONFLICT -> { + return if (lastGroupBlock is SqlConflictClauseBlock) { + SqlKeywordBlock( + child, + indentLevel, + sqlBlockFormattingCtx, + ) + } else { + SqlKeywordGroupBlock( + child, + indentLevel, + sqlBlockFormattingCtx, + ) + } + } + + else -> { + return SqlKeywordGroupBlock( + child, + indentLevel, + sqlBlockFormattingCtx, + ) + } + } + } + + fun getSubGroupBlock( + lastGroup: SqlBlock?, + child: ASTNode, + ): SqlBlock { + when (lastGroup) { + is SqlKeywordGroupBlock -> { + CreateTableUtil + .getCreateTableClauseSubGroup(lastGroup, child, sqlBlockFormattingCtx) + ?.let { return it } + + WithClauseUtil + .getWithClauseSubGroup(lastGroup, child, sqlBlockFormattingCtx) + ?.let { return it } + + InsertClauseUtil + .getInsertClauseSubGroup(lastGroup, child, sqlBlockFormattingCtx) + ?.let { return it } + + UpdateClauseUtil + .getUpdateClauseSubGroup( + lastGroup, + child, + sqlBlockFormattingCtx, + )?.let { return it } + + // List-type test data for IN clause + NotQueryGroupUtil + .getSubGroup(lastGroup, child, sqlBlockFormattingCtx) + ?.let { return it } + + return SqlSubQueryGroupBlock(child, sqlBlockFormattingCtx) + } + + is SqlColumnDefinitionRawGroupBlock -> + return SqlDataTypeParamBlock(child, sqlBlockFormattingCtx) + + else -> { + if (lastGroup is SqlSubGroupBlock) { + WithClauseUtil + .getWithClauseSubGroup(lastGroup, child, sqlBlockFormattingCtx) + ?.let { return it } + } + + if (PsiTreeUtil.prevLeaf(child.psi)?.elementType == SqlTypes.WORD) { + return SqlFunctionParamBlock(child, sqlBlockFormattingCtx) + } + + return SqlSubQueryGroupBlock(child, sqlBlockFormattingCtx) + } + } + } + + fun getCommaGroupBlock( + lastGroup: SqlBlock?, + child: ASTNode, + ): SqlBlock { + CreateTableUtil + .getColumnRawGroup(lastGroup, child, sqlBlockFormattingCtx) + ?.let { return it } + + return when (lastGroup) { + is SqlColumnRawGroupBlock, is SqlKeywordGroupBlock -> { + if (lastGroup.indent.indentLevel == IndentType.SECOND) { + SqlCommaBlock(child, sqlBlockFormattingCtx) + } else { + SqlColumnRawGroupBlock(child, sqlBlockFormattingCtx) + } + } + + is SqlWithCommonTableGroupBlock -> SqlWithCommonTableGroupBlock(child, sqlBlockFormattingCtx) + + else -> SqlCommaBlock(child, sqlBlockFormattingCtx) + } + } + + fun getWordBlock( + lastGroup: SqlBlock?, + child: ASTNode, + ): SqlBlock = + when (lastGroup) { + is SqlKeywordGroupBlock -> { + when { + SqlKeywordUtil.isBeforeTableKeyword(lastGroup.getNodeText()) -> { + SqlTableBlock( + child, + sqlBlockFormattingCtx, + ) + } + + lastGroup is SqlWithQueryGroupBlock -> SqlWithCommonTableGroupBlock(child, sqlBlockFormattingCtx) + + else -> SqlWordBlock(child, sqlBlockFormattingCtx) + } + } + + is SqlCreateTableColumnDefinitionGroupBlock -> { + // Top Column Definition Group Block + SqlCreateTableColumnDefinitionRawGroupBlock( + child, + sqlBlockFormattingCtx, + ) + } + + is SqlColumnDefinitionRawGroupBlock -> { + if (lastGroup.columnBlock == null) { + SqlColumnBlock( + child, + sqlBlockFormattingCtx, + ) + } else { + SqlWordBlock(child, sqlBlockFormattingCtx) + } + } + + else -> SqlWordBlock(child, sqlBlockFormattingCtx) + } + + fun getBlockCommentBlock( + child: ASTNode, + blockCommentSpacingBuilder: SqlCustomSpacingBuilder?, + ): SqlCommentBlock { + if (PsiTreeUtil.getChildOfType(child.psi, PsiComment::class.java) != null) { + return SqlBlockCommentBlock(child, sqlBlockFormattingCtx) + } + if (child.psi is SqlCustomElCommentExpr && + (child.psi as SqlCustomElCommentExpr).isConditionOrLoopDirective() + ) { + return SqlElConditionLoopCommentBlock( + child, + sqlBlockFormattingCtx, + blockCommentSpacingBuilder, + ) + } + return SqlElBlockCommentBlock( + child, + sqlBlockFormattingCtx, + blockCommentSpacingBuilder, + ) + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlKeywordUtil.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/util/SqlKeywordUtil.kt similarity index 78% rename from src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlKeywordUtil.kt rename to src/main/kotlin/org/domaframework/doma/intellij/formatter/util/SqlKeywordUtil.kt index c809b9f3..88ee24a2 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/formatter/SqlKeywordUtil.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/util/SqlKeywordUtil.kt @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.domaframework.doma.intellij.formatter +package org.domaframework.doma.intellij.formatter.util enum class IndentType( private val level: Int, private val group: Boolean = false, ) { FILE(0, true), + CONFLICT(1, true), TOP(1, true), SECOND(2, true), JOIN(3, true), @@ -65,6 +66,7 @@ class SqlKeywordUtil { companion object { private val TOP_KEYWORDS: Set = setOf( + "with", "select", "update", "insert", @@ -75,6 +77,9 @@ class SqlKeywordUtil { "truncate", "rename", "union", + "intersect", + "except", + "do", ) fun isTopKeyword(keyword: String): Boolean = TOP_KEYWORDS.contains(keyword.lowercase()) @@ -89,6 +94,8 @@ class SqlKeywordUtil { "having", "limit", "values", + "lateral", + "returning", ) fun isSecondKeyword(keyword: String): Boolean = SECOND_KEYWORD.contains(keyword.lowercase()) @@ -100,6 +107,14 @@ class SqlKeywordUtil { "on", ) + private val CONDITION_KEYWORD = + setOf( + "and", + "or", + ) + + fun isConditionKeyword(keyword: String): Boolean = CONDITION_KEYWORD.contains(keyword.lowercase()) + fun isSecondOptionKeyword(keyword: String): Boolean = SECOND_OPTION_KEYWORD.contains(keyword.lowercase()) private val BEFORE_TABLE_KEYWORD = @@ -110,6 +125,17 @@ class SqlKeywordUtil { "table", ) + private val SELECT_SECOND_OPTION_KEYWORD = + setOf( + "from", + "where", + "group", + "having", + "order", + ) + + fun isSelectSecondOptionKeyword(keyword: String): Boolean = SELECT_SECOND_OPTION_KEYWORD.contains(keyword.lowercase()) + fun isBeforeTableKeyword(keyword: String): Boolean = BEFORE_TABLE_KEYWORD.contains(keyword.lowercase()) private val JOIN_KEYWORD = @@ -202,7 +228,6 @@ class SqlKeywordUtil { "unique", "primary", "foreign", - "constraint", ) fun isAttributeKeyword(keyword: String): Boolean = ATTRIBUTE_KEYWORD.contains(keyword.lowercase()) @@ -227,6 +252,8 @@ class SqlKeywordUtil { private val OPTION_SQL_KEYWORDS = setOf( "as", + "not", + "materialized", "by", "to", "asc", @@ -240,14 +267,36 @@ class SqlKeywordUtil { "offset", "then", "in", + "recursive", + "breadth", + "depth", + "first", + "to", + "using", ) fun isOptionSqlKeyword(keyword: String): Boolean = OPTION_SQL_KEYWORDS.contains(keyword.lowercase()) + private val CONFLICT_ATTACHED_KEYWORDS = + setOf( + "conflict", + "constraint", + ) + + fun isConflictAttachedKeyword(keyword: String): Boolean = CONFLICT_ATTACHED_KEYWORDS.contains(keyword.lowercase()) + + private val WITH_OPTION_KEYWORDS = + setOf( + "search", + "cycle", + ) + + fun isWithOptionKeyword(keyword: String): Boolean = WITH_OPTION_KEYWORDS.contains(keyword.lowercase()) + private val SET_LINE_KEYWORDS = mapOf( "into" to setOf("insert"), - "from" to setOf("delete"), + "from" to setOf("delete", "distinct"), "distinct" to setOf("select"), "table" to setOf("create", "alter", "rename", "truncate", "drop"), "index" to setOf("create", "alter", "rename", "truncate", "drop"), @@ -256,9 +305,14 @@ class SqlKeywordUtil { "join" to setOf("outer", "inner", "left", "right"), "outer" to setOf("left", "right"), "inner" to setOf("left", "right"), - "by" to setOf("group", "order"), + "by" to setOf("group", "order", "first"), "and" to setOf("between"), "if" to setOf("table", "create"), + "conflict" to setOf("on"), + "nothing" to setOf("do"), + "constraint" to setOf("on"), + "update" to setOf("do"), + "set" to setOf("by", "cycle"), ) fun isSetLineKeyword( @@ -266,14 +320,15 @@ class SqlKeywordUtil { prevKeyword: String, ): Boolean = SET_LINE_KEYWORDS[keyword.lowercase()]?.contains(prevKeyword.lowercase()) == true + fun isComma(keyword: String): Boolean = keyword == "," + fun getIndentType(keywordText: String): IndentType { val keyword = keywordText.lowercase() return when { isTopKeyword(keyword) -> IndentType.TOP - isSecondKeyword(keyword) -> IndentType.SECOND - isSecondOptionKeyword(keyword) -> IndentType.SECOND_OPTION - isJoinKeyword(keyword) -> IndentType.JOIN - isJoinAttachedKeyword(keyword) -> IndentType.JOIN + isSecondKeyword(keyword) || isSelectSecondOptionKeyword(keyword) || isWithOptionKeyword(keyword) -> IndentType.SECOND + isSecondOptionKeyword(keyword) || isConditionKeyword(keyword) -> IndentType.SECOND_OPTION + isJoinKeyword(keyword) || isJoinAttachedKeyword(keyword) -> IndentType.JOIN isAttachedKeyword(keyword) -> IndentType.ATTACHED isThirdKeyword(keyword) -> IndentType.TIRD isInlineParentSqlKeyword(keyword) -> IndentType.INLINE @@ -282,7 +337,8 @@ class SqlKeywordUtil { isLiteralKeyword(keyword) -> IndentType.LITERAL isOptionSqlKeyword(keyword) -> IndentType.OPTIONS isColumnTypeKeyword(keyword) -> IndentType.COLUMN - keyword == "," -> IndentType.COMMA + isConflictAttachedKeyword(keyword) -> IndentType.ATTACHED + isComma(keyword) -> IndentType.COMMA else -> IndentType.NONE } } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/util/UpdateClauseUtil.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/util/UpdateClauseUtil.kt new file mode 100644 index 00000000..d1fe4710 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/util/UpdateClauseUtil.kt @@ -0,0 +1,66 @@ +/* + * 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.conflict.SqlDoGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.update.SqlUpdateColumnGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.update.SqlUpdateQueryGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.update.SqlUpdateSetGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.update.SqlUpdateValueGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubQueryGroupBlock +import org.domaframework.doma.intellij.formatter.builder.SqlBlockBuilder + +object UpdateClauseUtil { + fun getUpdateClauseSubGroup( + lastGroup: SqlBlock, + child: ASTNode, + sqlBlockFormattingCtx: SqlBlockFormattingContext, + ): SqlBlock? = + if (lastGroup is SqlUpdateSetGroupBlock) { + if (lastGroup.assignmentSymbol == null) { + SqlUpdateColumnGroupBlock(child, sqlBlockFormattingCtx) + } else if (lastGroup.columnDefinitionGroupBlock != null) { + SqlUpdateValueGroupBlock(child, sqlBlockFormattingCtx) + } else { + SqlSubQueryGroupBlock(child, sqlBlockFormattingCtx) + } + } else { + null + } + + fun getParentGroupBlock( + blockBuilder: SqlBlockBuilder, + childBlock: SqlUpdateQueryGroupBlock, + ): SqlBlock? { + var parentBlock: SqlBlock? = null + val topKeywordIndex = + blockBuilder.getGroupTopNodeIndex { block -> + block.indent.indentLevel == IndentType.TOP || block is SqlDoGroupBlock + } + if (topKeywordIndex >= 0) { + val copyParentBlock = + blockBuilder.getGroupTopNodeIndexHistory()[topKeywordIndex] + parentBlock = + copyParentBlock as? SqlDoGroupBlock ?: copyParentBlock.parentBlock + if (copyParentBlock.indent.indentLevel == childBlock.indent.indentLevel) { + blockBuilder.clearSubListGroupTopNodeIndexHistory(topKeywordIndex) + } + } + return parentBlock + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/util/WithClauseUtil.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/util/WithClauseUtil.kt new file mode 100644 index 00000000..b0f4044a --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/util/WithClauseUtil.kt @@ -0,0 +1,67 @@ +/* + * 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.group.keyword.with.SqlWithColumnGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithCommonTableGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithOptionGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.keyword.with.SqlWithQuerySubGroupBlock +import org.domaframework.doma.intellij.formatter.block.group.subgroup.SqlSubQueryGroupBlock +import org.domaframework.doma.intellij.psi.SqlTypes + +object WithClauseUtil { + fun getWithClauseSubGroup( + lastGroup: SqlBlock, + child: ASTNode, + sqlBlockFormattingCtx: SqlBlockFormattingContext, + ): SqlBlock? { + when (lastGroup) { + is SqlWithCommonTableGroupBlock -> { + return if (lastGroup.optionKeywordBlocks.isEmpty()) { + when (child.elementType) { + SqlTypes.LEFT_PAREN -> SqlWithColumnGroupBlock(child, sqlBlockFormattingCtx) + SqlTypes.COMMA -> SqlWithCommonTableGroupBlock(child, sqlBlockFormattingCtx) + else -> SqlSubQueryGroupBlock(child, sqlBlockFormattingCtx) + } + } else { + SqlWithQuerySubGroupBlock(child, sqlBlockFormattingCtx) + } + } + + is SqlWithQuerySubGroupBlock -> return SqlSubQueryGroupBlock(child, sqlBlockFormattingCtx) + } + return null + } + + fun getWithClauseKeywordGroup( + lastGroup: SqlBlock?, + child: ASTNode, + sqlBlockFormattingCtx: SqlBlockFormattingContext, + ): SqlBlock? { + if (SqlKeywordUtil.isWithOptionKeyword(child.text)) { + return SqlWithOptionGroupBlock(child, sqlBlockFormattingCtx) + } + if (lastGroup is SqlWithOptionGroupBlock) { + when (child.text.lowercase()) { + "set" -> return SqlKeywordBlock(child, IndentType.ATTACHED, sqlBlockFormattingCtx) + } + } + return null + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/formatter/visitor/SqlFormatVisitor.kt b/src/main/kotlin/org/domaframework/doma/intellij/formatter/visitor/SqlFormatVisitor.kt new file mode 100644 index 00000000..24aacc9e --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/formatter/visitor/SqlFormatVisitor.kt @@ -0,0 +1,85 @@ +/* + * 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.visitor + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiRecursiveElementVisitor +import com.intellij.psi.PsiWhiteSpace +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.psi.util.elementType +import org.domaframework.doma.intellij.extension.expr.isConditionOrLoopDirective +import org.domaframework.doma.intellij.psi.SqlBlockComment +import org.domaframework.doma.intellij.psi.SqlCustomElCommentExpr +import org.domaframework.doma.intellij.psi.SqlTypes + +class SqlFormatVisitor : PsiRecursiveElementVisitor() { + val replaces = mutableListOf() + var lastElement: PsiElement? = null + + override fun visitElement(element: PsiElement) { + super.visitElement(element) + if (element !is PsiFile && element.nextSibling == null) { + lastElement = element + } + + if (PsiTreeUtil.getParentOfType(element, SqlBlockComment::class.java) == null) { + when (element.elementType) { + SqlTypes.KEYWORD, SqlTypes.COMMA, SqlTypes.LEFT_PAREN, SqlTypes.RIGHT_PAREN, SqlTypes.WORD -> { + replaces.add(element) + } + + SqlTypes.OTHER -> { + if (element.text == "=") { + val updateSetKeyword = + replaces + .lastOrNull { it.elementType == SqlTypes.KEYWORD } + if (updateSetKeyword?.text?.lowercase() == "set") { + replaces.add(element) + } + } + } + + SqlTypes.BLOCK_COMMENT -> + if ( + element is SqlCustomElCommentExpr && + element.isConditionOrLoopDirective() + ) { + replaces.add(element) + } + } + } + } + + override fun visitWhiteSpace(space: PsiWhiteSpace) { + super.visitWhiteSpace(space) + if (PsiTreeUtil.getParentOfType(space, SqlBlockComment::class.java) == null) { + replaces.add(space) + } + return + + val nextElement = space.nextSibling + if (nextElement != null && + ( + space.text.contains("\n") || + nextElement.elementType == SqlTypes.LINE_COMMENT || + nextElement.elementType == SqlTypes.BLOCK_COMMENT + ) + ) { + replaces.add(space) + } + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 975b1d5b..b75e69e6 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -64,9 +64,9 @@ - - + implementationClass="org.domaframework.doma.intellij.formatter.builder.SqlFormattingModelBuilder"/> + + /* id */101 AND div = 't' ) +returning id, name diff --git a/src/test/testData/sql/formatter/DeleteReturning_format.sql b/src/test/testData/sql/formatter/DeleteReturning_format.sql new file mode 100644 index 00000000..6bd091a9 --- /dev/null +++ b/src/test/testData/sql/formatter/DeleteReturning_format.sql @@ -0,0 +1,8 @@ +DELETE FROM x + WHERE id IN ( SELECT id + , name + FROM x2 + WHERE id > /* id */101 + AND div = 't' ) +RETURNING id + , name diff --git a/src/test/testData/sql/formatter/FormattedDelete.sql b/src/test/testData/sql/formatter/Delete_format.sql similarity index 100% rename from src/test/testData/sql/formatter/FormattedDelete.sql rename to src/test/testData/sql/formatter/Delete_format.sql diff --git a/src/test/testData/sql/formatter/FormattedInsert.sql b/src/test/testData/sql/formatter/FormattedInsert.sql deleted file mode 100644 index 9a734d28..00000000 --- a/src/test/testData/sql/formatter/FormattedInsert.sql +++ /dev/null @@ -1,5 +0,0 @@ -INSERT INTO employee - (id - , name) - VALUES ( /* employees.id */0 - , /* employees.name */'name') diff --git a/src/test/testData/sql/formatter/FormattedSelect.sql b/src/test/testData/sql/formatter/FormattedSelect.sql deleted file mode 100644 index 5bff1601..00000000 --- a/src/test/testData/sql/formatter/FormattedSelect.sql +++ /dev/null @@ -1,56 +0,0 @@ -/** TopBlock */ -SELECT COUNT(DISTINCT (x)) - , o.* - , ISNULL(nbor.nearest - , 999) AS nearest -- column Line comment - /** From */ - FROM ( -- SubGroupLine - SELECT p.objid - , p.psfmag_g - p.extinction_g + 5 * LOG(u.propermotion / 100.) + 5 AS rpm - , p.psfmag_g - p.extinction_g - (p.psfmag_i - p.extinction_i) AS gi - , ISNULL(s.plate - , 0) AS plate - , ISNULL(s.mjd - , 0) AS mjd - , ISNULL(s.fiberid - , 0) AS fiberid - FROM phototag p - -- Line1 - JOIN usno u - -- Line2 - ON p.objid = u.objid - /** Join */ - LEFT OUTER JOIN specobj s - /** ON */ - ON p.objid = s.bestobjid - AND p.plate = s.plate - /** Where */ - WHERE p.TYPE = DBO.FPHOTOTYPE('Star') - -- Line3 - OR (p.flags & DBO.FPHOTOFLAGS('EDGE') = 0 - AND (p.psfmag_g - p.extinction_g) BETWEEN 15 AND 20) - /*%if status == 2 */ - -- Line4 - AND u.propermotion > 2. - /** And Group */ - AND (p.psfmag_g - p.extinction_g + 5 * LOG(u.propermotion / 100.) + 5 > 16.136 + 2.727 * (p.psfmag_g - p.extinction_g - (p.psfmag_i - p.extinction_i)) - OR p.psfmag_g - p.extinction_g - (p.psfmag_i - p.extinction_i) < 0.) - /*%end*/ ) AS o - LEFT OUTER JOIN ( SELECT n.objid - , MIN(n.distance) AS nearest - FROM neighbors n - JOIN phototag x - ON n.neighborobjid = x.objid - AND n.neighbormode = DBO.FPHOTOMODE('Primary') - OR n.MODE = /*# "active" */'mode' - AND n.status = 2 - AND n.flag = true - WHERE n.TYPE = DBO.FPHOTOTYPE('Star') - AND n.MODE = DBO.FPHOTOMODE('Primary') - OR n.neighbormode = DBO.FPHOTOMODE('Primary') - AND (x.TYPE = DBO.FPHOTOTYPE('Star') - AND x.TYPE = DBO.FPHOTOTYPE('Galaxy')) - OR x.modelmag_g BETWEEN 10 AND 21 - GROUP BY n.objid ) AS nbor - ON o.objid = nbor.objid - WHERE p.params IN /* params */(1, 2, 3) diff --git a/src/test/testData/sql/formatter/Insert.sql b/src/test/testData/sql/formatter/Insert.sql index 3675ab49..2119ff7e 100644 --- a/src/test/testData/sql/formatter/Insert.sql +++ b/src/test/testData/sql/formatter/Insert.sql @@ -1,3 +1,15 @@ -INSERT INTO employee(id, name) - VALUES ( /* employees.id */0 - , /* employees.name */'name') +insert into /*# tableName */ + (x1 , x2 + /*%for entity : entities */ + , /*# entity.itemIdentifier */ + /*%end*/ + , x3 , x4) values ( /* reportId */1 + , /* reportId */1 + /*%for entity : entities */ + , /* entity.value */'abc' + /*%end*/ + , /* @userId() */1 + , x5 + , /* @userId() */1 + , x6 , 1 + , /* @maxDateTime() */'9999-12-31') diff --git a/src/test/testData/sql/formatter/InsertConflictNothing.sql b/src/test/testData/sql/formatter/InsertConflictNothing.sql new file mode 100644 index 00000000..9fb06fd5 --- /dev/null +++ b/src/test/testData/sql/formatter/InsertConflictNothing.sql @@ -0,0 +1,2 @@ +insert into employee (id, username) values ( /* employees.id */0, /* employees.name */'name') +on conflict (username) on Constraint do nothing diff --git a/src/test/testData/sql/formatter/InsertConflictNothing_format.sql b/src/test/testData/sql/formatter/InsertConflictNothing_format.sql new file mode 100644 index 00000000..22f0d1af --- /dev/null +++ b/src/test/testData/sql/formatter/InsertConflictNothing_format.sql @@ -0,0 +1,7 @@ +INSERT INTO employee + (id + , username) + VALUES ( /* employees.id */0 + , /* employees.name */'name') +ON CONFLICT (username) ON CONSTRAINT +DO NOTHING diff --git a/src/test/testData/sql/formatter/InsertConflictUpdate.sql b/src/test/testData/sql/formatter/InsertConflictUpdate.sql new file mode 100644 index 00000000..823d6273 --- /dev/null +++ b/src/test/testData/sql/formatter/InsertConflictUpdate.sql @@ -0,0 +1,9 @@ +insert into employees (name, manager_id) +SELECT name, user_id +FROM user_settings +WHERE user_id = /*employee.id*/0 AND name = /*employee.name*/'name' +on conflict (id) do +update set name = EXCLUDED.name, + email = default +WHERE employees.name is Distinct from EXCLUDED.name +returning id, manager_id, name diff --git a/src/test/testData/sql/formatter/InsertConflictUpdateWithOutTable.sql b/src/test/testData/sql/formatter/InsertConflictUpdateWithOutTable.sql new file mode 100644 index 00000000..778ffc51 --- /dev/null +++ b/src/test/testData/sql/formatter/InsertConflictUpdateWithOutTable.sql @@ -0,0 +1,3 @@ +insert into users (username, email) +values ('user', 'user@example.com') +on CONFLICT ON constraint do update set email = EXCLUDED.email, created_at = CURRENT_TIMESTAMP diff --git a/src/test/testData/sql/formatter/InsertConflictUpdateWithOutTable_format.sql b/src/test/testData/sql/formatter/InsertConflictUpdateWithOutTable_format.sql new file mode 100644 index 00000000..418178fc --- /dev/null +++ b/src/test/testData/sql/formatter/InsertConflictUpdateWithOutTable_format.sql @@ -0,0 +1,9 @@ +INSERT INTO users + (username + , email) + VALUES ('user' + , 'user@example.com') +ON CONFLICT ON CONSTRAINT +DO UPDATE + SET email = EXCLUDED.email + , created_at = CURRENT_TIMESTAMP diff --git a/src/test/testData/sql/formatter/InsertConflictUpdate_format.sql b/src/test/testData/sql/formatter/InsertConflictUpdate_format.sql new file mode 100644 index 00000000..16c1891e --- /dev/null +++ b/src/test/testData/sql/formatter/InsertConflictUpdate_format.sql @@ -0,0 +1,16 @@ +INSERT INTO employees + (name + , manager_id) +SELECT name + , user_id + FROM user_settings + WHERE user_id = /*employee.id*/0 + AND name = /*employee.name*/'name' +ON CONFLICT (id) +DO UPDATE + SET name = EXCLUDED.name + , email = DEFAULT + WHERE employees.name IS DISTINCT FROM EXCLUDED.name +RETURNING id + , manager_id + , name diff --git a/src/test/testData/sql/formatter/InsertReturning.sql b/src/test/testData/sql/formatter/InsertReturning.sql new file mode 100644 index 00000000..90269a2b --- /dev/null +++ b/src/test/testData/sql/formatter/InsertReturning.sql @@ -0,0 +1,15 @@ +INSERT INTO /*# tableName */ + (x1 , x2 + /*%for entity : entities */ + , /*# entity.itemIdentifier */ + /*%end*/ , x3 , x4) + VALUES ( /* reportId */1 + , /* reportId */1 + /*%for entity : entities */ + , /* entity.value */'abc' + /*%end*/ + , /* @userId() */1 + , x5 + , /* @userId() */1 + , x6, 1 + , /* @maxDateTime() */'9999-12-31') returning x1, x2 \ No newline at end of file diff --git a/src/test/testData/sql/formatter/InsertReturning_format.sql b/src/test/testData/sql/formatter/InsertReturning_format.sql new file mode 100644 index 00000000..4b9fecde --- /dev/null +++ b/src/test/testData/sql/formatter/InsertReturning_format.sql @@ -0,0 +1,21 @@ +INSERT INTO /*# tableName */ + (x1 + , x2 + /*%for entity : entities */ + , /*# entity.itemIdentifier */ + /*%end*/ + , x3 + , x4) + VALUES ( /* reportId */1 + , /* reportId */1 + /*%for entity : entities */ + , /* entity.value */'abc' + /*%end*/ + , /* @userId() */1 + , x5 + , /* @userId() */1 + , x6 + , 1 + , /* @maxDateTime() */'9999-12-31') +RETURNING x1 + , x2 diff --git a/src/test/testData/sql/formatter/FormattedInsertWithBindVariable.sql b/src/test/testData/sql/formatter/InsertWithBindVariable_format.sql similarity index 100% rename from src/test/testData/sql/formatter/FormattedInsertWithBindVariable.sql rename to src/test/testData/sql/formatter/InsertWithBindVariable_format.sql diff --git a/src/test/testData/sql/formatter/Insert_format.sql b/src/test/testData/sql/formatter/Insert_format.sql new file mode 100644 index 00000000..b5e7119b --- /dev/null +++ b/src/test/testData/sql/formatter/Insert_format.sql @@ -0,0 +1,19 @@ +INSERT INTO /*# tableName */ + (x1 + , x2 + /*%for entity : entities */ + , /*# entity.itemIdentifier */ + /*%end*/ + , x3 + , x4) + VALUES ( /* reportId */1 + , /* reportId */1 + /*%for entity : entities */ + , /* entity.value */'abc' + /*%end*/ + , /* @userId() */1 + , x5 + , /* @userId() */1 + , x6 + , 1 + , /* @maxDateTime() */'9999-12-31') diff --git a/src/test/testData/sql/formatter/Select.sql b/src/test/testData/sql/formatter/Select.sql index adfbba6d..14dfc872 100644 --- a/src/test/testData/sql/formatter/Select.sql +++ b/src/test/testData/sql/formatter/Select.sql @@ -1,63 +1,54 @@ /** TopBlock */ -SELECT COUNT( DISTINCT (x)),o.* - , ISNULL(nbor.nearest - , 999) AS nearest -- column Line comment +SELECT COUNT(DISTINCT x) AS count_x + , o.* + , COALESCE(nbor.nearest, 999) +AS nearest /** From */ FROM ( -- SubGroupLine - SELECT p.objid - , p.psfmag_g - p.extinction_g + 5 * LOG(u.propermotion / 100.) + 5 AS rpm - , p.psfmag_g - p.extinction_g - (p.psfmag_i - p.extinction_i) AS gi - , ISNULL(s.plate - , 0) AS plate - , ISNULL(s.mjd - , 0) AS mjd - , ISNULL(s.fiberid - , 0) AS fiberid - + SELECT p.objid + , p.psfmag_g - p.extinction_g + 5 * LOG(10, u.propermotion / 100.0) + 5 AS rpm + , p.psfmag_g - p.extinction_g - (p.psfmag_i - p.extinction_i) AS gi + , COALESCE(s.plate, 0) AS plate + , COALESCE(s.mjd + , 0) AS mjd + , COALESCE(s.fiberid, 0) AS fiberid FROM phototag p --- Line1 + -- Line1 JOIN usno u --- Line2 + -- Line2 ON p.objid = u.objid /** Join */ LEFT OUTER JOIN specobj s /** ON */ - ON p.objid = s.bestobjid AND p.plate = s.plate + ON p.objid = s.bestobjid + AND p.plate = s.plate /** Where */ - WHERE p.TYPE = DBO.FPHOTOTYPE('Star') - -- Line3 - OR (p.flags & DBO.FPHOTOFLAGS('EDGE') = 0 - - AND (p.psfmag_g - p.extinction_g) BETWEEN 15 AND 20) -/*%if status == 2 */ --- Line4 - AND u.propermotion > 2. + WHERE p.TYPE = 'Star' + -- Line3 + OR ( p.flags & FPHOTOFLAGS('EDGE') = 0 AND (p.psfmag_g - p.extinction_g) BETWEEN 15 AND 20) + /*%if status == 2 */ + -- Line4 + and u.propermotion > 2.0 /** And Group */ - AND (p.psfmag_g - p.extinction_g + 5 * LOG(u.propermotion / 100.) + 5 > 16.136 + 2.727 * (p.psfmag_g - p.extinction_g - (p.psfmag_i - p.extinction_i)) - - OR p.psfmag_g - p.extinction_g - (p.psfmag_i - p.extinction_i) < 0.)/*%end*/) AS o - - LEFT OUTER JOIN ( SELECT n.objid - , MIN(n.distance) AS nearest - - FROM neighbors n - JOIN phototag x - ON n.neighborobjid = x.objid - AND n.neighbormode = DBO.FPHOTOMODE('Primary') - OR n.MODE = /*# "active" */'mode' - AND n.status =2 - AND n.flag = true - WHERE n.TYPE = DBO.FPHOTOTYPE('Star') - - AND n.MODE = DBO.FPHOTOMODE('Primary') - OR n.neighbormode = DBO.FPHOTOMODE('Primary') - AND (x.TYPE = DBO.FPHOTOTYPE('Star') - - AND x.TYPE = DBO.FPHOTOTYPE('Galaxy')) - OR x.modelmag_g BETWEEN 10 AND 21 - - GROUP BY n.objid ) AS nbor - ON o.objid = nbor.objid - WHERE p.params IN /* params */(1 - ,2 - ,3) \ No newline at end of file + and (p.psfmag_g - p.extinction_g + 5 * LOG(10, u.propermotion / 100.0) + 5 > 16.136 + 2.727 + or (p.psfmag_g - p.extinction_g - (p.psfmag_i - p.extinction_i)) < 0.0 + and (p.extinction_g - u.propermotion) > 0) + /*%end*/ ) as o + left outer join ( SELECT n.objid + , MIN(n.distance) AS nearest + + FROM neighbors n + JOIN phototag x + ON n.neighborobjid = x.objid + AND (n.neighbormode = 'Primary' + OR n.mode = 'mode') + AND n.status = 2 + AND n.flag = TRUE + + WHERE n.TYPE = 'Star' + OR (n.MODE = 'Primary' + OR n.neighbormode = 'Primary') + AND ( (x.TYPE = 'Star' + AND x.TYPE = 'Galaxy') OR x.modelmag_g BETWEEN 10 AND 21) + GROUP BY n.objid ) AS nbor ON o.objid = nbor.objid + WHERE o.objid IN /* params */(1, 2, 3) diff --git a/src/test/testData/sql/formatter/Select_format.sql b/src/test/testData/sql/formatter/Select_format.sql new file mode 100644 index 00000000..f22e7e12 --- /dev/null +++ b/src/test/testData/sql/formatter/Select_format.sql @@ -0,0 +1,59 @@ +/** TopBlock */ +SELECT COUNT(DISTINCT x) AS count_x + , o.* + , COALESCE(nbor.nearest + , 999) AS nearest + /** From */ + FROM ( -- SubGroupLine + SELECT p.objid + , p.psfmag_g - p.extinction_g + 5 * LOG(10 + , u.propermotion / 100.0) + 5 AS rpm + , p.psfmag_g - p.extinction_g - (p.psfmag_i - p.extinction_i) AS gi + , COALESCE(s.plate + , 0) AS plate + , COALESCE(s.mjd + , 0) AS mjd + , COALESCE(s.fiberid + , 0) AS fiberid + FROM phototag p + -- Line1 + JOIN usno u + -- Line2 + ON p.objid = u.objid + /** Join */ + LEFT OUTER JOIN specobj s + /** ON */ + ON p.objid = s.bestobjid + AND p.plate = s.plate + /** Where */ + WHERE p.TYPE = 'Star' + -- Line3 + OR (p.flags & FPHOTOFLAGS('EDGE') = 0 + AND (p.psfmag_g - p.extinction_g) BETWEEN 15 AND 20) + /*%if status == 2 */ + -- Line4 + AND u.propermotion > 2.0 + /** And Group */ + AND (p.psfmag_g - p.extinction_g + 5 * LOG(10 + , u.propermotion / 100.0) + 5 > 16.136 + 2.727 + OR (p.psfmag_g - p.extinction_g - (p.psfmag_i - p.extinction_i)) < 0.0 + AND (p.extinction_g - u.propermotion) > 0) + /*%end*/ ) AS o + LEFT OUTER JOIN ( SELECT n.objid + , MIN(n.distance) AS nearest + FROM neighbors n + JOIN phototag x + ON n.neighborobjid = x.objid + AND (n.neighbormode = 'Primary' + OR n.mode = 'mode') + AND n.status = 2 + AND n.flag = TRUE + WHERE n.TYPE = 'Star' + OR (n.MODE = 'Primary' + OR n.neighbormode = 'Primary') + AND ((x.TYPE = 'Star' + AND x.TYPE = 'Galaxy') + OR x.modelmag_g BETWEEN 10 AND 21) + GROUP BY n.objid ) AS nbor + ON o.objid = nbor.objid + WHERE o.objid IN /* params */(1, 2, 3) diff --git a/src/test/testData/sql/formatter/FormattedUpdateBindVariable.sql b/src/test/testData/sql/formatter/UpdateBindVariable_format.sql similarity index 100% rename from src/test/testData/sql/formatter/FormattedUpdateBindVariable.sql rename to src/test/testData/sql/formatter/UpdateBindVariable_format.sql diff --git a/src/test/testData/sql/formatter/UpdateReturning.sql b/src/test/testData/sql/formatter/UpdateReturning.sql new file mode 100644 index 00000000..a16c9d39 --- /dev/null +++ b/src/test/testData/sql/formatter/UpdateReturning.sql @@ -0,0 +1,4 @@ +update user set +name = /* user.name */'name', +rank = /*user.rank */3 +where id = /* user.id */1 returning id, name, rank \ No newline at end of file diff --git a/src/test/testData/sql/formatter/UpdateReturning_format.sql b/src/test/testData/sql/formatter/UpdateReturning_format.sql new file mode 100644 index 00000000..6afedd0d --- /dev/null +++ b/src/test/testData/sql/formatter/UpdateReturning_format.sql @@ -0,0 +1,7 @@ +UPDATE user + SET name = /* user.name */'name' + , rank = /*user.rank */3 + WHERE id = /* user.id */1 +RETURNING id + , name + , rank diff --git a/src/test/testData/sql/formatter/FormattedUpdateTupleAssignment.sql b/src/test/testData/sql/formatter/UpdateTupleAssignment_format.sql similarity index 100% rename from src/test/testData/sql/formatter/FormattedUpdateTupleAssignment.sql rename to src/test/testData/sql/formatter/UpdateTupleAssignment_format.sql diff --git a/src/test/testData/sql/formatter/FormattedUpdate.sql b/src/test/testData/sql/formatter/Update_format.sql similarity index 100% rename from src/test/testData/sql/formatter/FormattedUpdate.sql rename to src/test/testData/sql/formatter/Update_format.sql diff --git a/src/test/testData/sql/formatter/WithDelete.sql b/src/test/testData/sql/formatter/WithDelete.sql new file mode 100644 index 00000000..fac5be66 --- /dev/null +++ b/src/test/testData/sql/formatter/WithDelete.sql @@ -0,0 +1,6 @@ +WITH deleted_posts AS ( +delete FROM posts + where deleted = TRUE AND deleted_at < CURRENT_DATE - INTERVAL '30 days' +returning id +) +SELECT * FROM deleted_posts \ No newline at end of file diff --git a/src/test/testData/sql/formatter/WithDelete_format.sql b/src/test/testData/sql/formatter/WithDelete_format.sql new file mode 100644 index 00000000..207dc1e6 --- /dev/null +++ b/src/test/testData/sql/formatter/WithDelete_format.sql @@ -0,0 +1,8 @@ +WITH deleted_posts AS ( + DELETE FROM posts + WHERE deleted = TRUE + AND deleted_at < CURRENT_DATE - INTERVAL '30 days' + RETURNING id +) +SELECT * + FROM deleted_posts diff --git a/src/test/testData/sql/formatter/WithInsert.sql b/src/test/testData/sql/formatter/WithInsert.sql new file mode 100644 index 00000000..af26d51b --- /dev/null +++ b/src/test/testData/sql/formatter/WithInsert.sql @@ -0,0 +1,5 @@ +WITH new_user AS ( INSERT INTO users (name, email) + VALUES ('Alice Example', 'alice@example.com') RETURNING id,name) +INSERT INTO user_profiles (user_id, bio) +SELECT id , name + FROM new_user diff --git a/src/test/testData/sql/formatter/WithInsert_format.sql b/src/test/testData/sql/formatter/WithInsert_format.sql new file mode 100644 index 00000000..d289aa6d --- /dev/null +++ b/src/test/testData/sql/formatter/WithInsert_format.sql @@ -0,0 +1,15 @@ +WITH new_user AS ( + INSERT INTO users + (name + , email) + VALUES ('Alice Example' + , 'alice@example.com') + RETURNING id + , name +) +INSERT INTO user_profiles + (user_id + , bio) +SELECT id + , name + FROM new_user diff --git a/src/test/testData/sql/formatter/WithMultiQuery.sql b/src/test/testData/sql/formatter/WithMultiQuery.sql new file mode 100644 index 00000000..1cdd22eb --- /dev/null +++ b/src/test/testData/sql/formatter/WithMultiQuery.sql @@ -0,0 +1,13 @@ +with users AS ( +SELECT user_id, name FROM users WHERE name = 'premium' +), +active_users AS ( +SELECT user_id, name FROM users WHERE last_login > CURRENT_DATE - INTERVAL '30 days' +) +SELECT * FROM premium_users +INTERSECT +SELECT * FROM active_users +EXCEPT +SELECT user_id, name FROM users WHERE suspended = TRUE +ORDER BY name ASC +LIMIT 100 OFFSET 10 \ No newline at end of file diff --git a/src/test/testData/sql/formatter/WithMultiQuery_format.sql b/src/test/testData/sql/formatter/WithMultiQuery_format.sql new file mode 100644 index 00000000..213b8de3 --- /dev/null +++ b/src/test/testData/sql/formatter/WithMultiQuery_format.sql @@ -0,0 +1,24 @@ +WITH users AS ( + SELECT user_id + , name + FROM users + WHERE name = 'premium' +) +, active_users AS ( + SELECT user_id + , name + FROM users + WHERE last_login > CURRENT_DATE - INTERVAL '30 days' +) +SELECT * + FROM premium_users +INTERSECT +SELECT * + FROM active_users +EXCEPT +SELECT user_id + , name + FROM users + WHERE suspended = TRUE + ORDER BY name ASC + LIMIT 100 OFFSET 10 diff --git a/src/test/testData/sql/formatter/WithOptional.sql b/src/test/testData/sql/formatter/WithOptional.sql new file mode 100644 index 00000000..b76dee10 --- /dev/null +++ b/src/test/testData/sql/formatter/WithOptional.sql @@ -0,0 +1,8 @@ +WITH RECURSIVE org_tree (emp_id , name, manager_id, level) AS MATERIALIZED ( + SELECT id, name, manager_id, 1 + FROM employees + WHERE manager_id IS NULL + ) + search breadth first by emp_id set order_seq + cycle emp_id SET is_cycle to TRUE default FALSE USING path_ids +SELECT * FROM org_tree \ No newline at end of file diff --git a/src/test/testData/sql/formatter/WithOptional_format.sql b/src/test/testData/sql/formatter/WithOptional_format.sql new file mode 100644 index 00000000..d33ce8fd --- /dev/null +++ b/src/test/testData/sql/formatter/WithOptional_format.sql @@ -0,0 +1,14 @@ +WITH RECURSIVE org_tree (emp_id + , name + , manager_id + , level) AS MATERIALIZED ( + SELECT id + , name + , manager_id + , 1 + FROM employees + WHERE manager_id IS NULL +) +SEARCH BREADTH FIRST BY emp_id SET order_seq +CYCLE emp_id SET is_cycle TO TRUE DEFAULT FALSE USING path_ids +SELECT * FROM org_tree; \ No newline at end of file diff --git a/src/test/testData/sql/formatter/WithRecursive.sql b/src/test/testData/sql/formatter/WithRecursive.sql new file mode 100644 index 00000000..3c01786c --- /dev/null +++ b/src/test/testData/sql/formatter/WithRecursive.sql @@ -0,0 +1,10 @@ +with recursive org_tree (emp_id, name, manager_id, level) AS materialized ( +SELECT id, name, manager_id, 1 +FROM employees +WHERE manager_id IS NULL + union all +SELECT e.id, e.name, e.manager_id, ot.level + 1 +FROM employees e +JOIN org_tree ot ON e.manager_id = ot.emp_id +) +SELECT * FROM org_tree \ No newline at end of file diff --git a/src/test/testData/sql/formatter/WithRecursive_format.sql b/src/test/testData/sql/formatter/WithRecursive_format.sql new file mode 100644 index 00000000..d019f857 --- /dev/null +++ b/src/test/testData/sql/formatter/WithRecursive_format.sql @@ -0,0 +1,21 @@ +WITH RECURSIVE org_tree (emp_id + , name + , manager_id + , level) AS MATERIALIZED ( + SELECT id + , name + , manager_id + , 1 + FROM employees + WHERE manager_id IS NULL + UNION ALL + SELECT e.id + , e.name + , e.manager_id + , ot.level + 1 + FROM employees e + JOIN org_tree ot + ON e.manager_id = ot.emp_id +) +SELECT * + FROM org_tree diff --git a/src/test/testData/sql/formatter/WithSelect.sql b/src/test/testData/sql/formatter/WithSelect.sql new file mode 100644 index 00000000..f1e0caf7 --- /dev/null +++ b/src/test/testData/sql/formatter/WithSelect.sql @@ -0,0 +1,4 @@ +with users AS ( +SELECT user_id, name FROM users WHERE name = 'premium' +) +SELECT user_id, name FROM users WHERE suspended = TRUE \ No newline at end of file diff --git a/src/test/testData/sql/formatter/WithSelect_format.sql b/src/test/testData/sql/formatter/WithSelect_format.sql new file mode 100644 index 00000000..d6cc495e --- /dev/null +++ b/src/test/testData/sql/formatter/WithSelect_format.sql @@ -0,0 +1,10 @@ +WITH users AS ( + SELECT user_id + , name + FROM users + WHERE name = 'premium' +) +SELECT user_id + , name + FROM users + WHERE suspended = TRUE diff --git a/src/test/testData/sql/formatter/WithUnionAll.sql b/src/test/testData/sql/formatter/WithUnionAll.sql new file mode 100644 index 00000000..a2bfb3fb --- /dev/null +++ b/src/test/testData/sql/formatter/WithUnionAll.sql @@ -0,0 +1,12 @@ +with tables AS ( ( SELECT top, no_pre_as AS AS_NAME, pre_as, clm3 from demo +WHERE id = /*# "block" */1 ) +UNION ALL ( SELECT id2, no_pre_as2 AS AS_NAME2, pre_as2 FROM demo2 + WHERE id2 = /*# "block2" */1 ) ) +SELECT query.id3 + , query.no_pre_as3 AS AS_NAME3 + , query.pre_as3 + FROM demo3 query + INNER JOIN query1 q1 ON q1.id = query.id3 + LEFT JOIN query1 q2 ON query.id3 = q2.id AND query.pre_as3 = q2.sub_id + WHERE query.id3 = /* id */1 + ORDER BY query.id3, query.pre_as3 diff --git a/src/test/testData/sql/formatter/WithUnionAll_format.sql b/src/test/testData/sql/formatter/WithUnionAll_format.sql new file mode 100644 index 00000000..dd3cfc4b --- /dev/null +++ b/src/test/testData/sql/formatter/WithUnionAll_format.sql @@ -0,0 +1,26 @@ +WITH tables AS ( + ( SELECT top + , no_pre_as AS AS_NAME + , pre_as + , clm3 + FROM demo + WHERE id = /*# "block" */1 ) + UNION ALL + ( SELECT id2 + , no_pre_as2 AS AS_NAME2 + , pre_as2 + FROM demo2 + WHERE id2 = /*# "block2" */1 ) +) +SELECT query.id3 + , query.no_pre_as3 AS AS_NAME3 + , query.pre_as3 + FROM demo3 query + INNER JOIN query1 q1 + ON q1.id = query.id3 + LEFT JOIN query1 q2 + ON query.id3 = q2.id + AND query.pre_as3 = q2.sub_id + WHERE query.id3 = /* id */1 + ORDER BY query.id3 + , query.pre_as3 diff --git a/src/test/testData/sql/formatter/WithUpdate.sql b/src/test/testData/sql/formatter/WithUpdate.sql new file mode 100644 index 00000000..54267860 --- /dev/null +++ b/src/test/testData/sql/formatter/WithUpdate.sql @@ -0,0 +1,9 @@ +WITH recent_activity AS ( SELECT user_id, MAX(logged_in_at) AS last_login + FROM login_logs + GROUP BY user_id +), +updated_users AS ( UPDATE users u + SET last_login_at = ra.last_login + FROM recent_activity ra WHERE u.id = ra.user_id +returning id +)SELECT * FROM updated_users \ No newline at end of file diff --git a/src/test/testData/sql/formatter/WithUpdate_format.sql b/src/test/testData/sql/formatter/WithUpdate_format.sql new file mode 100644 index 00000000..a0288a00 --- /dev/null +++ b/src/test/testData/sql/formatter/WithUpdate_format.sql @@ -0,0 +1,15 @@ +WITH recent_activity AS ( + SELECT user_id + , MAX(logged_in_at) AS last_login + FROM login_logs + GROUP BY user_id +) +, updated_users AS ( + UPDATE users u + SET last_login_at = ra.last_login + FROM recent_activity ra + WHERE u.id = ra.user_id + RETURNING id +) +SELECT * + FROM updated_users