diff --git a/build.gradle.kts b/build.gradle.kts index fceddf6..e4b5622 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ import java.io.ByteArrayOutputStream plugins { id("java") alias(libs.plugins.kotlinJvm) - id("org.jetbrains.intellij.platform") version "2.0.0-beta5" // See https://github.com/JetBrains/intellij-platform-gradle-plugin/releases + id("org.jetbrains.intellij.platform") version "2.2.0" // See https://github.com/JetBrains/intellij-platform-gradle-plugin/releases id("me.filippov.gradle.jvm.wrapper") version "0.14.0" } diff --git a/gradle.properties b/gradle.properties index 5cce73e..2c12e55 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,7 +15,7 @@ PublishToken="_PLACEHOLDER_" # Release: 2020.2 # Nightly: 2020.3-SNAPSHOT # EAP: 2020.3-EAP2-SNAPSHOT -ProductVersion=2024.3-EAP2-SNAPSHOT +ProductVersion=2024.3 # 2021.3.3 # Kotlin 1.4 will bundle the stdlib dependency by default, causing problems with the version bundled with the IDE diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 12f0861..1cf3884 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] kotlin = "1.9.24" # https://plugins.jetbrains.com/docs/intellij/using-kotlin.html#kotlin-standard-library -rdGen = "2024.3.0" # https://github.com/JetBrains/rd/releases +rdGen = "2024.3.1" # https://github.com/JetBrains/rd/releases [libraries] kotlinStdLib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" } diff --git a/protocol/build.gradle.kts b/protocol/build.gradle.kts index fe03e78..8592af2 100644 --- a/protocol/build.gradle.kts +++ b/protocol/build.gradle.kts @@ -23,7 +23,7 @@ val RiderPluginId: String by rootProject rdgen { val csOutput = File(rootDir, "src/dotnet/${DotnetPluginId}") - val ktOutput = File(rootDir, "src/rider/main/kotlin/com/jetbrains/rider/plugins/${RiderPluginId.replace('.','/').toLowerCase()}") + val ktOutput = File(rootDir, "src/rider/main/kotlin/com/jetbrains/rider/plugins/${RiderPluginId.replace('.','/').lowercase()}") verbose = true packages = "model.rider" diff --git a/src/dotnet/ReSharperPlugin.BSMT/ReSharperPlugin.BSMT.Rider.csproj b/src/dotnet/ReSharperPlugin.BSMT/ReSharperPlugin.BSMT.Rider.csproj index 2a20522..41b7372 100644 --- a/src/dotnet/ReSharperPlugin.BSMT/ReSharperPlugin.BSMT.Rider.csproj +++ b/src/dotnet/ReSharperPlugin.BSMT/ReSharperPlugin.BSMT.Rider.csproj @@ -26,8 +26,6 @@ - - diff --git a/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/actions/BeatSaberProjectAction.kt b/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/actions/BeatSaberProjectAction.kt index c31311a..42ca6dc 100644 --- a/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/actions/BeatSaberProjectAction.kt +++ b/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/actions/BeatSaberProjectAction.kt @@ -9,6 +9,7 @@ import com.intellij.openapi.actionSystem.CommonDataKeys import com.intellij.openapi.components.service import com.intellij.platform.backend.workspace.WorkspaceModel import com.jetbrains.rider.projectView.hasSolution +import com.jetbrains.rider.projectView.solution import com.jetbrains.rider.projectView.workspace.ProjectModelEntity import com.jetbrains.rider.projectView.workspace.findProjects import kotlinx.coroutines.launch @@ -27,7 +28,8 @@ abstract class BeatSaberProjectAction : AnAction() { return } - e.presentation.isEnabledAndVisible = project.hasSolution == true + e.presentation.isVisible = project.hasSolution == true + e.presentation.isEnabled = project.solution.solutionLifecycle.fullStartupFinished.valueOrNull?.fullStartupTime != null findProjects = WorkspaceModel.getInstance(project).findProjects() val beatSaberProjectManager = project.service() diff --git a/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/actions/BeatSaberReferenceGeneratorAction.kt b/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/actions/BeatSaberReferenceGeneratorAction.kt index 3523d30..de5f035 100644 --- a/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/actions/BeatSaberReferenceGeneratorAction.kt +++ b/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/actions/BeatSaberReferenceGeneratorAction.kt @@ -1,9 +1,11 @@ package com.github.fernthedev.bsmt_rider.actions import com.github.fernthedev.bsmt_rider.helpers.BeatSaberReferenceManager +import com.intellij.ide.util.treeView.AbstractTreeNode import com.intellij.openapi.actionSystem.ActionUpdateThread import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.CommonDataKeys +import com.intellij.openapi.actionSystem.PlatformCoreDataKeys import com.intellij.openapi.components.service import com.intellij.platform.ide.progress.withBackgroundProgress import kotlinx.coroutines.launch @@ -14,14 +16,15 @@ class BeatSaberReferenceGeneratorAction : BeatSaberProjectAction() { override fun actionPerformed(e: AnActionEvent) { val project = e.getData(CommonDataKeys.PROJECT) + val selectedItem = e.getData(PlatformCoreDataKeys.SELECTED_ITEM) as? AbstractTreeNode<*> - if (project == null || !e.presentation.isEnabledAndVisible || findProjects == null) return + if (project == null || selectedItem == null || !e.presentation.isEnabledAndVisible || findProjects == null) return val referenceManager = project.service() referenceManager.scope.launch { withBackgroundProgress(project, "Adding to Beat Saber References", cancellable = true) { - referenceManager.askToAddReferences() + referenceManager.askToAddReferences(selectedItem.parent.name!!) } } } diff --git a/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/dialogue/BeatSaberChooseDialogue.kt b/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/dialogue/BeatSaberChooseDialogue.kt index 39e0ff0..304f26d 100644 --- a/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/dialogue/BeatSaberChooseDialogue.kt +++ b/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/dialogue/BeatSaberChooseDialogue.kt @@ -55,8 +55,8 @@ class BeatSaberChooseDialogue(val project: Project?) : DialogWrapper(project) { beatSaberInput.componentPopupMenu?.isVisible = AppSettingsState.instance.beatSaberFolders.isNotEmpty() beatSaberInput.isEditable = true - setAsDefault = CheckBox("Set this beat saber folder as default") - addToConfigCheckbox = CheckBox("Store this beat saber folder in config") + setAsDefault = CheckBox("Set as default") + addToConfigCheckbox = CheckBox("Store in config") // logic diff --git a/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/dialogue/BeatSaberReferencesDialogue.kt b/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/dialogue/BeatSaberReferencesDialogue.kt index d5aa585..86139d1 100644 --- a/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/dialogue/BeatSaberReferencesDialogue.kt +++ b/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/dialogue/BeatSaberReferencesDialogue.kt @@ -3,18 +3,17 @@ package com.github.fernthedev.bsmt_rider.dialogue import com.github.fernthedev.bsmt_rider.xml.ReferenceXML import com.intellij.openapi.project.Project import com.intellij.openapi.ui.DialogWrapper +import com.intellij.ui.CheckBoxList import com.intellij.ui.SearchTextField -import com.intellij.ui.components.JBCheckBox import com.intellij.ui.components.JBScrollPane -import com.intellij.ui.table.JBTable import com.intellij.util.PathUtil import com.intellij.util.ui.FormBuilder -import com.intellij.util.ui.UI +import com.intellij.util.ui.JBUI import java.awt.Dimension import java.io.File import java.util.* +import javax.swing.JCheckBox import javax.swing.JComponent -import javax.swing.table.AbstractTableModel /// $(Beat_Saber_Path)\Beat Saber_Data\Managed @@ -29,8 +28,7 @@ class BeatSaberReferencesDialogue( val references = ArrayList() private val _searchBox = SearchTextField(false) - private val _parentDirectoryCheckbox = JBCheckBox("Show parent directory", true) - private val _beatSaberReferences: JBTable + private val _beatSaberReferences: CheckBoxList private val _beatSaberReferencesScrollPane: JBScrollPane init { @@ -40,7 +38,7 @@ class BeatSaberReferencesDialogue( throw IllegalArgumentException("Beat saber folders are empty or not found!") val existingReferencesMatch = existingReferences.map { ref -> - PathUtil.getFileName(ref.stringHintPath) + ref.stringHintPath?.let { PathUtil.getFileName(it) } } @@ -55,72 +53,77 @@ class BeatSaberReferencesDialogue( // Merge list }.reduce { acc, arrayOfFiles -> acc + arrayOfFiles - // Remove duplicates - }.distinct().map { + }.map { BeatSaberReferencePair(false, it) } - title = "Beat Saber Reference Manager" + title = "Add Beat Saber Reference" - _beatSaberReferences = - JBTable(BeatSaberReferenceTable(_foundBeatSaberReferences, _parentDirectoryCheckbox.isSelected)) - _beatSaberReferences.setShowColumns(true) + _beatSaberReferences = CheckBoxList() + _beatSaberReferences.setItems(_foundBeatSaberReferences) { pair -> + pair.file.nameWithoutExtension + } + _beatSaberReferences.setCheckBoxListListener { index, value -> + _beatSaberReferences.getItemAt(index)!!.included = value + } _beatSaberReferencesScrollPane = JBScrollPane(_beatSaberReferences) _searchBox.textEditor.addCaretListener { - val beatSaberReferenceTable = _beatSaberReferences.model as BeatSaberReferenceTable - beatSaberReferenceTable.rows = - _foundBeatSaberReferences.filter { it.file.path.lowercase().contains(_searchBox.text.lowercase()) } - beatSaberReferenceTable.fireTableDataChanged() - } + val searchText = _searchBox.text.lowercase() + val filteredReferences = _foundBeatSaberReferences.filter { pair -> + pair.file.nameWithoutExtension.lowercase().contains(searchText) + } + + _beatSaberReferences.setItems(filteredReferences) { pair -> + pair.file.nameWithoutExtension + } - _parentDirectoryCheckbox.addChangeListener { - val beatSaberReferenceTable = _beatSaberReferences.model as BeatSaberReferenceTable - beatSaberReferenceTable.showParentFolder = _parentDirectoryCheckbox.isSelected - beatSaberReferenceTable.fireTableDataChanged() + refreshCheckboxes() } + setOKButtonText("Add") + refreshCheckboxes() init() } + private fun refreshCheckboxes() { + val model = _beatSaberReferences.model + for (i in 0 until model.size) { + val checkbox = model.getElementAt(i) as JCheckBox + val pair = _beatSaberReferences.getItemAt(i)!! + checkbox.isSelected = pair.included + checkbox.toolTipText = pair.file.absolutePath + } + } override fun createCenterPanel(): JComponent { // This is not a self assignment, they are different methods! // _beatSaberReferencesScrollPane.preferredSize = _beatSaberReferences.preferredSize - _beatSaberReferences.autoResizeMode = JBTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS - - _beatSaberReferences.columnModel.getColumn(0).maxWidth = UI.scale(100) - _beatSaberReferences.fillsViewportHeight = true // _beatSaberReferences.preferredScrollableViewportSize = _beatSaberReferences.preferredScrollableViewportSize val panel = FormBuilder.createFormBuilder() .addComponent(_searchBox) - .addComponent(_parentDirectoryCheckbox) - .addLabeledComponentFillVertically("BeatSaber references", _beatSaberReferencesScrollPane) + .addComponentFillVertically(_beatSaberReferencesScrollPane, 8) // .addComponentFillVertically(JPanel(GridLayout()), 0) .panel - panel.preferredSize = Dimension(UI.scale(340), UI.scale(350)) + panel.minimumSize = Dimension(JBUI.scale(400), JBUI.scale(400)) + panel.preferredSize = Dimension(JBUI.scale(400), JBUI.scale(400)) return panel } - override fun getPreferredFocusedComponent(): JComponent? { + override fun getPreferredFocusedComponent(): JComponent { return _searchBox } override fun doOKAction() { - val model = _beatSaberReferences.model - - for (i in 0 until model.rowCount) { - - val pair = model.getValueAt(i, -1) as BeatSaberReferencePair - + _foundBeatSaberReferences.forEach { pair -> if (pair.included) { references.add(pair.file) } @@ -130,127 +133,7 @@ class BeatSaberReferencesDialogue( } } -private enum class ColumnEnum( - val strName: String, - val clazz: Class<*>, - val editable: Boolean = false, -) { - INCLUDE("Include", Boolean::class.javaObjectType, true), - REFERENCE("Reference", String::class.java) -} - data class BeatSaberReferencePair( var included: Boolean = false, var file: File, ) - - -class BeatSaberReferenceTable(files: List, var showParentFolder: Boolean) : - AbstractTableModel() { - private val columns: Array = arrayOf(ColumnEnum.INCLUDE, ColumnEnum.REFERENCE) - - var rows: List = ArrayList(files) - set(value) { - field = ArrayList(value) - } - - /** - * Returns a default name for the column using spreadsheet conventions: - * A, B, C, ... Z, AA, AB, etc. If `column` cannot be found, - * returns an empty string. - * - * @param column the column being queried - * @return a string containing the default name of `column` - */ - override fun getColumnName(column: Int): String { - return columns[column].strName - } - - /** - * Returns `Object.class` regardless of `columnIndex`. - * - * @param columnIndex the column being queried - * @return the Object.class - */ - override fun getColumnClass(columnIndex: Int): Class<*> { - return columns[columnIndex].clazz - } - - /** - * Returns false. This is the default implementation for all cells. - * - * @param rowIndex the row being queried - * @param columnIndex the column being queried - * @return false - */ - override fun isCellEditable(rowIndex: Int, columnIndex: Int): Boolean { - return columns[columnIndex].editable - } - - /** - * Returns the number of rows in the model. A - * `JTable` uses this method to determine how many rows it - * should display. This method should be quick, as it - * is called frequently during rendering. - * - * @return the number of rows in the model - * @see .getColumnCount - */ - override fun getRowCount(): Int { - return rows.size - } - - /** - * Returns the number of columns in the model. A - * `JTable` uses this method to determine how many columns it - * should create and display by default. - * - * @return the number of columns in the model - * @see .getRowCount - */ - override fun getColumnCount(): Int { - return columns.size - } - - /** - * Returns the value for the cell at `columnIndex` and - * `rowIndex`. - * - * @param rowIndex the row whose value is to be queried - * @param columnIndex the column whose value is to be queried - * @return the value Object at the specified cell - */ - override fun getValueAt(rowIndex: Int, columnIndex: Int): Any? { - val pair = rows[rowIndex] - - return when (columnIndex) { - // We use this ourselves - -1 -> pair - 0 -> pair.included - 1 -> - if (showParentFolder) "${pair.file.parentFile.name}/${pair.file.nameWithoutExtension}" - else pair.file.nameWithoutExtension - - else -> null - } - } - - /** - * This empty implementation is provided so users don't have to implement - * this method if their data model is not editable. - * - * @param aValue value to assign to cell - * @param rowIndex row of cell - * @param columnIndex column of cell - */ - override fun setValueAt(aValue: Any?, rowIndex: Int, columnIndex: Int) { - - val pair = rows[rowIndex] - - when (columnIndex) { - 0 -> pair.included = aValue as Boolean - } - } - - -} \ No newline at end of file diff --git a/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/helpers/BeatSaberReferenceManager.kt b/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/helpers/BeatSaberReferenceManager.kt index 2d4a19f..56834df 100644 --- a/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/helpers/BeatSaberReferenceManager.kt +++ b/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/helpers/BeatSaberReferenceManager.kt @@ -28,6 +28,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.io.File +import kotlin.collections.ArrayList import kotlin.io.path.Path import kotlin.io.path.nameWithoutExtension @@ -43,7 +44,7 @@ class BeatSaberReferenceManager( if (empty) return emptyList() readActionBlocking { - itemGroup.subTags.forEach { + itemGroup.subTags.filter{ it.name == "Reference" }.forEach { if (it.subTags.isNotEmpty()) { val text = it.text @@ -76,6 +77,9 @@ class BeatSaberReferenceManager( require(itemGroup.isWritable) { "Cannot write to tag!" } require(itemGroup.containingFile.isWritable) { "Cannot write to file!" } } + + val references = itemGroup.subTags.filter { it.name == "Reference" } + val subTags = references.map { it.copy() as XmlTag }.toMutableList() // I hate this // This allows for undo @@ -83,23 +87,17 @@ class BeatSaberReferenceManager( refsToAdd.forEach { ref -> val tag = itemGroup.createChildTag("Reference", null, ref.toXMLNoRoot().replace("\r\n", "\n"), false); - val includeName = Path(ref.stringHintPath).nameWithoutExtension + val includeName = Path(ref.stringHintPath!!).nameWithoutExtension val include = includeName.replace("\r\n", "\n") tag.setAttribute("Include", include) - var added = false - for (subTag in itemGroup.subTags) { - val nextInclude = subTag.getAttributeValue("Include") - if (nextInclude != null && nextInclude > include) { - itemGroup.addBefore(tag, subTag) - added = true - break - } - } - if (!added) { - itemGroup.addSubTag(tag, false) - } + subTags.add(tag) + } + + references.forEach { it.delete() } + subTags.sortedBy { it.getAttributeValue("Include")?.lowercase() }.forEach { + itemGroup.addSubTag(it, false) } }) @@ -107,11 +105,11 @@ class BeatSaberReferenceManager( } - suspend fun askToAddReferences() { + suspend fun askToAddReferences(projectName: String) { require(!ApplicationManager.getApplication().isDispatchThread) val projectData: ProjectModelEntity = - WorkspaceModel.getInstance(project).findProjectsByName(project.name).first() + WorkspaceModel.getInstance(project).findProjectsByName(projectName).first() val projectRdData: RdProjectDescriptor = projectData.descriptor as RdProjectDescriptor val projectLocation = projectRdData.location as RdCustomLocation @@ -146,12 +144,13 @@ class BeatSaberReferenceManager( ?: throw IllegalAccessException("No user csproj file found ${project.projectFilePath}:${project.name}") // TODO: Make this get the beat saber dir from csproj.user? val managedPath = BeatSaberUtils.getAssembliesOfBeatSaber(path) + var ipaPath = BeatSaberUtils.getModLoaderOfBeatSaber(path) val libsPath = BeatSaberUtils.getLibsOfBeatSaber(path) val pluginsPath = BeatSaberUtils.getPluginsOfBeatSaber(path) // Open dialog and block until closed val refsFromDialogue = withContext(Dispatchers.EDT) { - val dialogue = BeatSaberReferencesDialogue(project, arrayOf(managedPath, libsPath, pluginsPath), refs) + val dialogue = BeatSaberReferencesDialogue(project, arrayOf(managedPath, ipaPath, libsPath, pluginsPath), refs) if (dialogue.showAndGet()) { dialogue.references diff --git a/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/helpers/BeatSaberUtils.kt b/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/helpers/BeatSaberUtils.kt index 6b7217a..3620b05 100644 --- a/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/helpers/BeatSaberUtils.kt +++ b/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/helpers/BeatSaberUtils.kt @@ -13,6 +13,7 @@ object BeatSaberUtils { fun getAssembliesOfBeatSaber(beatSaberPath: String): String = Path(beatSaberPath, "Beat Saber_Data", "Managed").toString() + fun getModLoaderOfBeatSaber(beatSaberPath: String): String = Path(beatSaberPath, "IPA").toString() fun getLibsOfBeatSaber(beatSaberPath: String): String = Path(beatSaberPath, "Libs").toString() diff --git a/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/helpers/ProjectUtils.kt b/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/helpers/ProjectUtils.kt index a32182d..7b2110d 100644 --- a/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/helpers/ProjectUtils.kt +++ b/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/helpers/ProjectUtils.kt @@ -92,9 +92,7 @@ class ProjectUtils( // ReloadProjectAction.execute() is the original code - while (project.solution.isLoading.valueOrNull == true) { - yieldThroughInvokeLater() - } + yieldThroughInvokeLater() withContext(Dispatchers.EDT) { // We do the same here sadly diff --git a/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/helpers/ThreadHelpers.kt b/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/helpers/ThreadHelpers.kt index e824189..d32579e 100644 --- a/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/helpers/ThreadHelpers.kt +++ b/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/helpers/ThreadHelpers.kt @@ -35,10 +35,12 @@ fun runReadActionSafely(runnable: () -> V): V { fun runWriteActionSafely(runnable: () -> Unit) { if (ApplicationManager.getApplication().isWriteAccessAllowed) { runnable(); + return } if (ApplicationManager.getApplication().isDispatchThread) { runWriteAction(runnable); + return } invokeLater { diff --git a/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/xml/ReferenceXML.kt b/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/xml/ReferenceXML.kt index 072823f..fa8907b 100644 --- a/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/xml/ReferenceXML.kt +++ b/src/rider/main/kotlin/com/github/fernthedev/bsmt_rider/xml/ReferenceXML.kt @@ -10,7 +10,7 @@ import com.intellij.util.xml.DomElement data class ReferenceXML( @JsonProperty("HintPath") @get:JsonProperty("HintPath") - val stringHintPath: String, + val stringHintPath: String?, @JsonProperty("Private") @get:JsonProperty("Private") @@ -19,7 +19,7 @@ data class ReferenceXML( // I really hate that I have to do this fun toXMLNoRoot() = """ - ${stringHintPath} + ${(stringHintPath?.let { "$it" }) ?: ""} ${private} """.trimIndent() } diff --git a/src/rider/main/resources/META-INF/plugin.xml b/src/rider/main/resources/META-INF/plugin.xml index 19143de..cbd0381 100644 --- a/src/rider/main/resources/META-INF/plugin.xml +++ b/src/rider/main/resources/META-INF/plugin.xml @@ -46,10 +46,9 @@ - - +