Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Prev Previous commit
Add dialog to automatically add mcdev annotations library if not present
  • Loading branch information
Earthcomputer committed Sep 6, 2020
commit 2d660a2801f5f60752d9bd50f755e5387a1c714e
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