Skip to content

Commit c290770

Browse files
authored
Merge pull request #383 from domaframework/feature/sql-generate-by-annotation
Add bidirectional conversion between SQL files and @SQL annotations
2 parents f288f30 + ae2f09f commit c290770

File tree

90 files changed

+2148
-182
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+2148
-182
lines changed

CLAUDE.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,28 @@ Action functionality for navigating between DAO files and SQL files
117117
Class names should have `Action` as a suffix.
118118
Classes should not have properties; necessary information should be obtained within functions.
119119

120+
#### Intention Actions for SQL/Annotation Conversion
121+
122+
**ConvertSqlFileToAnnotationAction**: Converts SQL file content to @Sql annotation
123+
- Extends `PsiElementBaseIntentionAction` for context-sensitive availability
124+
- Available when:
125+
- Current file is a SQL file with corresponding DAO method
126+
- DAO method doesn't have @Sql annotation
127+
- DAO method has supported annotation (@Select, @Insert, @Update, @Delete, etc.) with sqlFile=true
128+
- Uses `SqlAnnotationConverter` to perform the conversion
129+
- Invoked via Alt+Enter on SQL file
130+
131+
**ConvertSqlAnnotationToFileAction**: Converts @Sql annotation to SQL file
132+
- Extends `PsiElementBaseIntentionAction` for context-sensitive availability
133+
- Available when:
134+
- Cursor is on a DAO method with @Sql annotation
135+
- Method has supported annotation (@Select, @Insert, @Update, @Delete, etc.)
136+
- Uses `SqlAnnotationConverter` to perform the conversion
137+
- Creates SQL file in appropriate directory structure (META-INF/...)
138+
- Invoked via Alt+Enter on DAO method with @Sql annotation
139+
140+
Both actions use `WriteCommandAction` to ensure changes are undoable and properly tracked by IntelliJ's local history.
141+
120142
### LineMarker
121143
Line marker functionality for DAO methods and DOMA directives in SQL
122144

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@ such as generating SQL template files, navigating between files, and inspecting
1515

1616
# Features
1717

18+
## Intention Action
19+
20+
Provides Intention Action features for automatically generating SQL templates between SQL annotations and SQL files bidirectionally.
21+
22+
- **Generate corresponding SQL file from SQL annotation on DAO method**
23+
- When a DAO method has an `@Sql` annotation but the corresponding SQL template file does not exist, you can automatically generate it from the intention action (Alt+Enter).
24+
- **Generate corresponding SQL annotation from SQL file**
25+
- When a SQL file exists but the corresponding DAO method doesn't have an `@Sql` annotation, you can automatically add the annotation from the intention action (Alt+Enter) on the SQL file.
26+
27+
![IntentionAction](images/gif/IntentionAction.gif)
28+
29+
This streamlines the association between DAOs and SQL files and makes template creation more efficient.
30+
1831
## Actions
1932
The plugin adds several actions and gutter icons.
2033
Shortcut keys can be used for these actions.

images/gif/IntentionAction.gif

419 KB
Loading
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright Doma Tools Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.domaframework.doma.intellij.action.dao
17+
18+
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction
19+
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo
20+
import com.intellij.codeInsight.intention.preview.IntentionPreviewUtils
21+
import com.intellij.openapi.command.WriteCommandAction
22+
import com.intellij.openapi.editor.Editor
23+
import com.intellij.openapi.project.Project
24+
import com.intellij.psi.PsiElement
25+
import com.intellij.psi.PsiFile
26+
import com.intellij.psi.PsiMethod
27+
import com.intellij.psi.util.PsiTreeUtil
28+
import org.domaframework.doma.intellij.bundle.MessageBundle
29+
import org.domaframework.doma.intellij.common.psi.PsiDaoMethod
30+
import org.domaframework.doma.intellij.common.util.PluginLoggerUtil
31+
import org.domaframework.doma.intellij.extension.psi.DomaAnnotationType
32+
33+
/**
34+
* Intention action to convert @Sql annotation to SQL file
35+
*/
36+
class ConvertSqlAnnotationToFileAction : PsiElementBaseIntentionAction() {
37+
override fun getFamilyName(): String = MessageBundle.message("convert.sql.annotation.to.file.family")
38+
39+
override fun getText(): String = MessageBundle.message("convert.sql.annotation.to.file.text")
40+
41+
override fun isAvailable(
42+
project: Project,
43+
editor: Editor?,
44+
element: PsiElement,
45+
): Boolean {
46+
val method = PsiTreeUtil.getParentOfType(element, PsiMethod::class.java) ?: return false
47+
val psiDaoMethod = PsiDaoMethod(project, method)
48+
49+
// Check if method has @Sql annotation
50+
if (!psiDaoMethod.useSqlAnnotation()) {
51+
return false
52+
}
53+
54+
// Check if method has @Insert, @Update, or @Delete annotation
55+
val supportedTypes =
56+
listOf(
57+
DomaAnnotationType.Select,
58+
DomaAnnotationType.Script,
59+
DomaAnnotationType.SqlProcessor,
60+
DomaAnnotationType.Insert,
61+
DomaAnnotationType.Update,
62+
DomaAnnotationType.Delete,
63+
DomaAnnotationType.BatchInsert,
64+
DomaAnnotationType.BatchUpdate,
65+
DomaAnnotationType.BatchDelete,
66+
)
67+
68+
return supportedTypes.any { it.getPsiAnnotation(method) != null }
69+
}
70+
71+
override fun generatePreview(
72+
project: Project,
73+
editor: Editor,
74+
file: PsiFile,
75+
): IntentionPreviewInfo = IntentionPreviewInfo.EMPTY
76+
77+
override fun invoke(
78+
project: Project,
79+
editor: Editor?,
80+
element: PsiElement,
81+
) {
82+
// Do nothing when previewing
83+
if (IntentionPreviewUtils.isIntentionPreviewActive()) return
84+
val method = PsiTreeUtil.getParentOfType(element, PsiMethod::class.java) ?: return
85+
86+
val startTime = System.nanoTime()
87+
val converter = SqlAnnotationConverter(project, method)
88+
WriteCommandAction.runWriteCommandAction(project) {
89+
converter.convertToSqlFile()
90+
}
91+
92+
PluginLoggerUtil.countLogging(
93+
className = this::class.java.simpleName,
94+
actionName = "convertSqlAnnotationToFile",
95+
inputName = "IntentionAction",
96+
start = startTime,
97+
)
98+
}
99+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright Doma Tools Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.domaframework.doma.intellij.action.dao
17+
18+
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction
19+
import com.intellij.codeInsight.intention.preview.IntentionPreviewUtils
20+
import com.intellij.openapi.command.WriteCommandAction
21+
import com.intellij.openapi.editor.Editor
22+
import com.intellij.openapi.project.Project
23+
import com.intellij.psi.PsiElement
24+
import org.domaframework.doma.intellij.bundle.MessageBundle
25+
import org.domaframework.doma.intellij.common.dao.findDaoMethod
26+
import org.domaframework.doma.intellij.common.isSupportFileType
27+
import org.domaframework.doma.intellij.common.psi.PsiDaoMethod
28+
import org.domaframework.doma.intellij.common.util.PluginLoggerUtil
29+
import org.domaframework.doma.intellij.extension.psi.DomaAnnotationType
30+
31+
/**
32+
* Intention action to convert SQL file to @Sql annotation
33+
*/
34+
class ConvertSqlFileToAnnotationAction : PsiElementBaseIntentionAction() {
35+
override fun getFamilyName(): String = MessageBundle.message("convert.sql.file.to.annotation.family")
36+
37+
override fun getText(): String = MessageBundle.message("convert.sql.file.to.annotation.text")
38+
39+
override fun isAvailable(
40+
project: Project,
41+
editor: Editor?,
42+
element: PsiElement,
43+
): Boolean {
44+
if (!isSupportFileType(element.containingFile)) return false
45+
46+
val daoMethod = findDaoMethod(element.containingFile) ?: return false
47+
val psiDaoMethod = PsiDaoMethod(project, daoMethod)
48+
49+
// Check if method doesn't have @Sql annotation
50+
if (psiDaoMethod.useSqlAnnotation()) {
51+
return false
52+
}
53+
54+
// Check if method has @Insert, @Update, or @Delete annotation with sqlFile=true
55+
val supportedTypes =
56+
listOf(
57+
DomaAnnotationType.Select,
58+
DomaAnnotationType.Script,
59+
DomaAnnotationType.SqlProcessor,
60+
DomaAnnotationType.Insert,
61+
DomaAnnotationType.Update,
62+
DomaAnnotationType.Delete,
63+
DomaAnnotationType.BatchInsert,
64+
DomaAnnotationType.BatchUpdate,
65+
DomaAnnotationType.BatchDelete,
66+
)
67+
68+
val hasAnnotation =
69+
supportedTypes.any { type ->
70+
val annotation = type.getPsiAnnotation(daoMethod)
71+
annotation != null
72+
}
73+
74+
// Must have annotation with sqlFile=true and SQL file must exist
75+
return hasAnnotation && psiDaoMethod.sqlFile != null
76+
}
77+
78+
override fun invoke(
79+
project: Project,
80+
editor: Editor?,
81+
element: PsiElement,
82+
) {
83+
// Do nothing when previewing
84+
if (IntentionPreviewUtils.isIntentionPreviewActive()) return
85+
if (!isSupportFileType(element.containingFile)) return
86+
87+
val daoMethod = findDaoMethod(element.containingFile) ?: return
88+
89+
val startTime = System.nanoTime()
90+
val converter = SqlAnnotationConverter(project, daoMethod)
91+
WriteCommandAction.runWriteCommandAction(project) {
92+
converter.convertToSqlAnnotation()
93+
}
94+
95+
PluginLoggerUtil.countLogging(
96+
className = this::class.java.simpleName,
97+
actionName = "convertSqlFileToAnnotation",
98+
inputName = "IntentionAction",
99+
start = startTime,
100+
)
101+
}
102+
}

0 commit comments

Comments
 (0)