User: rednesto Date: 20 Dec 23 18:02 Revision: ee61ae7bceb322af4477f41e0d22c324d0b5beeb Summary: Migrate NBT editor provider to newer async api TeamCity URL: http://ci.mcdev.io:80/viewModification.html?tab=vcsModificationFiles&modId=8921&personal=false Index: src/main/kotlin/nbt/editor/NbtFileEditorProvider.kt =================================================================== --- src/main/kotlin/nbt/editor/NbtFileEditorProvider.kt (revision aba632ec2c0ba14cfa3200619103836a0732dfeb) +++ src/main/kotlin/nbt/editor/NbtFileEditorProvider.kt (revision ee61ae7bceb322af4477f41e0d22c324d0b5beeb) @@ -20,65 +20,50 @@ package com.demonwav.mcdev.nbt.editor +import com.demonwav.mcdev.asset.MCDevBundle import com.demonwav.mcdev.nbt.NbtVirtualFile import com.demonwav.mcdev.nbt.filetype.NbtFileType -import com.demonwav.mcdev.nbt.lang.NbttFile -import com.demonwav.mcdev.util.invokeAndWait -import com.demonwav.mcdev.util.invokeLater +import com.demonwav.mcdev.nbt.lang.NbttFileType import com.intellij.ide.actions.SaveAllAction import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.AnActionResult import com.intellij.openapi.actionSystem.ex.AnActionListener -import com.intellij.openapi.application.runReadAction -import com.intellij.openapi.command.UndoConfirmationPolicy -import com.intellij.openapi.command.WriteCommandAction +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.editor.EditorFactory +import com.intellij.openapi.editor.ex.EditorEx +import com.intellij.openapi.fileEditor.AsyncFileEditorProvider +import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.fileEditor.FileEditor import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.fileEditor.FileEditorPolicy import com.intellij.openapi.fileEditor.FileEditorState -import com.intellij.openapi.fileEditor.FileEditorStateLevel import com.intellij.openapi.fileEditor.impl.NonProjectFileWritingAccessProvider -import com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorProvider -import com.intellij.openapi.fileEditor.impl.text.TextEditorState import com.intellij.openapi.project.DumbAware import com.intellij.openapi.project.Project -import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Key import com.intellij.openapi.vfs.VirtualFile -import com.intellij.psi.PsiManager -import com.intellij.psi.codeStyle.CodeStyleManager import com.intellij.ui.EditorNotifications -import com.intellij.ui.components.JBLoadingPanel -import com.intellij.util.IncorrectOperationException -import java.awt.BorderLayout import java.beans.PropertyChangeListener -import javax.swing.JPanel -import org.jetbrains.concurrency.runAsync -class NbtFileEditorProvider : PsiAwareTextEditorProvider(), DumbAware { +class NbtFileEditorProvider : AsyncFileEditorProvider, DumbAware { override fun getEditorTypeId() = EDITOR_TYPE_ID override fun accept(project: Project, file: VirtualFile) = file.fileType == NbtFileType - override fun getPolicy() = FileEditorPolicy.NONE - override fun createEditor(project: Project, file: VirtualFile): FileEditor { - val fileEditor = NbtFileEditor(file) { nbtFile -> - invokeAndWait { - super.createEditor(project, nbtFile) - } - } - - runAsync { + override fun getPolicy() = FileEditorPolicy.HIDE_DEFAULT_EDITOR + override fun createEditorAsync(project: Project, file: VirtualFile): AsyncFileEditorProvider.Builder { - val nbtFile = NbtVirtualFile(file, project) + val nbtFile = NbtVirtualFile(file, project) - val allowWrite = runReadAction { NonProjectFileWritingAccessProvider.isWriteAccessAllowed(file, project) } + val allowWrite = NonProjectFileWritingAccessProvider.isWriteAccessAllowed(nbtFile, project) - if (allowWrite) { - NonProjectFileWritingAccessProvider.allowWriting(listOf(nbtFile)) - } + if (allowWrite) { + NonProjectFileWritingAccessProvider.allowWriting(listOf(nbtFile)) + } - fileEditor.ready(nbtFile, project) + return NbtEditorBuilder(project, nbtFile) - } + } - return fileEditor + override fun createEditor(project: Project, file: VirtualFile): FileEditor { + val nbtFile = NbtVirtualFile(file, project) + return NbtEditorBuilder(project, nbtFile).build() } companion object { @@ -86,75 +71,28 @@ } } -private class NbtFileEditor( - private val file: VirtualFile, - private val editorProvider: (NbtVirtualFile) -> FileEditor, -) : FileEditor { - - private var editor: FileEditor? = null - private val editorCheckedDisposable = Disposer.newCheckedDisposable() - private val component = JPanel(BorderLayout()) - private val tempUserData = mutableMapOf() - private var disposed = false - - init { - val loading = JBLoadingPanel(null, this) - loading.setLoadingText("Parsing NBT file") - loading.startLoading() - component.add(loading, BorderLayout.CENTER) +private class NbtEditorBuilder(val project: Project, val nbtFile: NbtVirtualFile) : AsyncFileEditorProvider.Builder() { + override fun build(): FileEditor { + val document = FileDocumentManager.getInstance().getDocument(nbtFile)!! + val backingEditor = EditorFactory.getInstance().createEditor(document, project, NbttFileType, false) + val fileEditor = NbtFileEditor(nbtFile, backingEditor, project) + return fileEditor } - - fun ready(nbtFile: NbtVirtualFile, project: Project) { - if (project.isDisposed) { - return - } +} - component.removeAll() +private class NbtFileEditor( + private val file: NbtVirtualFile, + private val editor: Editor, + project: Project, +) : FileEditor { - val toolbar = NbtToolbar(nbtFile) - nbtFile.toolbar = toolbar - editor = invokeAndWait { - editorProvider(nbtFile) - } - editor?.let { editor -> - try { - Disposer.register(this, editor) - Disposer.register(editor, editorCheckedDisposable) - } catch (e: IncorrectOperationException) { - // The editor can be disposed really quickly when opening a large number of NBT files - // Since everything happens basically at the same time, calling Disposer.isDisposed right before - // returns false but #dispose throws this IOE... - Disposer.dispose(this) - return@let - } - tempUserData.forEach { (k, v) -> - @Suppress("UNCHECKED_CAST") - editor.putUserData(k as Key, v) - } - tempUserData.clear() - invokeLater { - component.add(toolbar.panel, BorderLayout.NORTH) - component.add(editor.component, BorderLayout.CENTER) - } + init { + val toolbar = NbtToolbar(file) + file.toolbar = toolbar + (editor as? EditorEx)?.permanentHeaderComponent = toolbar.panel + editor.headerComponent = toolbar.panel - EditorNotifications.getInstance(project).updateAllNotifications() + EditorNotifications.getInstance(project).updateAllNotifications() - } - // This can be null if the file is too big to be parsed as a psi file - val psiFile = runReadAction { - PsiManager.getInstance(project).findFile(nbtFile) as? NbttFile - } ?: return - - WriteCommandAction.writeCommandAction(psiFile) - .shouldRecordActionForActiveDocument(false) - .withUndoConfirmationPolicy(UndoConfirmationPolicy.DO_NOT_REQUEST_CONFIRMATION) - .run { - CodeStyleManager.getInstance(project).reformat(psiFile, true) - } - - if (disposed) { - return - } - project.messageBus.connect(this).subscribe( AnActionListener.TOPIC, object : AnActionListener { @@ -169,72 +107,34 @@ return } - nbtFile.writeFile(this) + file.writeFile(this) } }, ) } - override fun isModified() = editor.exec { isModified } ?: false - override fun addPropertyChangeListener(listener: PropertyChangeListener) { - editor.exec { addPropertyChangeListener(listener) } - } + override fun isModified() = false + override fun addPropertyChangeListener(listener: PropertyChangeListener) = Unit - override fun getName() = editor.exec { name } ?: "" - override fun setState(state: FileEditorState) { - editor.exec { setState(state) } - } + override fun getName() = MCDevBundle("nbt.editor.name") + override fun setState(state: FileEditorState) = Unit - override fun getState(level: FileEditorStateLevel): FileEditorState = editor.exec { getState(level) } - ?: TextEditorState() + override fun getFile(): VirtualFile = file - override fun getFile(): VirtualFile = editor.exec { file } ?: file + override fun getComponent() = editor.component + override fun getPreferredFocusedComponent() = null + override fun getUserData(key: Key): T? = editor.getUserData(key) - override fun getComponent() = component - override fun getPreferredFocusedComponent() = editor.exec { preferredFocusedComponent } - override fun getUserData(key: Key): T? { - if (editor == null) { - @Suppress("UNCHECKED_CAST") - return tempUserData[key] as? T - } - return editor.exec { getUserData(key) } - } - override fun selectNotify() { - editor.exec { selectNotify() } - } + override fun putUserData(key: Key, value: T?) = editor.putUserData(key, value) - override fun putUserData(key: Key, value: T?) { - if (editor == null) { - tempUserData[key] = value - return - } - editor.exec { putUserData(key, value) } - } + override fun isValid() = true + override fun removePropertyChangeListener(listener: PropertyChangeListener) = Unit - override fun getCurrentLocation() = editor.exec { currentLocation } - override fun deselectNotify() { - editor.exec { deselectNotify() } - } - - override fun getBackgroundHighlighter() = editor.exec { backgroundHighlighter } - override fun isValid() = editor.exec { isValid } ?: true - override fun removePropertyChangeListener(listener: PropertyChangeListener) { - editor.exec { removePropertyChangeListener(listener) } - } - override fun dispose() { - disposed = true + EditorFactory.getInstance().releaseEditor(editor) } - override fun getStructureViewBuilder() = editor.exec { structureViewBuilder } override fun equals(other: Any?) = other is NbtFileEditor && other.component == this.component override fun hashCode() = editor.hashCode() override fun toString() = editor.toString() - - private inline fun FileEditor?.exec(action: FileEditor.() -> T): T? { - if (editor == null || editorCheckedDisposable.isDisposed) { - return null - } +} - return this?.action() - } -} Index: src/main/kotlin/nbt/lang/NbttFileViewProvider.kt =================================================================== --- src/main/kotlin/nbt/lang/NbttFileViewProvider.kt (revision ee61ae7bceb322af4477f41e0d22c324d0b5beeb) +++ src/main/kotlin/nbt/lang/NbttFileViewProvider.kt (revision ee61ae7bceb322af4477f41e0d22c324d0b5beeb) @@ -0,0 +1,51 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2023 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.nbt.lang + +import com.intellij.lang.Language +import com.intellij.openapi.fileTypes.FileType +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.FileViewProvider +import com.intellij.psi.FileViewProviderFactory +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiManager +import com.intellij.psi.SingleRootFileViewProvider + +class NbttFileViewProvider(manager: PsiManager, file: VirtualFile, eventSystemEnabled: Boolean) : + SingleRootFileViewProvider(manager, file, eventSystemEnabled, NbttLanguage) { + + override fun createFile(project: Project, file: VirtualFile, fileType: FileType): PsiFile = NbttFile(this) + + override fun createFile(file: VirtualFile, fileType: FileType, language: Language): PsiFile = NbttFile(this) + + override fun createFile(lang: Language): PsiFile = NbttFile(this) +} + +class NbttFileViewProviderFactory : FileViewProviderFactory { + + override fun createFileViewProvider( + file: VirtualFile, + language: Language?, + manager: PsiManager, + eventSystemEnabled: Boolean + ): FileViewProvider = NbttFileViewProvider(manager, file, eventSystemEnabled) +} Index: src/main/resources/META-INF/plugin.xml =================================================================== --- src/main/resources/META-INF/plugin.xml (revision aba632ec2c0ba14cfa3200619103836a0732dfeb) +++ src/main/resources/META-INF/plugin.xml (revision ee61ae7bceb322af4477f41e0d22c324d0b5beeb) @@ -233,6 +233,7 @@ + Index: src/main/resources/messages/MinecraftDevelopment.properties =================================================================== --- src/main/resources/messages/MinecraftDevelopment.properties (revision aba632ec2c0ba14cfa3200619103836a0732dfeb) +++ src/main/resources/messages/MinecraftDevelopment.properties (revision ee61ae7bceb322af4477f41e0d22c324d0b5beeb) @@ -165,6 +165,8 @@ nbt.lang.errors.wrapped_error_message=Malformed NBT file:\n{0} +nbt.editor.name=NBT Text + nbt.file.save_notify.success.title=Saved NBT file successfully nbt.file.save_notify.success.content={0} was saved successfully.