Skip to content

Commit

Permalink
Add dialog to automatically add mcdev annotations library if not present
Browse files Browse the repository at this point in the history
  • Loading branch information
Earthcomputer committed Sep 6, 2020
1 parent 95e914a commit 2d660a2
Show file tree
Hide file tree
Showing 13 changed files with 433 additions and 63 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ intellij {
version = ideaVersion
// Bundled plugin dependencies
setPlugins(
"java", "maven", "gradle", "Groovy",
"java", "maven", "gradle", "Groovy", "Kotlin",
// needed dependencies for unit tests
"properties", "junit",
// useful to have when running for mods.toml
Expand Down
7 changes: 7 additions & 0 deletions src/main/kotlin/MinecraftSettings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class MinecraftSettings : PersistentStateComponent<MinecraftSettings.State> {
var isShowEventListenerGutterIcons: Boolean = true,
var isShowChatColorGutterIcons: Boolean = true,
var isShowChatColorUnderlines: Boolean = false,
var isShowSideOnlyGutterIcons: Boolean = true,
var underlineType: MinecraftSettings.UnderlineType = MinecraftSettings.UnderlineType.DOTTED
)

Expand Down Expand Up @@ -62,6 +63,12 @@ class MinecraftSettings : PersistentStateComponent<MinecraftSettings.State> {
state.isShowChatColorUnderlines = showChatColorUnderlines
}

var isShowSideOnlyGutterIcons: Boolean
get() = state.isShowSideOnlyGutterIcons
set(showSideOnlyGutterIcons) {
state.isShowSideOnlyGutterIcons = showSideOnlyGutterIcons
}

var underlineType: UnderlineType
get() = state.underlineType
set(underlineType) {
Expand Down
32 changes: 23 additions & 9 deletions src/main/kotlin/sideonly/HardSideOnlyUsageInspection.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@

package com.demonwav.mcdev.sideonly

import com.demonwav.mcdev.util.findModule
import com.demonwav.mcdev.util.runInSmartModeFromReadAction
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.progress.util.ProgressWindow
import com.intellij.openapi.project.Project
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiAnnotation
Expand Down Expand Up @@ -57,19 +61,29 @@ class HardSideOnlyUsageInspection : BaseInspection() {
private class Fix(private val annotation: SmartPsiElementPointer<PsiAnnotation>) : InspectionGadgetsFix() {
override fun doFix(project: Project, descriptor: ProblemDescriptor) {
val annotation = this.annotation.element ?: return
val oldSide = SideOnlyUtil.getAnnotationSide(annotation, SideHardness.HARD)
val newAnnotation = JavaPsiFacade.getElementFactory(project).createAnnotationFromText(
"@${SideOnlyUtil.MCDEV_SIDEONLY_ANNOTATION}(${SideOnlyUtil.MCDEV_SIDE}.$oldSide)",
annotation
)
val createdAnnotation = annotation.replace(newAnnotation)
val codeStyleManager = JavaCodeStyleManager.getInstance(project)
codeStyleManager.shortenClassReferences(createdAnnotation)
createdAnnotation.containingFile?.let { codeStyleManager.optimizeImports(it) }
val module = annotation.findModule() ?: return
if (!SideOnlyUtil.ensureMcdevDependencyPresent(project, module, name, annotation.resolveScope)) {
return
}
project.runInSmartModeFromReadAction(ProgressWindow(true, project)) {
val oldSide = SideOnlyUtil.getAnnotationSide(annotation, SideHardness.HARD)
val newAnnotation = JavaPsiFacade.getElementFactory(project).createAnnotationFromText(
"@${SideOnlyUtil.MCDEV_SIDEONLY_ANNOTATION}(${SideOnlyUtil.MCDEV_SIDE}.$oldSide)",
annotation
)
WriteCommandAction.runWriteCommandAction(project) {
val createdAnnotation = annotation.replace(newAnnotation)
val codeStyleManager = JavaCodeStyleManager.getInstance(project)
codeStyleManager.shortenClassReferences(createdAnnotation)
createdAnnotation.containingFile?.let { codeStyleManager.optimizeImports(it) }
}
}
}

override fun getName() = "Replace with @CheckEnv"

override fun getFamilyName() = name

override fun startInWriteAction() = false
}
}
90 changes: 90 additions & 0 deletions src/main/kotlin/sideonly/MakeInferredMcdevAnnotationExplicit.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Minecraft Dev for IntelliJ
*
* https://minecraftdev.org
*
* Copyright (c) 2020 minecraft-dev
*
* MIT License
*/

package com.demonwav.mcdev.sideonly

import com.demonwav.mcdev.util.findModule
import com.intellij.codeInsight.FileModificationService
import com.intellij.codeInsight.intention.impl.BaseIntentionAction
import com.intellij.lang.java.JavaLanguage
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.project.Project
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiCompiledElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiModifierListOwner
import com.intellij.psi.codeStyle.JavaCodeStyleManager
import com.intellij.psi.util.PsiUtilCore

class MakeInferredMcdevAnnotationExplicit : BaseIntentionAction() {
override fun getFamilyName() = "Make Inferred MinecraftDev Annotations Explicit"

override fun getText() = "Make Inferred MinecraftDev Annotations Explicit"

override fun isAvailable(project: Project, editor: Editor, file: PsiFile): Boolean {
val leaf = file.findElementAt(editor.caretModel.offset) ?: return false
val owner = leaf.parent as? PsiModifierListOwner
return isAvailable(file, owner)
}

fun isAvailable(file: PsiFile, owner: PsiModifierListOwner?): Boolean {
if (owner != null &&
owner.language.isKindOf(JavaLanguage.INSTANCE) &&
isWritable(owner) &&
file.findModule() != null
) {
val annotation = SideOnlyUtil.getInferredAnnotationOnly(owner, SideHardness.HARD)
?: SideOnlyUtil.getInferredAnnotationOnly(owner, SideHardness.SOFT)
if (annotation != null) {
text = "Insert '@CheckEnv(Env.${annotation.side})'"
return true
}
}
return false
}

private fun isWritable(owner: PsiModifierListOwner): Boolean {
if (owner is PsiCompiledElement) return false
val vFile = PsiUtilCore.getVirtualFile(owner)
return vFile != null && vFile.isInLocalFileSystem
}

override fun invoke(project: Project, editor: Editor, file: PsiFile) {
val leaf = file.findElementAt(editor.caretModel.offset) ?: return
val owner = leaf.parent as? PsiModifierListOwner ?: return
makeAnnotationExplicit(project, file, owner)
}

fun makeAnnotationExplicit(project: Project, file: PsiFile, owner: PsiModifierListOwner) {
val modifierList = owner.modifierList ?: return
val module = file.findModule() ?: return
if (!SideOnlyUtil.ensureMcdevDependencyPresent(project, module, familyName, file.resolveScope)) {
return
}
if (!FileModificationService.getInstance().preparePsiElementForWrite(owner)) return
val facade = JavaPsiFacade.getInstance(project)
val inferredSide = SideOnlyUtil.getInferredAnnotationOnly(owner, SideHardness.HARD)
?: SideOnlyUtil.getInferredAnnotationOnly(owner, SideHardness.SOFT) ?: return
val inferred = facade.elementFactory.createAnnotationFromText(
"@${SideOnlyUtil.MCDEV_SIDEONLY_ANNOTATION}(${SideOnlyUtil.MCDEV_SIDE}.${inferredSide.side})",
owner
)
WriteCommandAction.runWriteCommandAction(project) {
DumbService.getInstance(project).withAlternativeResolveEnabled {
JavaCodeStyleManager.getInstance(project)
.shortenClassReferences(modifierList.addAfter(inferred, null))
}
}
}

override fun startInWriteAction() = false
}
42 changes: 0 additions & 42 deletions src/main/kotlin/sideonly/SideOnlyInferredAnnotationProvider.kt

This file was deleted.

92 changes: 92 additions & 0 deletions src/main/kotlin/sideonly/SideOnlyLineMarkerProvider.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Minecraft Dev for IntelliJ
*
* https://minecraftdev.org
*
* Copyright (c) 2020 minecraft-dev
*
* MIT License
*/

package com.demonwav.mcdev.sideonly

import com.demonwav.mcdev.MinecraftSettings
import com.intellij.codeInsight.daemon.LineMarkerInfo
import com.intellij.codeInsight.daemon.LineMarkerProvider
import com.intellij.icons.AllIcons
import com.intellij.ide.actions.ApplyIntentionAction
import com.intellij.openapi.actionSystem.DefaultActionGroup
import com.intellij.openapi.actionSystem.impl.SimpleDataContext
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.markup.GutterIconRenderer
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.popup.JBPopup
import com.intellij.openapi.ui.popup.JBPopupFactory
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiIdentifier
import com.intellij.psi.PsiModifierListOwner
import com.intellij.psi.util.PsiUtilCore
import com.intellij.ui.awt.RelativePoint
import java.awt.event.MouseEvent

class SideOnlyLineMarkerProvider : LineMarkerProvider {
override fun getLineMarkerInfo(element: PsiElement): LineMarkerInfo<*>? {
if (!MinecraftSettings.instance.isShowSideOnlyGutterIcons) {
return null
}
if (element !is PsiIdentifier) {
return null
}
val listOwner = element.parent as? PsiModifierListOwner ?: return null
val implicitHard = SideOnlyUtil.getInferredAnnotationOnly(listOwner, SideHardness.HARD)
val implicitSoft = SideOnlyUtil.getInferredAnnotationOnly(listOwner, SideHardness.SOFT)
val implicitAnnotation = implicitHard ?: implicitSoft ?: return null

var message = "Implicit "
message += if (implicitHard == null) {
"soft"
} else {
"hard"
}
message += "-sided annotation available: " + implicitAnnotation.reason
return LineMarkerInfo(
element,
element.textRange,
AllIcons.Gutter.ExtAnnotation,
{ message },
this::navigate,
GutterIconRenderer.Alignment.RIGHT
)
}

private fun navigate(event: MouseEvent, element: PsiElement) {
val listOwner = element.parent
val containingFile = listOwner.containingFile
val virtualFile = PsiUtilCore.getVirtualFile(listOwner)

if (virtualFile != null && containingFile != null) {
val project = listOwner.project
val editor = FileEditorManager.getInstance(project).selectedTextEditor
if (editor != null) {
editor.caretModel.moveToOffset(element.textOffset)
val file = PsiDocumentManager.getInstance(project).getPsiFile(editor.document)
if (file != null && virtualFile == file.virtualFile) {
val popup = createActionGroupPopup(containingFile, project, editor)
popup?.show(RelativePoint(event))
}
}
}
}

private fun createActionGroupPopup(file: PsiFile, project: Project, editor: Editor): JBPopup? {
val intention = MakeInferredMcdevAnnotationExplicit()
val action = ApplyIntentionAction(intention, intention.text, editor, file)
val group = DefaultActionGroup(action)
val context = SimpleDataContext.getProjectContext(null)
return JBPopupFactory.getInstance()
.createActionGroupPopup(null, group, context, JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, true)
}
}
Loading

0 comments on commit 2d660a2

Please sign in to comment.