Skip to content

Commit

Permalink
Merge branch '2023.2' into 2023.3
Browse files Browse the repository at this point in the history
  • Loading branch information
RedNesto committed Dec 18, 2023
2 parents b1fe6c4 + fbca0a5 commit 4b7455d
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 113 deletions.
65 changes: 32 additions & 33 deletions src/main/kotlin/nbt/NbtVirtualFile.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ import com.demonwav.mcdev.asset.MCDevBundle
import com.demonwav.mcdev.nbt.editor.CompressionSelection
import com.demonwav.mcdev.nbt.editor.NbtToolbar
import com.demonwav.mcdev.nbt.lang.NbttFile
import com.demonwav.mcdev.nbt.lang.NbttFileType
import com.demonwav.mcdev.nbt.lang.NbttLanguage
import com.demonwav.mcdev.util.runReadActionAsync
import com.demonwav.mcdev.util.runWriteTaskLater
import com.intellij.lang.Language
import com.intellij.notification.Notification
import com.intellij.notification.NotificationType
import com.intellij.openapi.fileEditor.impl.IdeDocumentHistoryImpl
Expand All @@ -41,42 +41,41 @@ import java.io.DataOutputStream
import java.util.concurrent.TimeUnit
import java.util.zip.GZIPOutputStream

class NbtVirtualFile(private val backingFile: VirtualFile, private val project: Project) :
LightVirtualFile(backingFile.name + ".nbtt", NbttFileType, ""),
IdeDocumentHistoryImpl.SkipFromDocumentHistory {
fun NbtVirtualFile(backingFile: VirtualFile, project: Project): NbtVirtualFile {
var language: Language = NbttLanguage

var text: String
var compressed: Boolean
var parseSuccessful: Boolean

try {
val (rootCompound, isCompressed) = Nbt.buildTagTree(backingFile.inputStream, TimeUnit.SECONDS.toMillis(10))
text = rootCompound.toString()
compressed = isCompressed
parseSuccessful = true
} catch (e: MalformedNbtFileException) {
text = MCDevBundle("nbt.lang.errors.wrapped_error_message", e.message)
compressed = false
parseSuccessful = false
}

val isCompressed: Boolean
lateinit var toolbar: NbtToolbar
val parseSuccessful: Boolean

init {
originalFile = backingFile
language = NbttLanguage

var text: String
var tempCompressed: Boolean
var tempParseSuccessful: Boolean

try {
val (rootCompound, isCompressed) = Nbt.buildTagTree(backingFile.inputStream, TimeUnit.SECONDS.toMillis(10))
text = rootCompound.toString()
tempCompressed = isCompressed
tempParseSuccessful = true
} catch (e: MalformedNbtFileException) {
text = MCDevBundle("nbt.lang.errors.wrapped_error_message", e.message)
tempCompressed = false
tempParseSuccessful = false
}
if (!parseSuccessful) {
language = PlainTextLanguage.INSTANCE
}

this.isCompressed = tempCompressed
this.parseSuccessful = tempParseSuccessful
return NbtVirtualFile(backingFile, project, language, text, compressed, parseSuccessful)
}

if (!this.parseSuccessful) {
language = PlainTextLanguage.INSTANCE
}
class NbtVirtualFile(
private val backingFile: VirtualFile,
private val project: Project,
language: Language,
text: String,
val isCompressed: Boolean,
val parseSuccessful: Boolean,
) : LightVirtualFile(backingFile.name + ".nbtt", language, text), IdeDocumentHistoryImpl.SkipFromDocumentHistory {

setContent(this, text, false)
}
lateinit var toolbar: NbtToolbar

override fun refresh(asynchronous: Boolean, recursive: Boolean, postRunnable: Runnable?) {
backingFile.refresh(asynchronous, recursive, postRunnable)
Expand Down
12 changes: 10 additions & 2 deletions src/main/kotlin/nbt/editor/NbtFileEditorProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ class NbtFileEditorProvider : PsiAwareTextEditorProvider(), DumbAware {
runAsync {
val nbtFile = NbtVirtualFile(file, project)

if (NonProjectFileWritingAccessProvider.isWriteAccessAllowed(file, project)) {
val allowWrite = runReadAction { NonProjectFileWritingAccessProvider.isWriteAccessAllowed(file, project) }
if (allowWrite) {
NonProjectFileWritingAccessProvider.allowWriting(listOf(nbtFile))
}

Expand All @@ -94,6 +95,7 @@ private class NbtFileEditor(
private val editorCheckedDisposable = Disposer.newCheckedDisposable()
private val component = JPanel(BorderLayout())
private val tempUserData = mutableMapOf<Any?, Any?>()
private var disposed = false

init {
val loading = JBLoadingPanel(null, this)
Expand Down Expand Up @@ -149,6 +151,10 @@ private class NbtFileEditor(
CodeStyleManager.getInstance(project).reformat(psiFile, true)
}

if (disposed) {
return
}

project.messageBus.connect(this).subscribe(
AnActionListener.TOPIC,
object : AnActionListener {
Expand Down Expand Up @@ -216,7 +222,9 @@ private class NbtFileEditor(
editor.exec { removePropertyChangeListener(listener) }
}

override fun dispose() {}
override fun dispose() {
disposed = true
}

override fun getStructureViewBuilder() = editor.exec { structureViewBuilder }
override fun equals(other: Any?) = other is NbtFileEditor && other.component == this.component
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ package com.demonwav.mcdev.platform.mixin.handlers.injectionPoint
import com.demonwav.mcdev.platform.mixin.reference.MixinSelector
import com.demonwav.mcdev.platform.mixin.reference.MixinSelectorParser
import com.demonwav.mcdev.platform.mixin.util.MixinConstants.Annotations.AT
import com.demonwav.mcdev.platform.mixin.util.findClassNodeByPsiClass
import com.demonwav.mcdev.platform.mixin.util.findMethod
import com.demonwav.mcdev.platform.mixin.util.shortName
import com.demonwav.mcdev.util.MemberReference
import com.demonwav.mcdev.util.constantStringValue
import com.demonwav.mcdev.util.descriptor
import com.demonwav.mcdev.util.fullQualifiedName
import com.demonwav.mcdev.util.internalName
import com.demonwav.mcdev.util.mapToArray
import com.demonwav.mcdev.util.shortName
import com.intellij.codeInsight.completion.JavaLookupElementBuilder
import com.intellij.codeInsight.lookup.LookupElementBuilder
Expand All @@ -40,12 +41,10 @@ import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMember
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiModifier
import com.intellij.psi.PsiNewExpression
import com.intellij.psi.PsiSubstitutor
import com.intellij.psi.util.parentOfType
import org.objectweb.asm.Opcodes
import org.objectweb.asm.Type
import org.objectweb.asm.tree.AbstractInsnNode
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.MethodInsnNode
Expand Down Expand Up @@ -109,18 +108,11 @@ class NewInsnInjectionPoint : InjectionPoint<PsiMember>() {
private val selector: MixinSelector,
) : NavigationVisitor() {
override fun visitNewExpression(expression: PsiNewExpression) {
val anonymousName = expression.anonymousClass?.fullQualifiedName?.replace('.', '/')
val anonymousClass = expression.anonymousClass
val anonymousName = anonymousClass?.fullQualifiedName?.replace('.', '/')
if (anonymousName != null) {
// guess descriptor
val hasThis = expression.parentOfType<PsiMethod>()?.hasModifierProperty(PsiModifier.STATIC) == false
val thisType = if (hasThis) expression.parentOfType<PsiClass>()?.internalName else null
val argTypes = expression.argumentList?.expressionTypes?.map { it.descriptor } ?: emptyList()
val bytecodeArgTypes = if (thisType != null) listOf(thisType) + argTypes else argTypes
val methodDesc = Type.getMethodDescriptor(
Type.VOID_TYPE,
*bytecodeArgTypes.mapToArray { Type.getType(it) },
)
if (selector.matchMethod(anonymousName, "<init>", methodDesc)) {
val method = findClassNodeByPsiClass(anonymousClass)?.findMethod(selector)
if (method != null && selector.matchMethod(anonymousName, method.name, method.desc)) {
addResult(expression)
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ import com.demonwav.mcdev.platform.mixin.util.mixinTargets
import com.intellij.codeInsight.daemon.GutterIconNavigationHandler
import com.intellij.codeInsight.daemon.LineMarkerInfo
import com.intellij.codeInsight.daemon.LineMarkerProviderDescriptor
import com.intellij.codeInsight.daemon.impl.PsiElementListNavigator
import com.intellij.ide.util.PsiClassListCellRenderer
import com.intellij.codeInsight.navigation.getPsiElementPopup
import com.intellij.openapi.editor.markup.GutterIconRenderer
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiIdentifier
import com.intellij.ui.awt.RelativePoint
import java.awt.event.MouseEvent

class MixinLineMarkerProvider : LineMarkerProviderDescriptor(), GutterIconNavigationHandler<PsiIdentifier> {
Expand Down Expand Up @@ -67,13 +67,8 @@ class MixinLineMarkerProvider : LineMarkerProviderDescriptor(), GutterIconNaviga
val targets = psiClass.mixinTargets
.mapNotNull { it.findSourceClass(psiClass.project, psiClass.resolveScope, canDecompile = true) }
if (targets.isNotEmpty()) {
PsiElementListNavigator.openTargets(
e,
targets.toTypedArray(),
"Choose target class of $name",
null,
PsiClassListCellRenderer(),
)
getPsiElementPopup(targets.toTypedArray<PsiElement>(), "Choose target class of $name")
.show(RelativePoint(e))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ import com.intellij.openapi.editor.colors.EditorColors
import com.intellij.openapi.editor.colors.EditorColorsManager
import com.intellij.openapi.fileEditor.FileEditor
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.ui.MessageDialogBuilder
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.findPsiFile
import com.intellij.psi.PsiManager
import com.intellij.ui.EditorNotificationPanel
import com.intellij.ui.EditorNotificationProvider
Expand All @@ -49,61 +50,67 @@ class TranslationEditorNotificationProvider : EditorNotificationProvider {
override fun collectNotificationData(
project: Project,
file: VirtualFile,
): Function<in FileEditor, out JComponent?> = Function { createNotificationPanel(file, project) }

private fun createNotificationPanel(file: VirtualFile, project: Project): InfoPanel? {
val locale = TranslationFiles.getLocale(file)
if (!show || !TranslationFiles.isTranslationFile(file) || locale == TranslationConstants.DEFAULT_LOCALE) {
): Function<in FileEditor, out JComponent?>? {
if (!show || !TranslationFiles.isTranslationFile(file) || TranslationFiles.isDefaultLocale(file)) {
return null
}

val missingTranslations = getMissingTranslations(project, file)
if (missingTranslations.any()) {
val panel = InfoPanel()
panel.text = "Translation file doesn't match default one (${TranslationConstants.DEFAULT_LOCALE} locale)."
panel.createActionLabel(
"Add missing default entries (won't reflect changes in original English localization)",
) {
val psi = PsiManager.getInstance(project).findFile(file) ?: return@createActionLabel
psi.applyWriteAction {
val fileEntries = missingTranslations.map {
TranslationFiles.FileEntry.Translation(it.key, it.text)
}
TranslationFiles.addAll(psi, fileEntries.asIterable())
EditorNotifications.updateAll()
}
if (missingTranslations.none()) {
return null
}

if (psi.findMcpModule() == null) {
// TranslationSorter.query requires an MCP module to work
return@createActionLabel
}
val hasMcpModule = file.findPsiFile(project)?.findMcpModule() != null
return Function {
createNotificationPanel(missingTranslations, hasMcpModule, file, project)
}
}

val sort = Messages.showYesNoDialog(
project,
"Would you like to sort all translations now?",
"Sort Translations",
Messages.getQuestionIcon(),
)
if (sort == Messages.YES) {
try {
TranslationSorter.query(project, psi, Ordering.LIKE_DEFAULT)
} catch (e: Exception) {
Notification(
"Translations sorting error",
"Error sorting translations",
e.message ?: e.stackTraceToString(),
NotificationType.WARNING,
).notify(project)
}
private fun createNotificationPanel(
missingTranslations: Sequence<Translation>,
hasMcpModule: Boolean,
file: VirtualFile,
project: Project
): InfoPanel {
val panel = InfoPanel()
panel.text = "Translation file doesn't match default one (${TranslationConstants.DEFAULT_LOCALE} locale)."
panel.createActionLabel(
"Add missing default entries (won't reflect changes in original English localization)",
) {
val psi = PsiManager.getInstance(project).findFile(file) ?: return@createActionLabel
psi.applyWriteAction {
val fileEntries = missingTranslations.map {
TranslationFiles.FileEntry.Translation(it.key, it.text)
}
TranslationFiles.addAll(psi, fileEntries.asIterable())
EditorNotifications.updateAll()
}

if (!hasMcpModule) {
// TranslationSorter.query requires an MCP module to work
return@createActionLabel
}
panel.createActionLabel("Hide notification") {
panel.isVisible = false
show = false

val sort = MessageDialogBuilder.yesNo("Sort Translations", "Would you like to sort all translations now?")
.ask(project)
if (sort) {
try {
TranslationSorter.query(project, psi, true, Ordering.LIKE_DEFAULT)
} catch (e: Exception) {
Notification(
"Translations sorting error",
"Error sorting translations",
e.message ?: e.stackTraceToString(),
NotificationType.WARNING,
).notify(project)
}
}
return panel
}
return null
panel.createActionLabel("Hide notification") {
panel.isVisible = false
show = false
}
return panel
}

private fun getMissingTranslations(project: Project, file: VirtualFile): Sequence<Translation> {
Expand Down
3 changes: 3 additions & 0 deletions src/main/kotlin/translations/TranslationFiles.kt
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ object TranslationFiles {
fun getLocale(file: VirtualFile?) =
file?.nameWithoutExtension?.lowercase(Locale.ENGLISH)

fun isDefaultLocale(file: VirtualFile?) =
file?.nameWithoutExtension?.lowercase(Locale.ENGLISH) == TranslationConstants.DEFAULT_LOCALE

tailrec fun seekTranslation(element: PsiElement): PsiNamedElement? {
// don't use elvis here, K2 doesn't think it's a tail recursive call if you do
val res = toTranslation(element)?.let { element as? PsiNamedElement }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
package com.demonwav.mcdev.translations.actions

import com.demonwav.mcdev.translations.TranslationFiles
import com.demonwav.mcdev.translations.index.TranslationIndex
import com.demonwav.mcdev.translations.sorting.TranslationSorter
import com.demonwav.mcdev.util.mcDomain
import com.intellij.notification.Notification
import com.intellij.notification.NotificationType
import com.intellij.openapi.actionSystem.AnAction
Expand All @@ -34,7 +36,11 @@ class SortTranslationsAction : AnAction() {
val file = e.getData(LangDataKeys.PSI_FILE) ?: return

try {
TranslationSorter.query(file.project, file)
TranslationSorter.query(
file.project,
file,
TranslationIndex.hasDefaultTranslations(file.project, file.virtualFile.mcDomain)
)
} catch (e: Exception) {
Notification(
"Translations sorting error",
Expand Down
11 changes: 11 additions & 0 deletions src/main/kotlin/translations/index/TranslationIndex.kt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@ class TranslationIndex : FileBasedIndexExtension<String, TranslationIndexEntry>(
fun getProjectDefaultTranslations(project: Project, domain: String? = null) =
getProjectDefaultEntries(project, domain).flatten()

fun hasDefaultTranslations(project: Project, domain: String? = null): Boolean {
return !FileBasedIndex.getInstance()
.processValues(
NAME,
TranslationConstants.DEFAULT_LOCALE,
null,
{ _, entry -> entry.sourceDomain != domain },
GlobalSearchScope.projectScope(project)
)
}

fun getTranslations(project: Project, file: VirtualFile): Sequence<Translation> {
return getEntries(
GlobalSearchScope.fileScope(project, file),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class NoTranslationInspection : TranslationInspection() {
val literalValue = literal.value as String
val key = translation?.key?.copy(infix = literalValue)?.full ?: literalValue
val result = Messages.showInputDialog(
project,
"Enter default value for \"$key\":",
"Create Translation",
Messages.getQuestionIcon(),
Expand Down
Loading

0 comments on commit 4b7455d

Please sign in to comment.