Skip to content

Commit

Permalink
Add import quick fix for unknown type (cashapp#2260)
Browse files Browse the repository at this point in the history
  • Loading branch information
aperfilyev committed Apr 9, 2021
1 parent 12aae2d commit 2fd5fc3
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 3 deletions.
1 change: 0 additions & 1 deletion sqldelight-idea-plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ intellij {

runPluginVerifier {
ideVersions = [
"IC-2019.3.5",
"IC-2020.1.4",
"IC-2020.2.4",
"IC-2020.3.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,32 @@
*/
package com.squareup.sqldelight.intellij

import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.lang.annotation.Annotator
import com.intellij.lang.annotation.HighlightSeverity
import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.PsiShortNamesCache
import com.squareup.sqldelight.core.lang.psi.JavaTypeMixin
import com.squareup.sqldelight.intellij.intentions.AddImportIntention

class SqlDelightClassNameElementAnnotator : Annotator {
override fun annotate(element: PsiElement, holder: AnnotationHolder) {
if (element !is JavaTypeMixin || element.reference.resolve() != null) return
holder.createErrorAnnotation(element, "Unresolved reference: ${element.text}")
if (element !is JavaTypeMixin || element.reference.resolve() != null) {
return
}

holder.newAnnotation(HighlightSeverity.ERROR, "Unresolved reference: ${element.text}")
.range(element)
.highlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL)
.apply {
val classes = PsiShortNamesCache.getInstance(element.project)
.getClassesByName(element.text, GlobalSearchScope.allScope(element.project))
if (classes.isNotEmpty()) {
withFix(AddImportIntention(element.text))
}
}
.create()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package com.squareup.sqldelight.intellij.intentions

import com.intellij.codeInsight.daemon.QuickFixBundle
import com.intellij.codeInsight.intention.BaseElementAtCaretIntentionAction
import com.intellij.codeInsight.navigation.NavigationUtil
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.openapi.ui.popup.PopupStep
import com.intellij.openapi.ui.popup.util.BaseListPopupStep
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.PsiShortNamesCache
import com.intellij.psi.util.parentOfType
import com.intellij.ui.popup.list.ListPopupImpl
import com.squareup.sqldelight.core.lang.psi.JavaTypeMixin
import com.squareup.sqldelight.core.lang.util.findChildrenOfType
import com.squareup.sqldelight.core.psi.SqlDelightImportStmt
import javax.swing.Icon

class AddImportIntention(private val key: String) : BaseElementAtCaretIntentionAction() {
override fun getFamilyName(): String = INTENTIONS_FAMILY_NAME_IMPORTS

override fun getText(): String = "Add import for $key"

override fun isAvailable(project: Project, editor: Editor, element: PsiElement): Boolean {
return true
}

override fun invoke(project: Project, editor: Editor, element: PsiElement) {
val type = element.parentOfType<JavaTypeMixin>() ?: return
val classes = PsiShortNamesCache.getInstance(project)
.getClassesByName(type.reference.canonicalText, GlobalSearchScope.allScope(project))
val document = editor.document
val file = element.containingFile
if (classes.size == 1) {
document.addImport(file, "import ${classes.first().qualifiedName};")
} else {
showImportPopup(project, editor, file, classes.sortedBy { it.qualifiedName })
}
}

private fun showImportPopup(
project: Project,
editor: Editor,
psiFile: PsiFile,
classes: List<PsiClass>
) {
val document = editor.document
val step = object : BaseListPopupStep<PsiClass>(
QuickFixBundle.message("class.to.import.chooser.title"), classes
) {
override fun isAutoSelectionEnabled(): Boolean {
return false
}

override fun getTextFor(value: PsiClass): String {
return requireNotNull(value.qualifiedName)
}

override fun getIconFor(value: PsiClass): Icon? {
return value.getIcon(0)
}

override fun onChosen(selectedValue: PsiClass?, finalChoice: Boolean): PopupStep<*>? {
if (selectedValue == null) {
return FINAL_CHOICE
}
if (finalChoice) {
return doFinalStep {
PsiDocumentManager.getInstance(project).commitAllDocuments()
WriteCommandAction.runWriteCommandAction(
project, QuickFixBundle.message("add.import"),
null,
{
document.addImport(psiFile, "import ${selectedValue.qualifiedName};")
}
)
}
}
return super.onChosen(selectedValue, finalChoice)
}
}
val popup = ListPopupImpl(project, step)
NavigationUtil.hidePopupIfDumbModeStarts(popup, project)
popup.showInBestPositionFor(editor)
}

private fun Document.addImport(file: PsiFile, import: String) {
val imports = file.findChildrenOfType<SqlDelightImportStmt>()
if (imports.isEmpty()) {
insertString(0, "$import\n\n")
} else {
val newImports = mutableListOf(import)
var endOffset = 0
for (imp in imports) {
newImports.add(imp.text)
endOffset = maxOf(endOffset, imp.textOffset + imp.textLength)
}
replaceString(0, endOffset, newImports.sorted().joinToString("\n"))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ package com.squareup.sqldelight.intellij.intentions

internal const val INTENTIONS_FAMILY_NAME_REFACTORINGS = "Refactorings"

internal const val INTENTIONS_FAMILY_NAME_IMPORTS = "Imports"

internal const val INTENTION_EXPAND_COLUMN_NAMES_TEXT = "Expand * into column names"

0 comments on commit 2fd5fc3

Please sign in to comment.