Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
2185adb
Implemented a dedicated class for explicit format processing, allowin…
xterao Aug 12, 2025
7e7d594
Implemented an action to generate SQL templates mutually between SQL …
xterao Aug 12, 2025
2b4dc3a
Add support for additional Doma annotation types in SQL conversion ac…
xterao Aug 12, 2025
82f9b23
Normalize category naming for Doma tools in plugin.xml
xterao Aug 12, 2025
6015d07
Add license header to InjectionSqlFormatter.kt
xterao Aug 12, 2025
7a6ab1e
Added test cases for Intention action (Annotation ⇒ SQL file).
xterao Aug 12, 2025
1fa7803
Fix to pass the actual SQL file so that the formatting PostProcessor …
xterao Aug 14, 2025
fbb021b
Modify action behavior to delete and regenerate the SQL file if it al…
xterao Aug 14, 2025
0df2762
Add a test to verify the formatting state of SQL files generated by t…
xterao Aug 14, 2025
4854769
Removed parentheses from annotations that no longer have options.
xterao Aug 14, 2025
6215ac8
Fixed indentation generation to ensure injected SQL starts immediatel…
xterao Aug 14, 2025
5db87a5
Implemented test cases for the action that converts from SQL files to…
xterao Aug 14, 2025
8a93d2d
Do not specify an invalid requester object.
xterao Aug 14, 2025
41dbfa2
Modify the condition to allow jumping to the target DAO method after …
xterao Aug 14, 2025
e423ad6
Add document for intention actions for bidirectional SQL/annotation c…
xterao Aug 14, 2025
ae2f09f
Add logging for SQL to annotation conversion actions
xterao Aug 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,28 @@ Action functionality for navigating between DAO files and SQL files
Class names should have `Action` as a suffix.
Classes should not have properties; necessary information should be obtained within functions.

#### Intention Actions for SQL/Annotation Conversion

**ConvertSqlFileToAnnotationAction**: Converts SQL file content to @Sql annotation
- Extends `PsiElementBaseIntentionAction` for context-sensitive availability
- Available when:
- Current file is a SQL file with corresponding DAO method
- DAO method doesn't have @Sql annotation
- DAO method has supported annotation (@Select, @Insert, @Update, @Delete, etc.) with sqlFile=true
- Uses `SqlAnnotationConverter` to perform the conversion
- Invoked via Alt+Enter on SQL file

**ConvertSqlAnnotationToFileAction**: Converts @Sql annotation to SQL file
- Extends `PsiElementBaseIntentionAction` for context-sensitive availability
- Available when:
- Cursor is on a DAO method with @Sql annotation
- Method has supported annotation (@Select, @Insert, @Update, @Delete, etc.)
- Uses `SqlAnnotationConverter` to perform the conversion
- Creates SQL file in appropriate directory structure (META-INF/...)
- Invoked via Alt+Enter on DAO method with @Sql annotation

Both actions use `WriteCommandAction` to ensure changes are undoable and properly tracked by IntelliJ's local history.

### LineMarker
Line marker functionality for DAO methods and DOMA directives in SQL

Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ such as generating SQL template files, navigating between files, and inspecting

# Features

## Intention Action

Provides Intention Action features for automatically generating SQL templates between SQL annotations and SQL files bidirectionally.

- **Generate corresponding SQL file from SQL annotation on DAO method**
- 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).
- **Generate corresponding SQL annotation from SQL file**
- 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.

![IntentionAction](images/gif/IntentionAction.gif)

This streamlines the association between DAOs and SQL files and makes template creation more efficient.

## Actions
The plugin adds several actions and gutter icons.
Shortcut keys can be used for these actions.
Expand Down
Binary file added images/gif/IntentionAction.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* 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 com.intellij.codeInsight.intention.preview.IntentionPreviewInfo
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.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.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() {
override fun getFamilyName(): String = MessageBundle.message("convert.sql.annotation.to.file.family")

override fun getText(): String = MessageBundle.message("convert.sql.annotation.to.file.text")

override fun isAvailable(
project: Project,
editor: Editor?,
element: PsiElement,
): Boolean {
val method = PsiTreeUtil.getParentOfType(element, PsiMethod::class.java) ?: return false
val psiDaoMethod = PsiDaoMethod(project, method)

// Check if method has @Sql annotation
if (!psiDaoMethod.useSqlAnnotation()) {
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 }
}

override fun generatePreview(
project: Project,
editor: Editor,
file: PsiFile,
): IntentionPreviewInfo = IntentionPreviewInfo.EMPTY

override fun invoke(
project: Project,
editor: Editor?,
element: PsiElement,
) {
// Do nothing when previewing
if (IntentionPreviewUtils.isIntentionPreviewActive()) return
val method = PsiTreeUtil.getParentOfType(element, PsiMethod::class.java) ?: return

val startTime = System.nanoTime()
val converter = SqlAnnotationConverter(project, method)
WriteCommandAction.runWriteCommandAction(project) {
converter.convertToSqlFile()
}

PluginLoggerUtil.countLogging(
className = this::class.java.simpleName,
actionName = "convertSqlAnnotationToFile",
inputName = "IntentionAction",
start = startTime,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* 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 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 org.domaframework.doma.intellij.bundle.MessageBundle
import org.domaframework.doma.intellij.common.dao.findDaoMethod
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() {
override fun getFamilyName(): String = MessageBundle.message("convert.sql.file.to.annotation.family")

override fun getText(): String = MessageBundle.message("convert.sql.file.to.annotation.text")

override fun isAvailable(
project: Project,
editor: Editor?,
element: PsiElement,
): Boolean {
if (!isSupportFileType(element.containingFile)) return false

val daoMethod = findDaoMethod(element.containingFile) ?: return false
val psiDaoMethod = PsiDaoMethod(project, daoMethod)

// Check if method doesn't have @Sql annotation
if (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)
annotation != null
}

// Must have annotation with sqlFile=true and SQL file must exist
return hasAnnotation && psiDaoMethod.sqlFile != null
}

override fun invoke(
project: Project,
editor: Editor?,
element: PsiElement,
) {
// Do nothing when previewing
if (IntentionPreviewUtils.isIntentionPreviewActive()) return
if (!isSupportFileType(element.containingFile)) return

val daoMethod = findDaoMethod(element.containingFile) ?: return

val startTime = System.nanoTime()
val converter = SqlAnnotationConverter(project, daoMethod)
WriteCommandAction.runWriteCommandAction(project) {
converter.convertToSqlAnnotation()
}

PluginLoggerUtil.countLogging(
className = this::class.java.simpleName,
actionName = "convertSqlFileToAnnotation",
inputName = "IntentionAction",
start = startTime,
)
}
}
Loading
Loading