1616package org.domaframework.doma.intellij.formatter
1717
1818import com.intellij.lang.ASTNode
19+ import com.intellij.openapi.editor.Document
1920import com.intellij.openapi.util.TextRange
2021import com.intellij.psi.PsiDocumentManager
2122import com.intellij.psi.PsiElement
@@ -26,6 +27,7 @@ import com.intellij.psi.TokenType
2627import com.intellij.psi.impl.source.codeStyle.PreFormatProcessor
2728import com.intellij.psi.util.PsiTreeUtil
2829import com.intellij.psi.util.elementType
30+ import com.intellij.psi.util.endOffset
2931import com.intellij.psi.util.prevLeafs
3032import org.domaframework.doma.intellij.psi.SqlBlockComment
3133import org.domaframework.doma.intellij.psi.SqlTypes
@@ -55,11 +57,17 @@ class SqlFormatPreProcessor : PreFormatProcessor {
5557 var index = keywordList.size
5658 var keywordIndex = replaceKeywordList.size
5759
60+ // Add a newline to the end of the file
5861 val documentLastElement = visitor.lastElement
5962 val documentLastRange = visitor.lastElement?.textRange
60- if (documentLastRange != null && documentLastRange.endOffset <= rangeToReformat.endOffset) {
61- if (documentLastElement !is PsiWhiteSpace || documentLastElement.text?.contains(" \n " ) == false ) {
62- document.insertString(documentLastRange.endOffset, " \n " )
63+ if (documentLastElement != null && documentLastRange != null && documentLastRange.endOffset <= rangeToReformat.endOffset) {
64+ if (documentLastElement !is PsiWhiteSpace ) {
65+ val textEnd = documentLastElement.endOffset
66+ document.insertString(textEnd, " \n " )
67+ } else {
68+ val textStart = documentLastElement.startOffset
69+ val textEnd = documentLastElement.endOffset
70+ document.replaceString(textStart, textEnd, " \n " )
6371 }
6472 }
6573
@@ -68,112 +76,71 @@ class SqlFormatPreProcessor : PreFormatProcessor {
6876 val textRangeStart = it.startOffset
6977 val textRangeEnd = textRangeStart + it.text.length
7078 if (it.elementType != TokenType .WHITE_SPACE ) {
79+ // Add a newline before any element that needs a newline+indent, without overlapping if there is already a newline
7180 index--
7281 var newKeyword = getUpperText(it)
7382 when (it.elementType) {
7483 SqlTypes .KEYWORD -> {
7584 keywordIndex--
76- newKeyword =
77- if (checkKeywordPrevElement(index, it) &&
78- SqlKeywordUtil .getIndentType(it.text).isNewLineGroup() ||
79- it.text.lowercase() == " end" ||
80- (
81- it.text.lowercase() == " as" &&
82- createQueryType == CreateQueryType .VIEW
83- )
84- ) {
85- if (SqlKeywordUtil .isSetLineKeyword(
86- it.text,
87- keywordList[index - 1 ].text,
88- )
89- ) {
90- getUpperText(it)
91- } else {
92- getNewLineString(it)
93- }
94- } else {
95- getUpperText(it)
96- }
85+ newKeyword = getKeywordNewText(index, it, createQueryType, keywordList)
9786 }
87+
9888 SqlTypes .LEFT_PAREN -> {
9989 newKeyword =
10090 if (createQueryType == CreateQueryType .TABLE ) {
101- getNewLineString(it)
91+ getNewLineString(it.prevSibling, getUpperText(it) )
10292 } else {
10393 getUpperText(it)
10494 }
10595 }
96+
10697 SqlTypes .RIGHT_PAREN -> {
107- val prefixElements =
108- getElementsBeforeKeyword(it.prevLeafs.toList()) { it.elementType == SqlTypes .LEFT_PAREN }
109- val containsColumnRaw =
110- prefixElements.findLast { isColumnDefinedRawElementType(it) } != null
111- newKeyword =
112- if (createQueryType == CreateQueryType .TABLE ) {
113- if (containsColumnRaw) {
114- getNewLineString(it)
115- } else {
116- getUpperText(it)
117- }
118- } else {
119- getUpperText(it)
120- }
98+ newKeyword = getRightPatternNewText(it, newKeyword, createQueryType)
12199 }
122100
123101 SqlTypes .WORD -> {
124- var prev = it.prevSibling
125- var isColumnName = true
126- while (prev != null && prev.elementType != SqlTypes .LEFT_PAREN && prev.elementType != SqlTypes .COMMA ) {
127- if (prev !is PsiWhiteSpace &&
128- prev.elementType != SqlTypes .LINE_COMMENT &&
129- prev.elementType != SqlTypes .BLOCK_COMMENT
130- ) {
131- isColumnName =
132- prev.elementType == SqlTypes .COMMA ||
133- prev.elementType == SqlTypes .LEFT_PAREN
134- break
135- }
136- prev = prev.prevSibling
137- }
138-
139- newKeyword =
140- if (createQueryType == CreateQueryType .TABLE && isColumnName) {
141- getNewLineString(it)
142- } else {
143- getUpperText(it)
144- }
102+ newKeyword = getWordNewText(it, newKeyword, createQueryType)
145103 }
104+
146105 SqlTypes .COMMA -> {
147- newKeyword = getNewLineString(it)
106+ newKeyword = getNewLineString(it.prevSibling, getUpperText(it) )
148107 }
149108 }
150109 document.deleteString(textRangeStart, textRangeEnd)
151110 document.insertString(textRangeStart, newKeyword)
152111 } else {
153- if (keywordIndex < replaceKeywordList.size) {
112+ // Remove spaces after newlines to reset indentation
113+ val nextSibling = it.nextSibling
114+ if (nextSibling.elementType == SqlTypes .LINE_COMMENT ) {
115+ document.replaceString(textRangeStart, textRangeEnd, " " )
116+ } else if (nextSibling.elementType == SqlTypes .BLOCK_COMMENT ) {
117+ removeSpacesAroundNewline(document, it.textRange)
118+ } else if (keywordIndex < replaceKeywordList.size) {
154119 val nextElement = replaceKeywordList[keywordIndex]
155- if ((
156- SqlKeywordUtil .getIndentType(nextElement.text ? : " " ).isNewLineGroup() &&
120+ if (isNewLineOnlyCreateTable(nextSibling) && createQueryType == CreateQueryType .TABLE ) {
121+ removeSpacesAroundNewline(document, it.textRange)
122+ } else if (isSubGroupFirstElement(nextElement)) {
123+ document.deleteString(textRangeStart, textRangeEnd)
124+ } else {
125+ val isNewLineGroup = SqlKeywordUtil .getIndentType(nextElement.text ? : " " ).isNewLineGroup()
126+ val isSetLineKeyword =
127+ if (keywordIndex > 0 ) {
157128 SqlKeywordUtil .isSetLineKeyword(
158- replaceKeywordList[keywordIndex] .text,
129+ nextElement .text,
159130 replaceKeywordList[keywordIndex - 1 ].text,
160131 )
161- ) ||
162- (
163- isNewLineOnlyCreateTable(nextElement) && createQueryType == CreateQueryType .TABLE
164- )
165- ) {
166- document.deleteString(textRangeStart, textRangeEnd)
167- document.insertString(textRangeStart, " " )
168- } else {
169- val currentIndent = it.text.substringAfter(" \n " , " " ).length
170- val start = textRangeEnd - currentIndent
171- document.deleteString(start, textRangeEnd)
132+ } else {
133+ false
134+ }
135+
136+ if (isNewLineGroup && ! isSetLineKeyword || keywordList[index].elementType == SqlTypes .COMMA ) {
137+ removeSpacesAroundNewline(document, it.textRange)
138+ } else {
139+ document.replaceString(textRangeStart, textRangeEnd, " " )
140+ }
172141 }
173142 } else {
174- val currentIndent = it.text.substringAfter(" \n " , " " ).length
175- val start = textRangeEnd - currentIndent
176- document.deleteString(start, textRangeEnd)
143+ removeSpacesAroundNewline(document, it.textRange)
177144 }
178145 }
179146 }
@@ -183,6 +150,103 @@ class SqlFormatPreProcessor : PreFormatProcessor {
183150 return rangeToReformat.grown(visitor.replaces.size)
184151 }
185152
153+ private fun removeSpacesAroundNewline (
154+ document : Document ,
155+ range : TextRange ,
156+ ) {
157+ val originalText = document.getText(range)
158+ val newText = originalText.replace(Regex (" \\ s*\\ n\\ s*" ), " \n " )
159+ document.replaceString(range.startOffset, range.endOffset, newText)
160+ }
161+
162+ /* *
163+ * Checks for special case keyword elements and specific combinations of keywords with line breaks and capitalization only
164+ */
165+ private fun getKeywordNewText (
166+ index : Int ,
167+ element : PsiElement ,
168+ createQueryType : CreateQueryType ,
169+ keywordList : List <PsiElement >,
170+ ): String =
171+ if (element.text.lowercase() == " end" ) {
172+ getNewLineString(element.prevSibling, getUpperText(element))
173+ } else if (isCreateViewAs(element, createQueryType)) {
174+ getNewLineString(element.prevSibling, getUpperText(element))
175+ } else if (isSubGroupFirstElement(element)) {
176+ getUpperText(element)
177+ } else if (SqlKeywordUtil .getIndentType(element.text).isNewLineGroup()) {
178+ if (index > 0 &&
179+ SqlKeywordUtil .isSetLineKeyword(
180+ element.text,
181+ keywordList[index - 1 ].text,
182+ )
183+ ) {
184+ getUpperText(element)
185+ } else {
186+ getNewLineString(element.prevSibling, getUpperText(element))
187+ }
188+ } else {
189+ getUpperText(element)
190+ }
191+
192+ private fun getRightPatternNewText (
193+ element : PsiElement ,
194+ newKeyword : String ,
195+ createQueryType : CreateQueryType ,
196+ ): String {
197+ var newKeyword1 = newKeyword
198+ val prefixElements =
199+ getElementsBeforeKeyword(element.prevLeafs.toList()) { it.elementType == SqlTypes .LEFT_PAREN }
200+ val containsColumnRaw =
201+ prefixElements.findLast { isColumnDefinedRawElementType(it) } != null
202+ newKeyword1 =
203+ if (createQueryType == CreateQueryType .TABLE ) {
204+ if (containsColumnRaw) {
205+ getNewLineString(element.prevSibling, getUpperText(element))
206+ } else {
207+ getUpperText(element)
208+ }
209+ } else {
210+ getUpperText(element)
211+ }
212+ return newKeyword1
213+ }
214+
215+ private fun getWordNewText (
216+ element : PsiElement ,
217+ newKeyword : String ,
218+ createQueryType : CreateQueryType ,
219+ ): String {
220+ newKeyword
221+ var prev = element.prevSibling
222+ var isColumnName = true
223+ while (prev != null && prev.elementType != SqlTypes .LEFT_PAREN && prev.elementType != SqlTypes .COMMA ) {
224+ if (prev !is PsiWhiteSpace &&
225+ prev.elementType != SqlTypes .LINE_COMMENT &&
226+ prev.elementType != SqlTypes .BLOCK_COMMENT
227+ ) {
228+ isColumnName =
229+ prev.elementType == SqlTypes .COMMA ||
230+ prev.elementType == SqlTypes .LEFT_PAREN
231+ break
232+ }
233+ prev = prev.prevSibling
234+ }
235+
236+ return if (createQueryType == CreateQueryType .TABLE && isColumnName) {
237+ getNewLineString(element.prevSibling, getUpperText(element))
238+ } else {
239+ getUpperText(element)
240+ }
241+ }
242+
243+ private fun isCreateViewAs (
244+ element : PsiElement ,
245+ createQueryType : CreateQueryType ,
246+ ): Boolean =
247+ element.text.lowercase() == " as" &&
248+ createQueryType == CreateQueryType .VIEW
249+
186250 private fun isColumnDefinedRawElementType (element : PsiElement ): Boolean =
187251 element.elementType == SqlTypes .WORD ||
188252 element.elementType == SqlTypes .KEYWORD ||
@@ -214,6 +278,9 @@ class SqlFormatPreProcessor : PreFormatProcessor {
214278 return attachmentKeywordType
215279 }
216280
281+ /* *
282+ * The column definition elements of Create Table, "(", "WORD", and ")" must be line breaks
283+ */
217284 private fun isNewLineOnlyCreateTable (nextElement : PsiElement ): Boolean =
218285 nextElement.elementType == SqlTypes .LEFT_PAREN ||
219286 nextElement.elementType == SqlTypes .RIGHT_PAREN ||
@@ -224,11 +291,14 @@ class SqlFormatPreProcessor : PreFormatProcessor {
224291 isLeft : (T ) -> Boolean ,
225292 ): List <T > = elements.takeWhile { element -> ! isLeft(element) }
226293
227- private fun getNewLineString (element : PsiElement ): String =
228- if (element.prevSibling?.text?.contains(" \n " ) == false ) {
229- " \n ${getUpperText(element)} "
294+ private fun getNewLineString (
295+ prevElement : PsiElement ? ,
296+ text : String ,
297+ ): String =
298+ if (prevElement?.text?.contains(" \n " ) == false ) {
299+ " \n $text "
230300 } else {
231- getUpperText(element)
301+ text
232302 }
233303
234304 private fun getUpperText (element : PsiElement ): String =
@@ -238,13 +308,9 @@ class SqlFormatPreProcessor : PreFormatProcessor {
238308 element.text
239309 }
240310
241- private fun checkKeywordPrevElement (
242- index : Int ,
243- element : PsiElement ,
244- ): Boolean =
245- index > 0 &&
246- element.prevSibling != null &&
247- element.prevSibling.elementType != SqlTypes .LEFT_PAREN
311+ private fun isSubGroupFirstElement (element : PsiElement ): Boolean =
312+ getElementsBeforeKeyword(element.prevLeafs.toList()) { it.elementType == SqlTypes .LEFT_PAREN }
313+ .findLast { it !is PsiWhiteSpace } == null
248314}
249315
250316private class SqlFormatVisitor : PsiRecursiveElementVisitor () {
@@ -259,10 +325,7 @@ private class SqlFormatVisitor : PsiRecursiveElementVisitor() {
259325
260326 if (PsiTreeUtil .getParentOfType(element, SqlBlockComment ::class .java) == null ) {
261327 when (element.elementType) {
262- SqlTypes .KEYWORD , SqlTypes .COMMA -> {
263- replaces.add(element)
264- }
265- SqlTypes .LEFT_PAREN , SqlTypes .RIGHT_PAREN , SqlTypes .WORD -> {
328+ SqlTypes .KEYWORD , SqlTypes .COMMA , SqlTypes .LEFT_PAREN , SqlTypes .RIGHT_PAREN , SqlTypes .WORD -> {
266329 replaces.add(element)
267330 }
268331 }
@@ -273,7 +336,11 @@ private class SqlFormatVisitor : PsiRecursiveElementVisitor() {
273336 super .visitWhiteSpace(space)
274337 val nextElement = space.nextSibling
275338 if (nextElement != null &&
276- space.text.contains(" \n " )
339+ (
340+ space.text.contains(" \n " ) ||
341+ nextElement.elementType == SqlTypes .LINE_COMMENT ||
342+ nextElement.elementType == SqlTypes .BLOCK_COMMENT
343+ )
277344 ) {
278345 replaces.add(space)
279346 }
0 commit comments