@@ -40,15 +40,14 @@ class DaoInjectionSqlVisitor(
4040 private data class FormattingTask (
4141 val expression : PsiLiteralExpression ,
4242 val formattedText : String ,
43- val baseIndent : String ,
4443 )
4544
4645 companion object {
4746 private const val TEMP_FILE_PREFIX = " temp_format"
4847 private const val SQL_FILE_EXTENSION = " .sql"
49- private const val SQL_COMMENT_PATTERN = " ^[ \\ t]*/\\ *"
5048 private const val TRIPLE_QUOTE = " \"\"\" "
5149 private const val WRITE_COMMAND_NAME = " Format Injected SQL"
50+ private const val BASE_INDENT = " \t\t\t " // getBaseIndent(formattedSql)
5251 }
5352
5453 private val formattingTasks = mutableListOf<FormattingTask >()
@@ -58,36 +57,39 @@ class DaoInjectionSqlVisitor(
5857 val injected: PsiFile ? = InjectionSqlUtil .initInjectionElement(element, project, expression)
5958 if (injected != null ) {
6059 // Format SQL and store the task
61- val originalSqlText = injected.text
62- val formattedSql = formatAsTemporarySqlFile(originalSqlText)
63- // Keep the current top line indent
64- val baseIndent = getBaseIndent(formattedSql)
6560 val originalText = expression.value?.toString() ? : return
66-
67- if (formattedSql != originalText) {
68- formattingTasks.add(FormattingTask (expression, formattedSql, baseIndent))
69- }
61+ val removeIndent = removeIndentLines(originalText)
62+ formattingTasks.add(FormattingTask (expression, removeIndent))
7063 }
7164 }
7265
73- /* *
74- * Extracts the base indentation from the first non-blank, non-comment line.
75- */
76- private fun getBaseIndent (string : String ): String {
77- val lines = string.lines()
78- val commentRegex = Regex (SQL_COMMENT_PATTERN )
79-
80- // Skip blank lines and comment lines
81- val firstContentLineIndex =
82- lines.indexOfFirst { line ->
83- line.isNotBlank() && ! commentRegex.matches(line)
66+ private fun removeIndentLines (sqlText : String ): String {
67+ val lines = sqlText.lines()
68+ val commentStartRegex = Regex (" ^[ \t ]*/[*][ \t ]*\\ *" )
69+ val commentEndRegex = Regex (" \\ */.*$" )
70+ var blockComment = false
71+ val removeIndentLines =
72+ lines.map { line ->
73+ if (blockComment) {
74+ if (commentEndRegex.containsMatchIn(line)) {
75+ blockComment = false
76+ }
77+ SINGLE_SPACE .plus(line.dropWhile { it.isWhitespace() })
78+ } else {
79+ val baseLine =
80+ if (commentStartRegex.containsMatchIn(line)) {
81+ blockComment = true
82+ // Exclude spaces between `/*` and the comment content element,
83+ // as IntelliJ IDEA's Java formatter may insert a space there during formatting.
84+ line.replace(commentStartRegex, " /**" )
85+ } else {
86+ line
87+ }
88+ baseLine.dropWhile { it.isWhitespace() }
89+ }
8490 }
8591
86- return if (firstContentLineIndex >= 0 ) {
87- lines[firstContentLineIndex].takeWhile { it.isWhitespace() }
88- } else {
89- " "
90- }
92+ return removeIndentLines.joinToString(StringUtil .LINE_SEPARATE )
9193 }
9294
9395 /* *
@@ -138,10 +140,11 @@ class DaoInjectionSqlVisitor(
138140 */
139141 private fun replaceHostStringLiteral (
140142 task : FormattingTask ,
141- removeSpace : (String , Boolean ) -> String ,
143+ sqlPostProcessorProcess : (String , Boolean ) -> String ,
142144 ) {
143145 try {
144- val formattedLiteral = createFormattedLiteral(task, removeSpace)
146+ // Keep the current top line indent
147+ val formattedLiteral = createFormattedLiteral(task, sqlPostProcessorProcess)
145148 replaceInDocument(task.expression, formattedLiteral)
146149 } catch (_: Exception ) {
147150 // Silently ignore formatting failures
@@ -150,16 +153,19 @@ class DaoInjectionSqlVisitor(
150153
151154 private fun createFormattedLiteral (
152155 task : FormattingTask ,
153- removeSpace : (String , Boolean ) -> String ,
156+ sqlPostProcessorProcess : (String , Boolean ) -> String ,
154157 ): String {
155- val newLiteralText = createFormattedLiteralText(task.formattedText)
156- val normalizedText = normalizeIndentation(newLiteralText, task.baseIndent)
157- val cleanedText = removeSpace(normalizedText, false )
158+ // Retrieve the same formatted string as when formatting a regular SQL file.
159+ val formattedSql = formatAsTemporarySqlFile(task.formattedText)
160+ val cleanedText = sqlPostProcessorProcess(formattedSql, false )
161+ // Generate text aligned with the literal element using the formatted string.
162+ val newLiteralText = createFormattedLiteralText(cleanedText)
163+ val normalizedText = normalizeIndentation(newLiteralText)
158164
159165 val elementFactory =
160166 com.intellij.psi.JavaPsiFacade
161167 .getElementFactory(project)
162- val newLiteral = elementFactory.createExpressionFromText(cleanedText , task.expression)
168+ val newLiteral = elementFactory.createExpressionFromText(normalizedText , task.expression)
163169 return newLiteral.text
164170 }
165171
@@ -192,10 +198,7 @@ class DaoInjectionSqlVisitor(
192198 /* *
193199 * Normalizes indentation by removing base indent and reapplying it consistently.
194200 */
195- private fun normalizeIndentation (
196- sqlText : String ,
197- baseIndent : String ,
198- ): String {
201+ private fun normalizeIndentation (sqlText : String ): String {
199202 val lines = sqlText.lines()
200203 if (lines.isEmpty()) return sqlText
201204
@@ -206,8 +209,8 @@ class DaoInjectionSqlVisitor(
206209 contentLines.map { line ->
207210 when {
208211 line.isBlank() -> line
209- line.startsWith(baseIndent ) -> baseIndent + line.removePrefix(baseIndent )
210- else -> baseIndent + line
212+ line.startsWith(BASE_INDENT ) -> BASE_INDENT + line.removePrefix(BASE_INDENT )
213+ else -> BASE_INDENT + line
211214 }
212215 }
213216
0 commit comments