-
Notifications
You must be signed in to change notification settings - Fork 515
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create view intention and live template
- Loading branch information
1 parent
d19e01c
commit 1857441
Showing
5 changed files
with
191 additions
and
0 deletions.
There are no files selected for viewing
74 changes: 74 additions & 0 deletions
74
...dea-plugin/src/main/kotlin/app/cash/sqldelight/intellij/intentions/CreateViewIntention.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package app.cash.sqldelight.intellij.intentions | ||
|
||
import com.alecstrong.sql.psi.core.psi.SqlCompoundSelectStmt | ||
import com.alecstrong.sql.psi.core.psi.SqlCreateViewStmt | ||
import com.alecstrong.sql.psi.core.psi.SqlStmtList | ||
import com.alecstrong.sql.psi.core.psi.SqlTypes | ||
import com.intellij.codeInsight.intention.BaseElementAtCaretIntentionAction | ||
import com.intellij.codeInsight.template.TemplateManager | ||
import com.intellij.codeInsight.template.impl.TextExpression | ||
import com.intellij.openapi.command.WriteCommandAction | ||
import com.intellij.openapi.editor.Document | ||
import com.intellij.openapi.editor.Editor | ||
import com.intellij.openapi.project.Project | ||
import com.intellij.psi.PsiDocumentManager | ||
import com.intellij.psi.PsiElement | ||
import com.intellij.psi.util.PsiTreeUtil | ||
import com.intellij.psi.util.parentOfType | ||
import org.jetbrains.kotlin.psi.psiUtil.endOffset | ||
import org.jetbrains.kotlin.psi.psiUtil.startOffset | ||
|
||
internal class CreateViewIntention : BaseElementAtCaretIntentionAction() { | ||
|
||
override fun getFamilyName(): String { | ||
return INTENTIONS_FAMILY_NAME_REFACTORINGS | ||
} | ||
|
||
override fun getText(): String { | ||
return "Create view" | ||
} | ||
|
||
override fun isAvailable(project: Project, editor: Editor?, element: PsiElement): Boolean { | ||
val selectStmt = element.parentOfType<SqlCompoundSelectStmt>(true) | ||
return selectStmt != null && selectStmt.parentOfType<SqlCreateViewStmt>() == null | ||
} | ||
|
||
override fun invoke(project: Project, editor: Editor, element: PsiElement) { | ||
PsiDocumentManager.getInstance(project).commitAllDocuments() | ||
|
||
val selectStmt = element.parentOfType<SqlCompoundSelectStmt>(true) ?: return | ||
val stmtList = selectStmt.parentOfType<SqlStmtList>() ?: return | ||
val container = stmtList.stmtList.firstOrNull { PsiTreeUtil.isAncestor(it, selectStmt, true) } ?: return | ||
|
||
val semi = PsiTreeUtil.findSiblingForward(container, SqlTypes.SEMI, false, null) ?: return | ||
|
||
val containerStart = container.startOffset | ||
val containerEnd = semi.endOffset | ||
val text = editor.document.getDocumentTextFragment(containerStart, containerEnd) | ||
val offset = selectStmt.startOffset - containerStart | ||
|
||
val templateManager = TemplateManager.getInstance(project) | ||
|
||
WriteCommandAction.runWriteCommandAction(project) { | ||
editor.document.deleteString(containerStart, containerEnd) | ||
|
||
val template = templateManager.createTemplate("", "") | ||
template.addTextSegment(text.substring(0, offset)) | ||
template.addTextSegment("SELECT * FROM ") | ||
val expression = TextExpression("some_view") | ||
template.addVariableSegment("NAME") | ||
template.addSelectionStartVariable() | ||
template.addTextSegment(text.substring(selectStmt.endOffset - containerStart)) | ||
|
||
template.addTextSegment("\n\nCREATE VIEW ") | ||
template.addVariable("NAME", expression, true) | ||
template.addTextSegment(" AS ${selectStmt.text};") | ||
|
||
templateManager.startTemplate(editor, template) | ||
} | ||
} | ||
|
||
private fun Document.getDocumentTextFragment(startOffset: Int, endOffset: Int): String { | ||
return charsSequence.subSequence(startOffset, endOffset).toString(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
...idea-plugin/src/main/resources/intentionDescriptions/CreateViewIntention/description.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<html> | ||
<body> | ||
Creates a VIEW from SELECT statement | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
100 changes: 100 additions & 0 deletions
100
...plugin/src/test/kotlin/app/cash/sqldelight/intellij/intentions/CreateViewIntentionTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package app.cash.sqldelight.intellij.intentions | ||
|
||
import app.cash.sqldelight.core.lang.SqlDelightFileType | ||
import app.cash.sqldelight.intellij.SqlDelightFixtureTestCase | ||
import com.google.common.truth.Truth | ||
import com.google.common.truth.Truth.assertThat | ||
import junit.framework.TestCase | ||
|
||
class CreateViewIntentionTest : SqlDelightFixtureTestCase() { | ||
|
||
fun testIntentionAvailableOnSelectStmt() { | ||
myFixture.configureByText( | ||
SqlDelightFileType, | ||
CREATE_TABLE + """ | ||
|SELECT column_1 | ||
|FROM ta<caret>ble_1; | ||
""".trimMargin(), | ||
) | ||
|
||
val intention = CreateViewIntention() | ||
|
||
assertThat( | ||
intention.isAvailable( | ||
myFixture.project, | ||
myFixture.editor, | ||
myFixture.file.findElementAt(myFixture.editor.caretModel.offset)!!, | ||
), | ||
) | ||
.isTrue() | ||
} | ||
|
||
fun testIntentionNotAvailableInsideCreateView() { | ||
myFixture.configureByText( | ||
SqlDelightFileType, | ||
CREATE_TABLE + """ | ||
|SELECT * FROM some_view; | ||
| | ||
|CREATE VIEW some_view AS SE<caret>LECT * FROM table_1; | ||
""".trimMargin(), | ||
) | ||
|
||
val intention = CreateViewIntention() | ||
|
||
assertThat( | ||
intention.isAvailable( | ||
myFixture.project, | ||
myFixture.editor, | ||
myFixture.file.findElementAt(myFixture.editor.caretModel.offset)!!, | ||
), | ||
) | ||
.isFalse() | ||
} | ||
|
||
fun testIntentionExecution() { | ||
myFixture.configureByText( | ||
SqlDelightFileType, | ||
CREATE_TABLE + """ | ||
|SELECT column_1 | ||
|FROM table_1 | ||
|WHERE column_1 = ( | ||
| SELECT co<caret>lumn_2 | ||
| FROM table_2 | ||
|); | ||
""".trimMargin(), | ||
) | ||
|
||
val intention = CreateViewIntention() | ||
intention.invoke( | ||
myFixture.project, | ||
myFixture.editor, | ||
myFixture.file.findElementAt(myFixture.editor.caretModel.offset)!!, | ||
) | ||
|
||
myFixture.checkResult( | ||
CREATE_TABLE + """ | ||
|SELECT column_1 | ||
|FROM table_1 | ||
|WHERE column_1 = ( | ||
| SELECT * FROM some_view | ||
|); | ||
| | ||
|CREATE VIEW some_view AS SELECT column_2 | ||
| FROM table_2; | ||
""".trimMargin(), | ||
) | ||
} | ||
|
||
companion object { | ||
val CREATE_TABLE = """ | ||
|CREATE TABLE table_1 ( | ||
| column_1 INTEGER NOT NULL | ||
|); | ||
| | ||
|CREATE TABLE table_2 ( | ||
| column_2 INTEGER NOT NULL | ||
|); | ||
| | ||
""".trimMargin() | ||
} | ||
} |