From 6039c869ba0e7bff49c62d3b5e89db3ed9bea43b Mon Sep 17 00:00:00 2001 From: xterao Date: Thu, 11 Sep 2025 11:08:51 +0900 Subject: [PATCH 1/5] =?UTF-8?q?Enable=20invoking=20the=20=E2=80=9Cconvert?= =?UTF-8?q?=20to=20Sql=20annotation=E2=80=9D=20action=20from=20the=20metho?= =?UTF-8?q?d=20side.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dao/ConvertSqlAnnotationToFileAction.kt | 22 +---- .../dao/ConvertSqlFileToAnnotationAction.kt | 92 ++++++++++++++----- .../action/dao/ConvertSqlIntentionAction.kt | 34 +++++++ src/main/resources/META-INF/plugin.xml | 6 ++ 4 files changed, 115 insertions(+), 39 deletions(-) create mode 100644 src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlIntentionAction.kt diff --git a/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlAnnotationToFileAction.kt b/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlAnnotationToFileAction.kt index dd84028e..ee76869a 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlAnnotationToFileAction.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlAnnotationToFileAction.kt @@ -15,7 +15,6 @@ */ package org.domaframework.doma.intellij.action.dao -import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo import com.intellij.codeInsight.intention.preview.IntentionPreviewUtils import com.intellij.openapi.command.WriteCommandAction @@ -28,12 +27,11 @@ import com.intellij.psi.util.PsiTreeUtil import org.domaframework.doma.intellij.bundle.MessageBundle import org.domaframework.doma.intellij.common.psi.PsiDaoMethod import org.domaframework.doma.intellij.common.util.PluginLoggerUtil -import org.domaframework.doma.intellij.extension.psi.DomaAnnotationType /** * Intention action to convert @Sql annotation to SQL file */ -class ConvertSqlAnnotationToFileAction : PsiElementBaseIntentionAction() { +class ConvertSqlAnnotationToFileAction : ConvertSqlIntentionAction() { override fun getFamilyName(): String = MessageBundle.message("convert.sql.annotation.to.file.family") override fun getText(): String = MessageBundle.message("convert.sql.annotation.to.file.text") @@ -47,24 +45,12 @@ class ConvertSqlAnnotationToFileAction : PsiElementBaseIntentionAction() { val psiDaoMethod = PsiDaoMethod(project, method) // Check if method has @Sql annotation - if (!psiDaoMethod.useSqlAnnotation()) { + // When a Sql annotation is present, a virtual SQL file is associated; + // therefore, check the parent and exclude the injected (inline) SQL. + if (!psiDaoMethod.useSqlAnnotation() || psiDaoMethod.sqlFile != null && psiDaoMethod.sqlFile?.parent != null) { return false } - // Check if method has @Insert, @Update, or @Delete annotation - val supportedTypes = - listOf( - DomaAnnotationType.Select, - DomaAnnotationType.Script, - DomaAnnotationType.SqlProcessor, - DomaAnnotationType.Insert, - DomaAnnotationType.Update, - DomaAnnotationType.Delete, - DomaAnnotationType.BatchInsert, - DomaAnnotationType.BatchUpdate, - DomaAnnotationType.BatchDelete, - ) - return supportedTypes.any { it.getPsiAnnotation(method) != null } } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlFileToAnnotationAction.kt b/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlFileToAnnotationAction.kt index 52a5df0f..1dc58083 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlFileToAnnotationAction.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlFileToAnnotationAction.kt @@ -15,23 +15,24 @@ */ package org.domaframework.doma.intellij.action.dao -import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction import com.intellij.codeInsight.intention.preview.IntentionPreviewUtils import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMethod +import com.intellij.psi.util.PsiTreeUtil import org.domaframework.doma.intellij.bundle.MessageBundle import org.domaframework.doma.intellij.common.dao.findDaoMethod +import org.domaframework.doma.intellij.common.isJavaOrKotlinFileType import org.domaframework.doma.intellij.common.isSupportFileType import org.domaframework.doma.intellij.common.psi.PsiDaoMethod import org.domaframework.doma.intellij.common.util.PluginLoggerUtil -import org.domaframework.doma.intellij.extension.psi.DomaAnnotationType /** * Intention action to convert SQL file to @Sql annotation */ -class ConvertSqlFileToAnnotationAction : PsiElementBaseIntentionAction() { +class ConvertSqlFileToAnnotationAction : ConvertSqlIntentionAction() { override fun getFamilyName(): String = MessageBundle.message("convert.sql.file.to.annotation.family") override fun getText(): String = MessageBundle.message("convert.sql.file.to.annotation.text") @@ -41,30 +42,45 @@ class ConvertSqlFileToAnnotationAction : PsiElementBaseIntentionAction() { editor: Editor?, element: PsiElement, ): Boolean { - if (!isSupportFileType(element.containingFile)) return false + val file = element.containingFile + if (isJavaOrKotlinFileType(file)) { + return checkOnMethod(element, project) + } + + if (isSupportFileType(file)) { + return checkOnSqlFile(element, project) + } + return false + } + + private fun checkOnMethod( + element: PsiElement, + project: Project, + ): Boolean { + val daoMethod = PsiTreeUtil.getParentOfType(element, PsiMethod::class.java) ?: return false + return checkAvailable(project, daoMethod) + } + + private fun checkOnSqlFile( + element: PsiElement, + project: Project, + ): Boolean { val daoMethod = findDaoMethod(element.containingFile) ?: return false + return checkAvailable(project, daoMethod) + } + + private fun checkAvailable( + project: Project, + daoMethod: PsiMethod, + ): Boolean { val psiDaoMethod = PsiDaoMethod(project, daoMethod) // Check if method doesn't have @Sql annotation - if (psiDaoMethod.useSqlAnnotation()) { + if (psiDaoMethod.sqlFile == null || psiDaoMethod.useSqlAnnotation()) { return false } - // Check if method has @Insert, @Update, or @Delete annotation with sqlFile=true - val supportedTypes = - listOf( - DomaAnnotationType.Select, - DomaAnnotationType.Script, - DomaAnnotationType.SqlProcessor, - DomaAnnotationType.Insert, - DomaAnnotationType.Update, - DomaAnnotationType.Delete, - DomaAnnotationType.BatchInsert, - DomaAnnotationType.BatchUpdate, - DomaAnnotationType.BatchDelete, - ) - val hasAnnotation = supportedTypes.any { type -> val annotation = type.getPsiAnnotation(daoMethod) @@ -82,8 +98,42 @@ class ConvertSqlFileToAnnotationAction : PsiElementBaseIntentionAction() { ) { // Do nothing when previewing if (IntentionPreviewUtils.isIntentionPreviewActive()) return - if (!isSupportFileType(element.containingFile)) return + val file = element.containingFile + if (isJavaOrKotlinFileType(file)) { + return processOnMethod(element, project) + } + + // Process if the file type is SQL + if (isSupportFileType(file)) { + return processOnSqlFile(element, project) + } + } + + private fun processOnMethod( + element: PsiElement, + project: Project, + ) { + val daoMethod = PsiTreeUtil.getParentOfType(element, PsiMethod::class.java) ?: return + + val startTime = System.nanoTime() + val converter = SqlAnnotationConverter(project, daoMethod) + WriteCommandAction.runWriteCommandAction(project) { + converter.convertToSqlAnnotation() + } + + PluginLoggerUtil.countLogging( + className = this::class.java.simpleName, + actionName = "convertSqlFileToAnnotationOnMethod", + inputName = "IntentionAction", + start = startTime, + ) + } + + private fun processOnSqlFile( + element: PsiElement, + project: Project, + ) { val daoMethod = findDaoMethod(element.containingFile) ?: return val startTime = System.nanoTime() @@ -94,7 +144,7 @@ class ConvertSqlFileToAnnotationAction : PsiElementBaseIntentionAction() { PluginLoggerUtil.countLogging( className = this::class.java.simpleName, - actionName = "convertSqlFileToAnnotation", + actionName = "convertSqlFileToAnnotationOnSQL", inputName = "IntentionAction", start = startTime, ) diff --git a/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlIntentionAction.kt b/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlIntentionAction.kt new file mode 100644 index 00000000..6d083cfc --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlIntentionAction.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.action.dao + +import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction +import org.domaframework.doma.intellij.extension.psi.DomaAnnotationType + +abstract class ConvertSqlIntentionAction : PsiElementBaseIntentionAction() { + protected val supportedTypes = + setOf( + DomaAnnotationType.Select, + DomaAnnotationType.Script, + DomaAnnotationType.SqlProcessor, + DomaAnnotationType.Insert, + DomaAnnotationType.Update, + DomaAnnotationType.Delete, + DomaAnnotationType.BatchInsert, + DomaAnnotationType.BatchUpdate, + DomaAnnotationType.BatchDelete, + ) +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 84ee4153..c90dbae8 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -106,6 +106,12 @@ Doma tools true + + JAVA + org.domaframework.doma.intellij.action.dao.ConvertSqlFileToAnnotationAction + Doma tools + true + From ad71f4d12ec49d7f8edfe8e9c6af06787324b8e5 Mon Sep 17 00:00:00 2001 From: xterao Date: Thu, 11 Sep 2025 11:09:12 +0900 Subject: [PATCH 2/5] Do not open the file in the editor after generating the SQL file. --- .../doma/intellij/action/dao/SqlAnnotationConverter.kt | 2 +- .../doma/intellij/common/psi/PsiDaoMethod.kt | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/org/domaframework/doma/intellij/action/dao/SqlAnnotationConverter.kt b/src/main/kotlin/org/domaframework/doma/intellij/action/dao/SqlAnnotationConverter.kt index bbb1069c..60b77f7e 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/action/dao/SqlAnnotationConverter.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/action/dao/SqlAnnotationConverter.kt @@ -243,7 +243,7 @@ class SqlAnnotationConverter( private fun generateSqlFileWithContent(content: String) { // First generate the empty SQL file using existing functionality - psiDaoMethod.generateSqlFile() + psiDaoMethod.generateSqlFile(false) // Then update its content ApplicationManager.getApplication().invokeLater { diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/psi/PsiDaoMethod.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/psi/PsiDaoMethod.kt index 8c2923a3..40b939ac 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/psi/PsiDaoMethod.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/psi/PsiDaoMethod.kt @@ -169,7 +169,7 @@ class PsiDaoMethod( } } - fun generateSqlFile() { + fun generateSqlFile(fileJump: Boolean = true) { ApplicationManager.getApplication().runReadAction { if (sqlFilePath.isEmpty()) return@runReadAction val rootDir = psiProject.getContentRoot(daoFile) ?: return@runReadAction @@ -202,9 +202,11 @@ class PsiDaoMethod( .findDirectory(virtualFile) ?: return@runWriteCommandAction sqlOutputDirPath.findFile(sqlFileName)?.delete() val sqlVirtualFile = sqlOutputDirPath.createFile(sqlFileName).virtualFile ?: return@runWriteCommandAction - FileEditorManager - .getInstance(psiProject) - .openFile(sqlVirtualFile, true) + if (fileJump) { + FileEditorManager + .getInstance(psiProject) + .openFile(sqlVirtualFile, true) + } writeEmptyElementSqlFile(sqlVirtualFile) } } From d4f1c93463a203b25e78e996bd923cbc1fc8e5d9 Mon Sep 17 00:00:00 2001 From: xterao Date: Thu, 11 Sep 2025 14:23:06 +0900 Subject: [PATCH 3/5] Add tests for SQL file conversion actions and refactor existing tests --- .../action/dao/ConvertSqlActionTest.kt | 92 +++++++++++++++++++ .../ConvertSqlAnnotationToFileActionTest.kt | 69 ++++---------- .../ConvertSqlFileToAnnotationActionTest.kt | 17 +++- ...WithSqlFileConvertAnnotationDao.after.java | 16 ++++ ...SelectWithSqlFileConvertAnnotationDao.java | 11 +++ .../selectEmployee.sql | 3 + 6 files changed, 152 insertions(+), 56 deletions(-) create mode 100644 src/test/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlActionTest.kt create mode 100644 src/test/testData/src/main/java/doma/example/dao/sqltoannotation/SelectWithSqlFileConvertAnnotationDao.after.java create mode 100644 src/test/testData/src/main/java/doma/example/dao/sqltoannotation/SelectWithSqlFileConvertAnnotationDao.java create mode 100644 src/test/testData/src/main/resources/META-INF/doma/example/dao/sqltoannotation/SelectWithSqlFileConvertAnnotationDao/selectEmployee.sql diff --git a/src/test/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlActionTest.kt b/src/test/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlActionTest.kt new file mode 100644 index 00000000..133ad96a --- /dev/null +++ b/src/test/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlActionTest.kt @@ -0,0 +1,92 @@ +/* + * 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.action.dao + +import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.fileEditor.FileEditorManager +import com.intellij.psi.PsiDocumentManager +import org.domaframework.doma.intellij.DomaSqlTest + +abstract class ConvertSqlActionTest : DomaSqlTest() { + protected fun doConvertAction( + daoName: String, + convertFamilyName: String, + sqlConversionPackage: String, + convertActionName: String, + ) { + addDaoJavaFile("$sqlConversionPackage/$daoName.java") + + val daoClass = findDaoClass("$sqlConversionPackage.$daoName") + myFixture.configureFromExistingVirtualFile(daoClass.containingFile.virtualFile) + println("convertActionName: $convertActionName") + val intention = myFixture.findSingleIntention(convertActionName) + + assertNotNull( + "$convertActionName intention should be available", + intention, + ) + assertEquals(convertActionName, intention.text) + assertEquals(convertFamilyName, intention.familyName) + + myFixture.launchAction(intention) + myFixture.checkResultByFile("java/doma/example/dao/$sqlConversionPackage/$daoName.after.java") + } + + protected fun doConvertActionTest( + daoName: String, + sqlToAnnotationPackage: String, + convertFamilyName: String, + ) { + addDaoJavaFile("$sqlToAnnotationPackage/$daoName.java") + val daoClass = findDaoClass("$sqlToAnnotationPackage.$daoName") + myFixture.configureFromExistingVirtualFile(daoClass.containingFile.virtualFile) + + val intentions = myFixture.availableIntentions + val convertIntention = intentions.find { it is ConvertSqlFileToAnnotationAction } + + assertNull("$convertFamilyName intention should NOT be available without @Sql annotation", convertIntention) + } + + protected fun doTestSqlFormat( + daoName: String, + sqlFileName: String, + sqlConversionPackage: String, + isScript: Boolean = false, + ) { + val openedEditor = FileEditorManager.getInstance(project).selectedEditors + val extension = if (isScript) "script" else "sql" + val openSqlFile = openedEditor.find { it.file.name == sqlFileName.substringAfter("/").plus(".$extension") } + + if (openSqlFile != null) { + fail("SQL file $sqlFileName.$extension should be opened after conversion") + return + } + // If the generated `PsiFile` has an associated `Document`, explicitly reload it to ensure memory–disk consistency. + // If not reloaded, the test may produce: *Unexpected memory–disk conflict in tests for*. + val fdm = FileDocumentManager.getInstance() + fdm.saveAllDocuments() + PsiDocumentManager.getInstance(project).commitAllDocuments() + + val newSqlFile = findSqlFile("$sqlConversionPackage/$daoName/$sqlFileName.$extension") + if (newSqlFile == null) { + fail("Not Found $sqlFileName.$extension") + return + } + fdm.getDocument(newSqlFile)?.let { fdm.reloadFromDisk(it) } + myFixture.configureFromExistingVirtualFile(newSqlFile) + myFixture.checkResultByFile("resources/META-INF/doma/example/dao/$sqlConversionPackage/$daoName/$sqlFileName.after.$extension") + } +} diff --git a/src/test/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlAnnotationToFileActionTest.kt b/src/test/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlAnnotationToFileActionTest.kt index 51252a06..58cd7d97 100644 --- a/src/test/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlAnnotationToFileActionTest.kt +++ b/src/test/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlAnnotationToFileActionTest.kt @@ -15,12 +15,9 @@ */ package org.domaframework.doma.intellij.action.dao -import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.fileEditor.FileEditorManager -import com.intellij.psi.PsiDocumentManager -import org.domaframework.doma.intellij.DomaSqlTest -class ConvertSqlAnnotationToFileActionTest : DomaSqlTest() { +class ConvertSqlAnnotationToFileActionTest : ConvertSqlActionTest() { private val sqlConversionPackage = "sqltofile" private val convertActionName = "Convert to SQL file (set sqlFile=true)" private val convertFamilyName = "Convert @Sql annotation to SQL file" @@ -112,26 +109,12 @@ class ConvertSqlAnnotationToFileActionTest : DomaSqlTest() { fun testIntentionNotAvailableForMethodWithoutSqlAnnotation() { val daoName = "NoSqlAnnotationDao" - addDaoJavaFile("$sqlConversionPackage/$daoName.java") - val daoClass = findDaoClass("$sqlConversionPackage.$daoName") - myFixture.configureFromExistingVirtualFile(daoClass.containingFile.virtualFile) - - val intentions = myFixture.availableIntentions - val convertIntention = intentions.find { it is ConvertSqlAnnotationToFileAction } - - assertNull("$convertFamilyName intention should NOT be available without @Sql annotation", convertIntention) + doConvertActionTest(daoName, sqlConversionPackage, convertFamilyName) } fun testIntentionNotAvailableForUnsupportedAnnotation() { val daoName = "UnsupportedAnnotationDao" - addDaoJavaFile("$sqlConversionPackage/$daoName.java") - val daoClass = findDaoClass("$sqlConversionPackage.$daoName") - myFixture.configureFromExistingVirtualFile(daoClass.containingFile.virtualFile) - - val intentions = myFixture.availableIntentions - val convertIntention = intentions.find { it is ConvertSqlAnnotationToFileAction } - - assertNull("$convertFamilyName intention should NOT be available without @Sql annotation", convertIntention) + doConvertActionTest(daoName, sqlConversionPackage, convertFamilyName) } private fun doTest( @@ -139,27 +122,18 @@ class ConvertSqlAnnotationToFileActionTest : DomaSqlTest() { sqlFileName: String, isScript: Boolean = false, ) { - addDaoJavaFile("$sqlConversionPackage/$daoName.java") - - val daoClass = findDaoClass("$sqlConversionPackage.$daoName") - myFixture.configureFromExistingVirtualFile(daoClass.containingFile.virtualFile) - val intention = myFixture.findSingleIntention(convertActionName) - - assertNotNull( - "$convertActionName intention should be available", - intention, + doConvertAction( + daoName, + convertFamilyName, + sqlConversionPackage, + convertActionName, ) - assertEquals(convertActionName, intention.text) - assertEquals(convertFamilyName, intention.familyName) - - myFixture.launchAction(intention) - myFixture.checkResultByFile("java/doma/example/dao/$sqlConversionPackage/$daoName.after.java") // Test SQL File Generation val sqlFile = "$sqlFileName.${if (isScript) "script" else "sql"}" val openedEditor = FileEditorManager.getInstance(project).selectedEditors - assertTrue( - "Open File is Not $sqlFileName", + assertFalse( + "Open File is $sqlFileName", openedEditor.any { it.file.name == sqlFile.substringAfter("/") }, ) @@ -172,22 +146,11 @@ class ConvertSqlAnnotationToFileActionTest : DomaSqlTest() { sqlFileName: String, isScript: Boolean = false, ) { - val openedEditor = FileEditorManager.getInstance(project).selectedEditors - val extension = if (isScript) "script" else "sql" - val sqlFile = openedEditor.find { it.file.name == sqlFileName.substringAfter("/").plus(".$extension") } - - if (sqlFile == null) { - fail("SQL file $sqlFileName.$extension should be opened after conversion") - return - } - // If the generated `PsiFile` has an associated `Document`, explicitly reload it to ensure memory–disk consistency. - // If not reloaded, the test may produce: *Unexpected memory–disk conflict in tests for*. - val fdm = FileDocumentManager.getInstance() - fdm.saveAllDocuments() - PsiDocumentManager.getInstance(project).commitAllDocuments() - fdm.getDocument(sqlFile.file)?.let { fdm.reloadFromDisk(it) } - - myFixture.configureFromExistingVirtualFile(sqlFile.file) - myFixture.checkResultByFile("resources/META-INF/doma/example/dao/$sqlConversionPackage/$daoName/$sqlFileName.after.$extension") + doTestSqlFormat( + daoName, + sqlFileName, + sqlConversionPackage, + isScript, + ) } } diff --git a/src/test/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlFileToAnnotationActionTest.kt b/src/test/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlFileToAnnotationActionTest.kt index dda847f4..f48c6c7d 100644 --- a/src/test/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlFileToAnnotationActionTest.kt +++ b/src/test/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlFileToAnnotationActionTest.kt @@ -17,9 +17,8 @@ package org.domaframework.doma.intellij.action.dao import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.psi.PsiDocumentManager -import org.domaframework.doma.intellij.DomaSqlTest -class ConvertSqlFileToAnnotationActionTest : DomaSqlTest() { +class ConvertSqlFileToAnnotationActionTest : ConvertSqlActionTest() { private val sqlToAnnotationPackage = "sqltoannotation" private val convertActionName = "Convert to @Sql annotation (set sqlFile=false)" private val convertFamilyName = "Convert SQL file to @Sql annotation" @@ -87,7 +86,10 @@ class ConvertSqlFileToAnnotationActionTest : DomaSqlTest() { val intentions = myFixture.availableIntentions val convertIntention = intentions.find { it is ConvertSqlFileToAnnotationAction } - assertNull("$convertFamilyName intention should NOT be available when @Sql annotation already exists", convertIntention) + assertNull( + "$convertFamilyName intention should NOT be available when @Sql annotation already exists", + convertIntention, + ) } fun testIntentionNotAvailableForMethodWithoutSqlFile() { @@ -108,6 +110,15 @@ class ConvertSqlFileToAnnotationActionTest : DomaSqlTest() { doTest(daoName, sqlFileName) } + fun testSelectWithSqlFileConvertAnnotation() { + val daoName = "SelectWithSqlFileConvertAnnotationDao" + val sqlFileName = "selectEmployee.sql" + doConvertActionTest(daoName, sqlToAnnotationPackage, convertFamilyName) + // Test SQL File Removed + val generatedSql = findSqlFile("$sqlToAnnotationPackage/$daoName/$sqlFileName") + assertTrue("SQL File [$sqlFileName] should exists ", generatedSql == null) + } + private fun doTest( daoName: String, sqlFileName: String, diff --git a/src/test/testData/src/main/java/doma/example/dao/sqltoannotation/SelectWithSqlFileConvertAnnotationDao.after.java b/src/test/testData/src/main/java/doma/example/dao/sqltoannotation/SelectWithSqlFileConvertAnnotationDao.after.java new file mode 100644 index 00000000..3e20f3d4 --- /dev/null +++ b/src/test/testData/src/main/java/doma/example/dao/sqltoannotation/SelectWithSqlFileConvertAnnotationDao.after.java @@ -0,0 +1,16 @@ +package doma.example.dao.sqltoannotation; + +import doma.example.entity.Employee; +import org.seasar.doma.Dao; +import org.seasar.doma.Select; + +@Dao +public interface SelectWithSqlFileConvertAnnotationDao { + @Select + @Sql(""" + SELECT * + FROM employee + WHERE id = /* id */1 + """) + Employee selectEmployee(int id); +} \ No newline at end of file diff --git a/src/test/testData/src/main/java/doma/example/dao/sqltoannotation/SelectWithSqlFileConvertAnnotationDao.java b/src/test/testData/src/main/java/doma/example/dao/sqltoannotation/SelectWithSqlFileConvertAnnotationDao.java new file mode 100644 index 00000000..2cd43592 --- /dev/null +++ b/src/test/testData/src/main/java/doma/example/dao/sqltoannotation/SelectWithSqlFileConvertAnnotationDao.java @@ -0,0 +1,11 @@ +package doma.example.dao.sqltoannotation; + +import doma.example.entity.Employee; +import org.seasar.doma.Dao; +import org.seasar.doma.Select; + +@Dao +public interface SelectWithSqlFileConvertAnnotationDao { + @Select + Employee selectEmployee(int id); +} \ No newline at end of file diff --git a/src/test/testData/src/main/resources/META-INF/doma/example/dao/sqltoannotation/SelectWithSqlFileConvertAnnotationDao/selectEmployee.sql b/src/test/testData/src/main/resources/META-INF/doma/example/dao/sqltoannotation/SelectWithSqlFileConvertAnnotationDao/selectEmployee.sql new file mode 100644 index 00000000..99ee2141 --- /dev/null +++ b/src/test/testData/src/main/resources/META-INF/doma/example/dao/sqltoannotation/SelectWithSqlFileConvertAnnotationDao/selectEmployee.sql @@ -0,0 +1,3 @@ +SELECT * + FROM employee + WHERE id = /* id */1 From 9c23aa8f80ad954b2d5ccf0fb1d515ec63fc0b30 Mon Sep 17 00:00:00 2001 From: xterao Date: Thu, 11 Sep 2025 15:14:11 +0900 Subject: [PATCH 4/5] Enhance SQL annotation handling by adding DAO class checks in conversion actions --- .../intellij/action/dao/ConvertSqlAnnotationToFileAction.kt | 5 ++++- .../intellij/action/dao/ConvertSqlFileToAnnotationAction.kt | 5 +++-- .../org/domaframework/doma/intellij/common/dao/DaoClass.kt | 3 ++- .../domaframework/doma/intellij/common/util/DomaClassName.kt | 2 ++ 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlAnnotationToFileAction.kt b/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlAnnotationToFileAction.kt index ee76869a..f050649a 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlAnnotationToFileAction.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlAnnotationToFileAction.kt @@ -25,6 +25,7 @@ import com.intellij.psi.PsiFile import com.intellij.psi.PsiMethod import com.intellij.psi.util.PsiTreeUtil import org.domaframework.doma.intellij.bundle.MessageBundle +import org.domaframework.doma.intellij.common.dao.getDaoClass import org.domaframework.doma.intellij.common.psi.PsiDaoMethod import org.domaframework.doma.intellij.common.util.PluginLoggerUtil @@ -47,7 +48,9 @@ class ConvertSqlAnnotationToFileAction : ConvertSqlIntentionAction() { // Check if method has @Sql annotation // When a Sql annotation is present, a virtual SQL file is associated; // therefore, check the parent and exclude the injected (inline) SQL. - if (!psiDaoMethod.useSqlAnnotation() || psiDaoMethod.sqlFile != null && psiDaoMethod.sqlFile?.parent != null) { + if (getDaoClass(method.containingFile) == null || !psiDaoMethod.useSqlAnnotation() || + psiDaoMethod.sqlFile != null && psiDaoMethod.sqlFile?.parent != null + ) { return false } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlFileToAnnotationAction.kt b/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlFileToAnnotationAction.kt index 1dc58083..e64c4d27 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlFileToAnnotationAction.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlFileToAnnotationAction.kt @@ -24,6 +24,7 @@ import com.intellij.psi.PsiMethod import com.intellij.psi.util.PsiTreeUtil import org.domaframework.doma.intellij.bundle.MessageBundle import org.domaframework.doma.intellij.common.dao.findDaoMethod +import org.domaframework.doma.intellij.common.dao.getDaoClass import org.domaframework.doma.intellij.common.isJavaOrKotlinFileType import org.domaframework.doma.intellij.common.isSupportFileType import org.domaframework.doma.intellij.common.psi.PsiDaoMethod @@ -42,8 +43,8 @@ class ConvertSqlFileToAnnotationAction : ConvertSqlIntentionAction() { editor: Editor?, element: PsiElement, ): Boolean { - val file = element.containingFile - if (isJavaOrKotlinFileType(file)) { + val file = element.containingFile ?: return false + if (isJavaOrKotlinFileType(file) && getDaoClass(file) != null) { return checkOnMethod(element, project) } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/dao/DaoClass.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/dao/DaoClass.kt index 4451fa22..fa0fa7cd 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/dao/DaoClass.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/dao/DaoClass.kt @@ -18,8 +18,9 @@ package org.domaframework.doma.intellij.common.dao import com.intellij.psi.PsiClass import com.intellij.psi.PsiFile import com.intellij.psi.util.PsiTreeUtil +import org.domaframework.doma.intellij.common.util.DomaClassName fun getDaoClass(file: PsiFile): PsiClass? = PsiTreeUtil .findChildrenOfType(file, PsiClass::class.java) - .firstOrNull { it.hasAnnotation("org.seasar.doma.Dao") } + .firstOrNull { it.hasAnnotation(DomaClassName.DAO.className) } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/util/DomaClassName.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/util/DomaClassName.kt index 0d5042ed..ee725e6e 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/util/DomaClassName.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/util/DomaClassName.kt @@ -18,6 +18,8 @@ package org.domaframework.doma.intellij.common.util enum class DomaClassName( val className: String, ) { + DAO("org.seasar.doma.Dao"), + OPTIONAL("java.util.Optional"), OPTIONAL_INT("java.util.OptionalInt"), OPTIONAL_DOUBLE("java.util.OptionalDouble"), From f4eb99f4983031b69ba14c8cbba1aebe28ec1a73 Mon Sep 17 00:00:00 2001 From: xterao Date: Thu, 11 Sep 2025 15:20:12 +0900 Subject: [PATCH 5/5] Refactor SQL annotation conversion actions to use a common supported types definition --- .../dao/ConvertSqlAnnotationToFileAction.kt | 5 +-- .../dao/ConvertSqlFileToAnnotationAction.kt | 5 +-- .../action/dao/ConvertSqlIntentionAction.kt | 34 ------------------- .../action/dao/SqlAnnotationConverter.kt | 28 ++++++++------- 4 files changed, 21 insertions(+), 51 deletions(-) delete mode 100644 src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlIntentionAction.kt diff --git a/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlAnnotationToFileAction.kt b/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlAnnotationToFileAction.kt index f050649a..6d3ddfa6 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlAnnotationToFileAction.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlAnnotationToFileAction.kt @@ -15,6 +15,7 @@ */ package org.domaframework.doma.intellij.action.dao +import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo import com.intellij.codeInsight.intention.preview.IntentionPreviewUtils import com.intellij.openapi.command.WriteCommandAction @@ -32,7 +33,7 @@ import org.domaframework.doma.intellij.common.util.PluginLoggerUtil /** * Intention action to convert @Sql annotation to SQL file */ -class ConvertSqlAnnotationToFileAction : ConvertSqlIntentionAction() { +class ConvertSqlAnnotationToFileAction : PsiElementBaseIntentionAction() { override fun getFamilyName(): String = MessageBundle.message("convert.sql.annotation.to.file.family") override fun getText(): String = MessageBundle.message("convert.sql.annotation.to.file.text") @@ -54,7 +55,7 @@ class ConvertSqlAnnotationToFileAction : ConvertSqlIntentionAction() { return false } - return supportedTypes.any { it.getPsiAnnotation(method) != null } + return SqlAnnotationConverter.supportedTypes.any { it.getPsiAnnotation(method) != null } } override fun generatePreview( diff --git a/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlFileToAnnotationAction.kt b/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlFileToAnnotationAction.kt index e64c4d27..a3589d5e 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlFileToAnnotationAction.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlFileToAnnotationAction.kt @@ -15,6 +15,7 @@ */ package org.domaframework.doma.intellij.action.dao +import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction import com.intellij.codeInsight.intention.preview.IntentionPreviewUtils import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.editor.Editor @@ -33,7 +34,7 @@ import org.domaframework.doma.intellij.common.util.PluginLoggerUtil /** * Intention action to convert SQL file to @Sql annotation */ -class ConvertSqlFileToAnnotationAction : ConvertSqlIntentionAction() { +class ConvertSqlFileToAnnotationAction : PsiElementBaseIntentionAction() { override fun getFamilyName(): String = MessageBundle.message("convert.sql.file.to.annotation.family") override fun getText(): String = MessageBundle.message("convert.sql.file.to.annotation.text") @@ -83,7 +84,7 @@ class ConvertSqlFileToAnnotationAction : ConvertSqlIntentionAction() { } val hasAnnotation = - supportedTypes.any { type -> + SqlAnnotationConverter.supportedTypes.any { type -> val annotation = type.getPsiAnnotation(daoMethod) annotation != null } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlIntentionAction.kt b/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlIntentionAction.kt deleted file mode 100644 index 6d083cfc..00000000 --- a/src/main/kotlin/org/domaframework/doma/intellij/action/dao/ConvertSqlIntentionAction.kt +++ /dev/null @@ -1,34 +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.action.dao - -import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction -import org.domaframework.doma.intellij.extension.psi.DomaAnnotationType - -abstract class ConvertSqlIntentionAction : PsiElementBaseIntentionAction() { - protected val supportedTypes = - setOf( - DomaAnnotationType.Select, - DomaAnnotationType.Script, - DomaAnnotationType.SqlProcessor, - DomaAnnotationType.Insert, - DomaAnnotationType.Update, - DomaAnnotationType.Delete, - DomaAnnotationType.BatchInsert, - DomaAnnotationType.BatchUpdate, - DomaAnnotationType.BatchDelete, - ) -} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/action/dao/SqlAnnotationConverter.kt b/src/main/kotlin/org/domaframework/doma/intellij/action/dao/SqlAnnotationConverter.kt index 60b77f7e..70b5940d 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/action/dao/SqlAnnotationConverter.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/action/dao/SqlAnnotationConverter.kt @@ -48,6 +48,21 @@ class SqlAnnotationConverter( private val psiDaoMethod = PsiDaoMethod(project, method) private val elementFactory = JavaPsiFacade.getElementFactory(project) + companion object { + val supportedTypes = + setOf( + DomaAnnotationType.Select, + DomaAnnotationType.Script, + DomaAnnotationType.SqlProcessor, + DomaAnnotationType.Insert, + DomaAnnotationType.Update, + DomaAnnotationType.Delete, + DomaAnnotationType.BatchInsert, + DomaAnnotationType.BatchUpdate, + DomaAnnotationType.BatchDelete, + ) + } + /** * Convert @Sql annotation to SQL file */ @@ -95,19 +110,6 @@ class SqlAnnotationConverter( } private fun findTargetAnnotation(): PsiAnnotation? { - val supportedTypes = - listOf( - DomaAnnotationType.Select, - DomaAnnotationType.Script, - DomaAnnotationType.SqlProcessor, - DomaAnnotationType.Insert, - DomaAnnotationType.Update, - DomaAnnotationType.Delete, - DomaAnnotationType.BatchInsert, - DomaAnnotationType.BatchUpdate, - DomaAnnotationType.BatchDelete, - ) - for (type in supportedTypes) { val annotation = type.getPsiAnnotation(method) if (annotation != null) {