Skip to content

Commit

Permalink
Create view intention and live template
Browse files Browse the repository at this point in the history
  • Loading branch information
aperfilyev committed Apr 18, 2023
1 parent d19e01c commit 1857441
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 0 deletions.
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();
}
}
4 changes: 4 additions & 0 deletions sqldelight-idea-plugin/src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@
<className>app.cash.sqldelight.intellij.intentions.QualifyColumnNameIntention</className>
<category>SQLDelight</category>
</intentionAction>
<intentionAction>
<className>app.cash.sqldelight.intellij.intentions.CreateViewIntention</className>
<category>SQLDelight</category>
</intentionAction>

<copyPastePostProcessor
implementation="app.cash.sqldelight.intellij.SqlDelightCopyPasteProcessor"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<html>
<body>
Creates a VIEW from SELECT statement
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,12 @@
<option name="SQLDELIGHT" value="true" />
</context>
</template>
<template name="view" value="CREATE VIEW $view$ AS SELECT * FROM $table$$END$;"
description="new view definition" toReformat="false" toShortenFQNames="true">
<variable name="view" expression="" defaultValue="" alwaysStopAt="true"/>
<variable name="table" expression="complete()" defaultValue="" alwaysStopAt="true"/>
<context>
<option name="SQLDELIGHT" value="true"/>
</context>
</template>
</templateSet>
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()
}
}

0 comments on commit 1857441

Please sign in to comment.