├── img
├── how-to-use-1.png
├── how-to-use-2.png
├── how-to-use-3.png
├── how-to-use-4.png
├── how-to-use-5.png
├── rider-monogame-cover.pdn
├── rider-monogame-cover.png
├── rider-plus-monogame-128.png
└── rider-plus-monogame-200.png
├── test
└── MonoGameSolution
│ ├── Desktop381
│ ├── Program.cs
│ ├── Icon.bmp
│ ├── Icon.ico
│ ├── Content
│ │ ├── Content.mgcb
│ │ └── File.fx
│ ├── .config
│ │ └── dotnet-tools.json
│ ├── Desktop381.csproj
│ ├── Game1.cs
│ └── app.manifest
│ ├── Desktop38
│ ├── Icon.bmp
│ ├── Icon.ico
│ ├── Program.cs
│ ├── Content
│ │ └── Content.mgcb
│ ├── Desktop38.csproj
│ ├── Game1.cs
│ └── app.manifest
│ ├── .config
│ └── dotnet-tools.json
│ └── MonoGameSolution.sln
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── src
├── rider
│ └── main
│ │ ├── resources
│ │ ├── icons
│ │ │ ├── fx.png
│ │ │ ├── mgcb.png
│ │ │ ├── mgcb@2x.png
│ │ │ ├── mgcb_dark.png
│ │ │ ├── spritefont.png
│ │ │ └── mgcb@2x_dark.png
│ │ ├── fileTemplates
│ │ │ ├── Content Builder File.mgcb.ft
│ │ │ ├── Sprite Effect File.fx.ft
│ │ │ ├── Effect File.fx.ft
│ │ │ ├── SpriteFont File.spritefont.ft
│ │ │ └── Localized SpriteFont File.spritefont.ft
│ │ ├── META-INF
│ │ │ ├── pluginIcon.svg
│ │ │ └── plugin.xml
│ │ ├── messages
│ │ │ └── MonoGameUiBundle.properties
│ │ └── xmlSchema
│ │ │ └── SpriteFont.xsd
│ │ └── kotlin
│ │ └── me
│ │ └── seclerp
│ │ └── rider
│ │ ├── plugins
│ │ └── monogame
│ │ │ ├── KnownNotificationGroups.kt
│ │ │ ├── mgcb
│ │ │ ├── previewer
│ │ │ │ ├── properties
│ │ │ │ │ ├── MgcbProperty.kt
│ │ │ │ │ ├── MgcbPropertyValueColumn.kt
│ │ │ │ │ ├── MgcbPropertyTableModel.kt
│ │ │ │ │ ├── MgcbPropertyTable.kt
│ │ │ │ │ ├── MgcbPropertyKeyColumn.kt
│ │ │ │ │ ├── MgcbPropertyColumn.kt
│ │ │ │ │ └── MgcbPropertyRenderer.kt
│ │ │ │ ├── tree
│ │ │ │ │ ├── MgcbFolderNode.kt
│ │ │ │ │ ├── MgcbTreeNode.kt
│ │ │ │ │ ├── MgcbBuildEntryNode.kt
│ │ │ │ │ ├── MgcbNodeRenderer.kt
│ │ │ │ │ └── MgcbTree.kt
│ │ │ │ ├── MgcbEditorNotificationPanel.kt
│ │ │ │ ├── listeners
│ │ │ │ │ ├── MgcbPendingUpdateListener.kt
│ │ │ │ │ ├── MgcbProcessedUpdateListener.kt
│ │ │ │ │ ├── MgcbFileListener.kt
│ │ │ │ │ ├── MgcbDocumentListener.kt
│ │ │ │ │ └── MgcbPendingUpdateListenerImpl.kt
│ │ │ │ ├── MgcbPreviewerTopics.kt
│ │ │ │ ├── services
│ │ │ │ │ ├── MgcbAnalyzer.kt
│ │ │ │ │ └── MgcbBuildTreeManager.kt
│ │ │ │ ├── MgcbEditorWithPreview.kt
│ │ │ │ ├── MgcbEditorProvider.kt
│ │ │ │ ├── MgcbEditorFloatingToolbarProvider.kt
│ │ │ │ ├── MgcbModel.kt
│ │ │ │ ├── MgcbModelBuilderVisitor.kt
│ │ │ │ └── MgcbEditorPreviewer.kt
│ │ │ ├── MgcbLexerAdapter.kt
│ │ │ ├── MgcbLanguage.kt
│ │ │ ├── actions
│ │ │ │ ├── commands
│ │ │ │ │ ├── RegisterMgcbEditorCommand.kt
│ │ │ │ │ ├── CliCommandResult.kt
│ │ │ │ │ ├── InstallMgcbEditorCommand.kt
│ │ │ │ │ ├── CheckMgcbEditorInstalledCommand.kt
│ │ │ │ │ ├── MgcbEditorCommand.kt
│ │ │ │ │ ├── Extensions.kt
│ │ │ │ │ ├── MgcbProcessAdapter.kt
│ │ │ │ │ └── CliCommand.kt
│ │ │ │ └── OpenExternalEditorAction.kt
│ │ │ ├── psi
│ │ │ │ ├── MgcbPsiImplUtil.kt
│ │ │ │ ├── MgcbTokenType.kt
│ │ │ │ ├── MgcbElementType.kt
│ │ │ │ └── MgcbFile.kt
│ │ │ ├── MgcbSyntaxHighlighterFactory.kt
│ │ │ ├── toolset
│ │ │ │ ├── MgcbResolvedTool.kt
│ │ │ │ ├── MgcbToolsetState.kt
│ │ │ │ ├── DotNetToolsVersion.kt
│ │ │ │ └── MgcbToolsetHost.kt
│ │ │ ├── MgcbFileType.kt
│ │ │ ├── state
│ │ │ │ └── MgcbEditorGlobalState.kt
│ │ │ ├── Mgcb.bnf
│ │ │ ├── MgcbParserDefinition.kt
│ │ │ ├── settings
│ │ │ │ └── MgcbColorSettingsPage.kt
│ │ │ ├── Mgcb.flex
│ │ │ ├── MgcbCompletionContributor.kt
│ │ │ └── MgcbSyntaxHighlighter.kt
│ │ │ ├── templates
│ │ │ ├── MonoGameProjectTemplateCustomizer.kt
│ │ │ ├── MonoGameTemplateMetadata.kt
│ │ │ ├── MonoGameTemplateType.kt
│ │ │ └── MonoGameProjectTemplateGenerator.kt
│ │ │ ├── MonoGameIcons.kt
│ │ │ ├── effect
│ │ │ └── EffectFileType.kt
│ │ │ ├── spritefont
│ │ │ ├── SpriteFontFileType.kt
│ │ │ └── SpriteFontXmlSchemaProvider.kt
│ │ │ ├── MonoGameUiBundle.kt
│ │ │ ├── settings
│ │ │ ├── MonoGameSettingsConfigurable.kt
│ │ │ └── MgcbToolsetConfigurableProvider.kt
│ │ │ └── CommonExtensions.kt
│ │ └── extensions
│ │ ├── commandLine
│ │ ├── CliCommandExecutor.kt
│ │ ├── CommandBuilder.kt
│ │ └── DefaultCommandExecutor.kt
│ │ ├── ClipboardUtil.kt
│ │ ├── observables
│ │ └── RdObservableProperty.kt
│ │ └── workspaceModel
│ │ └── WorkspaceModelExtensions.kt
└── dotnet
│ ├── Rider.Plugins.MonoGame
│ ├── ZoneMarker.cs
│ ├── Options
│ │ └── ZoneMarker.cs
│ ├── Mgcb
│ │ ├── KnownMgcbVersions.cs
│ │ ├── MgcbToolset.cs
│ │ ├── KnownDotNetToolsCommands.cs
│ │ ├── KnownDotNetTools.cs
│ │ ├── ProjectDotnetToolsTracker.cs
│ │ ├── MgcbEditorCommandNameResolver.cs
│ │ ├── MgcbToolsetTracker.cs
│ │ └── MgcbEditorToolResolver.cs
│ ├── Extensions
│ │ ├── DotNetToolLocalCacheExtensions.cs
│ │ └── FlowExtensions.cs
│ ├── IMonoGameRiderZone.cs
│ ├── Rider.Plugins.MonoGame.csproj
│ ├── Templates.DotSettings
│ └── MonoGameRdModelHost.cs
│ ├── Rider.Plugins.MonoGame.sln.DotSettings
│ ├── Plugin.props
│ ├── Rider.Plugins.MonoGame.Tests
│ └── Rider.Plugins.MonoGame.Tests.csproj
│ ├── Directory.Packages.props
│ ├── Directory.Build.props
│ └── Rider.Plugins.MonoGame.sln
├── .gitattributes
├── .github
├── variables
│ └── .env
└── workflows
│ ├── environment
│ └── action.yml
│ ├── build.yml
│ └── deploy.yml
├── .gitignore
├── renovate.json
├── .idea
├── .idea.ReSharperPlugin.MonoGameRider
│ └── .idea
│ │ └── runConfigurations
│ │ ├── Rider__Frontend__Unix_.xml
│ │ ├── rdgen__Windows_.xml
│ │ ├── rdgen__Unix_.xml
│ │ ├── Rider__Frontend__Windows_.xml
│ │ ├── VisualStudio.xml
│ │ ├── Rider__Unix_.xml
│ │ └── Rider__Windows_.xml
└── runConfigurations
│ ├── Rider.xml
│ └── rdgen.xml
├── gradle.properties
├── LICENSE
├── protocol
├── src
│ └── main
│ │ └── kotlin
│ │ └── model
│ │ └── rider
│ │ └── MonoGameRiderModel.kt
└── build.gradle.kts
├── settings.gradle.kts
├── docs
└── Previewer Architecture.drawio
├── README.md
├── CHANGELOG.md
└── gradlew.bat
/img/how-to-use-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seclerp/rider-monogame/HEAD/img/how-to-use-1.png
--------------------------------------------------------------------------------
/img/how-to-use-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seclerp/rider-monogame/HEAD/img/how-to-use-2.png
--------------------------------------------------------------------------------
/img/how-to-use-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seclerp/rider-monogame/HEAD/img/how-to-use-3.png
--------------------------------------------------------------------------------
/img/how-to-use-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seclerp/rider-monogame/HEAD/img/how-to-use-4.png
--------------------------------------------------------------------------------
/img/how-to-use-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seclerp/rider-monogame/HEAD/img/how-to-use-5.png
--------------------------------------------------------------------------------
/img/rider-monogame-cover.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seclerp/rider-monogame/HEAD/img/rider-monogame-cover.pdn
--------------------------------------------------------------------------------
/img/rider-monogame-cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seclerp/rider-monogame/HEAD/img/rider-monogame-cover.png
--------------------------------------------------------------------------------
/test/MonoGameSolution/Desktop381/Program.cs:
--------------------------------------------------------------------------------
1 | using var game = new MonoGameProjectDesktop381.Game1();
2 | game.Run();
--------------------------------------------------------------------------------
/img/rider-plus-monogame-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seclerp/rider-monogame/HEAD/img/rider-plus-monogame-128.png
--------------------------------------------------------------------------------
/img/rider-plus-monogame-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seclerp/rider-monogame/HEAD/img/rider-plus-monogame-200.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seclerp/rider-monogame/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/src/rider/main/resources/icons/fx.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seclerp/rider-monogame/HEAD/src/rider/main/resources/icons/fx.png
--------------------------------------------------------------------------------
/src/rider/main/resources/icons/mgcb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seclerp/rider-monogame/HEAD/src/rider/main/resources/icons/mgcb.png
--------------------------------------------------------------------------------
/test/MonoGameSolution/Desktop38/Icon.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seclerp/rider-monogame/HEAD/test/MonoGameSolution/Desktop38/Icon.bmp
--------------------------------------------------------------------------------
/test/MonoGameSolution/Desktop38/Icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seclerp/rider-monogame/HEAD/test/MonoGameSolution/Desktop38/Icon.ico
--------------------------------------------------------------------------------
/src/rider/main/resources/icons/mgcb@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seclerp/rider-monogame/HEAD/src/rider/main/resources/icons/mgcb@2x.png
--------------------------------------------------------------------------------
/test/MonoGameSolution/Desktop381/Icon.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seclerp/rider-monogame/HEAD/test/MonoGameSolution/Desktop381/Icon.bmp
--------------------------------------------------------------------------------
/test/MonoGameSolution/Desktop381/Icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seclerp/rider-monogame/HEAD/test/MonoGameSolution/Desktop381/Icon.ico
--------------------------------------------------------------------------------
/src/rider/main/resources/icons/mgcb_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seclerp/rider-monogame/HEAD/src/rider/main/resources/icons/mgcb_dark.png
--------------------------------------------------------------------------------
/src/rider/main/resources/icons/spritefont.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seclerp/rider-monogame/HEAD/src/rider/main/resources/icons/spritefont.png
--------------------------------------------------------------------------------
/src/rider/main/resources/icons/mgcb@2x_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seclerp/rider-monogame/HEAD/src/rider/main/resources/icons/mgcb@2x_dark.png
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Set the default behavior, in case people don't have core.autocrlf set.
2 | * text=auto
3 |
4 | # Preserve line endings in gradle scripts
5 | gradlew* -text diff
6 |
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/KnownNotificationGroups.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame
2 |
3 | object KnownNotificationGroups {
4 | val monoGameRider = "MonoGameRider"
5 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/properties/MgcbProperty.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer.properties
2 |
3 | data class MgcbProperty(val key: String, val value: String)
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/MgcbLexerAdapter.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb
2 |
3 | import com.intellij.lexer.FlexAdapter
4 |
5 | class MgcbLexerAdapter : FlexAdapter(MgcbLexer(null))
--------------------------------------------------------------------------------
/.github/variables/.env:
--------------------------------------------------------------------------------
1 | PLUGIN_NAME=rider-monogame
2 | PLUGINS_REPOSITORY=https://plugins.jetbrains.com
3 | ARTIFACTS_FOLDER=artifacts
4 | ARTIFACTS_CHANGELOG=changelog.txt
5 | ARTIFACTS_VERSION=version.txt
6 | ARTIFACTS_PLUGIN_ID=pluginId.txt
--------------------------------------------------------------------------------
/src/dotnet/Rider.Plugins.MonoGame/ZoneMarker.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Application.BuildScript.Application.Zones;
2 |
3 | namespace Rider.Plugins.MonoGame;
4 |
5 | [ZoneMarker]
6 | public class ZoneMarker : IRequire
7 | {
8 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/MgcbLanguage.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb
2 |
3 | import com.intellij.lang.Language
4 |
5 | class MgcbLanguage : Language("MGCB") {
6 | companion object {
7 | val Instance = MgcbLanguage()
8 | }
9 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/tree/MgcbFolderNode.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer.tree
2 |
3 | import com.intellij.util.PlatformIcons
4 |
5 | class MgcbFolderNode(name: String)
6 | : MgcbTreeNode(name, PlatformIcons.FOLDER_ICON)
--------------------------------------------------------------------------------
/src/dotnet/Rider.Plugins.MonoGame/Options/ZoneMarker.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Application.BuildScript.Application.Zones;
2 | using JetBrains.Application.UI.Options.OptionPages;
3 |
4 | namespace Rider.Plugins.MonoGame.Options;
5 |
6 | [ZoneMarker]
7 | public class ZoneMarker : IRequire
8 | {
9 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/tree/MgcbTreeNode.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer.tree
2 |
3 | import javax.swing.Icon
4 | import javax.swing.tree.DefaultMutableTreeNode
5 |
6 | abstract class MgcbTreeNode(name: String, val icon: Icon) : DefaultMutableTreeNode(name)
--------------------------------------------------------------------------------
/test/MonoGameSolution/Desktop38/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Desktop38
4 | {
5 | public static class Program
6 | {
7 | [STAThread]
8 | static void Main()
9 | {
10 | using (var game = new Game1())
11 | game.Run();
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build artifacts
2 | /.intellijPlatform/
3 | [Bb]in/
4 | [Oo]bj/
5 | build
6 | output
7 | .gradle
8 | .tmp
9 |
10 | # User-specific files
11 | *.suo
12 | *.user
13 | *.sln.docstates
14 | *.cache
15 |
16 | # IDEs
17 | **/.idea/
18 | !**/.idea/runConfigurations/
19 | .vs
20 | _ReSharper*
21 | _dotTrace*
22 | gen
23 | Rd
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/actions/commands/RegisterMgcbEditorCommand.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.actions.commands
2 |
3 | import com.intellij.execution.configurations.GeneralCommandLine
4 |
5 | class RegisterMgcbEditorCommand : CliCommand(GeneralCommandLine("mgcb-editor", "--register"))
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://cache-redirector.jetbrains.com/services.gradle.org/distributions/gradle-8.14.3-all.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/src/dotnet/Rider.Plugins.MonoGame/Mgcb/KnownMgcbVersions.cs:
--------------------------------------------------------------------------------
1 | using NuGet.Versioning;
2 |
3 | namespace Rider.Plugins.MonoGame.Mgcb;
4 |
5 | public class KnownMgcbVersions
6 | {
7 | public static readonly NuGetVersion Version381 = NuGetVersion.Parse("3.8.1");
8 | public static readonly NuGetVersion Version380 = NuGetVersion.Parse("3.8.0");
9 | }
--------------------------------------------------------------------------------
/src/dotnet/Rider.Plugins.MonoGame/Mgcb/MgcbToolset.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.DataFlow;
2 |
3 | namespace Rider.Plugins.MonoGame.Mgcb;
4 |
5 | public class MgcbToolset where TTool : class
6 | {
7 | public IProperty Editor { get; init; }
8 |
9 | public void Unset()
10 | {
11 | Editor.SetValue(null);
12 | }
13 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/properties/MgcbPropertyValueColumn.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer.properties
2 |
3 | class MgcbPropertyValueColumn : MgcbPropertyColumn("Value") {
4 | override fun valueOf(item: MgcbProperty?): String? {
5 | return item?.value
6 | }
7 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/actions/commands/CliCommandResult.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.actions.commands
2 |
3 | data class CliCommandResult(
4 | val command: String,
5 | val exitCode: Int,
6 | val output: String,
7 | val succeeded: Boolean,
8 | val error: String? = null
9 | )
10 |
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/properties/MgcbPropertyTableModel.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer.properties
2 |
3 | import com.intellij.util.ui.ListTableModel
4 |
5 | class MgcbPropertyTableModel : ListTableModel(
6 | MgcbPropertyKeyColumn(),
7 | MgcbPropertyValueColumn()
8 | )
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/actions/commands/InstallMgcbEditorCommand.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.actions.commands
2 |
3 | import com.intellij.execution.configurations.GeneralCommandLine
4 |
5 | class InstallMgcbEditorCommand : CliCommand(
6 | GeneralCommandLine("dotnet", "tool", "install", "-g", "dotnet-mgcb-editor"))
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/properties/MgcbPropertyTable.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer.properties
2 |
3 | import com.intellij.ui.table.TableView
4 |
5 | class MgcbPropertyTable(model: MgcbPropertyTableModel) : TableView(model) {
6 | override fun createDefaultTableHeader() = JBTableHeader()
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/properties/MgcbPropertyKeyColumn.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer.properties
2 |
3 | import com.intellij.util.ui.ColumnInfo
4 |
5 | class MgcbPropertyKeyColumn : MgcbPropertyColumn("Key") {
6 | override fun valueOf(item: MgcbProperty?): String? {
7 | return item?.key
8 | }
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/test/MonoGameSolution/.config/dotnet-tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "isRoot": true,
4 | "tools": {
5 | "dotnet-mgcb-editor-mac": {
6 | "version": "3.8.4",
7 | "commands": [
8 | "mgcb-editor-mac"
9 | ]
10 | },
11 | "dotnet-mgcb-editor": {
12 | "version": "3.8.4",
13 | "commands": [
14 | "mgcb-editor"
15 | ]
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/extensions/commandLine/CliCommandExecutor.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.extensions.commandLine
2 |
3 | import com.intellij.execution.configurations.GeneralCommandLine
4 | import com.intellij.openapi.project.Project
5 |
6 | abstract class CliCommandExecutor(
7 | protected val intellijProject: Project
8 | ) {
9 | abstract fun execute(command: GeneralCommandLine)
10 | }
--------------------------------------------------------------------------------
/.github/workflows/environment/action.yml:
--------------------------------------------------------------------------------
1 | name: Import environment variables
2 | description: Import environment variables from a file
3 | inputs:
4 | envfile:
5 | description: '.env file to import'
6 | required: true
7 | default: .github/variables/.env
8 | runs:
9 | using: "composite"
10 | steps:
11 | - shell: pwsh
12 | run: Get-Content ${{ inputs.envfile }} | Add-Content -Path $env:GITHUB_ENV
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/psi/MgcbPsiImplUtil.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.psi
2 |
3 | fun MgcbOption.getKey(): String? {
4 | val keyNode = node.findChildByType(MgcbTypes.OPTION_KEY)
5 | return keyNode?.text
6 | }
7 |
8 | fun MgcbOption.getValue(): String? {
9 | val valueNode = node.findChildByType(MgcbTypes.OPTION_VALUE)
10 | return valueNode?.text
11 | }
12 |
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/properties/MgcbPropertyColumn.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer.properties
2 |
3 | import com.intellij.util.ui.ColumnInfo
4 |
5 | abstract class MgcbPropertyColumn(name: String) : ColumnInfo(name) {
6 | private val renderer by lazy { MgcbPropertyRenderer() }
7 | override fun getRenderer(item: MgcbProperty?) = renderer
8 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/tree/MgcbBuildEntryNode.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer.tree
2 |
3 | import com.intellij.icons.AllIcons
4 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.BuildEntry
5 | import javax.swing.Icon
6 |
7 | class MgcbBuildEntryNode(name: String, icon: Icon?, val buildEntry: BuildEntry)
8 | : MgcbTreeNode(name, icon ?: AllIcons.FileTypes.Text)
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/actions/commands/CheckMgcbEditorInstalledCommand.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.actions.commands
2 |
3 | import com.intellij.execution.configurations.GeneralCommandLine
4 | import java.nio.charset.Charset
5 |
6 | class CheckMgcbEditorInstalledCommand : CliCommand(
7 | GeneralCommandLine("mgcb-editor", "--help")
8 | .withCharset(Charset.forName("UTF-8"))
9 | )
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/psi/MgcbTokenType.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.psi
2 |
3 | import com.intellij.psi.tree.IElementType
4 | import me.seclerp.rider.plugins.monogame.mgcb.MgcbLanguage
5 |
6 | class MgcbTokenType(debugName: String) : IElementType(debugName, MgcbLanguage.Instance) {
7 | override fun toString(): String {
8 | return "MgcbTokenType." + super.toString()
9 | }
10 | }
--------------------------------------------------------------------------------
/src/dotnet/Rider.Plugins.MonoGame/Mgcb/KnownDotNetToolsCommands.cs:
--------------------------------------------------------------------------------
1 | namespace Rider.Plugins.MonoGame.Mgcb;
2 |
3 | public class KnownDotNetToolsCommands
4 | {
5 | public const string MgcbEditor = "mgcb-editor";
6 | public const string MgcbEditorWindows = "mgcb-editor-windows";
7 | public const string MgcbEditorLinux = "mgcb-editor-linux";
8 | public const string MgcbEditorMac = "mgcb-editor-mac";
9 | public const string Mgcb = "mgcb";
10 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/extensions/ClipboardUtil.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.extensions
2 |
3 | import java.awt.Toolkit
4 | import java.awt.datatransfer.StringSelection
5 |
6 | internal object ClipboardUtil {
7 | fun copyToClipboard(text: String) {
8 | val selection = StringSelection(text)
9 | val clipboard = Toolkit.getDefaultToolkit().systemClipboard
10 | clipboard.setContents(selection, null)
11 | }
12 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/psi/MgcbElementType.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.psi
2 |
3 | import com.intellij.psi.tree.IElementType
4 | import me.seclerp.rider.plugins.monogame.mgcb.MgcbLanguage
5 |
6 | class MgcbElementType(debugName: String) : IElementType(debugName, MgcbLanguage.Instance) {
7 | override fun toString(): String {
8 | return "MgcbElementType." + super.toString()
9 | }
10 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/MgcbEditorNotificationPanel.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer
2 |
3 | import com.intellij.openapi.fileEditor.FileEditor
4 | import com.intellij.ui.EditorNotificationPanel
5 |
6 | class MgcbEditorNotificationPanel(fileEditor: FileEditor) : EditorNotificationPanel(fileEditor) {
7 | fun install() {
8 | executeAction("InstallMgcbEditorAction")
9 | }
10 | }
--------------------------------------------------------------------------------
/src/dotnet/Rider.Plugins.MonoGame/Mgcb/KnownDotNetTools.cs:
--------------------------------------------------------------------------------
1 | namespace Rider.Plugins.MonoGame.Mgcb;
2 |
3 | public class KnownDotNetTools
4 | {
5 | public const string MgcbEditor = "dotnet-mgcb-editor";
6 | public const string MgcbEditorWindows = "dotnet-mgcb-editor-windows";
7 | public const string MgcbEditorLinux = "dotnet-mgcb-editor-linux";
8 | public const string MgcbEditorMac = "dotnet-mgcb-editor-mac";
9 | public const string Mgcb = "dotnet-mgcb";
10 | }
--------------------------------------------------------------------------------
/test/MonoGameSolution/Desktop38/Content/Content.mgcb:
--------------------------------------------------------------------------------
1 |
2 | #----------------------------- Global Properties ----------------------------#
3 |
4 | /outputDir:bin/$(Platform)
5 | /intermediateDir:obj/$(Platform)
6 | /platform:DesktopGL
7 | /config:
8 | /profile:Reach
9 | /compress:False
10 |
11 | #-------------------------------- References --------------------------------#
12 |
13 |
14 | #---------------------------------- Content ---------------------------------#
15 |
16 |
--------------------------------------------------------------------------------
/src/rider/main/resources/fileTemplates/Content Builder File.mgcb.ft:
--------------------------------------------------------------------------------
1 | #----------------------------- Global Properties ----------------------------#
2 |
3 | /outputDir:bin/$(Platform)
4 | /intermediateDir:obj/$(Platform)
5 | /platform:DesktopGL
6 | /config:
7 | /profile:Reach
8 | /compress:False
9 |
10 | #-------------------------------- References --------------------------------#
11 |
12 |
13 | #---------------------------------- Content ---------------------------------#
14 |
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/templates/MonoGameProjectTemplateCustomizer.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.templates
2 |
3 | import com.jetbrains.rider.projectView.projectTemplates.providers.ProjectTemplateCustomizer
4 |
5 | internal class MonoGameProjectTemplateCustomizer : ProjectTemplateCustomizer {
6 | private val monoGameTemplateTypes = setOf(MonoGameTemplateType())
7 | override fun getCustomProjectTemplateTypes() = monoGameTemplateTypes
8 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/MonoGameIcons.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame
2 |
3 | import com.intellij.openapi.util.IconLoader
4 |
5 | object MonoGameIcons {
6 | @JvmField
7 | val MgcbFile = IconLoader.getIcon("icons/mgcb.png", javaClass)
8 |
9 | @JvmField
10 | val SpriteFontFile = IconLoader.getIcon("icons/spritefont.png", javaClass)
11 |
12 | @JvmField
13 | val EffectFile = IconLoader.getIcon("icons/fx.png", javaClass)
14 | }
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": ["config:recommended"],
4 | "packageRules": [
5 | {
6 | "groupName": ".NET MGCB tools",
7 | "matchManagers": ["nuget", "dotnet-tools"],
8 | "matchPackageNames": [
9 | "dotnet-mgcb-editor",
10 | "dotnet-mgcb-editor-mac",
11 | "dotnet-mgcb-editor-windows",
12 | "dotnet-mgcb-editor-linux",
13 | "dotnet-mgcb"
14 | ]
15 | }
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/src/dotnet/Rider.Plugins.MonoGame/Extensions/DotNetToolLocalCacheExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using JetBrains.Annotations;
3 | using JetBrains.ProjectModel.NuGet.DotNetTools;
4 |
5 | namespace Rider.Plugins.MonoGame.Extensions;
6 |
7 | public static class DotNetToolLocalCacheExtensions
8 | {
9 | [CanBeNull]
10 | public static LocalTool GetLocalTool(this DotNetToolLocalCache cache, string packageId) =>
11 | cache.GetAllLocalTools().FirstOrDefault(tool => tool.PackageId == packageId);
12 | }
--------------------------------------------------------------------------------
/src/dotnet/Rider.Plugins.MonoGame/Extensions/FlowExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using JetBrains.Annotations;
3 |
4 | namespace Rider.Plugins.MonoGame.Extensions;
5 |
6 | public static class FlowExtensions
7 | {
8 | public static T Apply(this T self, Action action)
9 | {
10 | action(self);
11 | return self;
12 | }
13 |
14 | public static TTarget Let(this TSource self, [NotNull] Func mapping)
15 | {
16 | return mapping(self);
17 | }
18 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/actions/commands/MgcbEditorCommand.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.actions.commands
2 |
3 | import com.intellij.execution.configurations.GeneralCommandLine
4 | import com.intellij.openapi.project.Project
5 | import java.nio.charset.Charset
6 |
7 | class MgcbEditorCommand(mgcbFilePath: String, project: Project)
8 | : CliCommand(GeneralCommandLine("mgcb-editor", mgcbFilePath)
9 | .withCharset(Charset.forName("UTF-8")))
10 |
--------------------------------------------------------------------------------
/src/dotnet/Rider.Plugins.MonoGame.sln.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | True
3 | True
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/MgcbSyntaxHighlighterFactory.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb
2 |
3 | import com.intellij.openapi.fileTypes.SyntaxHighlighter
4 | import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory
5 | import com.intellij.openapi.project.Project
6 | import com.intellij.openapi.vfs.VirtualFile
7 |
8 | class MgcbSyntaxHighlighterFactory : SyntaxHighlighterFactory() {
9 | override fun getSyntaxHighlighter(p0: Project?, p1: VirtualFile?): SyntaxHighlighter =
10 | MgcbSyntaxHighlighter()
11 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/toolset/MgcbResolvedTool.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.toolset
2 |
3 | import me.seclerp.rider.plugins.monogame.ToolDefinition
4 |
5 | sealed class MgcbResolvedTool {
6 | class Local(val definition: ToolDefinition) : MgcbResolvedTool()
7 | class Global(val definition: ToolDefinition) : MgcbResolvedTool()
8 | object None : MgcbResolvedTool()
9 | }
10 |
11 | fun MgcbResolvedTool.isNone() = this is MgcbResolvedTool.None
12 | fun MgcbResolvedTool.isNotNone() = this !is MgcbResolvedTool.None
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/listeners/MgcbPendingUpdateListener.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer.listeners
2 |
3 | import com.intellij.openapi.vfs.VirtualFile
4 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.MgcbPreviewerTopics
5 | import java.util.*
6 |
7 | interface MgcbPendingUpdateListener : EventListener {
8 | companion object {
9 | @JvmField
10 | val TOPIC = MgcbPreviewerTopics.MGCB_PENDING_UPDATE_TOPIC
11 | }
12 |
13 | fun handle(file: VirtualFile) {}
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/src/dotnet/Rider.Plugins.MonoGame/IMonoGameRiderZone.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Application.BuildScript.Application.Zones;
2 | using JetBrains.ProjectModel.NuGet;
3 | using JetBrains.ReSharper.Feature.Services.Daemon;
4 | using JetBrains.ReSharper.Psi;
5 | using JetBrains.ReSharper.Psi.CSharp;
6 |
7 | namespace Rider.Plugins.MonoGame;
8 |
9 | [ZoneDefinition]
10 | // [ZoneDefinitionConfigurableFeature("Title", "Description", IsInProductSection: false)]
11 | public interface IMonoGameRiderZone : IPsiLanguageZone,
12 | IRequire,
13 | IRequire,
14 | IRequire
15 | {
16 | }
--------------------------------------------------------------------------------
/test/MonoGameSolution/Desktop381/Content/Content.mgcb:
--------------------------------------------------------------------------------
1 |
2 | #----------------------------- Global Properties ----------------------------#
3 |
4 | /outputDir:bin/$(Platform)
5 | /intermediateDir:obj/$(Platform)
6 | /platform:DesktopGL
7 | /config:
8 | /profile:Reach
9 | /compress:False
10 |
11 | #-------------------------------- References --------------------------------#
12 |
13 |
14 | #---------------------------------- Content ---------------------------------#
15 |
16 | #begin File.fx
17 | /importer:EffectImporter
18 | /processor:EffectProcessor
19 | /processorParam:DebugMode=Auto
20 | /build:File.fx
21 |
22 |
--------------------------------------------------------------------------------
/.idea/.idea.ReSharperPlugin.MonoGameRider/.idea/runConfigurations/Rider__Frontend__Unix_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/dotnet/Plugin.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 2022.3.0-eap01
5 |
6 | MonoGame
7 | JetBrains Rider plugin for MonoGame
8 |
9 | Andrew Rublyov
10 | Copyright $([System.DateTime]::Now.Year) Andrew Rublyov
11 | resharper plugin
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/dotnet/Rider.Plugins.MonoGame.Tests/Rider.Plugins.MonoGame.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net48
5 | false
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/.idea/.idea.ReSharperPlugin.MonoGameRider/.idea/runConfigurations/rdgen__Windows_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/psi/MgcbFile.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.psi
2 |
3 | import com.intellij.extapi.psi.PsiFileBase
4 | import com.intellij.openapi.fileTypes.FileType
5 | import com.intellij.psi.FileViewProvider
6 | import me.seclerp.rider.plugins.monogame.mgcb.MgcbFileType
7 | import me.seclerp.rider.plugins.monogame.mgcb.MgcbLanguage
8 |
9 | class MgcbFile(viewProvider: FileViewProvider) : PsiFileBase(viewProvider, MgcbLanguage.Instance) {
10 | override fun getFileType(): FileType = MgcbFileType.Instance
11 |
12 | override fun toString(): String = "MGCB File"
13 | }
--------------------------------------------------------------------------------
/.idea/.idea.ReSharperPlugin.MonoGameRider/.idea/runConfigurations/rdgen__Unix_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/.idea.ReSharperPlugin.MonoGameRider/.idea/runConfigurations/Rider__Frontend__Windows_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/templates/MonoGameTemplateMetadata.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.templates
2 |
3 | internal object MonoGameTemplateMetadata {
4 | internal object Names {
5 | const val CROSS_PLATFORM_APP = "Cross-Platform Desktop App"
6 | const val WINDOWS_DESKTOP_APP = "Windows Desktop App"
7 | const val ANDROID_APP = "Android App"
8 | const val IOS_APP = "iOS App"
9 | const val GAME_LIB = "Game Library"
10 | const val CONTENT_PIPELINE_EXTENSION = "Content Pipeline Extension"
11 | const val SHARED_LIB = "Shared Library Project"
12 | }
13 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/listeners/MgcbProcessedUpdateListener.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer.listeners
2 |
3 | import com.intellij.openapi.vfs.VirtualFile
4 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.MgcbModel
5 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.MgcbPreviewerTopics
6 | import java.util.*
7 |
8 | interface MgcbProcessedUpdateListener : EventListener {
9 | companion object {
10 | @JvmField
11 | val TOPIC = MgcbPreviewerTopics.MGCB_PROCESSED_UPDATE_TOPIC
12 | }
13 |
14 | fun handle(file: VirtualFile, mgcbModel: MgcbModel) {}
15 | }
16 |
--------------------------------------------------------------------------------
/.idea/.idea.ReSharperPlugin.MonoGameRider/.idea/runConfigurations/VisualStudio.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/MgcbFileType.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb
2 |
3 | import com.intellij.openapi.fileTypes.LanguageFileType
4 | import me.seclerp.rider.plugins.monogame.MonoGameIcons
5 | import javax.swing.Icon
6 |
7 | class MgcbFileType : LanguageFileType(MgcbLanguage.Instance) {
8 | override fun getName(): String = "MonoGame Content Pipeline File"
9 |
10 | override fun getDescription(): String = "Content Pipeline file used by MonoGame"
11 |
12 | override fun getDefaultExtension(): String = "mgcb"
13 |
14 | override fun getIcon(): Icon = MonoGameIcons.MgcbFile
15 |
16 | companion object {
17 | val Instance = MgcbFileType()
18 | }
19 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/MgcbPreviewerTopics.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer
2 |
3 | import com.intellij.util.messages.Topic
4 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.listeners.MgcbPendingUpdateListener
5 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.listeners.MgcbProcessedUpdateListener
6 |
7 | object MgcbPreviewerTopics {
8 | @JvmField
9 | @Topic.ProjectLevel
10 | val MGCB_PENDING_UPDATE_TOPIC = Topic.create("MgcbPendingUpdateTopic", MgcbPendingUpdateListener::class.java)
11 |
12 | @JvmField
13 | @Topic.ProjectLevel
14 | val MGCB_PROCESSED_UPDATE_TOPIC = Topic.create("MgcbProcessedUpdateTopic", MgcbProcessedUpdateListener::class.java)
15 | }
--------------------------------------------------------------------------------
/src/dotnet/Directory.Packages.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/tree/MgcbNodeRenderer.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer.tree
2 |
3 | import com.intellij.ide.util.treeView.NodeRenderer
4 | import javax.swing.JTree
5 |
6 | class MgcbNodeRenderer : NodeRenderer() {
7 | override fun customizeCellRenderer(
8 | tree: JTree,
9 | value: Any?,
10 | selected: Boolean,
11 | expanded: Boolean,
12 | leaf: Boolean,
13 | row: Int,
14 | hasFocus: Boolean
15 | ) {
16 | super.customizeCellRenderer(tree, value, selected, expanded, leaf, row, hasFocus)
17 |
18 | if (value is MgcbTreeNode) {
19 | icon = fixIconIfNeeded(value.icon, selected, hasFocus)
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/src/dotnet/Rider.Plugins.MonoGame/Mgcb/ProjectDotnetToolsTracker.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Application.FileSystemTracker;
2 | using JetBrains.Application.Threading;
3 | using JetBrains.Lifetimes;
4 | using JetBrains.ProjectModel;
5 | using JetBrains.ProjectModel.NuGet.DotNetTools;
6 | using JetBrains.Util;
7 |
8 | namespace Rider.Plugins.MonoGame.Mgcb;
9 |
10 | public class ProjectDotnetToolsTracker : NuGetDotnetToolsTrackerBase
11 | {
12 | public ProjectDotnetToolsTracker(
13 | Lifetime lifetime,
14 | IProject project,
15 | IFileSystemTracker fileSystemTracker,
16 | IShellLocks locks,
17 | ILogger logger,
18 | ISolutionToolset solutionToolset) : base(lifetime, project.Location, fileSystemTracker, locks, logger, solutionToolset)
19 | {
20 | }
21 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/effect/EffectFileType.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.effect
2 |
3 | import com.intellij.openapi.fileTypes.LanguageFileType
4 | import com.intellij.openapi.fileTypes.PlainTextLanguage
5 | import me.seclerp.rider.plugins.monogame.MonoGameIcons
6 | import javax.swing.Icon
7 |
8 | class EffectFileType : LanguageFileType(PlainTextLanguage.INSTANCE) {
9 | override fun getName(): String = "MonoGame Effect File"
10 |
11 | override fun getDescription(): String = "Shader language effect used by MonoGame"
12 |
13 | override fun getDefaultExtension(): String = "fx"
14 |
15 | override fun getIcon(): Icon = MonoGameIcons.EffectFile
16 |
17 | companion object {
18 | val Instance = EffectFileType()
19 | }
20 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/properties/MgcbPropertyRenderer.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer.properties
2 |
3 | import com.intellij.util.ui.JBUI
4 | import java.awt.Component
5 | import javax.swing.JTable
6 | import javax.swing.table.DefaultTableCellRenderer
7 |
8 | class MgcbPropertyRenderer : DefaultTableCellRenderer() {
9 | override fun getTableCellRendererComponent(
10 | table: JTable?,
11 | value: Any?,
12 | isSelected: Boolean,
13 | hasFocus: Boolean,
14 | row: Int,
15 | column: Int
16 | ): Component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column).apply {
17 | border = JBUI.Borders.empty(JBUI.scale(2), JBUI.scale(8))
18 | }
19 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/spritefont/SpriteFontFileType.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.spritefont
2 |
3 | import com.intellij.lang.xml.XMLLanguage
4 | import com.intellij.openapi.fileTypes.LanguageFileType
5 | import me.seclerp.rider.plugins.monogame.MonoGameIcons
6 | import javax.swing.Icon
7 |
8 | class SpriteFontFileType : LanguageFileType(XMLLanguage.INSTANCE) {
9 | override fun getName(): String = "MonoGame SpriteFont File"
10 |
11 | override fun getDescription(): String = "Description of a font used by MonoGame"
12 |
13 | override fun getDefaultExtension(): String = "spritefont"
14 |
15 | override fun getIcon(): Icon = MonoGameIcons.SpriteFontFile
16 |
17 | companion object {
18 | val Instance = SpriteFontFileType()
19 | }
20 | }
--------------------------------------------------------------------------------
/test/MonoGameSolution/Desktop381/.config/dotnet-tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "isRoot": true,
4 | "tools": {
5 | "dotnet-mgcb": {
6 | "version": "3.8.4",
7 | "commands": [
8 | "mgcb"
9 | ]
10 | },
11 | "dotnet-mgcb-editor": {
12 | "version": "3.8.4",
13 | "commands": [
14 | "mgcb-editor"
15 | ]
16 | },
17 | "dotnet-mgcb-editor-linux": {
18 | "version": "3.8.4",
19 | "commands": [
20 | "mgcb-editor-linux"
21 | ]
22 | },
23 | "dotnet-mgcb-editor-windows": {
24 | "version": "3.8.4",
25 | "commands": [
26 | "mgcb-editor-windows"
27 | ]
28 | },
29 | "dotnet-mgcb-editor-mac": {
30 | "version": "3.8.4",
31 | "commands": [
32 | "mgcb-editor-mac"
33 | ]
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/MonoGameUiBundle.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame
2 |
3 | import com.intellij.DynamicBundle
4 | import org.jetbrains.annotations.Nls
5 | import org.jetbrains.annotations.PropertyKey
6 | import java.util.function.Supplier
7 |
8 | private const val BUNDLE = "messages.MonoGameUiBundle"
9 |
10 | object MonoGameUiBundle : DynamicBundle(BUNDLE) {
11 |
12 | @Nls
13 | fun message(
14 | @PropertyKey(resourceBundle = BUNDLE) key: String,
15 | vararg params: Any
16 | ): String {
17 | return getMessage(key, *params)
18 | }
19 |
20 | fun messagePointer(
21 | @PropertyKey(resourceBundle = BUNDLE) key: String,
22 | vararg params: Any
23 | ): Supplier<@Nls String> {
24 | return getLazyMessage(key, *params)
25 | }
26 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/settings/MonoGameSettingsConfigurable.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.settings
2 |
3 | import com.intellij.openapi.options.SearchableConfigurable
4 | import com.intellij.ui.dsl.builder.panel
5 | import me.seclerp.rider.plugins.monogame.MonoGameUiBundle
6 |
7 | class MonoGameSettingsConfigurable : SearchableConfigurable {
8 | override fun createComponent() = panel {
9 | row {
10 | label(MonoGameUiBundle.message("configurable.group.tools.monogame.settings.description"))
11 | }
12 | }
13 |
14 | override fun isModified() = false
15 |
16 | override fun apply() {}
17 |
18 | override fun getDisplayName() = MonoGameUiBundle.message("configurable.group.tools.monogame.settings.display.name")
19 |
20 | override fun getId() = "tools.monogame"
21 | }
--------------------------------------------------------------------------------
/.idea/runConfigurations/Rider.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
11 |
16 |
17 |
18 | true
19 |
20 |
21 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/rdgen.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | true
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/services/MgcbAnalyzer.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer.services
2 |
3 | import com.intellij.openapi.components.Service
4 | import com.intellij.psi.util.PsiTreeUtil
5 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.MgcbModel
6 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.MgcbModelBuilderVisitor
7 | import me.seclerp.rider.plugins.monogame.mgcb.psi.MgcbFile
8 | import me.seclerp.rider.plugins.monogame.mgcb.psi.MgcbOption
9 |
10 | @Service
11 | class MgcbAnalyzer {
12 | fun analyzeFile(file: MgcbFile): MgcbModel {
13 | val mgcbOptions = PsiTreeUtil.getChildrenOfType(file, MgcbOption::class.java)
14 | val visitor = MgcbModelBuilderVisitor()
15 | mgcbOptions?.forEach { it.accept(visitor) }
16 |
17 | return visitor.resultingModel
18 | }
19 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/state/MgcbEditorGlobalState.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.state
2 |
3 | import com.intellij.openapi.components.PersistentStateComponent
4 | import com.intellij.openapi.components.Service
5 | import com.intellij.openapi.components.State
6 | import com.intellij.openapi.components.Storage
7 |
8 | class MgcbEditorGlobalState {
9 | var installNotificationClosed: Boolean = false
10 | }
11 |
12 | @Service
13 | @State(name = "EfCoreCommonOptions", storages = [Storage("MgcbEditorGlobalState.xml")])
14 | class MgcbEditorGlobalStateService : PersistentStateComponent {
15 | private var myState = MgcbEditorGlobalState()
16 |
17 | override fun getState(): MgcbEditorGlobalState = myState
18 |
19 | override fun loadState(state: MgcbEditorGlobalState) {
20 | myState = state
21 | }
22 | }
--------------------------------------------------------------------------------
/src/rider/main/resources/fileTemplates/Sprite Effect File.fx.ft:
--------------------------------------------------------------------------------
1 | \#if OPENGL
2 | \#define SV_POSITION POSITION
3 | \#define VS_SHADERMODEL vs_3_0
4 | \#define PS_SHADERMODEL ps_3_0
5 | \#else
6 | \#define VS_SHADERMODEL vs_4_0_level_9_1
7 | \#define PS_SHADERMODEL ps_4_0_level_9_1
8 | #endif
9 |
10 | Texture2D SpriteTexture;
11 |
12 | sampler2D SpriteTextureSampler = sampler_state
13 | {
14 | Texture = ;
15 | };
16 |
17 | struct VertexShaderOutput
18 | {
19 | float4 Position : SV_POSITION;
20 | float4 Color : COLOR0;
21 | float2 TextureCoordinates : TEXCOORD0;
22 | };
23 |
24 | float4 MainPS(VertexShaderOutput input) : COLOR
25 | {
26 | return tex2D(SpriteTextureSampler,input.TextureCoordinates) * input.Color;
27 | }
28 |
29 | technique SpriteDrawing
30 | {
31 | pass P0
32 | {
33 | PixelShader = compile PS_SHADERMODEL MainPS();
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/extensions/observables/RdObservableProperty.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.extensions.observables
2 |
3 | import com.intellij.openapi.Disposable
4 | import com.intellij.openapi.observable.properties.ObservableProperty
5 | import com.intellij.openapi.rd.createLifetime
6 | import com.jetbrains.rd.util.lifetime.Lifetime
7 | import com.jetbrains.rd.util.reactive.IProperty
8 | import com.jetbrains.rd.util.reactive.IPropertyView
9 |
10 | class RdObservableProperty (
11 | private val propertyView: IPropertyView,
12 | private val propertyLifetime: Lifetime
13 | ) : ObservableProperty {
14 | override fun get() = propertyView.value
15 |
16 | override fun afterChange(parentDisposable: Disposable?, listener: (T) -> Unit) {
17 | val lifetime = parentDisposable?.createLifetime() ?: propertyLifetime
18 | propertyView.change.advise(lifetime, listener)
19 | }
20 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/listeners/MgcbFileListener.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer.listeners
2 |
3 | import com.intellij.openapi.project.Project
4 | import com.intellij.openapi.vfs.newvfs.BulkFileListener
5 | import com.intellij.openapi.vfs.newvfs.events.VFileEvent
6 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.MgcbPreviewerTopics
7 |
8 | class MgcbFileListener(
9 | project: Project,
10 | ) : BulkFileListener {
11 | private val publisher = project.messageBus.syncPublisher(MgcbPreviewerTopics.MGCB_PENDING_UPDATE_TOPIC)
12 |
13 | override fun after(events: MutableList) {
14 | val mgcbFiles = events
15 | .filter { it.file?.extension == "mgcb" }
16 | .mapNotNull { it.file }
17 |
18 | for (file in mgcbFiles) {
19 | publisher.handle(file)
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/toolset/MgcbToolsetState.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.toolset
2 |
3 | import com.intellij.openapi.components.PersistentStateComponent
4 | import com.intellij.openapi.components.State
5 | import com.intellij.openapi.components.Storage
6 | import com.intellij.openapi.components.service
7 | import com.intellij.util.xmlb.XmlSerializerUtil
8 |
9 | @State(
10 | name = "me.seclerp.rider.plugins.monogame.mgcb.toolset.MgcbToolsetState",
11 | storages = [Storage("MgcbToolsetState.xml")]
12 | )
13 | class MgcbToolsetState : PersistentStateComponent {
14 | companion object {
15 | fun getInstance() = service()
16 | }
17 | override fun getState(): MgcbToolsetState {
18 | return this
19 | }
20 |
21 | override fun loadState(state: MgcbToolsetState) {
22 | XmlSerializerUtil.copyBean(state, this)
23 | }
24 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/listeners/MgcbDocumentListener.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer.listeners
2 |
3 | import com.intellij.openapi.editor.event.DocumentEvent
4 | import com.intellij.openapi.editor.event.DocumentListener
5 | import com.intellij.openapi.project.Project
6 | import com.intellij.openapi.vfs.VirtualFile
7 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.MgcbPreviewerTopics
8 | import javax.swing.Timer
9 |
10 | class MgcbDocumentListener(
11 | project: Project,
12 | private val file: VirtualFile
13 | ) : DocumentListener {
14 | private val publisher = project.messageBus.syncPublisher(MgcbPreviewerTopics.MGCB_PENDING_UPDATE_TOPIC)
15 | private val timer = Timer(1000) { publisher.handle(file) }
16 |
17 | init {
18 | timer.isRepeats = false
19 | timer.stop()
20 | }
21 |
22 | override fun documentChanged(event: DocumentEvent) {
23 | timer.restart()
24 | }
25 | }
--------------------------------------------------------------------------------
/.idea/.idea.ReSharperPlugin.MonoGameRider/.idea/runConfigurations/Rider__Unix_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/.idea.ReSharperPlugin.MonoGameRider/.idea/runConfigurations/Rider__Windows_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/actions/commands/Extensions.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.actions.commands
2 |
3 | import com.intellij.openapi.progress.runBackgroundableTask
4 | import com.intellij.openapi.project.Project
5 |
6 | fun executeCommandUnderProgress(
7 | project: Project, taskTitle: String,
8 | what: (Unit) -> CliCommandResult
9 | ) {
10 | runBackgroundableTask(taskTitle, project, false) {
11 | val result = what(Unit)
12 | if (!result.succeeded) {
13 | val errorTextBuilder = StringBuilder()
14 | errorTextBuilder.append("Command: ${result.command}")
15 |
16 | if (result.output.trim().isNotEmpty())
17 | errorTextBuilder.append("\n\nOutput:\n${result.output}")
18 |
19 | if (result.error?.trim()?.isNotEmpty() == true)
20 | errorTextBuilder.append("\n\nError:\n${result.error}")
21 |
22 | errorTextBuilder.append("\n\nExit code: ${result.exitCode}")
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/src/dotnet/Rider.Plugins.MonoGame/Rider.Plugins.MonoGame.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(MSBuildToolsPath)\Microsoft.CSharp.targets
5 |
6 |
7 |
8 |
9 |
10 | net48
11 | Rider.Plugins.MonoGame
12 | $(AssemblyName)
13 | false
14 | $(DefineConstants);RIDER
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/dotnet/Rider.Plugins.MonoGame/Mgcb/MgcbEditorCommandNameResolver.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using JetBrains.Annotations;
3 | using JetBrains.ProjectModel.NuGet.DotNetTools;
4 |
5 | namespace Rider.Plugins.MonoGame.Mgcb;
6 |
7 | public static class MgcbEditorCommandNameResolver
8 | {
9 | // TODO: Replace with global tool version lookup using project.assets.json and DotNetSettings.xml
10 | public static string Resolve([NotNull] GlobalToolCacheEntry tool) => tool.ToolName switch
11 | {
12 | KnownDotNetTools.MgcbEditor => KnownDotNetToolsCommands.MgcbEditor,
13 | KnownDotNetTools.MgcbEditorWindows => KnownDotNetToolsCommands.MgcbEditorWindows,
14 | KnownDotNetTools.MgcbEditorLinux => KnownDotNetToolsCommands.MgcbEditorLinux,
15 | KnownDotNetTools.MgcbEditorMac => KnownDotNetToolsCommands.MgcbEditorMac,
16 | var other => other
17 | };
18 |
19 | public static string Resolve([NotNull] LocalTool tool) => tool.Commands
20 | .Select(command => command.Name)
21 | .FirstOrDefault() ?? tool.PackageId;
22 | }
--------------------------------------------------------------------------------
/test/MonoGameSolution/Desktop381/Content/File.fx:
--------------------------------------------------------------------------------
1 | #if OPENGL
2 | #define SV_POSITION POSITION
3 | #define VS_SHADERMODEL vs_3_0
4 | #define PS_SHADERMODEL ps_3_0
5 | #else
6 | #define VS_SHADERMODEL vs_4_0_level_9_1
7 | #define PS_SHADERMODEL ps_4_0_level_9_1
8 | #endif
9 |
10 | matrix WorldViewProjection;
11 |
12 | struct VertexShaderInput
13 | {
14 | float4 Position : POSITION0;
15 | float4 Color : COLOR0;
16 | };
17 |
18 | struct VertexShaderOutput
19 | {
20 | float4 Position : SV_POSITION;
21 | float4 Color : COLOR0;
22 | };
23 |
24 | VertexShaderOutput MainVS(in VertexShaderInput input)
25 | {
26 | VertexShaderOutput output = (VertexShaderOutput)0;
27 |
28 | output.Position = mul(input.Position, WorldViewProjection);
29 | output.Color = input.Color;
30 |
31 | return output;
32 | }
33 |
34 | float4 MainPS(VertexShaderOutput input) : COLOR
35 | {
36 | return input.Color;
37 | }
38 |
39 | technique BasicColorDrawing
40 | {
41 | pass P0
42 | {
43 | VertexShader = compile VS_SHADERMODEL MainVS();
44 | PixelShader = compile PS_SHADERMODEL MainPS();
45 | }
46 | };
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Any property can be overwritten from command-line via
2 | # -P=
3 | javaVersion=21
4 |
5 | dotnetPluginId=Rider.Plugins.MonoGame
6 | riderPluginId=me.seclerp.rider.plugins.monogame
7 | pluginVersion=253.1.0
8 |
9 | rdVersion=2025.3.1
10 | rdKotlinVersion=2.2.0
11 | intellijPlatformGradleVersion=2.10.4
12 | gradleJvmWrapperVersion=0.15.0
13 |
14 | buildConfiguration=Debug
15 |
16 | publishToken="_PLACEHOLDER_"
17 | publishChannel=default
18 |
19 | # Possible values (minor is omitted):
20 | # Release: 2020.2
21 | # Nightly: 2020.3-SNAPSHOT
22 | # EAP: 2020.3-EAP2-SNAPSHOT
23 | productVersion=2025.3
24 |
25 | # Kotlin 1.4 will bundle the stdlib dependency by default, causing problems with the version bundled with the IDE
26 | # https://blog.jetbrains.com/kotlin/2020/07/kotlin-1-4-rc-released/#stdlib-default
27 | kotlin.stdlib.default.dependency=false
28 |
29 | # To use IDE builds from Maven repo rather than official installers.
30 | org.jetbrains.intellij.platform.buildFeature.useBinaryReleases=false
31 | org.gradle.jvmargs=-Xmx8192M
32 |
--------------------------------------------------------------------------------
/src/rider/main/resources/META-INF/pluginIcon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Andrew Rublyov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/listeners/MgcbPendingUpdateListenerImpl.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer.listeners
2 |
3 | import com.intellij.openapi.project.Project
4 | import com.intellij.openapi.vfs.VirtualFile
5 | import com.intellij.psi.PsiManager
6 | import com.jetbrains.rider.util.idea.getService
7 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.MgcbPreviewerTopics
8 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.services.MgcbAnalyzer
9 | import me.seclerp.rider.plugins.monogame.mgcb.psi.MgcbFile
10 |
11 | class MgcbPendingUpdateListenerImpl(
12 | private val project: Project
13 | ) : MgcbPendingUpdateListener {
14 | private val publisher = project.messageBus.syncPublisher(MgcbPreviewerTopics.MGCB_PROCESSED_UPDATE_TOPIC)
15 | private val mgcbAnalyzer = project.getService()
16 |
17 | override fun handle(file: VirtualFile) {
18 | val mgcbFile = PsiManager.getInstance(project).findFile(file) as MgcbFile
19 | val mgcbModel = mgcbAnalyzer.analyzeFile(mgcbFile)
20 |
21 | publisher.handle(file, mgcbModel)
22 | }
23 | }
--------------------------------------------------------------------------------
/src/rider/main/resources/fileTemplates/Effect File.fx.ft:
--------------------------------------------------------------------------------
1 | \#if OPENGL
2 | \#define SV_POSITION POSITION
3 | \#define VS_SHADERMODEL vs_3_0
4 | \#define PS_SHADERMODEL ps_3_0
5 | \#else
6 | \#define VS_SHADERMODEL vs_4_0_level_9_1
7 | \#define PS_SHADERMODEL ps_4_0_level_9_1
8 | #endif
9 |
10 | matrix WorldViewProjection;
11 |
12 | struct VertexShaderInput
13 | {
14 | float4 Position : POSITION0;
15 | float4 Color : COLOR0;
16 | };
17 |
18 | struct VertexShaderOutput
19 | {
20 | float4 Position : SV_POSITION;
21 | float4 Color : COLOR0;
22 | };
23 |
24 | VertexShaderOutput MainVS(in VertexShaderInput input)
25 | {
26 | VertexShaderOutput output = (VertexShaderOutput)0;
27 |
28 | output.Position = mul(input.Position, WorldViewProjection);
29 | output.Color = input.Color;
30 |
31 | return output;
32 | }
33 |
34 | float4 MainPS(VertexShaderOutput input) : COLOR
35 | {
36 | return input.Color;
37 | }
38 |
39 | technique BasicColorDrawing
40 | {
41 | pass P0
42 | {
43 | VertexShader = compile VS_SHADERMODEL MainVS();
44 | PixelShader = compile PS_SHADERMODEL MainPS();
45 | }
46 | };
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/MgcbEditorWithPreview.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer
2 |
3 | import com.intellij.openapi.fileEditor.TextEditor
4 | import com.intellij.openapi.fileEditor.TextEditorWithPreview
5 | import com.intellij.openapi.project.Project
6 | import com.intellij.openapi.vfs.VirtualFile
7 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.listeners.MgcbDocumentListener
8 |
9 | class MgcbEditorWithPreview(
10 | private val project: Project,
11 | private val file: VirtualFile,
12 | textEditor: TextEditor,
13 | previewer: MgcbEditorPreviewer
14 | ) : TextEditorWithPreview(textEditor, previewer, MgcbEditorWithPreview::javaClass.name, Layout.SHOW_EDITOR_AND_PREVIEW) {
15 | private val documentListener = MgcbDocumentListener(project, file)
16 |
17 | init {
18 | textEditor.editor.document.addDocumentListener(documentListener)
19 | }
20 |
21 | override val isShowFloatingToolbar = false
22 |
23 | override fun dispose() {
24 | textEditor.editor.document.removeDocumentListener(documentListener)
25 |
26 | super.dispose()
27 | }
28 | }
--------------------------------------------------------------------------------
/src/rider/main/resources/messages/MonoGameUiBundle.properties:
--------------------------------------------------------------------------------
1 | configurable.group.tools.monogame.settings.description=Settings related to MonoGame tools for Rider
2 | configurable.group.tools.monogame.settings.display.name=MonoGame
3 |
4 | loading.title=Loading...
5 | settings.mgcb.display.name=MGCB
6 | settings.mgcb.editor.group=MonoGame Content Builder Editor (mgcb-editor)
7 | settings.mgcb.editor.group.global=Global tools
8 | settings.mgcb.editor.group.solution=Solution-level tools
9 | settings.mgcb.editor.group.project=Project-level tools
10 |
11 | command.execution.title=Executing command
12 | command.execution.error.title=Command Failed
13 | command.execution.error.message.exception=Error occurred while command execution: {0}.\
14 | See logs for details.
15 | command.execution.error.message.code=Status code doesn't indicate success: {0}.\
16 | See logs for details.
17 |
18 | command.mgcb.open.title=Open in external MGCB editor
19 | command.mgcb.open.missing.editor.title=mgcb-editor tool is missing
20 |
21 | templates.loading.templates=Loading templates...
22 | templates.no.templates.found=No MonoGame templates were found.
23 | templates.no.templates.install.hint=Please install them using the following command:
24 |
--------------------------------------------------------------------------------
/protocol/src/main/kotlin/model/rider/MonoGameRiderModel.kt:
--------------------------------------------------------------------------------
1 | package model.rider
2 |
3 | import com.jetbrains.rider.model.nova.ide.SolutionModel
4 | import com.jetbrains.rd.generator.nova.*
5 | import com.jetbrains.rd.generator.nova.csharp.CSharp50Generator
6 | import com.jetbrains.rd.generator.nova.kotlin.Kotlin11Generator
7 |
8 | @Suppress("unused")
9 | object MonoGameRiderModel : Ext(SolutionModel.Solution) {
10 |
11 | private val ToolDefinition = structdef {
12 | field("packageId", PredefinedType.string)
13 | field("commandName", PredefinedType.string)
14 | field("version", PredefinedType.string)
15 | }
16 |
17 | private val MgcbEditorToolset = aggregatedef("MgcbEditorToolset") {
18 | property("editor", ToolDefinition.nullable)
19 | }
20 |
21 | private val projectId = PredefinedType.guid
22 |
23 | init {
24 | setting(CSharp50Generator.Namespace, "Rider.Plugins.MonoGame")
25 | setting(Kotlin11Generator.Namespace, "me.seclerp.rider.plugins.monogame")
26 |
27 | field("mgcbGlobalToolset", MgcbEditorToolset)
28 | field("mgcbSolutionToolset", MgcbEditorToolset)
29 | map("mgcbProjectsToolsets", projectId, MgcbEditorToolset)
30 | }
31 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/extensions/workspaceModel/WorkspaceModelExtensions.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("UnstableApiUsage")
2 |
3 | package me.seclerp.rider.extensions.workspaceModel
4 |
5 | import com.intellij.openapi.project.Project
6 | import com.intellij.openapi.vfs.VirtualFile
7 | import com.intellij.platform.backend.workspace.WorkspaceModel
8 | import com.intellij.platform.backend.workspace.virtualFile
9 | import com.jetbrains.rider.projectView.workspace.ProjectModelEntity
10 | import com.jetbrains.rider.projectView.workspace.containingProjectEntity
11 | import com.jetbrains.rider.projectView.workspace.getContentRootUrl
12 | import com.jetbrains.rider.projectView.workspace.getProjectModelEntities
13 |
14 | fun WorkspaceModel.containingProjectEntity(file: VirtualFile, project: Project): ProjectModelEntity? {
15 | return getProjectModelEntities(file, project)
16 | .firstOrNull()
17 | ?.containingProjectEntity()
18 | }
19 |
20 | fun WorkspaceModel.containingProjectDirectory(file: VirtualFile, project: Project): VirtualFile? {
21 | return containingProjectEntity(file, project)
22 | ?.getContentRootUrl(WorkspaceModel.getInstance(project).getVirtualFileUrlManager())
23 | ?.virtualFile
24 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/templates/MonoGameTemplateType.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.templates
2 |
3 | import com.jetbrains.rd.util.lifetime.Lifetime
4 | import com.jetbrains.rider.model.RdProjectTemplate
5 | import com.jetbrains.rider.projectView.projectTemplates.NewProjectDialogContext
6 | import com.jetbrains.rider.projectView.projectTemplates.ProjectTemplatesSharedModel
7 | import com.jetbrains.rider.projectView.projectTemplates.templateTypes.PredefinedProjectTemplateType
8 | import com.jetbrains.rider.projectView.projectTemplates.utils.hasClassification
9 | import me.seclerp.rider.plugins.monogame.MonoGameIcons
10 |
11 | internal class MonoGameTemplateType : PredefinedProjectTemplateType() {
12 | override val icon = MonoGameIcons.MgcbFile
13 | override val name = "MonoGame"
14 | override val order = 50
15 |
16 | override fun acceptableForTemplate(projectTemplate: RdProjectTemplate): Boolean {
17 | return projectTemplate.hasClassification("MonoGame")
18 | }
19 |
20 | override fun createGenerator(lifetime: Lifetime, context: NewProjectDialogContext, sharedModel: ProjectTemplatesSharedModel) =
21 | MonoGameProjectTemplateGenerator(lifetime, context, sharedModel, projectTemplates)
22 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/actions/commands/MgcbProcessAdapter.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.actions.commands
2 |
3 | import com.intellij.execution.process.ProcessAdapter
4 | import com.intellij.execution.process.ProcessEvent
5 | import com.intellij.execution.process.ProcessOutputTypes
6 | import com.intellij.openapi.util.Key
7 |
8 | class MgcbProcessAdapter(private val commandString: String, private val listener: (CliCommandResult) -> Unit) : ProcessAdapter() {
9 | private val outputBuilder = StringBuilder()
10 | private val errorBuilder = StringBuilder()
11 |
12 | override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) {
13 | val builder =
14 | when (outputType) {
15 | ProcessOutputTypes.STDERR -> errorBuilder
16 | else -> outputBuilder
17 | }
18 |
19 | builder.append(event.text)
20 | }
21 |
22 | override fun processTerminated(event: ProcessEvent) {
23 | val cliCommandResult = CliCommandResult(
24 | commandString,
25 | event.exitCode,
26 | outputBuilder.toString(),
27 | event.exitCode == 0,
28 | errorBuilder.toString()
29 | )
30 |
31 | listener(cliCommandResult)
32 | }
33 | }
--------------------------------------------------------------------------------
/test/MonoGameSolution/Desktop381/Desktop381.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | WinExe
4 | net6.0
5 | Major
6 | false
7 | false
8 | MonoGameProjectDesktop381
9 |
10 |
11 | app.manifest
12 | Icon.ico
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/MgcbEditorProvider.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer
2 |
3 | import com.intellij.openapi.fileEditor.FileEditor
4 | import com.intellij.openapi.fileEditor.FileEditorPolicy
5 | import com.intellij.openapi.fileEditor.FileEditorProvider
6 | import com.intellij.openapi.fileEditor.TextEditor
7 | import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider
8 | import com.intellij.openapi.project.DumbAware
9 | import com.intellij.openapi.project.Project
10 | import com.intellij.openapi.vfs.VirtualFile
11 | import me.seclerp.rider.plugins.monogame.mgcb.MgcbFileType
12 |
13 | class MgcbEditorProvider : FileEditorProvider, DumbAware {
14 | override fun getEditorTypeId() = "MgcbEditorWithPreviewer"
15 | override fun getPolicy() = FileEditorPolicy.HIDE_DEFAULT_EDITOR
16 |
17 | override fun accept(project: Project, file: VirtualFile) =
18 | file.fileType is MgcbFileType
19 |
20 | override fun createEditor(project: Project, file: VirtualFile): FileEditor {
21 | val textEditor = TextEditorProvider.getInstance().createEditor(project, file) as TextEditor
22 | val previewerEditor = MgcbEditorPreviewer(project, file)
23 |
24 | return MgcbEditorWithPreview(project, file, textEditor, previewerEditor)
25 | }
26 | }
--------------------------------------------------------------------------------
/test/MonoGameSolution/Desktop38/Desktop38.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | WinExe
4 | netcoreapp3.1
5 | false
6 | false
7 |
8 |
9 | app.manifest
10 | Icon.ico
11 |
12 |
13 | dotnet --roll-forward Major
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/Mgcb.bnf:
--------------------------------------------------------------------------------
1 | {
2 | parserClass="me.seclerp.rider.plugins.monogame.mgcb.parser.MgcbParser"
3 |
4 | extends="com.intellij.extapi.psi.ASTWrapperPsiElement"
5 |
6 | psiClassPrefix="Mgcb"
7 | psiImplClassSuffix="Impl"
8 | psiPackage="me.seclerp.rider.plugins.monogame.mgcb.psi"
9 | psiImplPackage="me.seclerp.rider.plugins.monogame.mgcb.psi"
10 |
11 | elementTypeHolderClass="me.seclerp.rider.plugins.monogame.mgcb.psi.MgcbTypes"
12 | elementTypeClass="me.seclerp.rider.plugins.monogame.mgcb.psi.MgcbElementType"
13 | tokenTypeClass="me.seclerp.rider.plugins.monogame.mgcb.psi.MgcbTokenType"
14 | }
15 |
16 | mgcbFile ::= item_*
17 |
18 | private item_ ::= (
19 | option
20 | | set_instruction
21 | | if_instruction
22 | | COMMENT
23 | | WHITE_SPACE
24 | )
25 |
26 | option ::= (
27 | OPTION_KEY OPTION_SEPARATOR OPTION_VALUE
28 | | OPTION_KEY OPTION_SEPARATOR
29 | | OPTION_KEY
30 | )
31 |
32 | set_instruction ::= (
33 | SET_KEYWORD WHITE_SPACE PREPROCESSOR_IDENTIFIER EQ PREPROCESSOR_VALUE
34 | | SET_KEYWORD WHITE_SPACE PREPROCESSOR_IDENTIFIER
35 | )
36 |
37 | if_instruction ::= (
38 | IF_KEYWORD WHITE_SPACE PREPROCESSOR_IDENTIFIER EQ PREPROCESSOR_VALUE WHITE_SPACE item_* ENDIF_KEYWORD
39 | | IF_KEYWORD WHITE_SPACE PREPROCESSOR_IDENTIFIER WHITE_SPACE item_* ENDIF_KEYWORD
40 | )
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/CommonExtensions.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame
2 |
3 | import com.intellij.util.ui.ListTableModel
4 |
5 | fun String.substringAfter(regex: Regex, missingDelimiterValue: String = this): String {
6 | val firstMatch = regex.find(this) ?: return missingDelimiterValue
7 |
8 | return substring(firstMatch.range.first + 1)
9 | }
10 |
11 | fun String.substringBefore(regex: Regex, missingDelimiterValue: String = this): String {
12 | val firstMatch = regex.find(this) ?: return missingDelimiterValue
13 |
14 | return substring(0, firstMatch.range.last)
15 | }
16 |
17 | fun String.substringBeforeLast(regex: Regex, missingDelimiterValue: String = this): String {
18 | val matches = regex.findAll(this)
19 |
20 | if (!matches.any()) {
21 | return missingDelimiterValue
22 | }
23 |
24 | return substring(0, matches.last().range.last)
25 | }
26 |
27 | fun String.substringAfterLast(regex: Regex, missingDelimiterValue: String = this): String {
28 | val matches = regex.findAll(this)
29 |
30 | if (!matches.any()) {
31 | return missingDelimiterValue
32 | }
33 |
34 | return substring(matches.last().range.last + 1)
35 | }
36 |
37 | fun ListTableModel.removeAllRows() {
38 | val cachedRowCount = rowCount
39 | for (row in 0 until cachedRowCount)
40 | removeRow(0)
41 | }
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "rider-monogame"
2 |
3 | pluginManagement {
4 | val rdVersion: String by settings
5 | val rdKotlinVersion: String by settings
6 | val intellijPlatformGradleVersion: String by settings
7 | val gradleJvmWrapperVersion: String by settings
8 |
9 | repositories {
10 | maven("https://cache-redirector.jetbrains.com/intellij-dependencies")
11 | maven("https://cache-redirector.jetbrains.com/plugins.gradle.org")
12 | maven("https://cache-redirector.jetbrains.com/maven-central")
13 |
14 | if (rdVersion == "SNAPSHOT") {
15 | mavenLocal()
16 | }
17 | }
18 |
19 | plugins {
20 | id("com.jetbrains.rdgen") version rdVersion
21 | id("org.jetbrains.kotlin.jvm") version rdKotlinVersion
22 | id("org.jetbrains.intellij.platform") version intellijPlatformGradleVersion
23 | id("me.filippov.gradle.jvm.wrapper") version gradleJvmWrapperVersion
24 | }
25 |
26 | resolutionStrategy {
27 | eachPlugin {
28 | when (requested.id.name) {
29 | // This required to correctly rd-gen plugin resolution.
30 | // Maybe we should switch our naming to match Gradle plugin naming convention.
31 | "rdgen" -> {
32 | useModule("com.jetbrains.rd:rd-gen:${rdVersion}")
33 | }
34 | }
35 | }
36 | }
37 | }
38 |
39 | include(":protocol")
--------------------------------------------------------------------------------
/src/dotnet/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Latest
5 | MSB3277
6 | true
7 | false
8 | None
9 |
10 | obj\$(MSBuildProjectName)\
11 | $(DefaultItemExcludes);obj\**
12 | bin\$(MSBuildProjectName)\$(Configuration)\
13 | false
14 |
15 |
16 |
17 |
18 | true
19 | true
20 |
21 |
22 |
23 | TRACE;DEBUG;JET_MODE_ASSERT
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/test/MonoGameSolution/Desktop381/Game1.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Microsoft.Xna.Framework.Graphics;
3 | using Microsoft.Xna.Framework.Input;
4 |
5 | namespace MonoGameProjectDesktop381;
6 |
7 | public class Game1 : Game
8 | {
9 | private GraphicsDeviceManager _graphics;
10 | private SpriteBatch _spriteBatch;
11 |
12 | public Game1()
13 | {
14 | _graphics = new GraphicsDeviceManager(this);
15 | Content.RootDirectory = "Content";
16 | IsMouseVisible = true;
17 | }
18 |
19 | protected override void Initialize()
20 | {
21 | // TODO: Add your initialization logic here
22 |
23 | base.Initialize();
24 | }
25 |
26 | protected override void LoadContent()
27 | {
28 | _spriteBatch = new SpriteBatch(GraphicsDevice);
29 |
30 | // TODO: use this.Content to load your game content here
31 | }
32 |
33 | protected override void Update(GameTime gameTime)
34 | {
35 | if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed ||
36 | Keyboard.GetState().IsKeyDown(Keys.Escape))
37 | Exit();
38 |
39 | // TODO: Add your update logic here
40 |
41 | base.Update(gameTime);
42 | }
43 |
44 | protected override void Draw(GameTime gameTime)
45 | {
46 | GraphicsDevice.Clear(Color.CornflowerBlue);
47 |
48 | // TODO: Add your drawing code here
49 |
50 | base.Draw(gameTime);
51 | }
52 | }
--------------------------------------------------------------------------------
/test/MonoGameSolution/Desktop38/Game1.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Microsoft.Xna.Framework.Graphics;
3 | using Microsoft.Xna.Framework.Input;
4 |
5 | namespace Desktop38
6 | {
7 | public class Game1 : Game
8 | {
9 | private GraphicsDeviceManager _graphics;
10 | private SpriteBatch _spriteBatch;
11 |
12 | public Game1()
13 | {
14 | _graphics = new GraphicsDeviceManager(this);
15 | Content.RootDirectory = "Content";
16 | IsMouseVisible = true;
17 | }
18 |
19 | protected override void Initialize()
20 | {
21 | // TODO: Add your initialization logic here
22 |
23 | base.Initialize();
24 | }
25 |
26 | protected override void LoadContent()
27 | {
28 | _spriteBatch = new SpriteBatch(GraphicsDevice);
29 |
30 | // TODO: use this.Content to load your game content here
31 | }
32 |
33 | protected override void Update(GameTime gameTime)
34 | {
35 | if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
36 | Exit();
37 |
38 | // TODO: Add your update logic here
39 |
40 | base.Update(gameTime);
41 | }
42 |
43 | protected override void Draw(GameTime gameTime)
44 | {
45 | GraphicsDevice.Clear(Color.CornflowerBlue);
46 |
47 | // TODO: Add your drawing code here
48 |
49 | base.Draw(gameTime);
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/test/MonoGameSolution/MonoGameSolution.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.0.31903.59
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Desktop381", "Desktop381\Desktop381.csproj", "{A8AFF547-57D0-44C3-A6CE-23860F2D638E}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Desktop38", "Desktop38\Desktop38.csproj", "{3E97F549-0D4F-4A53-B863-81AAFE51B6DB}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(SolutionProperties) = preSolution
16 | HideSolutionNode = FALSE
17 | EndGlobalSection
18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
19 | {A8AFF547-57D0-44C3-A6CE-23860F2D638E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
20 | {A8AFF547-57D0-44C3-A6CE-23860F2D638E}.Debug|Any CPU.Build.0 = Debug|Any CPU
21 | {A8AFF547-57D0-44C3-A6CE-23860F2D638E}.Release|Any CPU.ActiveCfg = Release|Any CPU
22 | {A8AFF547-57D0-44C3-A6CE-23860F2D638E}.Release|Any CPU.Build.0 = Release|Any CPU
23 | {3E97F549-0D4F-4A53-B863-81AAFE51B6DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
24 | {3E97F549-0D4F-4A53-B863-81AAFE51B6DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
25 | {3E97F549-0D4F-4A53-B863-81AAFE51B6DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
26 | {3E97F549-0D4F-4A53-B863-81AAFE51B6DB}.Release|Any CPU.Build.0 = Release|Any CPU
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/MgcbEditorFloatingToolbarProvider.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer
2 |
3 | import com.intellij.openapi.Disposable
4 | import com.intellij.openapi.actionSystem.CommonDataKeys
5 | import com.intellij.openapi.actionSystem.DataContext
6 | import com.intellij.openapi.actionSystem.DefaultActionGroup
7 | import com.intellij.openapi.editor.Editor
8 | import com.intellij.openapi.editor.toolbar.floating.FloatingToolbarComponent
9 | import com.intellij.openapi.editor.toolbar.floating.FloatingToolbarProvider
10 | import com.intellij.openapi.fileEditor.FileDocumentManager
11 | import com.intellij.testFramework.LightVirtualFileBase
12 | import me.seclerp.rider.plugins.monogame.mgcb.MgcbFileType
13 | import me.seclerp.rider.plugins.monogame.mgcb.actions.OpenExternalEditorAction
14 |
15 | class MgcbEditorFloatingToolbarProvider : FloatingToolbarProvider {
16 | override val actionGroup = DefaultActionGroup(OpenExternalEditorAction())
17 | override val autoHideable = false
18 |
19 | override fun register(dataContext: DataContext, component: FloatingToolbarComponent, parentDisposable: Disposable) {
20 | val editor = dataContext.getData(CommonDataKeys.EDITOR) ?: return
21 | if (editor.isMgcbFileEditor()) {
22 | component.scheduleShow()
23 | }
24 | }
25 |
26 | private fun Editor.isMgcbFileEditor(): Boolean {
27 | val documentManager = FileDocumentManager.getInstance()
28 | val virtualFile = documentManager.getFile(document) ?: return false
29 | return virtualFile !is LightVirtualFileBase
30 | && virtualFile.isValid
31 | && virtualFile.fileType == MgcbFileType.Instance
32 | }
33 | }
--------------------------------------------------------------------------------
/test/MonoGameSolution/Desktop38/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | true/pm
39 | permonitorv2,permonitor
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/test/MonoGameSolution/Desktop381/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | true/pm
39 | permonitorv2,permonitor
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/MgcbParserDefinition.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb
2 |
3 | import com.intellij.lang.ASTNode
4 | import com.intellij.lang.ParserDefinition
5 | import com.intellij.lang.PsiParser
6 | import com.intellij.lexer.Lexer
7 | import com.intellij.openapi.project.Project
8 | import com.intellij.psi.FileViewProvider
9 | import com.intellij.psi.PsiElement
10 | import com.intellij.psi.PsiFile
11 | import com.intellij.psi.TokenType
12 | import com.intellij.psi.tree.IFileElementType
13 | import com.intellij.psi.tree.TokenSet
14 | import me.seclerp.rider.plugins.monogame.mgcb.parser.MgcbParser
15 | import me.seclerp.rider.plugins.monogame.mgcb.psi.MgcbFile
16 | import me.seclerp.rider.plugins.monogame.mgcb.psi.MgcbTypes
17 |
18 | class MgcbParserDefinition : ParserDefinition {
19 | val WHITE_SPACES = TokenSet.create(TokenType.WHITE_SPACE)
20 |
21 | val COMMENTS = TokenSet.create(MgcbTypes.COMMENT)
22 |
23 | val FILE = IFileElementType(MgcbLanguage.Instance)
24 |
25 | override fun createLexer(project: Project?): Lexer = MgcbLexerAdapter()
26 |
27 | override fun getWhitespaceTokens(): TokenSet = WHITE_SPACES
28 |
29 | override fun getCommentTokens(): TokenSet = COMMENTS
30 |
31 | override fun getStringLiteralElements(): TokenSet = TokenSet.EMPTY
32 |
33 | override fun createParser(project: Project?): PsiParser = MgcbParser()
34 |
35 | override fun getFileNodeType(): IFileElementType = FILE
36 |
37 | override fun createFile(viewProvider: FileViewProvider): PsiFile = MgcbFile(viewProvider)
38 |
39 | override fun spaceExistenceTypeBetweenTokens(left: ASTNode?, right: ASTNode?): ParserDefinition.SpaceRequirements =
40 | ParserDefinition.SpaceRequirements.MAY
41 |
42 | override fun createElement(node: ASTNode?): PsiElement = MgcbTypes.Factory.createElement(node)
43 | }
--------------------------------------------------------------------------------
/protocol/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import com.jetbrains.rd.generator.gradle.RdGenTask
2 |
3 | plugins {
4 | id("com.jetbrains.rdgen")
5 | id("org.jetbrains.kotlin.jvm")
6 | }
7 |
8 | repositories {
9 | maven("https://cache-redirector.jetbrains.com/intellij-dependencies")
10 | maven("https://cache-redirector.jetbrains.com/maven-central")
11 | }
12 |
13 | val repoRoot: File = projectDir.parentFile
14 |
15 | sourceSets {
16 | main {
17 | kotlin {
18 | srcDir(repoRoot.resolve("protocol/src/main/kotlin/model"))
19 | }
20 | }
21 | }
22 |
23 | rdgen {
24 | verbose = true
25 | packages = "model"
26 |
27 | generator {
28 | language = "kotlin"
29 | transform = "asis"
30 | root = "com.jetbrains.rider.model.nova.ide.IdeRoot"
31 | namespace = "com.jetbrains.rider.plugins.efcore.model"
32 | directory = file("$repoRoot/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/rd").absolutePath
33 | }
34 |
35 | generator {
36 | language = "csharp"
37 | transform = "reversed"
38 | root = "com.jetbrains.rider.model.nova.ide.IdeRoot"
39 | namespace = "Rider.Plugins.EfCore"
40 | directory = file("$repoRoot/src/dotnet/Rider.Plugins.MonoGame/Rd").absolutePath
41 | }
42 | }
43 |
44 | tasks.withType {
45 | dependsOn(sourceSets["main"].runtimeClasspath)
46 | classpath(sourceSets["main"].runtimeClasspath)
47 | }
48 |
49 | dependencies {
50 | val rdVersion: String by project
51 | val rdKotlinVersion: String by project
52 |
53 | implementation("com.jetbrains.rd:rd-gen:$rdVersion")
54 | implementation("org.jetbrains.kotlin:kotlin-stdlib:$rdKotlinVersion")
55 | implementation(
56 | project(
57 | mapOf(
58 | "path" to ":",
59 | "configuration" to "riderModel"
60 | )
61 | )
62 | )
63 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/toolset/DotNetToolsVersion.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.toolset
2 |
3 | import org.jetbrains.annotations.NonNls
4 |
5 | class DotNetToolsVersion private constructor(private val version: String) : Comparable {
6 | companion object {
7 | @NonNls
8 | val SEMVER_REGEX =
9 | Regex("(\\d+)\\.(\\d+)\\.(\\d+)(?:-([\\dA-Za-z-]+(?:\\.[\\dA-Za-z-]+)*))?(?:\\+[\\dA-Za-z-]+)?")
10 |
11 | fun parse(version: String): DotNetToolsVersion? {
12 | val match = SEMVER_REGEX.find(version) ?: return null
13 |
14 | if (match.groups.size < 3) {
15 | return null
16 | }
17 |
18 | return DotNetToolsVersion(version)
19 | }
20 | }
21 |
22 | override operator fun compareTo(that: DotNetToolsVersion?): Int {
23 | if (that == null) return 1
24 | val thisParts = version.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
25 | val thatParts = version.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
26 | val length = Math.max(thisParts.size, thatParts.size)
27 | for (i in 0 until length) {
28 | val thisPart = if (i < thisParts.size) thisParts[i].toInt() else 0
29 | val thatPart = if (i < thatParts.size) thatParts[i].toInt() else 0
30 | if (thisPart < thatPart) return -1
31 | if (thisPart > thatPart) return 1
32 | }
33 | return 0
34 | }
35 |
36 | override fun equals(other: Any?): Boolean {
37 | if (this === other) return true
38 | if (other == null) return false
39 | return if (this.javaClass != other.javaClass) false else this.compareTo(other as DotNetToolsVersion) == 0
40 | }
41 |
42 | override fun toString() = version
43 | override fun hashCode() = version.hashCode()
44 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/actions/commands/CliCommand.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.actions.commands
2 |
3 | import com.intellij.execution.configurations.GeneralCommandLine
4 | import com.intellij.execution.process.OSProcessHandler
5 | import com.intellij.execution.util.ExecUtil
6 | import com.intellij.openapi.progress.ProgressIndicator
7 | import com.intellij.openapi.progress.ProgressManager
8 | import com.intellij.openapi.progress.Task
9 | import com.intellij.openapi.project.Project
10 |
11 | @Suppress("UnstableApiUsage")
12 | open class CliCommand(
13 | private val command: GeneralCommandLine,
14 | ) {
15 | fun executeLater(listener: (CliCommandResult) -> Unit = {}) {
16 | try {
17 | val handler = OSProcessHandler(command)
18 | handler.addProcessListener(MgcbProcessAdapter(command.commandLineString, listener))
19 | handler.startNotify()
20 | } catch(e: Exception) {
21 | e.printStackTrace()
22 | listener(CliCommandResult(command.commandLineString, -1, e.toString(), false))
23 | }
24 | }
25 |
26 | fun executeUnderProgress(project: Project, title: String, listener: (CliCommandResult) -> Unit = {}) {
27 | ProgressManager.getInstance().run(object : Task.Backgroundable(project, title, false) {
28 | override fun run(progress: ProgressIndicator) {
29 | try {
30 | val output = ExecUtil.execAndGetOutput(command)
31 | val commandResult = CliCommandResult(command.commandLineString, output.exitCode, output.stdout, output.exitCode == 0, output.stderr)
32 | listener(commandResult)
33 | } catch(e: Exception) {
34 | e.printStackTrace()
35 | listener(CliCommandResult(command.commandLineString, -1, e.toString(), false))
36 | }
37 | }
38 | })
39 | }
40 | }
--------------------------------------------------------------------------------
/src/dotnet/Rider.Plugins.MonoGame.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rider.Plugins.MonoGame", "Rider.Plugins.MonoGame\Rider.Plugins.MonoGame.csproj", "{084172D1-A9C6-46D0-96AD-05C5B09A5E5D}"
4 | EndProject
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rider.Plugins.MonoGame.Tests", "Rider.Plugins.MonoGame.Tests\Rider.Plugins.MonoGame.Tests.csproj", "{01C3DEF5-50B2-47CB-9467-19BC6DDF9D3D}"
6 | EndProject
7 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "misc", "misc", "{4A9ABB95-3762-448B-B5BF-099E46DB22DE}"
8 | ProjectSection(SolutionItems) = preProject
9 | Plugin.props = Plugin.props
10 | ..\..\README.md = ..\..\README.md
11 | ..\..\CHANGELOG.md = ..\..\CHANGELOG.md
12 | ..\rider\main\resources\META-INF\plugin.xml = ..\rider\main\resources\META-INF\plugin.xml
13 | ..\..\gradle.properties = ..\..\gradle.properties
14 | RiderSdk.PackageVersions.Generated.props = RiderSdk.PackageVersions.Generated.props
15 | Directory.Build.props = Directory.Build.props
16 | EndProjectSection
17 | EndProject
18 | Global
19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
20 | Debug|Any CPU = Debug|Any CPU
21 | Release|Any CPU = Release|Any CPU
22 | EndGlobalSection
23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
24 | {084172D1-A9C6-46D0-96AD-05C5B09A5E5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {084172D1-A9C6-46D0-96AD-05C5B09A5E5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {084172D1-A9C6-46D0-96AD-05C5B09A5E5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {084172D1-A9C6-46D0-96AD-05C5B09A5E5D}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {01C3DEF5-50B2-47CB-9467-19BC6DDF9D3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {01C3DEF5-50B2-47CB-9467-19BC6DDF9D3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {01C3DEF5-50B2-47CB-9467-19BC6DDF9D3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {01C3DEF5-50B2-47CB-9467-19BC6DDF9D3D}.Release|Any CPU.Build.0 = Release|Any CPU
32 | EndGlobalSection
33 | EndGlobal
34 |
--------------------------------------------------------------------------------
/src/rider/main/resources/xmlSchema/SpriteFont.xsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/docs/Previewer Architecture.drawio:
--------------------------------------------------------------------------------
1 | 7Vtbl6I4EP41PrYHEoL4OH2bfeg+6x535/IYISIzSDwhttq/foMEuQRoulWEdvqlSZGEUFXfV0lRDuDdcvuV4dXimTrEHwDN2Q7g/QAAHSJD/Isku1hiASlwmefITqlg6r0SKdSkdO05JMx15JT63FvlhTYNAmLznAwzRjf5bnPq55+6wi5RBFMb+6r0u+fwRfIWo1T+F/HcRfJk3RzHd5Y46SzfJFxgh24yIvgwgHeMUh5fLbd3xI+Ul+glHvdYcfewMEYC3mTAj8mrNf31yID59dvz93/59AYbN7p8j5DvkjcmjlCAbFLGF9SlAfYfUukto+vAIdG0mmilfZ4oXQmhLoS/COc7aU285lSIFnzpy7tixWz3Ixo/REnzp5xu37jf5lq7bGtCmLcknDApjF8gWnWlYqQopGtmkxptQOlgmLmE12lNP9hPOD6hYjlsJwYy4mPuveQXgqUHuod+cugXxvAu02FFvYCHmZknkUB0kGBCllyfhJJhFAxe6G8kTlreX1zEK0hamVdJRXsneodDySW+YH8t1fDs2rNHzydPXshJIMxWdLi8O20WHifTFd4baiNIJe86cnrCONnW21s1T6JHPa9HCCVCNynAdakrbZEBt6lVWzSj1fcrzewTCE+IN9QQb/BIuB1lnJHi0d88xtfYj5xa3Lhb4MAVhigz4ROeiXiYUzv2PTcQ17bQVsRht5EveyLgfJE3lp7jxBYmofeKZ/v5IsVLfhCTo9sBuq8Dg4yGcnAag7JGqvbESuTcaENdgxI97yO7lJ0k6m4KI+h8HgonKFrrBJSEVAM+ThVzifC8ii7tne8JYDH4NhfNYgg+zQ4CbP9298D8e83FNETKwxiDOjoRgaE8gQFNJTCzhL+sc/EXHF8pgVlNCawCV+0wmGEqCPhUpBW7Xx1rASsHmIR7Pkph+XUnKGyLzyzFmnMRicBwKTZaA2D6PGIisc0y3ejqntrrZaS/KsILKCcpR4F29mDib4i6RWIJqV4dienJufotFkPgkiyWLDPj96q3T0jgeIEruv23crBwbKXHP2sihitg4Iz+PpzwQd5GCVKWWzdKcgznPt3YC8z4UDwCz3BIKjBzAqDAUeEQNy45rGglOBmdDScXCfan9PdxQ3+Pz88X8/dxKc/rFTwfHbNjn+/MQVtQeoHkLXDxo7amqKXDLH/uhBdoSv2gYovVDhSASv11W54OQgGiUfegoCtquWYoNE3+Qq3c0i1BQU2wRlCA/YEC0mHnoADUJF9drJ0w8uKRTYkyL3uogqig2XFJanvU5qEKlG9iqpi7o4qF4+Jp9eKKTZ7fkAc6qlhkWJ1TrNWnqHjCAAgbB0B0yQBoGIrjf65kplWLmCiZaUoVfDR9ef4UJSzfpVTF027mKHWjczlKo1en11OSk9GUnC6aszHUfeSnIiej4uyTISfdzEHm2E8tLXCVGlDqdlLd5CqodY+r/qQXlE1TZ2vL9FEfasvUSo63MuJ1+C3e6UyG5JD7qClQM5EK5bOlRwzwB8rFYo7OQhmgPkBZLUl5K6PfSygftladgXKvqhzODuVRp6EMYR+gXJ47r/si0UsoQ61jUEbXWjZuNC27rLJoO8kA1Kvf1lzCPvqxVQUfYtUiS47G9awKRnpd//OwKrrWzxDNwX3sr0JacR5kGe07j6GWCKuBVZUkJZPahFGbhGGUtfxk5ZOmpQbt0vLJ85UZG30C9rk34Ag0RLtR8VmspVAOFEBF+9hDtXE5krqzdUV5FKCyIuITFdyIZvqb5pjQ0l+Gw4f/AQ==
--------------------------------------------------------------------------------
/src/dotnet/Rider.Plugins.MonoGame/Templates.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | True
3 | True
4 | MonoGameRider Template
5 | True
6 | 0
7 | True
8 | True
9 | 2.0
10 | InCSharpTypeMember
11 | monogamerider_template
12 | True
13 | private string MonoGameRider { get; set; }$END$
14 |
--------------------------------------------------------------------------------
/src/rider/main/resources/fileTemplates/SpriteFont File.spritefont.ft:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
14 | Arial
15 |
16 |
20 | 12
21 |
22 |
26 | 0
27 |
28 |
32 | true
33 |
34 |
38 |
39 |
40 |
44 |
45 |
46 |
53 |
54 |
55 |
56 | ~
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/settings/MgcbColorSettingsPage.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.settings
2 |
3 | import com.intellij.openapi.editor.colors.TextAttributesKey
4 | import com.intellij.openapi.fileTypes.SyntaxHighlighter
5 | import com.intellij.openapi.options.colors.AttributesDescriptor
6 | import com.intellij.openapi.options.colors.ColorDescriptor
7 | import com.intellij.openapi.options.colors.ColorSettingsPage
8 | import me.seclerp.rider.plugins.monogame.MonoGameIcons
9 | import me.seclerp.rider.plugins.monogame.mgcb.MgcbSyntaxHighlighter
10 | import javax.swing.Icon
11 |
12 | class MgcbColorSettingsPage : ColorSettingsPage {
13 | override fun getAttributeDescriptors(): Array = DESCRIPTORS
14 |
15 | override fun getColorDescriptors(): Array = ColorDescriptor.EMPTY_ARRAY
16 |
17 | override fun getDisplayName(): String = "MGCB"
18 |
19 | override fun getIcon(): Icon = MonoGameIcons.MgcbFile
20 |
21 | override fun getHighlighter(): SyntaxHighlighter = MgcbSyntaxHighlighter()
22 |
23 | override fun getDemoText(): String = """# Directories
24 | /outputDir:bin/foo
25 | /intermediateDir:obj/foo
26 |
27 | /rebuild
28 |
29 | ${'$'}set BuildEffects=Yes
30 |
31 | # Build a texture
32 | /importer:TextureImporter
33 | /processor:TextureProcessor
34 | /processorParam:ColorKeyEnabled=false
35 | /build:Textures\wood.png
36 | /build:Textures\metal.png
37 | /build:Textures\plastic.png"""
38 |
39 | override fun getAdditionalHighlightingTagToDescriptorMap(): MutableMap? = null
40 |
41 | companion object {
42 | private val DESCRIPTORS = arrayOf(
43 | AttributesDescriptor("Option name", MgcbSyntaxHighlighter.OPTION_KEY),
44 | AttributesDescriptor("Option separator", MgcbSyntaxHighlighter.OPTION_SEPARATOR),
45 | AttributesDescriptor("Option value", MgcbSyntaxHighlighter.OPTION_VALUE),
46 | AttributesDescriptor("Preprocessor keywords", MgcbSyntaxHighlighter.PREPROCESSOR_KEYWORD),
47 | AttributesDescriptor("Preprocessor identifier", MgcbSyntaxHighlighter.PREPROCESSOR_IDENTIFIER),
48 | AttributesDescriptor("Preprocessor separator", MgcbSyntaxHighlighter.PREPROCESSOR_SEPARATOR),
49 | AttributesDescriptor("Preprocessor value", MgcbSyntaxHighlighter.PREPROCESSOR_VALUE),
50 | // AttributesDescriptor("Bad value", MgcbSyntaxHighlighter.BAD_CHARACTER)
51 | )
52 | }
53 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/tree/MgcbTree.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer.tree
2 |
3 | import com.intellij.ui.treeStructure.Tree
4 | import javax.swing.tree.DefaultTreeModel
5 | import javax.swing.tree.TreePath
6 |
7 | class MgcbTree(val root: MgcbFolderNode = MgcbFolderNode("Content")) : Tree(root) {
8 | init {
9 | this.expandsSelectedPaths = true
10 | }
11 |
12 | fun reload() {
13 | this.treeDidChange()
14 | (model as DefaultTreeModel).reload()
15 | }
16 |
17 | fun clear() {
18 | root.removeAllChildren()
19 | }
20 |
21 | fun getExpandedUrls(): Set {
22 | val expanded = mutableListOf()
23 | for (i in 0 until rowCount - 1) {
24 | val currentPath: TreePath = getPathForRow(i)
25 | val nextPath: TreePath = getPathForRow(i + 1)
26 | if (currentPath.isDescendant(nextPath)) {
27 | expanded.add(currentPath)
28 | }
29 | }
30 |
31 | return expanded.map { treePath -> treePath.path.joinToString("/") { it.toString() } }.toSet()
32 | }
33 |
34 | fun getSelectedUrl(): String? {
35 | return selectionPath?.path?.joinToString("/") { it.toString() }
36 | }
37 |
38 | fun restoreExpandedUrl(urls: Set) {
39 | restoreExpandedInternal(root, urls)
40 | }
41 |
42 | fun restoreSelectedUrl(url: String) {
43 | selectionPath = null
44 | restoreSelectedUrlInternal(root, url)
45 | }
46 |
47 | private fun restoreExpandedInternal(node: MgcbTreeNode, urls: Set) {
48 | for (i in 0 until node.childCount) {
49 | val childNode = node.getChildAt(i) as MgcbTreeNode
50 | val url = childNode.path.joinToString("/") { it.toString() }
51 |
52 | if (urls.contains(url)) {
53 | val path = TreePath(childNode.path)
54 | expandPath(path)
55 | }
56 |
57 | if (childNode.childCount > 0) {
58 | restoreExpandedInternal(childNode, urls)
59 | }
60 | }
61 | }
62 |
63 | private fun restoreSelectedUrlInternal(node: MgcbTreeNode, url: String) {
64 | val nodeUrl = node.path.joinToString("/") { it.toString() }
65 |
66 | if (nodeUrl == url) {
67 | selectionPath = TreePath(node.path)
68 | return
69 | }
70 |
71 | for (i in 0 until node.childCount) {
72 | val childNode = node.getChildAt(i) as MgcbTreeNode
73 |
74 | restoreSelectedUrlInternal(childNode, url)
75 | }
76 | }
77 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/MgcbModel.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer
2 |
3 | data class MgcbModel(
4 | // Specifies the directory where all content is written.
5 | // Defaults to the current working directory.
6 | var outputDir: String? = null,
7 | // Specifies the directory where all intermediate files are written.
8 | // Defaults to the current working directory.
9 | var intermediateDir: String? = null,
10 | // Force a full rebuild of all content.
11 | var rebuild: Boolean = false,
12 | // Delete all previously built content and intermediate files.
13 | // Only the /intermediateDir and /outputDir need to be defined for clean to do its job.
14 | var clean: Boolean = false,
15 | // Only build content that changed since the last build.
16 | var incremental: Boolean = false,
17 | // An optional parameter which adds an assembly reference which contains importers, processors, or writers needed during content building.
18 | var references: MutableList = mutableListOf(),
19 | // Set the target platform for this build. It must be a member of the TargetPlatform enum
20 | var platform: String? = null,
21 | // Set the target graphics profile for this build. It must be a member of the GraphicsProfile enum
22 | var profile: String? = null,
23 | // The optional build configuration name from the build system. This is sometimes used as a hint in content processors.
24 | var config: String? = null,
25 | // Uses LZ4 compression to compress the contents of the XNB files
26 | var compress: Boolean = false,
27 | // Instructs the content builder to build the specified content file using the previously set switches and options
28 | var buildEntries: MutableList = mutableListOf(),
29 | // Allows a debugger to attach to the MGCB executable before content is built
30 | var launchDebugger: Boolean = false
31 | )
32 |
33 | data class BuildEntry(
34 | // Source path to the asset to build
35 | var contentFilepath: String? = null,
36 | // Destination path of the asset to build to
37 | var destinationFilepath: String? = null,
38 | // An optional parameter which defines the class name of the content importer for reading source content
39 | var importer: String? = null,
40 | // An optional parameter which defines the class name of the content processor for processing imported content
41 | var processor: String? = null,
42 | // An optional parameter which defines a parameter name and value to set on a content processor
43 | var processorParams: Map = mapOf(),
44 | )
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/MgcbModelBuilderVisitor.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer
2 |
3 | import me.seclerp.rider.plugins.monogame.mgcb.psi.*
4 |
5 | class MgcbModelBuilderVisitor : MgcbVisitor() {
6 | val resultingModel = MgcbModel()
7 | private val currentProcessorParams = mutableMapOf()
8 |
9 | private var currentBuildEntry = BuildEntry()
10 |
11 | override fun visitOption(option: MgcbOption) {
12 | super.visitOption(option)
13 | val optionValue = option.getValue()
14 |
15 | when (option.getKey()) {
16 | "/outputDir" -> resultingModel.outputDir = optionValue
17 | "/intermediateDir" -> resultingModel.intermediateDir = optionValue
18 | "/rebuild" -> resultingModel.rebuild = true
19 | "/clean" -> resultingModel.clean = true
20 | "/incremental" -> resultingModel.incremental = true
21 | "/reference" -> resultingModel.references.add(optionValue!!)
22 | "/platform" -> resultingModel.platform = optionValue
23 | "/profile" -> resultingModel.profile = optionValue
24 | "/config" -> resultingModel.config = optionValue
25 | "/compress" -> resultingModel.compress = true
26 |
27 | "/importer" -> currentBuildEntry.importer = optionValue
28 | "/processor" -> {
29 | currentProcessorParams.clear()
30 | currentBuildEntry.processor = optionValue
31 | }
32 | "/processorParam" -> {
33 | val paramKey = optionValue!!.substringBefore("=")
34 | val paramValue = optionValue.substringAfter("=")
35 |
36 | currentProcessorParams[paramKey] = paramValue
37 | }
38 | "/build" -> {
39 | val sourcePath = optionValue!!.substringBefore(";")
40 | val destinationPath = optionValue.substringAfter(";", "")
41 |
42 | currentBuildEntry.contentFilepath = sourcePath
43 | if (destinationPath != "") {
44 | currentBuildEntry.destinationFilepath = destinationPath
45 | }
46 | currentBuildEntry.processorParams = currentProcessorParams.toMap()
47 | resultingModel.buildEntries.add(currentBuildEntry)
48 | currentBuildEntry = BuildEntry(
49 | importer = currentBuildEntry.importer,
50 | processor = currentBuildEntry.processor
51 | )
52 | }
53 | "/launchdebugger" -> resultingModel.launchDebugger = true
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/src/rider/main/resources/fileTemplates/Localized SpriteFont File.spritefont.ft:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
14 | Arial
15 |
16 |
20 | 12
21 |
22 |
26 | 0
27 |
28 |
32 | true
33 |
34 |
38 |
39 |
40 |
44 |
45 |
46 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/toolset/MgcbToolsetHost.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.toolset
2 |
3 | import com.intellij.openapi.components.Service
4 | import com.intellij.openapi.components.service
5 | import com.intellij.openapi.project.Project
6 | import com.jetbrains.rd.platform.util.lifetime
7 | import com.jetbrains.rd.util.reactive.compose
8 | import com.jetbrains.rider.model.RdProjectDescriptor
9 | import com.jetbrains.rider.projectView.solution
10 | import me.seclerp.rider.plugins.monogame.ToolDefinition
11 | import me.seclerp.rider.plugins.monogame.monoGameRiderModel
12 | import java.util.*
13 |
14 | @Service(Service.Level.PROJECT)
15 | class MgcbToolsetHost(private val intellijProject: Project) {
16 | companion object {
17 | fun getInstance(project: Project) = project.service()
18 | }
19 |
20 | private val hostModel by lazy { intellijProject.solution.monoGameRiderModel }
21 |
22 | val globalToolset by lazy { hostModel.mgcbGlobalToolset }
23 | val solutionToolset by lazy { hostModel.mgcbSolutionToolset }
24 | val projectsToolset by lazy { hostModel.mgcbProjectsToolsets }
25 | val editorTool by lazy {
26 | hostModel.mgcbGlobalToolset.editor
27 | .compose(hostModel.mgcbSolutionToolset.editor) { globalToolset, solutionToolset ->
28 | // Solution (local) toolset always has priority over global
29 | when {
30 | solutionToolset is ToolDefinition -> MgcbResolvedTool.Local(solutionToolset)
31 | globalToolset is ToolDefinition -> MgcbResolvedTool.Global(globalToolset)
32 | else -> MgcbResolvedTool.None
33 | }
34 | }
35 | }
36 |
37 | private val projectsEditorTools by lazy {
38 | val projectTools = mutableMapOf()
39 | hostModel.mgcbProjectsToolsets.view(intellijProject.lifetime) { toolsetRuntime, key, value ->
40 | toolsetRuntime.bracketIfAlive({
41 | value.editor.view(toolsetRuntime) { editorLifetime, editor ->
42 | projectTools[key] = when(editor) {
43 | null -> MgcbResolvedTool.None
44 | else -> MgcbResolvedTool.Local(editor)
45 | }
46 | }
47 | }, { projectTools.remove(key) })
48 | }
49 | projectTools
50 | }
51 |
52 | fun getEditorTool() = editorTool.value
53 |
54 | fun getEditorTool(project: RdProjectDescriptor): MgcbResolvedTool {
55 | val projectTool = projectsEditorTools[project.originalGuid]
56 | if (projectTool == null || projectTool.isNone())
57 | return getEditorTool()
58 | return projectTool
59 | }
60 |
61 | fun areToolsAvailable() = getEditorTool() !is MgcbResolvedTool.None
62 |
63 | fun areToolsAvailable(project: RdProjectDescriptor) = getEditorTool(project) !is MgcbResolvedTool.None
64 | }
65 |
66 |
--------------------------------------------------------------------------------
/src/dotnet/Rider.Plugins.MonoGame/MonoGameRdModelHost.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using JetBrains.Annotations;
3 | using JetBrains.Application.Parts;
4 | using JetBrains.Collections.Viewable;
5 | using JetBrains.Lifetimes;
6 | using JetBrains.ProjectModel;
7 | using JetBrains.ProjectModel.NuGet.DotNetTools;
8 | using JetBrains.Rd.Base;
9 | using JetBrains.RdBackend.Common.Features;
10 | using JetBrains.ReSharper.Feature.Services.Protocol;
11 | using Rider.Plugins.MonoGame.Mgcb;
12 |
13 | namespace Rider.Plugins.MonoGame;
14 |
15 | [SolutionComponent(Instantiation.ContainerAsyncPrimaryThread)]
16 | public class MonoGameRdModelHost
17 | {
18 | public MonoGameRdModelHost(
19 | Lifetime lifetime,
20 | ISolution solution,
21 | MgcbToolsetTracker toolsetTracker)
22 | {
23 | var model = solution.GetProtocolSolution().GetMonoGameRiderModel();
24 |
25 | BindGlobalToolset(toolsetTracker.MgcbGlobalToolset, model.MgcbGlobalToolset, lifetime);
26 | BindLocalToolset(toolsetTracker.MgcbSolutionToolset, model.MgcbSolutionToolset, lifetime);
27 | BindProjectsToolsets(toolsetTracker.MgcbProjectsToolset, model.MgcbProjectsToolsets, lifetime);
28 | }
29 |
30 | private void BindGlobalToolset(MgcbToolset source, MgcbEditorToolset target, Lifetime lifetime)
31 | {
32 | source.Editor.FlowInto(lifetime, target.Editor, cacheEntry =>
33 | cacheEntry is not null ? MapGlobalTool(cacheEntry) : null);
34 | }
35 |
36 | private void BindLocalToolset(MgcbToolset source, MgcbEditorToolset target, Lifetime lifetime)
37 | {
38 | source.Editor.FlowInto(lifetime, target.Editor, cacheEntry =>
39 | cacheEntry is not null ? MapLocalTool(cacheEntry) : null);
40 | }
41 |
42 | private void BindProjectsToolsets(IViewableMap> source, IViewableMap target, Lifetime lifetime)
43 | {
44 | source.View(lifetime, (projectLifetime, project, toolProperty) =>
45 | {
46 | projectLifetime.Bracket(
47 | () =>
48 | {
49 | var projectToolset = new MgcbEditorToolset();
50 | BindLocalToolset(toolProperty, projectToolset, projectLifetime);
51 | target.Add(project.Guid, projectToolset);
52 | },
53 | () =>
54 | {
55 | Unset(target[project.Guid]);
56 | target.Remove(project.Guid);
57 | });
58 | });
59 | }
60 |
61 | [NotNull]
62 | private static ToolDefinition MapGlobalTool([NotNull] GlobalToolCacheEntry tool) =>
63 | new (tool.ToolName, MgcbEditorCommandNameResolver.Resolve(tool), tool.Version.ToString());
64 |
65 | [NotNull]
66 | private static ToolDefinition MapLocalTool([NotNull] LocalTool tool) =>
67 | new (tool.PackageId, MgcbEditorCommandNameResolver.Resolve(tool), tool.Version);
68 |
69 | private static void Unset(MgcbEditorToolset toolset)
70 | {
71 | toolset.Editor.SetValue(null);
72 | }
73 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/spritefont/SpriteFontXmlSchemaProvider.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.spritefont
2 |
3 | import com.intellij.javaee.ExternalResourceManagerEx
4 | import com.intellij.openapi.module.Module
5 | import com.intellij.openapi.util.ModificationTracker
6 | import com.intellij.openapi.vfs.VfsUtil
7 | import com.intellij.psi.PsiFile
8 | import com.intellij.psi.PsiManager
9 | import com.intellij.psi.util.CachedValue
10 | import com.intellij.psi.util.CachedValueProvider
11 | import com.intellij.psi.util.CachedValuesManager
12 | import com.intellij.psi.xml.XmlFile
13 | import com.intellij.xml.XmlSchemaProvider
14 | import com.intellij.openapi.diagnostic.logger
15 |
16 | class SpriteFontXmlSchemaProvider : XmlSchemaProvider() {
17 | companion object {
18 | private val logger = logger()
19 | const val SCHEMA_LOCATION = "/xmlSchema/SpriteFont.xsd"
20 | const val FILE_EXTENSION = "spritefont"
21 | }
22 |
23 | private var schema: CachedValue? = null
24 |
25 | override fun getSchema(url: String, module: Module?, baseFile: PsiFile): XmlFile? {
26 | logger.info("Schema location: $SCHEMA_LOCATION")
27 | val project = baseFile.project
28 | logger.info("Project: ${project.name}")
29 |
30 | if (schema != null) {
31 | logger.info("Returning cached schema for location: $SCHEMA_LOCATION")
32 | return schema?.value
33 | }
34 |
35 | schema = CachedValuesManager.getManager(project).createCachedValue(object : CachedValueProvider {
36 | override fun compute(): CachedValueProvider.Result {
37 | logger.info("Computing value for cached schema")
38 | val resource = javaClass.getResource(SCHEMA_LOCATION)!!
39 | val fileByURL = VfsUtil.findFileByURL(resource)
40 | if (fileByURL == null) {
41 | logger.error("xsd file '$SCHEMA_LOCATION' not found")
42 | return CachedValueProvider.Result(null, ModificationTracker.EVER_CHANGED)
43 | }
44 |
45 | val psiFile = PsiManager.getInstance(project).findFile(fileByURL)
46 | if (psiFile !is XmlFile) {
47 | logger.warn("PSI file is not XML file")
48 | return CachedValueProvider.Result(null, ModificationTracker.EVER_CHANGED)
49 | }
50 |
51 | logger.info("PSI file null: ${false}")
52 | val manager = ExternalResourceManagerEx.getInstanceEx()
53 | val externalResourcesTracker = ModificationTracker { manager.getModificationCount(project) }
54 | return CachedValueProvider.Result.create(psiFile, psiFile, externalResourcesTracker)
55 | }
56 | }, false)
57 |
58 | logger.info("Added schema to cache for location: $SCHEMA_LOCATION")
59 | return schema?.value
60 | }
61 |
62 | override fun isAvailable(file: XmlFile): Boolean {
63 | return file.originalFile.virtualFile?.extension.equals(FILE_EXTENSION, true)
64 | }
65 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/extensions/commandLine/CommandBuilder.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.extensions.commandLine
2 |
3 | import com.intellij.execution.configurations.GeneralCommandLine
4 | import com.intellij.openapi.project.Project
5 | import com.jetbrains.rider.model.dotNetActiveRuntimeModel
6 | import com.jetbrains.rider.projectView.solution
7 | import com.jetbrains.rider.projectView.solutionDirectoryPath
8 | import com.jetbrains.rider.shared.run.FormatPreservingCommandLine
9 | import com.jetbrains.rider.shared.run.withRawParameters
10 | import org.jetbrains.annotations.NonNls
11 | import java.nio.charset.Charset
12 |
13 | open class CommandBuilder(vararg baseCommands: @NonNls String) {
14 | @NonNls
15 | protected var generalCommandLine =
16 | FormatPreservingCommandLine()
17 | .withRawParameters(baseCommands.joinToString(" "))
18 | .withCharset(Charset.forName("UTF-8"))
19 |
20 | fun executable(executable: String) {
21 | generalCommandLine = generalCommandLine.withExePath(executable)
22 | }
23 |
24 | fun workingDirectory(workingDirectory: String) {
25 | generalCommandLine = generalCommandLine.withWorkDirectory(workingDirectory)
26 | }
27 |
28 | @NonNls
29 | fun param(value: String) {
30 | generalCommandLine = generalCommandLine.withParameters(value)
31 | }
32 |
33 |
34 | @NonNls
35 | fun param(name: String, value: String) {
36 | generalCommandLine = generalCommandLine.withParameters(name, value)
37 | }
38 |
39 | @NonNls
40 | fun nullableParam(value: String?) {
41 | if (value != null)
42 | generalCommandLine = generalCommandLine.withParameters(value)
43 | }
44 |
45 | @NonNls
46 | fun nullableParam(name: String, value: String?) {
47 | if (value != null)
48 | generalCommandLine = generalCommandLine.withParameters(name, value)
49 | }
50 |
51 | @NonNls
52 | fun paramWhen(key: String, condition: Boolean) {
53 | if (condition)
54 | generalCommandLine = generalCommandLine.withParameters(key)
55 | }
56 |
57 | fun environment(key: String, value: String) {
58 | generalCommandLine = generalCommandLine.withEnvironment(key, value)
59 | }
60 |
61 | fun build(): GeneralCommandLine = generalCommandLine
62 | }
63 |
64 | fun buildDotnetCommand(project: Project, vararg baseCommands: @NonNls String, builder: CommandBuilder.() -> Unit = {}) =
65 | CommandBuilder(*baseCommands)
66 | .apply {
67 | val activeToolset = project.solution.dotNetActiveRuntimeModel.activeRuntime.valueOrNull
68 | executable(activeToolset?.dotNetCliExePath ?: throw Exception(".NET / .NET Core is not configured, unable to run commands."))
69 | workingDirectory(project.solutionDirectoryPath.toString())
70 | environment("DOTNET_SKIP_FIRST_TIME_EXPERIENCE", "true")
71 | environment("DOTNET_NOLOGO", "true")
72 | }
73 | .apply(builder)
74 | .build()
75 |
76 | fun buildCommand(vararg baseCommands: @NonNls String, builder: CommandBuilder.() -> Unit = {}) =
77 | CommandBuilder(*baseCommands).apply(builder).build()
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/extensions/commandLine/DefaultCommandExecutor.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.extensions.commandLine
2 |
3 | import com.intellij.execution.configurations.GeneralCommandLine
4 | import com.intellij.execution.util.ExecUtil
5 | import com.intellij.openapi.components.Service
6 | import com.intellij.openapi.components.service
7 | import com.intellij.openapi.diagnostic.logger
8 | import com.intellij.openapi.progress.runBackgroundableTask
9 | import com.intellij.openapi.project.Project
10 | import com.intellij.openapi.ui.Messages
11 | import com.jetbrains.rd.util.string.printToString
12 | import me.seclerp.rider.plugins.monogame.MonoGameUiBundle
13 | import java.io.BufferedReader
14 | import java.io.IOException
15 | import java.util.concurrent.TimeUnit
16 |
17 | @Service
18 | class DefaultCommandExecutor(
19 | intellijProject: Project
20 | ) : CliCommandExecutor(intellijProject) {
21 | companion object {
22 | fun getInstance(project: Project) = project.service()
23 | }
24 | private val logger = logger()
25 |
26 | override fun execute(command: GeneralCommandLine) {
27 | runBackgroundableTask(MonoGameUiBundle.message("command.execution.title"), intellijProject, false) {
28 | try {
29 | command.toProcessBuilder()
30 | val process = command
31 | .toProcessBuilder()
32 | .redirectOutput(ProcessBuilder.Redirect.PIPE)
33 | .redirectError(ProcessBuilder.Redirect.PIPE)
34 | .start()
35 | process.waitFor(30, TimeUnit.MINUTES)
36 | val exitCode = process.exitValue()
37 | val output = process.inputStream.bufferedReader().readText()
38 | val error = process.errorStream.bufferedReader().readText()
39 |
40 | // val executionResult = ExecUtil.execAndGetOutput(command)
41 | // val output = executionResult.stdout
42 | // val error = executionResult.stderr
43 | // val exitCode = executionResult.exitCode
44 | if (exitCode != 0) {
45 | failed(command.commandLineString, output, error, exitCode)
46 | }
47 | } catch (e: Exception) {
48 | failed(command.commandLineString, e)
49 | }
50 | }
51 | }
52 |
53 | private fun failed(command: String, exception: Exception) {
54 | logger.error(buildString {
55 | append("Command '$command' failed\n")
56 | append("\tException: ${exception.stackTraceToString()}")
57 | })
58 | Messages.showErrorDialog(
59 | MonoGameUiBundle.message("command.execution.error.message.exception", exception.message ?: ""),
60 | MonoGameUiBundle.message("command.execution.error.title"))
61 | }
62 |
63 | private fun failed(command: String, stdout: String, stderr: String, exitCode: Int) {
64 | logger.error(buildString {
65 | append("Command '$command' failed with exit code $exitCode\n")
66 | append("\tSTDOUT: $stdout\n")
67 | append("\tSTDERR: $stderr")
68 | })
69 | Messages.showErrorDialog(
70 | MonoGameUiBundle.message("command.execution.error.message.code", exitCode),
71 | MonoGameUiBundle.message("command.execution.error.title"))
72 | }
73 | }
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build & Text
2 | on:
3 | push:
4 | branches: [ release/* ]
5 | pull_request:
6 | branches: [ release/* ]
7 | workflow_dispatch:
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 | strategy:
13 | fail-fast: false
14 | matrix:
15 | os: [ windows-latest, macos-latest, ubuntu-latest ]
16 | steps:
17 | - name: 📝 Fetch Sources
18 | uses: actions/checkout@v5
19 |
20 | - name: 🛠 Import environment
21 | uses: ./.github/workflows/environment
22 |
23 | - name: 🧐 Validate Gradle wrapper
24 | uses: gradle/actions/wrapper-validation@v4
25 |
26 | - name: 🛠 Setup JDK
27 | uses: actions/setup-java@v4
28 | with:
29 | java-version: 21
30 | distribution: jetbrains
31 |
32 | - name: 🛠 Setup .NET SDK
33 | uses: actions/setup-dotnet@v5
34 | with:
35 | dotnet-version: 9.0.300
36 |
37 | - name: 🔧 Prepare build devenv
38 | uses: gradle/gradle-build-action@v2
39 | with:
40 | cache-disabled: true
41 | arguments: prepare
42 |
43 | - name: 🏗 Build Plugin (Stable)
44 | uses: gradle/gradle-build-action@v2
45 | with:
46 | arguments: buildPlugin
47 |
48 | - name: 📦 Prepare artifacts folder
49 | shell: pwsh
50 | run: |
51 | mkdir artifacts
52 |
53 | - name: 📦 Emit metadata artifacts
54 | shell: pwsh
55 | run: |
56 | $PROPERTIES = ./gradlew properties
57 |
58 | $VERSION_ROW = $PROPERTIES | Select-String "pluginVersion"
59 | $VERSION = ($VERSION_ROW -split ':')[1].Trim()
60 |
61 | $PLUGIN_ID_ROW = $PROPERTIES | Select-String "riderPluginId"
62 | $PLUGIN_ID = ($PLUGIN_ID_ROW -split ':')[1].Trim()
63 |
64 | echo "PLUGIN_VERSION=$VERSION" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
65 | echo $VERSION > ${{ env.ARTIFACTS_FOLDER }}/${{ env.ARTIFACTS_VERSION }}
66 |
67 | echo "PLUGIN_ID=$PLUGIN_ID" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
68 | echo $PLUGIN_ID > ${{ env.ARTIFACTS_FOLDER }}/${{ env.ARTIFACTS_PLUGIN_ID }}
69 |
70 | - name: 📦 Emit plugin archive artifact
71 | shell: pwsh
72 | run: |
73 | Get-ChildItem -Path "build\distributions\*.zip" |
74 | Move-Item -Destination "${{ env.ARTIFACTS_FOLDER }}\${{ env.PLUGIN_NAME }}-${{ env.PLUGIN_VERSION }}.zip" -Force
75 |
76 | - name: 📦 Emit changelog artifact
77 | shell: pwsh
78 | run: |
79 | $CHANGELOG = (./gradlew getChangelog --console=plain -q --no-header)
80 | # We need to escape CHANGELOG because it will be transferred over HTTP for GitHub Release creation
81 | $CHANGELOG = $CHANGELOG -replace '%', '%25'
82 | $CHANGELOG = $CHANGELOG -replace "`n", '%0A'
83 | $CHANGELOG = $CHANGELOG -replace "`r", '%0D'
84 | echo $CHANGELOG
85 | echo $CHANGELOG > ${{ env.ARTIFACTS_FOLDER }}/${{ env.ARTIFACTS_CHANGELOG }}
86 |
87 | - name: 📦 Tree artifacts
88 | shell: pwsh
89 | run: |
90 | tree ${{ env.ARTIFACTS_FOLDER }}
91 |
92 | - name: 📦 Upload artifacts
93 | uses: actions/upload-artifact@v4
94 | with:
95 | name: plugin-artifacts-${{ matrix.os }}
96 | path: ${{ env.ARTIFACTS_FOLDER }}/
97 | if-no-files-found: error
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/Mgcb.flex:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb;
2 |
3 | import com.intellij.lexer.FlexLexer;
4 | import com.intellij.psi.tree.IElementType;
5 | import me.seclerp.rider.plugins.monogame.mgcb.psi.MgcbTypes;
6 | import com.intellij.psi.TokenType;
7 |
8 | %%
9 |
10 | %class MgcbLexer
11 | %implements FlexLexer
12 | %unicode
13 | %function advance
14 | %type IElementType
15 | %eof{ return;
16 | %eof}
17 |
18 | CRLF=\R
19 | EOL=[\r\n]
20 | SPACES=[\ \t\f]
21 | WHITE_SPACE=[\ \n\t\f]
22 | COMMENT="#"[^\r\n]*
23 |
24 | OPTION_KEY="/"[^\ \t:\r\n]+
25 | OPTION_SEPARATOR=":"
26 | OPTION_VALUE=[^\r\n]+
27 |
28 | PREPROCESSOR_IDENTIFIER=[^\ \t:=\r\n]+
29 | PREPROCESSOR_VALUE=[^\ \t:=\r\n]+
30 | EQ="="
31 |
32 | SET_KEYWORD="$set"
33 | IF_KEYWORD="$if"
34 | ENDIF_KEYWORD="$endif"
35 |
36 | %state WAITING_OPTION_KEY
37 | %state WAITING_OPTION_VALUE
38 | %state WAITING_SET_KEY
39 | %state WAITING_SET_VALUE
40 | %state WAITING_IF_KEY
41 | %state WAITING_IF_VALUE
42 | %state WAITING_WHITESPACE
43 |
44 | %%
45 |
46 | {COMMENT} { yybegin(YYINITIAL); return MgcbTypes.COMMENT; }
47 | {OPTION_KEY} { yybegin(WAITING_OPTION_KEY); return MgcbTypes.OPTION_KEY; }
48 | {WHITE_SPACE}*{EOL} { yybegin(YYINITIAL); return MgcbTypes.WHITE_SPACE; }
49 | {OPTION_SEPARATOR} { yybegin(WAITING_OPTION_VALUE); return MgcbTypes.OPTION_SEPARATOR; }
50 | {WHITE_SPACE}*{EOL} { yybegin(YYINITIAL); return MgcbTypes.WHITE_SPACE; }
51 | {OPTION_VALUE} { yybegin(WAITING_WHITESPACE); return MgcbTypes.OPTION_VALUE; }
52 |
53 | {SET_KEYWORD} { yybegin(WAITING_SET_KEY); return MgcbTypes.SET_KEYWORD; }
54 | {SPACES}+ { yybegin(WAITING_SET_KEY); return MgcbTypes.WHITE_SPACE; }
55 | {PREPROCESSOR_IDENTIFIER} { yybegin(WAITING_SET_KEY); return MgcbTypes.PREPROCESSOR_IDENTIFIER; }
56 | {WHITE_SPACE}*{EOL} { yybegin(YYINITIAL); return MgcbTypes.WHITE_SPACE; }
57 | {EQ} { yybegin(WAITING_SET_VALUE); return MgcbTypes.EQ; }
58 | {PREPROCESSOR_VALUE} { yybegin(WAITING_WHITESPACE); return MgcbTypes.PREPROCESSOR_VALUE; }
59 |
60 | {IF_KEYWORD} { yybegin(WAITING_IF_KEY); return MgcbTypes.IF_KEYWORD; }
61 | {SPACES}+ { yybegin(WAITING_IF_KEY); return MgcbTypes.WHITE_SPACE; }
62 | {PREPROCESSOR_IDENTIFIER} { yybegin(WAITING_IF_KEY); return MgcbTypes.PREPROCESSOR_IDENTIFIER; }
63 | {WHITE_SPACE}*{EOL} { yybegin(YYINITIAL); return MgcbTypes.WHITE_SPACE; }
64 | {EQ} { yybegin(WAITING_IF_VALUE); return MgcbTypes.EQ; }
65 | {PREPROCESSOR_VALUE} { yybegin(WAITING_WHITESPACE); return MgcbTypes.PREPROCESSOR_VALUE; }
66 | {ENDIF_KEYWORD} { yybegin(YYINITIAL); return MgcbTypes.ENDIF_KEYWORD; }
67 |
68 | ({CRLF}|{WHITE_SPACE})+ { yybegin(YYINITIAL); return MgcbTypes.WHITE_SPACE; }
69 | ({CRLF}|{WHITE_SPACE})+ { yybegin(YYINITIAL); return MgcbTypes.WHITE_SPACE; }
70 |
71 | [^] { return TokenType.BAD_CHARACTER; }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/MgcbCompletionContributor.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb
2 |
3 | import com.intellij.codeInsight.completion.*
4 | import com.intellij.codeInsight.lookup.LookupElementBuilder
5 | import com.intellij.patterns.PlatformPatterns
6 | import com.intellij.util.ProcessingContext
7 | import me.seclerp.rider.plugins.monogame.mgcb.psi.MgcbTypes
8 |
9 | class MgcbCompletionContributor : CompletionContributor() {
10 | init {
11 | extend(CompletionType.BASIC, PlatformPatterns.psiElement(MgcbTypes.OPTION_KEY), OptionCompletionProvider())
12 | // extend(CompletionType.BASIC, PlatformPatterns.psiElement(), KeywordCompletionProvider())
13 | }
14 |
15 | abstract class MgcbCompletionProvider(private val values: Array)
16 | : CompletionProvider() {
17 | override fun addCompletions(params: CompletionParameters, ctx: ProcessingContext, resultSet: CompletionResultSet) {
18 | values.forEach {
19 | resultSet.addElement(LookupElementBuilder.create(it))
20 | }
21 | }
22 |
23 | companion object {
24 | private const val specialMarker = "IntellijIdeaRulezzz"
25 |
26 | // fun getPrefix(params: CompletionParameters): String {
27 | // val text = params.position.text
28 | // println(text)
29 | //
30 | // if (text == specialMarker) {
31 | // return specialMarker
32 | // }
33 | //
34 | // if (!text.endsWith(specialMarker)) {
35 | // return ""
36 | // }
37 | //
38 | // return text.substring(0, text.length - specialMarker.length)
39 | // }
40 | }
41 | }
42 |
43 | // class KeywordCompletionProvider : MgcbCompletionProvider(Keywords, ::handleInsert) {
44 | // companion object {
45 | // private fun handleInsert(ctx: InsertionContext, item: LookupElement): Unit {
46 | // ctx.commitDocument()
47 | // }
48 | //
49 | // val Keywords = arrayOf(
50 | //// "\$set",
51 | // "\$if",
52 | //// "\$endif"
53 | // )
54 | // }
55 | // }
56 |
57 | class OptionCompletionProvider : MgcbCompletionProvider(OptionTypes) {
58 | override fun addCompletions(
59 | params: CompletionParameters,
60 | ctx: ProcessingContext,
61 | resultSet: CompletionResultSet
62 | ) {
63 | val prefixMatcher = resultSet.prefixMatcher.cloneWithPrefix("/${resultSet.prefixMatcher.prefix}")
64 | super.addCompletions(params, ctx, resultSet.withPrefixMatcher(prefixMatcher))
65 | }
66 |
67 | companion object {
68 | val OptionTypes = arrayOf(
69 | "/outputDir",
70 | "/intermediateDir",
71 | "/rebuild",
72 | "/clean",
73 | "/incremental",
74 | "/reference",
75 | "/platform",
76 | "/profile",
77 | "/config",
78 | "/compress",
79 | "/importer",
80 | "/processor",
81 | "/processorParam",
82 | "/build",
83 | "/launchdebugger"
84 | )
85 | }
86 | }
87 | //
88 | // class MgcbPrefixMatcher : PrefixMatcher() {
89 | // override fun prefixMatches(p0: String): Boolean {
90 | // TODO("Not yet implemented")
91 | // }
92 | //
93 | // override fun cloneWithPrefix(p0: String): PrefixMatcher {
94 | // TODO("Not yet implemented")
95 | // }
96 | // }
97 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/MgcbSyntaxHighlighter.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb
2 |
3 | import com.intellij.lexer.Lexer
4 | import com.intellij.openapi.editor.DefaultLanguageHighlighterColors
5 | import com.intellij.openapi.editor.HighlighterColors
6 | import com.intellij.openapi.editor.colors.TextAttributesKey
7 | import com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey
8 | import com.intellij.openapi.fileTypes.SyntaxHighlighterBase
9 | import com.intellij.psi.TokenType
10 | import com.intellij.psi.tree.IElementType
11 | import me.seclerp.rider.plugins.monogame.mgcb.psi.MgcbTypes
12 |
13 | class MgcbSyntaxHighlighter : SyntaxHighlighterBase() {
14 | override fun getHighlightingLexer(): Lexer {
15 | return MgcbLexerAdapter()
16 | }
17 |
18 | override fun getTokenHighlights(tokenType: IElementType): Array =
19 | when (tokenType) {
20 | MgcbTypes.OPTION_KEY -> { OPTION_KEY_KEYS }
21 | MgcbTypes.OPTION_SEPARATOR -> { OPTION_SEPARATOR_KEYS }
22 | MgcbTypes.OPTION_VALUE -> { OPTION_VALUE_KEYS }
23 | MgcbTypes.SET_KEYWORD -> { PREPROCESSOR_KEYWORD_KEYS }
24 | MgcbTypes.IF_KEYWORD -> { PREPROCESSOR_KEYWORD_KEYS }
25 | MgcbTypes.ENDIF_KEYWORD -> { PREPROCESSOR_KEYWORD_KEYS }
26 | MgcbTypes.PREPROCESSOR_IDENTIFIER -> { PREPROCESSOR_IDENTIFIER_KEYS }
27 | MgcbTypes.EQ -> { PREPROCESSOR_SEPARATOR_KEYS }
28 | MgcbTypes.PREPROCESSOR_VALUE -> { PREPROCESSOR_VALUE_KEYS }
29 | MgcbTypes.COMMENT -> { COMMENT_KEYS }
30 | TokenType.BAD_CHARACTER -> { BAD_CHAR_KEYS }
31 | else -> { EMPTY_KEYS }
32 | }
33 |
34 | companion object {
35 | val OPTION_KEY: TextAttributesKey = createTextAttributesKey("MGCB_OPTION_KEY", DefaultLanguageHighlighterColors.IDENTIFIER)
36 | val OPTION_SEPARATOR: TextAttributesKey = createTextAttributesKey("MGCB_OPTION_SEPARATOR", DefaultLanguageHighlighterColors.OPERATION_SIGN)
37 | val OPTION_VALUE: TextAttributesKey = createTextAttributesKey("MGCB_OPTION_VALUE", DefaultLanguageHighlighterColors.STRING)
38 | val PREPROCESSOR_KEYWORD: TextAttributesKey = createTextAttributesKey("MGCB_PREPROCESSOR_KEYWORD", DefaultLanguageHighlighterColors.KEYWORD)
39 | val PREPROCESSOR_IDENTIFIER: TextAttributesKey = createTextAttributesKey("MGCB_PREPROCESSOR_IDENTIFIER", DefaultLanguageHighlighterColors.IDENTIFIER)
40 | val PREPROCESSOR_SEPARATOR: TextAttributesKey = createTextAttributesKey("MGCB_PREPROCESSOR_SEPARATOR", DefaultLanguageHighlighterColors.OPERATION_SIGN)
41 | val PREPROCESSOR_VALUE: TextAttributesKey = createTextAttributesKey("MGCB_PREPROCESSOR_VALUE", DefaultLanguageHighlighterColors.STRING)
42 | val COMMENT: TextAttributesKey = createTextAttributesKey("MGCB_COMMENT", DefaultLanguageHighlighterColors.LINE_COMMENT)
43 | val BAD_CHARACTER: TextAttributesKey = createTextAttributesKey("MGCB_BAD_CHARACTER", HighlighterColors.BAD_CHARACTER)
44 |
45 | private val OPTION_KEY_KEYS: Array = arrayOf(OPTION_KEY)
46 | private val OPTION_SEPARATOR_KEYS: Array = arrayOf(OPTION_SEPARATOR)
47 | private val OPTION_VALUE_KEYS: Array = arrayOf(OPTION_VALUE)
48 | private val PREPROCESSOR_KEYWORD_KEYS: Array = arrayOf(PREPROCESSOR_KEYWORD)
49 | private val PREPROCESSOR_IDENTIFIER_KEYS: Array = arrayOf(PREPROCESSOR_IDENTIFIER)
50 | private val PREPROCESSOR_SEPARATOR_KEYS: Array = arrayOf(PREPROCESSOR_SEPARATOR)
51 | private val PREPROCESSOR_VALUE_KEYS: Array = arrayOf(PREPROCESSOR_VALUE)
52 | private val COMMENT_KEYS: Array = arrayOf(COMMENT)
53 | private val BAD_CHAR_KEYS: Array = arrayOf(BAD_CHARACTER)
54 | private val EMPTY_KEYS: Array = arrayOfNulls(0)
55 | }
56 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
MonoGame plugin for JetBrains Rider
3 |

4 |
5 | This plugin improves MonoGame usage experience inside JetBrains Rider.
6 |
7 |

8 |
9 |
10 | ---
11 |
12 |
13 |
MGCB file editing
14 |
15 |
16 |
17 |
18 |
Autocomplete and syntax highlighting
19 | All supported MGCB options properly highlighted and could be autocompleted
20 |
21 |
22 |
23 |
24 |
25 |
26 |
Build entries previewer
27 | See all your assets in a realtime tree view according to their declarations
28 |
29 |
30 |
31 |
32 |
33 |
34 |
Table view for a build entry properties
35 | Review build entry properties and processor parameters in a table representation
36 |
37 |
38 |
39 |
40 |
41 |
42 |
Open in external MGCB editor action
43 | Jump to external MGCB editor GUI in one click
44 |
45 |
46 |
47 |
48 |
49 |
Other
50 |
51 |
52 |
53 |
54 |
Additional file templates
55 |
56 | Easily create MonoGame related assets using file templates under Add menu
57 |
58 |
59 |
60 | ---
61 |
62 | ### How to install
63 |
64 | #### Using marketplace:
65 |
66 | 1. Go to `Settings` / `Plugins` / `Marketplace`
67 | 1. Search for "MonoGame"
68 | 1. Click `Install`, then `Save`
69 | 1. After saving restart Rider
70 |
71 | #### Using `.zip` file
72 | 1. Go to [**Releases**](https://github.com/seclerp/rider-monogame/releases)
73 | 2. Download the latest release of plugin for your edition of JetBrains Rider (Stable or EAP)
74 | 3. Proceed to `Settings` / `Plugins` / `⚙` / `Install plugin from disk`
75 | 4. Click `Save`
76 | 5. After saving restart Rider
77 |
78 | ### How to use
79 |
80 | Just open .mgcb file for editing. Previewer will be on the right side of the editor.
81 |
82 | Additional file templates are located under Add section of a folder or project context menu.
83 |
84 | ### Requirements
85 |
86 | - JetBrains Rider **2025.1+**
87 |
88 | - Project with MonoGame installed (**3.8+ is recommended**)
89 |
90 | > **Note**: Projects with older versions of MonoGame might work, but with issues
91 |
92 | ### Development
93 |
94 | > **Note**: You should have JDK 21 and .NET SDK 8.0+ installed and configured.
95 |
96 | #### Preparing
97 |
98 | `./gradlew rdgen` - generates RD protocol data for plugin internal communication
99 |
100 | #### Building plugin parts
101 |
102 | `./gradlew buildPlugin`
103 |
104 | It will build both frontend and backend parts.
105 |
106 | #### Running
107 |
108 | Next command will start instance of JetBrains Rider with plugin attached to it:
109 |
110 | `./gradlew runIde`
111 |
112 | ### Contributing
113 |
114 | Contributions are welcome! 🎉
115 |
116 | It's better to create an issue with description of your bug/feature before creating pull requests.
117 |
118 | #### About branching
119 |
120 | This project uses customized git strategy.
121 |
122 | Each `release/*` branch plays main development branch role for specific release.
123 |
124 | For example, `release/251` means that branch is related to `251.*` release cycle for `2025.1` Rider version.
125 |
126 | ### See also
127 |
128 | - [**Marketplace page**](https://plugins.jetbrains.com/plugin/18415-monogame)
129 | - [**Changelog**](CHANGELOG.md)
130 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
4 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5 |
6 | ## [Unreleased]
7 |
8 | ## [253.1.0] - 2025-11-12
9 | ### Added
10 | - General: Support for Rider 2025.3
11 |
12 | ## [252.0.0-eap02] - 2025-05-28
13 | ### Added
14 | - General: Support for Rider 2025.2 (EAP 2)
15 |
16 | ## [251.1.0] - 2025-05-28
17 | ### Added
18 | - General: Support for Rider 2025.1
19 |
20 | ## [251.0.0-eap05] - 2025-02-26
21 | ### Added
22 | - General: Support for Rider 2025.1.0 EAP 5
23 |
24 | ## [243.1.0] - 2024-12-21
25 | ### Added
26 | - General: Support for Rider 2024.3.2
27 |
28 | ## [243.0.0-eap02] - 2024-09-28
29 | ### Added
30 | - General: Support for Rider 2024.3 EAP 2
31 |
32 | ## [242.1.0] - 2024-09-22
33 | ### Added
34 | - General: Support for Rider 2024.2
35 |
36 | ## [242.0.0-eap03] - 2024-06-21
37 | ### Added
38 | - General: Support for Rider 2024.2 (EAP 3)
39 |
40 | ## [241.0.0-rc1] - 2024-03-28
41 | ### Added
42 | - General: Support for Rider 2024.1 RC
43 |
44 | ## [233.1.0] - 2023-12-27
45 | ### Added
46 | - General: Support for Rider 2023.3
47 |
48 | ## [232.0.0-rc1] - 2023-07-27
49 | ### Added
50 | - General: Support for Rider 2023.2
51 |
52 | ## [231.1.0] - 2023-07-11
53 | ### Added
54 | - General: Support for Rider 2023.1 (#16)
55 | - MGCB: Support for new MGCB Editor tools layout of MonoGame 3.8.1 (#13)
56 | - MGCB Previewer: Minor style adjustments for parameters and preprocessor values tables
57 | ### Changed
58 | - MGCB: Refactor of `mgcb-editor` tools detection logic (#14)
59 | ### Fixed
60 | - MGCB: False-positive trigger of `Install` toolbar decorator even if tools are installed (#15)
61 | ### Removed
62 | - MGCB: Temporary removed `Install` action due to broken behavior (#15)
63 |
64 | ## [223.0.0] - 2022-09-30
65 | ### Added
66 | - Enable support for Rider 2022.3 EAP
67 |
68 | ## [222.0.0] - 2022-08-15
69 | ### Added
70 | - Add support for Rider 2022.2
71 | ### Changed
72 | - Change version numbering to reflect Rider version in it
73 |
74 | ## [1.0.1] - 2022-04-27
75 | ### Changed
76 | - Upgrade Rider SDK to 2022.1
77 | ### Fixed
78 | - Fix: Exception Thrown when switching solutions
79 |
80 | ## [1.0.0] - 2022-01-15
81 | ### Added
82 | - Autocomplete and syntax highlighting
83 | - Build entries provider
84 | - Table view for a build entry properties
85 | - Open in external MGCB editor action
86 | - Additional file templates
87 |
88 | [Unreleased]: https://github.com/seclerp/rider-monogame/compare/v252.0.0-eap02...v253.1.0
89 | [253.1.0]: https://github.com/seclerp/rider-monogame/compare/v252.0.0-eap02...v253.1.0
90 | [252.0.0-eap02]: https://github.com/seclerp/rider-monogame/compare/v251.1.0...252.0.0-eap02
91 | [251.1.0]: https://github.com/seclerp/rider-monogame/compare/v251.0.0-eap05...v251.1.0
92 | [251.0.0-eap05]: https://github.com/seclerp/rider-monogame/compare/v243.1.0...v251.0.0-eap05
93 | [243.1.0]: https://github.com/seclerp/rider-monogame/compare/v243.0.0-eap02...v243.1.0
94 | [243.0.0-eap02]: https://github.com/seclerp/rider-monogame/compare/v242.1.0...v243.0.0-eap02
95 | [242.1.0]: https://github.com/seclerp/rider-monogame/compare/v242.0.0-eap03...v242.1.0
96 | [242.0.0-eap03]: https://github.com/seclerp/rider-monogame/compare/v241.0.0-rc1...v242.0.0-eap03
97 | [241.0.0-rc1]: https://github.com/seclerp/rider-monogame/compare/v233.1.0...v241.0.0-rc1
98 | [233.1.0]: https://github.com/seclerp/rider-monogame/compare/v232.0.0-rc1...v233.1.0
99 | [232.0.0-rc1]: https://github.com/seclerp/rider-monogame/compare/v231.1.0...v232.0.0-rc1
100 | [231.1.0]: https://github.com/seclerp/rider-monogame/compare/v223.0.0...v231.1.0
101 | [223.0.0]: https://github.com/seclerp/rider-monogame/compare/v1.0.1...v223.0.0
102 | [222.0.0]: https://github.com/seclerp/rider-monogame/compare/v1.0.1...v222.0.0
103 | [1.0.1]: https://github.com/seclerp/rider-monogame/compare/v1.0.0...v1.0.1
104 | [1.0.0]: https://github.com/seclerp/rider-monogame/releases/tag/v1.0.0
105 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Deploy
2 | on:
3 | workflow_run:
4 | workflows: [Build & Text]
5 | types: [completed]
6 | branches: [ release/* ]
7 | jobs:
8 | on-success-build:
9 | runs-on: ubuntu-latest
10 | if: ${{ github.event.workflow_run.conclusion == 'success' }}
11 | environment: production
12 | steps:
13 | - name: 📝 Fetch Sources
14 | uses: actions/checkout@v5
15 |
16 | - name: 🛠 Import environment
17 | uses: ./.github/workflows/environment
18 |
19 | - name: 🛠 Setup JDK
20 | uses: actions/setup-java@v4
21 | with:
22 | java-version: 21
23 | distribution: jetbrains
24 |
25 | - name: 📦 Download artifacts
26 | uses: dawidd6/action-download-artifact@v2
27 | with:
28 | name: plugin-artifacts-ubuntu-latest
29 | path: ${{ env.ARTIFACTS_FOLDER }}
30 | run_id: ${{ github.event.workflow_run.id }}
31 | if_no_artifact_found: fail
32 |
33 | - name: 📦 Tree artifacts
34 | shell: pwsh
35 | run: |
36 | tree ${{ env.ARTIFACTS_FOLDER }}
37 |
38 | - name: 📦 Extract metadata
39 | shell: pwsh
40 | run: |
41 | $PLUGIN_VERSION = (cat "${{ env.ARTIFACTS_FOLDER }}/${{ env.ARTIFACTS_VERSION }}")
42 | echo "PLUGIN_VERSION=$PLUGIN_VERSION" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
43 |
44 | $PLUGIN_ID = (cat "${{ env.ARTIFACTS_FOLDER }}/${{ env.ARTIFACTS_PLUGIN_ID }}")
45 | echo "PLUGIN_ID=$PLUGIN_ID" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
46 |
47 | - name: 🚀 Create Release
48 | uses: ncipollo/release-action@v1
49 | with:
50 | name: ${{ env.PLUGIN_VERSION }}
51 | commit: ${{ github.sha }} # Commit SHA
52 | tag: v${{ env.PLUGIN_VERSION }} # 'v' + version from gradle.properties
53 | draft: true
54 | bodyFile: ${{ env.ARTIFACTS_FOLDER }}/${{ env.ARTIFACTS_CHANGELOG }}
55 | artifacts: "${{ env.ARTIFACTS_FOLDER }}/${{ env.PLUGIN_NAME }}-${{ env.PLUGIN_VERSION }}.zip"
56 | token: ${{ secrets.GITHUB_TOKEN }}
57 |
58 | - name: 🚀 Publish Plugin
59 | shell: pwsh
60 | run: |
61 | $uploadUrl = '${{ env.PLUGINS_REPOSITORY }}/api/updates/upload'
62 | $token = '${{ secrets.JB_PUBLISH_TOKEN }}'
63 | Write-Host "Upload URL: $uploadUrl"
64 |
65 | $pluginXmlId = Get-Content -Raw -Path '${{ env.ARTIFACTS_FOLDER }}/${{ env.ARTIFACTS_PLUGIN_ID }}'
66 | Write-Host "Plugin ID to upload: $pluginXmlId"
67 | $channel = 'stable'
68 | Write-Host "Channel: $channel"
69 | $pluginPath = '${{ env.ARTIFACTS_FOLDER }}/${{ env.PLUGIN_NAME }}-${{ env.PLUGIN_VERSION }}.zip'
70 | Write-Host "Plugin archive location: $pluginPath"
71 |
72 | $content = [System.Net.Http.MultipartFormDataContent]::new()
73 | $content.Add((New-Object System.Net.Http.StringContent $pluginXmlId.Trim()), 'xmlId')
74 | $content.Add((New-Object System.Net.Http.StringContent $channel), 'channel')
75 | $filePart = [System.Net.Http.StreamContent]::new([System.IO.File]::OpenRead($pluginPath))
76 | $filePart.Headers.ContentDisposition = 'form-data; name="file"; filename="{0}"' -f (Split-Path -Leaf $pluginPath)
77 | $content.Add($filePart, 'file')
78 | Write-Host "Multipart form data created"
79 |
80 | Write-Host "Requesting POST $uploadUrl..."
81 | $client = [System.Net.Http.HttpClient]::new()
82 | $client.DefaultRequestHeaders.Authorization = New-Object System.Net.Http.Headers.AuthenticationHeaderValue('Bearer', $token)
83 | $result = $client.PostAsync($uploadUrl, $content).Result
84 |
85 | $status = $result.StatusCode
86 | $isSuccess = $result.IsSuccessStatusCode
87 | Write-Host "Status Code: $status"
88 | Write-Host "Is Success: $isSuccess"
89 |
90 | $responseContent = $result.Content.ReadAsStringAsync().Result
91 | echo "Response: $responseContent"
92 |
93 | if (-not $isSuccess) {
94 | Write-Host "Request failed. Exiting."
95 | exit 1
96 | }
97 |
--------------------------------------------------------------------------------
/src/dotnet/Rider.Plugins.MonoGame/Mgcb/MgcbToolsetTracker.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using JetBrains.Application.FileSystemTracker;
3 | using JetBrains.Application.Parts;
4 | using JetBrains.Application.Threading;
5 | using JetBrains.Collections.Viewable;
6 | using JetBrains.DataFlow;
7 | using JetBrains.Lifetimes;
8 | using JetBrains.ProjectModel;
9 | using JetBrains.ProjectModel.NuGet.DotNetTools;
10 | using JetBrains.Util;
11 | using Rider.Plugins.MonoGame.Extensions;
12 |
13 | namespace Rider.Plugins.MonoGame.Mgcb;
14 |
15 | ///
16 | /// MonoGame MGCB tools aka mgcb-editor could be installed in different places:
17 | /// - globally on user's machine (3.8.0 approach)
18 | /// - locally in solution (3.8.0 approach)
19 | /// - locally in project (3.8.1 approach)
20 | /// So we need to track all these places.
21 | ///
22 | [SolutionComponent(Instantiation.ContainerAsyncPrimaryThread)]
23 | public class MgcbToolsetTracker
24 | {
25 | public MgcbToolset MgcbGlobalToolset;
26 | public MgcbToolset MgcbSolutionToolset;
27 | public IViewableMap> MgcbProjectsToolset;
28 |
29 | private IDictionary _projectToolsTrackers = new Dictionary();
30 |
31 | public MgcbToolsetTracker(
32 | IViewableProjectsCollection projectsCollection,
33 | Lifetime lifetime,
34 | SolutionDotnetToolsTracker solutionToolsTracker,
35 | IFileSystemTracker fileSystemTracker,
36 | IShellLocks locks,
37 | ILogger logger,
38 | ISolutionToolset toolset)
39 | {
40 | // while (!Debugger.IsAttached) Thread.Sleep(1000);
41 | MgcbGlobalToolset = CreateGlobalToolset(solutionToolsTracker);
42 | MgcbSolutionToolset = CreateSolutionToolset(solutionToolsTracker);
43 | MgcbProjectsToolset = new ViewableMap>();
44 |
45 | projectsCollection.Projects.View(
46 | lifetime,
47 | (projectLifetime, project) =>
48 | {
49 | if (project.Kind != ProjectItemKind.PROJECT || project.ProjectFile == null)
50 | return;
51 |
52 | projectLifetime.Bracket(
53 | () =>
54 | {
55 | var tracker = new ProjectDotnetToolsTracker(projectLifetime, project,
56 | fileSystemTracker, locks,
57 | logger, toolset);
58 | _projectToolsTrackers.Add(project, tracker);
59 | MgcbProjectsToolset.Add(project, CreateProjectToolset(tracker));
60 | },
61 | () =>
62 | {
63 | _projectToolsTrackers.Remove(project);
64 | MgcbProjectsToolset[project].Unset();
65 | MgcbProjectsToolset.Remove(project);
66 | });
67 | });
68 | }
69 |
70 | private MgcbToolset CreateGlobalToolset(SolutionDotnetToolsTracker solutionToolsTracker) =>
71 | new()
72 | {
73 | Editor = ObserveGlobalTool(solutionToolsTracker),
74 | };
75 |
76 | private MgcbToolset CreateSolutionToolset(SolutionDotnetToolsTracker solutionToolsTracker) =>
77 | new()
78 | {
79 | Editor = ObserveLocalTool(solutionToolsTracker),
80 | };
81 |
82 | private MgcbToolset CreateProjectToolset(ProjectDotnetToolsTracker projectToolsTracker) =>
83 | new()
84 | {
85 | Editor = ObserveLocalTool(projectToolsTracker),
86 | };
87 |
88 | private IProperty ObserveGlobalTool(NuGetDotnetToolsTrackerBase toolsTracker) =>
89 | toolsTracker.DotNetToolCache.Select(
90 | nameof(MgcbToolsetTracker),
91 | cache => cache?.ToolGlobalCache?.Let(MgcbEditorToolResolver.Resolve));
92 |
93 | private IProperty ObserveLocalTool(NuGetDotnetToolsTrackerBase toolsTracker) =>
94 | toolsTracker.DotNetToolCache.Select(
95 | nameof(MgcbToolsetTracker),
96 | cache => cache?.ToolLocalCache?.Let(MgcbEditorToolResolver.Resolve));
97 | }
--------------------------------------------------------------------------------
/src/dotnet/Rider.Plugins.MonoGame/Mgcb/MgcbEditorToolResolver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Runtime.InteropServices;
4 | using JetBrains.Annotations;
5 | using JetBrains.ProjectModel.NuGet.DotNetTools;
6 | using NuGet.Versioning;
7 | using Rider.Plugins.MonoGame.Extensions;
8 |
9 | namespace Rider.Plugins.MonoGame.Mgcb;
10 |
11 | public static class MgcbEditorToolResolver
12 | {
13 | private static readonly string PlatformSpecificToolName = ResolvePlatformSpecificToolName();
14 |
15 | [CanBeNull]
16 | public static LocalTool Resolve([NotNull] DotNetToolLocalCache cache)
17 | {
18 | var platformSpecificTool = cache.GetLocalTool(PlatformSpecificToolName);
19 | var platformSpecificToolRef = platformSpecificTool?.Let(VersionedTool.Create);
20 |
21 | var platformAgnosticTool = cache.GetLocalTool(KnownDotNetTools.MgcbEditor);
22 | var platformAgnosticToolRef = platformAgnosticTool?.Let(VersionedTool.Create);
23 |
24 | return Resolve(platformSpecificToolRef, platformAgnosticToolRef)?.Tool;
25 | }
26 |
27 | [CanBeNull]
28 | public static GlobalToolCacheEntry Resolve([NotNull] DotNetToolGlobalCache cache)
29 | {
30 | var platformSpecificTool = cache.GetGlobalTool(PlatformSpecificToolName)?.FirstOrDefault();
31 | var platformSpecificToolRef = platformSpecificTool?.Let(VersionedTool.Create);
32 |
33 | var platformAgnosticTool = cache.GetGlobalTool(KnownDotNetTools.MgcbEditor)?.FirstOrDefault();
34 | var platformAgnosticToolRef = platformAgnosticTool?.Let(VersionedTool.Create);
35 |
36 | return Resolve(platformSpecificToolRef, platformAgnosticToolRef)?.Tool;
37 | }
38 |
39 | [CanBeNull]
40 | private static VersionedTool Resolve(
41 | [CanBeNull] VersionedTool platformSpecificVersionedTool,
42 | [CanBeNull] VersionedTool platformAgnosticVersionedTool)
43 | {
44 | switch (platformSpecificVersionedTool, platformAgnosticVersionedTool)
45 | {
46 | // If we have only platform-specific (i.e. mgcb-editor-mac) tool, use it
47 | case (not null, null):
48 | {
49 | return platformSpecificVersionedTool;
50 | }
51 | // If we have only platform-agnostic tool (i.e. mgcb-editor)
52 | case (null, not null):
53 | {
54 | // We need to check if it's only a bootstrapper (>=3.8.1) or an editor itself (<=3.8.0)
55 | // If it's a bootsrapper, return null as it requires a platform-specific tool that we checked for before
56 | if (platformAgnosticVersionedTool.Version >= KnownMgcbVersions.Version381)
57 | {
58 | return null;
59 | }
60 |
61 | // If it's an editor itself, use it
62 | return platformAgnosticVersionedTool;
63 | }
64 | // If we have both, specific one always has the priority because it's always newer by default
65 | case (not null, not null):
66 | {
67 | return platformSpecificVersionedTool;
68 | }
69 | // Nothing at all, nothing to choose from.
70 | case (null, null):
71 | {
72 | return null;
73 | }
74 | }
75 | }
76 |
77 | private static string ResolvePlatformSpecificToolName()
78 | {
79 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
80 | return KnownDotNetTools.MgcbEditorWindows;
81 |
82 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
83 | return KnownDotNetTools.MgcbEditorLinux;
84 |
85 | if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
86 | return KnownDotNetTools.MgcbEditorMac;
87 |
88 | throw new NotImplementedException("Unsupported Operating System");
89 | }
90 |
91 | private record VersionedTool(
92 | NuGetVersion Version,
93 | [CanBeNull] TTool Tool
94 | );
95 |
96 | private static class VersionedTool
97 | {
98 | public static VersionedTool Create(GlobalToolCacheEntry tool) =>
99 | new(tool.Version, tool);
100 |
101 | public static VersionedTool Create(LocalTool tool) =>
102 | new(NuGetVersion.Parse(tool.Version), tool);
103 | }
104 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/actions/OpenExternalEditorAction.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.actions
2 |
3 | import com.intellij.openapi.actionSystem.ActionUpdateThread
4 | import com.intellij.openapi.actionSystem.AnAction
5 | import com.intellij.openapi.actionSystem.AnActionEvent
6 | import com.intellij.openapi.actionSystem.CommonDataKeys
7 | import com.intellij.openapi.project.Project
8 | import com.intellij.openapi.vfs.VirtualFile
9 | import com.intellij.platform.backend.workspace.workspaceModel
10 | import com.jetbrains.rider.model.RdProjectDescriptor
11 | import me.seclerp.rider.extensions.commandLine.CommandBuilder
12 | import me.seclerp.rider.extensions.commandLine.DefaultCommandExecutor
13 | import me.seclerp.rider.extensions.commandLine.buildCommand
14 | import me.seclerp.rider.extensions.commandLine.buildDotnetCommand
15 | import me.seclerp.rider.extensions.workspaceModel.containingProjectDirectory
16 | import me.seclerp.rider.extensions.workspaceModel.containingProjectEntity
17 | import me.seclerp.rider.plugins.monogame.MonoGameIcons
18 | import me.seclerp.rider.plugins.monogame.MonoGameUiBundle
19 | import me.seclerp.rider.plugins.monogame.mgcb.toolset.MgcbResolvedTool
20 | import me.seclerp.rider.plugins.monogame.mgcb.toolset.MgcbToolsetHost
21 |
22 | @Suppress("DialogTitleCapitalization", "UnstableApiUsage")
23 | class OpenExternalEditorAction : AnAction(MonoGameIcons.MgcbFile) {
24 | override fun actionPerformed(actionEvent: AnActionEvent) {
25 | val intellijProject = actionEvent.project ?: return
26 | val file = actionEvent.dataContext.getData(CommonDataKeys.VIRTUAL_FILE) ?: return
27 | val dotnetProject = intellijProject.workspaceModel.containingProjectEntity(file, intellijProject) ?: return
28 | val descriptor = dotnetProject.descriptor as? RdProjectDescriptor ?: return
29 | val dotnetProjectDirectory = intellijProject.workspaceModel.containingProjectDirectory(file, intellijProject) ?: return
30 | val editorTool = MgcbToolsetHost.getInstance(intellijProject).getEditorTool(descriptor)
31 | runEditor(intellijProject, editorTool, dotnetProjectDirectory, file)
32 | }
33 |
34 | override fun getActionUpdateThread() = ActionUpdateThread.EDT
35 |
36 | override fun update(actionEvent: AnActionEvent) {
37 | actionEvent.presentation.isEnabledAndVisible = false
38 | val intellijProject = actionEvent.project ?: return
39 | val file = actionEvent.dataContext.getData(CommonDataKeys.VIRTUAL_FILE) ?: return
40 | val dotnetProject = intellijProject.workspaceModel.containingProjectEntity(file, intellijProject) ?: return
41 | val descriptor = dotnetProject.descriptor as? RdProjectDescriptor ?: return
42 | val mgcbEditorInstalled = MgcbToolsetHost.getInstance(intellijProject).areToolsAvailable(descriptor)
43 | actionEvent.presentation.isVisible = true
44 | actionEvent.presentation.isEnabled = mgcbEditorInstalled
45 | actionEvent.presentation.text =
46 | if (mgcbEditorInstalled)
47 | MonoGameUiBundle.message("command.mgcb.open.title")
48 | else
49 | MonoGameUiBundle.message("command.mgcb.open.missing.editor.title")
50 | }
51 |
52 | // .NET tools have different rules for running local and global tools.
53 | // For global tool, it should be executed as a stand-alone program, like that:
54 | // > mgcb-editor
55 | // For local tool, it should be executed as a sub-command for 'dotnet', like that:
56 | // > dotnet mgcb-editor
57 | // or in more explicit way:
58 | // > dotnet tool run mgcb-editor
59 | // Source: https://learn.microsoft.com/en-us/dotnet/core/tools/global-tools#use-a-tool
60 | private fun runEditor(intellijProject: Project, editorTool: MgcbResolvedTool, projectDirectory: VirtualFile, contentFile: VirtualFile) {
61 | fun CommandBuilder.configureEditorCommand() {
62 | workingDirectory(projectDirectory.path)
63 | param(contentFile.path)
64 | }
65 |
66 | val command =
67 | when (editorTool) {
68 | is MgcbResolvedTool.Local -> buildDotnetCommand(intellijProject, editorTool.definition.commandName) { configureEditorCommand() }
69 | is MgcbResolvedTool.Global -> buildCommand(editorTool.definition.commandName) { configureEditorCommand() }
70 | is MgcbResolvedTool.None -> null
71 | }
72 |
73 | DefaultCommandExecutor.getInstance(intellijProject).execute(command ?: return)
74 | }
75 | }
--------------------------------------------------------------------------------
/src/rider/main/resources/META-INF/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 | me.seclerp.rider.plugins.monogame
3 | MonoGame
4 | _PLACEHOLDER_
5 | Andrew Rublyov
6 |
7 | com.intellij.modules.rider
8 | messages.MonoGameUiBundle
9 |
10 |
11 | This plugin improves MonoGame usage experience inside JetBrains Rider
13 |
14 | MGCB file editing
15 |
16 | - Autocomplete and syntax highlighting
17 | - Build entries tree previewer
18 | - Table view for a build entry properties
19 | - Open in external MGCB editor action
20 |
21 | File templates for
22 |
23 | - MGCB
24 | - Effect
25 | - Sprite Effect
26 | - SpriteFont
27 | - Localized SpriteFont
28 |
29 |
30 |
31 | Links
32 |
36 |
37 | ]]>
38 |
39 |
40 |
41 |
43 |
44 |
46 |
47 |
49 |
50 |
52 |
54 |
55 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
79 |
80 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
95 |
97 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem GRADLE JVM WRAPPER START MARKER
42 |
43 | setlocal
44 | set BUILD_DIR=%LOCALAPPDATA%\gradle-jvm
45 | set JVM_TARGET_DIR=%BUILD_DIR%\jdk-21.0.3_windows-x64_bin-125c41\
46 |
47 | set JVM_URL=https://download.oracle.com/java/21/archive/jdk-21.0.3_windows-x64_bin.zip
48 |
49 | set IS_TAR_GZ=0
50 | set JVM_TEMP_FILE=gradle-jvm.zip
51 |
52 | if /I "%JVM_URL:~-7%"==".tar.gz" (
53 | set IS_TAR_GZ=1
54 | set JVM_TEMP_FILE=gradle-jvm.tar.gz
55 | )
56 |
57 | set POWERSHELL=%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe
58 |
59 | if not exist "%JVM_TARGET_DIR%" MD "%JVM_TARGET_DIR%"
60 |
61 | if not exist "%JVM_TARGET_DIR%.flag" goto downloadAndExtractJvm
62 |
63 | set /p CURRENT_FLAG=<"%JVM_TARGET_DIR%.flag"
64 | if "%CURRENT_FLAG%" == "%JVM_URL%" goto continueWithJvm
65 |
66 | :downloadAndExtractJvm
67 |
68 | PUSHD "%BUILD_DIR%"
69 | if errorlevel 1 goto fail
70 |
71 | echo Downloading %JVM_URL% to %BUILD_DIR%\%JVM_TEMP_FILE%
72 | if exist "%JVM_TEMP_FILE%" DEL /F "%JVM_TEMP_FILE%"
73 | "%POWERSHELL%" -nologo -noprofile -Command "Set-StrictMode -Version 3.0; $ErrorActionPreference = \"Stop\"; (New-Object Net.WebClient).DownloadFile('%JVM_URL%', '%JVM_TEMP_FILE%')"
74 | if errorlevel 1 goto fail
75 |
76 | POPD
77 |
78 | RMDIR /S /Q "%JVM_TARGET_DIR%"
79 | if errorlevel 1 goto fail
80 |
81 | MKDIR "%JVM_TARGET_DIR%"
82 | if errorlevel 1 goto fail
83 |
84 | PUSHD "%JVM_TARGET_DIR%"
85 | if errorlevel 1 goto fail
86 |
87 | echo Extracting %BUILD_DIR%\%JVM_TEMP_FILE% to %JVM_TARGET_DIR%
88 |
89 | if "%IS_TAR_GZ%"=="1" (
90 | tar xf "..\\%JVM_TEMP_FILE%"
91 | ) else (
92 | "%POWERSHELL%" -nologo -noprofile -command "Set-StrictMode -Version 3.0; $ErrorActionPreference = \"Stop\"; Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::ExtractToDirectory('..\\%JVM_TEMP_FILE%', '.');"
93 | )
94 | if errorlevel 1 goto fail
95 |
96 | DEL /F "..\%JVM_TEMP_FILE%"
97 | if errorlevel 1 goto fail
98 |
99 | POPD
100 |
101 | echo %JVM_URL%>"%JVM_TARGET_DIR%.flag"
102 | if errorlevel 1 goto fail
103 |
104 | :continueWithJvm
105 |
106 | set JAVA_HOME=
107 | for /d %%d in ("%JVM_TARGET_DIR%"*) do if exist "%%d\bin\java.exe" set JAVA_HOME=%%d
108 | if not exist "%JAVA_HOME%\bin\java.exe" (
109 | echo Unable to find java.exe under %JVM_TARGET_DIR%
110 | goto fail
111 | )
112 |
113 | endlocal & set JAVA_HOME=%JAVA_HOME%
114 |
115 | @rem GRADLE JVM WRAPPER END MARKER
116 |
117 | @rem Find java.exe
118 | if defined JAVA_HOME goto findJavaFromJavaHome
119 |
120 | set JAVA_EXE=java.exe
121 | %JAVA_EXE% -version >NUL 2>&1
122 | if %ERRORLEVEL% equ 0 goto execute
123 |
124 | echo. 1>&2
125 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
126 | echo. 1>&2
127 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
128 | echo location of your Java installation. 1>&2
129 |
130 | goto fail
131 |
132 | :findJavaFromJavaHome
133 | set JAVA_HOME=%JAVA_HOME:"=%
134 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
135 |
136 | if exist "%JAVA_EXE%" goto execute
137 |
138 | echo. 1>&2
139 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
140 | echo. 1>&2
141 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
142 | echo location of your Java installation. 1>&2
143 |
144 | goto fail
145 |
146 | :execute
147 | @rem Setup the command line
148 |
149 | set CLASSPATH=
150 |
151 |
152 | @rem Execute Gradle
153 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
154 |
155 | :end
156 | @rem End local scope for the variables with windows NT shell
157 | if %ERRORLEVEL% equ 0 goto mainEnd
158 |
159 | :fail
160 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
161 | rem the _cmd.exe /c_ return code!
162 | set EXIT_CODE=%ERRORLEVEL%
163 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
164 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
165 | exit /b %EXIT_CODE%
166 |
167 | :mainEnd
168 | if "%OS%"=="Windows_NT" endlocal
169 |
170 | :omega
171 |
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/settings/MgcbToolsetConfigurableProvider.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.settings
2 |
3 | import com.intellij.openapi.observable.properties.AtomicProperty
4 | import com.intellij.openapi.options.ConfigurableProvider
5 | import com.intellij.openapi.options.SearchableConfigurable
6 | import com.intellij.openapi.project.Project
7 | import com.intellij.openapi.ui.DialogPanel
8 | import com.intellij.openapi.util.text.HtmlBuilder
9 | import com.intellij.platform.backend.workspace.workspaceModel
10 | import com.intellij.ui.dsl.builder.bindText
11 | import com.intellij.ui.dsl.builder.panel
12 | import com.jetbrains.rd.platform.util.lifetime
13 | import com.jetbrains.rd.util.reactive.AddRemove
14 | import com.jetbrains.rd.util.reactive.map
15 | import com.jetbrains.rider.model.RdProjectDescriptor
16 | import com.jetbrains.rider.projectView.workspace.findProjects
17 | import me.seclerp.rider.extensions.observables.RdObservableProperty
18 | import me.seclerp.rider.plugins.monogame.MonoGameUiBundle
19 | import me.seclerp.rider.plugins.monogame.ToolDefinition
20 | import me.seclerp.rider.plugins.monogame.mgcb.toolset.MgcbToolsetHost
21 | import java.util.UUID
22 |
23 | class MgcbToolsetConfigurableProvider(private val project: Project) : ConfigurableProvider() {
24 | override fun createConfigurable() = MgcbToolsetConfigurable(project)
25 |
26 | @Suppress("UnstableApiUsage")
27 | class MgcbToolsetConfigurable(private val project: Project) : SearchableConfigurable {
28 | private val lifetime = project.lifetime.createNested()
29 | private val fallbackProperty by lazy { AtomicProperty(MonoGameUiBundle.message("loading.title")) }
30 | private val mgcbToolsetHost by lazy { MgcbToolsetHost.getInstance(project) }
31 |
32 | private val globalToolsetInfo by lazy {
33 | if (project.isDefault) fallbackProperty
34 | else mgcbToolsetHost.globalToolset.editor
35 | .map { HtmlBuilder().append(getPresentableToolsetInfo(it)).wrapWith("code").wrapWith("html").toString() }
36 | .let { RdObservableProperty(it, lifetime) }
37 | }
38 |
39 | private val solutionToolsetInfo by lazy {
40 | if (project.isDefault) fallbackProperty
41 | else mgcbToolsetHost.solutionToolset.editor
42 | .map { HtmlBuilder().append(getPresentableToolsetInfo(it)).wrapWith("code").wrapWith("html").toString() }
43 | .let { RdObservableProperty(it, lifetime) }
44 | }
45 |
46 | private val projectsToolsetInfo by lazy {
47 | if (project.isDefault) fallbackProperty
48 | else {
49 | val toolMap = mutableMapOf>()
50 | val property = AtomicProperty(MonoGameUiBundle.message("loading.title"))
51 | mgcbToolsetHost.projectsToolset
52 | .adviseAddRemove(lifetime) { event, key, value ->
53 | when (event) {
54 | AddRemove.Add -> {
55 | value.editor.view(lifetime) { lifetime, editor ->
56 | val toolsetInfo = RdObservableProperty(value.editor.map(::getPresentableToolsetInfo), lifetime)
57 | toolMap[key] = toolsetInfo
58 | property.set(getPresentableProjectsInfo(toolMap))
59 | lifetime.onTermination { toolMap.remove(key) }
60 | }
61 | }
62 |
63 | AddRemove.Remove -> {
64 | toolMap.remove(key)
65 | }
66 | }
67 |
68 | property.set(getPresentableProjectsInfo(toolMap))
69 | }
70 | property
71 | }
72 | }
73 |
74 | @Suppress("DialogTitleCapitalization")
75 | override fun createComponent(): DialogPanel {
76 | return panel {
77 | group(MonoGameUiBundle.message("settings.mgcb.editor.group")) {
78 | row(MonoGameUiBundle.message("settings.mgcb.editor.group.global")) {
79 | label(MonoGameUiBundle.message("loading.title"))
80 | .bindText(globalToolsetInfo)
81 | }
82 | row(MonoGameUiBundle.message("settings.mgcb.editor.group.solution")) {
83 | label(MonoGameUiBundle.message("loading.title"))
84 | .bindText(solutionToolsetInfo)
85 | }
86 | row(MonoGameUiBundle.message("settings.mgcb.editor.group.project")) {
87 | label(MonoGameUiBundle.message("loading.title"))
88 | .bindText(projectsToolsetInfo)
89 | }
90 | }
91 | }
92 | }
93 |
94 | override fun disposeUIResources() {
95 | lifetime.terminate()
96 | }
97 |
98 | override fun isModified(): Boolean {
99 | return false
100 | }
101 |
102 | override fun apply() {
103 | // TODO
104 | }
105 |
106 | override fun getDisplayName() = MonoGameUiBundle.message("settings.mgcb.display.name")
107 | override fun getId() = "monogame.tools.mgcb"
108 |
109 | private fun getPresentableToolsetInfo(editor: ToolDefinition?) =
110 | getPresentableToolInfo("mgcb-editor", editor)
111 |
112 | private fun getPresentableToolInfo(fallbackName: String, tool: ToolDefinition?) = buildString {
113 | val name = tool?.packageId ?: fallbackName
114 | append("$name: ")
115 | if (tool == null) {
116 | append("Not found")
117 | } else {
118 | append(tool.version)
119 | }
120 | }
121 |
122 | private fun getProjectName(id: UUID) = project.workspaceModel
123 | .findProjects()
124 | .mapNotNull { it.descriptor as? RdProjectDescriptor }
125 | .firstOrNull { it.originalGuid == id }
126 | ?.name ?: "Unknown"
127 |
128 | private fun getPresentableProjectsInfo(toolMap: MutableMap>): String = toolMap
129 | .map { "${getProjectName(it.key)}: ${it.value.get()}" }
130 | .joinToString("\n")
131 | .let { HtmlBuilder().append(it).wrapWith("code").wrapWith("html").toString() }
132 | }
133 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/templates/MonoGameProjectTemplateGenerator.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.templates
2 |
3 | import com.intellij.icons.AllIcons
4 | import com.intellij.openapi.editor.colors.EditorColorsManager
5 | import com.intellij.openapi.editor.colors.EditorFontType
6 | import com.intellij.openapi.ui.DialogPanel
7 | import com.intellij.ui.AnimatedIcon
8 | import com.intellij.ui.EditorNotificationPanel
9 | import com.intellij.ui.components.fields.ExtendableTextComponent
10 | import com.intellij.ui.components.fields.ExtendableTextField
11 | import com.intellij.ui.components.panels.NonOpaquePanel
12 | import com.intellij.ui.components.panels.VerticalLayout
13 | import com.intellij.ui.dsl.builder.Panel
14 | import com.intellij.ui.dsl.builder.Row
15 | import com.intellij.ui.dsl.builder.TopGap
16 | import com.intellij.ui.dsl.builder.panel
17 | import com.intellij.util.ui.JBUI
18 | import com.jetbrains.rd.util.lifetime.Lifetime
19 | import com.jetbrains.rd.util.reactive.IOptProperty
20 | import com.jetbrains.rider.model.RdProjectTemplate
21 | import com.jetbrains.rider.projectView.projectTemplates.NewProjectDialogContext
22 | import com.jetbrains.rider.projectView.projectTemplates.ProjectTemplatesSharedModel
23 | import com.jetbrains.rider.projectView.projectTemplates.generators.TypeListBasedProjectTemplateGenerator
24 | import me.seclerp.rider.extensions.ClipboardUtil
25 | import me.seclerp.rider.plugins.monogame.MonoGameIcons
26 | import me.seclerp.rider.plugins.monogame.MonoGameUiBundle
27 | import me.seclerp.rider.plugins.monogame.templates.MonoGameTemplateMetadata.Names
28 | import java.awt.BorderLayout
29 | import java.awt.Component
30 | import javax.swing.JLabel
31 | import javax.swing.SwingConstants
32 |
33 | internal class MonoGameProjectTemplateGenerator(
34 | lifetime: Lifetime,
35 | context: NewProjectDialogContext,
36 | sharedModel: ProjectTemplatesSharedModel,
37 | projectTemplates: IOptProperty>
38 | ) : TypeListBasedProjectTemplateGenerator(lifetime, context, sharedModel, projectTemplates) {
39 | init {
40 | context.isReady.afterChange {
41 | val templates = tryGetAcceptableTemplates()
42 | when {
43 | it && templates.isNullOrEmpty() -> setTemplatesMissingState()
44 | it && !templates.isNullOrEmpty() -> setReadyState()
45 | else -> setLoadingState()
46 | }
47 | }
48 | }
49 |
50 | override val defaultName = "MonoGameProject1"
51 |
52 | override fun getPredefinedTypes() = listOf(
53 | TemplateTypeWithIcon(Names.CROSS_PLATFORM_APP, MonoGameIcons.MgcbFile),
54 | TemplateTypeWithIcon(Names.WINDOWS_DESKTOP_APP, MonoGameIcons.MgcbFile),
55 | TemplateTypeWithIcon(Names.ANDROID_APP, MonoGameIcons.MgcbFile),
56 | TemplateTypeWithIcon(Names.IOS_APP, MonoGameIcons.MgcbFile),
57 | TemplateTypeWithIcon(Names.GAME_LIB, MonoGameIcons.MgcbFile),
58 | TemplateTypeWithIcon(Names.CONTENT_PIPELINE_EXTENSION, MonoGameIcons.MgcbFile),
59 | TemplateTypeWithIcon(Names.SHARED_LIB, MonoGameIcons.MgcbFile),
60 | )
61 |
62 | override fun getType(template: RdProjectTemplate) = template.name
63 | .removePrefix("MonoGame ")
64 | .replace("Application", "App")
65 |
66 | private var myLoadingRow: Row? = null
67 | private var myTemplatesMissingRow: Row? = null
68 | private var myTemplatesRow: Row? = null
69 |
70 | override fun createTemplateSpecificPanelAfterLanguage(): DialogPanel {
71 | return panel {
72 | myLoadingRow = createLoadingRow().apply { visible(false)}
73 | myTemplatesMissingRow = createMissingTemplatesRow().apply { visible(false)}
74 | myTemplatesRow = createReadyRow().apply { visible(false)}
75 | setLoadingState()
76 | }
77 | }
78 |
79 | private fun setLoadingState() {
80 | myTemplatesRow?.visible(false)
81 | myTemplatesMissingRow?.visible(false)
82 | myLoadingRow?.visible(true)
83 | }
84 |
85 | private fun setReadyState() {
86 | myLoadingRow?.visible(false)
87 | myTemplatesMissingRow?.visible(false)
88 | myTemplatesRow?.visible(true)
89 | }
90 |
91 | private fun setTemplatesMissingState() {
92 | myLoadingRow?.visible(false)
93 | myTemplatesRow?.visible(false)
94 | myTemplatesMissingRow?.visible(true)
95 | }
96 |
97 | private fun Panel.createLoadingRow() = row(" ") {
98 | cell(JLabel(MonoGameUiBundle.message("templates.loading.templates"), AnimatedIcon.Default(), SwingConstants.LEFT))
99 | }
100 |
101 | private fun Panel.createReadyRow() = row {
102 | cell(super.createTemplateSpecificPanelAfterLanguage())
103 | }
104 |
105 | private fun Panel.createMissingTemplatesRow() = row(" ") {
106 | val installCommand = "dotnet new install MonoGame.Templates.CSharp"
107 | cell(CustomNotificationPanel(EditorNotificationPanel.Status.Warning))
108 | .applyToComponent {
109 | text(MonoGameUiBundle.message("templates.no.templates.found"))
110 | addBottomItem(JLabel(MonoGameUiBundle.message("templates.no.templates.install.hint")).apply {
111 | border = JBUI.Borders.emptyTop(8)
112 | })
113 | addBottomItem(CodeSnippetTextField(installCommand, 50))
114 | }
115 | .resizableColumn()
116 | topGap(TopGap.SMALL)
117 | }
118 |
119 | private class CustomNotificationPanel(status: Status) : EditorNotificationPanel(status) {
120 | private val myBottomPanel = NonOpaquePanel(VerticalLayout(8, VerticalLayout.FILL))
121 |
122 | init {
123 | add(myBottomPanel, BorderLayout.SOUTH)
124 | }
125 |
126 | fun addBottomItem(comp: Component) {
127 | myBottomPanel.add(comp)
128 | }
129 | }
130 |
131 | private class CodeSnippetTextField(text: String, columns: Int = 20) : ExtendableTextField(text, columns) {
132 | init {
133 | isEditable = false
134 | font = EditorColorsManager.getInstance().globalScheme.getFont(EditorFontType.PLAIN)
135 | addExtension(CopyToClipboardExtension())
136 | }
137 |
138 | private inner class CopyToClipboardExtension : ExtendableTextComponent.Extension {
139 | override fun getIcon(hovered: Boolean) = AllIcons.General.Copy
140 |
141 | override fun getActionOnClick() = Runnable {
142 | ClipboardUtil.copyToClipboard(this@CodeSnippetTextField.text)
143 | }
144 | }
145 | }
146 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/MgcbEditorPreviewer.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer
2 |
3 | import com.intellij.openapi.editor.colors.EditorColors
4 | import com.intellij.openapi.editor.colors.EditorColorsManager
5 | import com.intellij.openapi.fileEditor.FileEditor
6 | import com.intellij.openapi.fileEditor.FileEditorLocation
7 | import com.intellij.openapi.fileEditor.FileEditorState
8 | import com.intellij.openapi.project.Project
9 | import com.intellij.openapi.util.UserDataHolderBase
10 | import com.intellij.openapi.vfs.VirtualFile
11 | import com.intellij.psi.PsiManager
12 | import com.intellij.ui.JBColor
13 | import com.intellij.ui.JBSplitter
14 | import com.intellij.ui.components.JBLabel
15 | import com.intellij.ui.components.JBScrollPane
16 | import com.intellij.ui.scale.JBUIScale
17 | import com.intellij.util.ui.JBUI
18 | import com.jetbrains.rider.util.idea.getService
19 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.listeners.MgcbProcessedUpdateListener
20 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.properties.MgcbProperty
21 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.properties.MgcbPropertyTableModel
22 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.properties.MgcbPropertyTable
23 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.services.MgcbAnalyzer
24 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.services.MgcbBuildTreeManager
25 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.tree.MgcbBuildEntryNode
26 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.tree.MgcbTreeNode
27 | import me.seclerp.rider.plugins.monogame.mgcb.psi.MgcbFile
28 | import me.seclerp.rider.plugins.monogame.removeAllRows
29 | import java.beans.PropertyChangeListener
30 | import javax.swing.JPanel
31 |
32 | class MgcbEditorPreviewer(
33 | private val project: Project,
34 | private val currentFile: VirtualFile,
35 | ) : UserDataHolderBase(), FileEditor {
36 |
37 | private val analyzerService = project.getService()
38 | private val mgcbTreeService = project.getService()
39 |
40 | private var model: MgcbModel? = null
41 | private val tree = mgcbTreeService.createEmpty()
42 |
43 | private val propertiesModel = MgcbPropertyTableModel()
44 | private val processorParamsModel = MgcbPropertyTableModel()
45 |
46 | private val connection = project.messageBus.connect()
47 |
48 | private val previewerPanel = lazy {
49 | val root = JBSplitter(false, 0.5f).apply {
50 | dividerWidth = 2
51 | val schema = EditorColorsManager.getInstance().globalScheme
52 | divider.background = JBColor.lazy {
53 | schema.getColor(EditorColors.PREVIEW_BORDER_COLOR) ?: schema.defaultBackground
54 | }
55 | }
56 |
57 | val mgcbFile = PsiManager.getInstance(project).findFile(currentFile) as MgcbFile
58 | model = analyzerService.analyzeFile(mgcbFile)
59 |
60 | val entriesTree = getBuildEntriesTreePanel()
61 | val propertiesPanel = getPropertiesPanel()
62 |
63 | connection.subscribe(MgcbPreviewerTopics.MGCB_PROCESSED_UPDATE_TOPIC, object : MgcbProcessedUpdateListener {
64 | override fun handle(file: VirtualFile, mgcbModel: MgcbModel) {
65 | if (file == currentFile) {
66 | applyMgcbModel(mgcbModel)
67 | }
68 | }
69 | })
70 |
71 | root.apply {
72 | firstComponent = entriesTree
73 | secondComponent = propertiesPanel
74 | }
75 | }
76 |
77 | private fun applyMgcbModel(mgcbModel: MgcbModel) {
78 | mgcbTreeService.updateTree(mgcbModel, tree)
79 | }
80 |
81 | private fun getBuildEntriesTreePanel(): JPanel {
82 | mgcbTreeService.updateTree(model!!, tree)
83 |
84 | tree.addTreeSelectionListener {
85 | selectedNodeChanged(tree.lastSelectedPathComponent as? MgcbTreeNode)
86 | }
87 |
88 | return JBUI.Panels.simplePanel(tree)
89 | }
90 |
91 | private fun getPropertiesPanel(): JPanel {
92 | val propertiesPanel = createKeyValueTable("Properties", propertiesModel)
93 | val processorParamsPanel = createKeyValueTable("Processor parameters", processorParamsModel)
94 |
95 | return JBSplitter(true, 0.75f).apply {
96 | firstComponent = propertiesPanel
97 | secondComponent = processorParamsPanel
98 | dividerWidth = 2
99 | val schema = EditorColorsManager.getInstance().globalScheme
100 | divider.background = JBColor.lazy {
101 | schema.getColor(EditorColors.PREVIEW_BORDER_COLOR) ?: schema.defaultBackground
102 | }
103 | }
104 | }
105 |
106 | private fun selectedNodeChanged(node: MgcbTreeNode?) {
107 | propertiesModel.removeAllRows()
108 | processorParamsModel.removeAllRows()
109 |
110 | when(node) {
111 | is MgcbBuildEntryNode -> {
112 | propertiesModel.apply {
113 | addRow(MgcbProperty("Name", node.userObject.toString()))
114 | addRow(MgcbProperty("Source Path", node.buildEntry.contentFilepath ?: ""))
115 | addRow(MgcbProperty("Destination Path", node.buildEntry.destinationFilepath ?: node.buildEntry.contentFilepath ?: ""))
116 | addRow(MgcbProperty("Importer", node.buildEntry.importer ?: ""))
117 | addRow(MgcbProperty("Processor", node.buildEntry.processor ?: ""))
118 | }
119 |
120 | processorParamsModel.apply {
121 | node.buildEntry.processorParams.forEach {
122 | addRow(MgcbProperty(it.key, it.value))
123 | }
124 | }
125 | }
126 | }
127 | }
128 |
129 | private fun createKeyValueTable(label: String, model: MgcbPropertyTableModel): JPanel {
130 | val propertiesTable = MgcbPropertyTable(model).apply {
131 | border = JBUI.Borders.empty(JBUI.insets(JBUIScale.scale(8)))
132 | }
133 | val title = JBLabel(label).apply {
134 | border = JBUI.Borders.empty(JBUI.insets(JBUIScale.scale(8)))
135 | }
136 |
137 | return JBUI.Panels.simplePanel(JBScrollPane(propertiesTable))
138 | .addToTop(title)
139 | }
140 |
141 | override fun getComponent() = previewerPanel.value
142 |
143 | override fun getPreferredFocusedComponent() = component
144 | override fun getName(): String = "MGCB Preview"
145 | override fun isModified() = false
146 | override fun isValid() = true
147 |
148 | override fun setState(p0: FileEditorState) {}
149 | override fun addPropertyChangeListener(p0: PropertyChangeListener) {}
150 | override fun removePropertyChangeListener(p0: PropertyChangeListener) {}
151 | override fun getCurrentLocation(): FileEditorLocation? = null
152 |
153 | override fun dispose() {
154 | connection.dispose()
155 | }
156 | }
--------------------------------------------------------------------------------
/src/rider/main/kotlin/me/seclerp/rider/plugins/monogame/mgcb/previewer/services/MgcbBuildTreeManager.kt:
--------------------------------------------------------------------------------
1 | package me.seclerp.rider.plugins.monogame.mgcb.previewer.services
2 |
3 | import com.intellij.openapi.components.Service
4 | import com.intellij.openapi.util.IconLoader
5 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.BuildEntry
6 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.MgcbModel
7 | import me.seclerp.rider.plugins.monogame.mgcb.previewer.tree.*
8 | import me.seclerp.rider.plugins.monogame.substringAfterLast
9 | import me.seclerp.rider.plugins.monogame.substringBeforeLast
10 |
11 | @Service
12 | class MgcbBuildTreeManager {
13 | fun createEmpty(): MgcbTree {
14 | val tree = MgcbTree()
15 | tree.cellRenderer = MgcbNodeRenderer()
16 | tree.isRootVisible = true
17 |
18 | return tree
19 | }
20 |
21 | fun updateTree(model: MgcbModel, tree: MgcbTree) {
22 | val buildPaths = model.buildEntries
23 |
24 | // 1. Get all parent folders per each path
25 | val parentsPerPath = buildPaths.map { getParentsHierarchy(it) }
26 |
27 | // 2. Merge + distinct
28 | val flatten = parentsPerPath.flatten().distinctBy { it.first }
29 |
30 | // 3. Get full parent paths
31 | val pathsWithParents = flatten.map { Triple(getParentPath(it.first), it.first, it.second) }
32 |
33 | // 4. Add each item to the corresponding parent in dict
34 | val nodeCache = buildNodeCache(pathsWithParents)
35 |
36 | // 5. Process every tree node and create corresponding tree
37 | val topLevelNodes = pathsWithParents
38 | .filter { it.first == "" }
39 | .map { createNodeFrom(it.second, nodeCache) }
40 |
41 | val currentSelection = tree.getSelectedUrl()
42 | val currentExpanded = tree.getExpandedUrls()
43 |
44 | tree.clear()
45 |
46 | for (node in topLevelNodes) {
47 | tree.root.add(node)
48 | }
49 |
50 | tree.reload()
51 | tree.restoreExpandedUrl(currentExpanded)
52 |
53 | if (currentSelection != null) {
54 | tree.restoreSelectedUrl(currentSelection)
55 | }
56 | }
57 |
58 | // Will transform
59 | // a/b/c
60 | //
61 | // To
62 | // a
63 | // a/b
64 | // a/b/c
65 | private fun getParentsHierarchy(entry: BuildEntry) : List> {
66 | val normalizedPath = entry.contentFilepath!!.trim { it.toString().matches(delimiterRegex) }
67 | val pathParts = normalizedPath.split(delimiterRegex).toTypedArray()
68 |
69 | return mutableListOf>().apply {
70 | val aggregatedPath: StringBuilder = StringBuilder(normalizedPath.length)
71 | for (i in 0 until pathParts.count()) {
72 | val part = pathParts[i]
73 | if (i > 0) {
74 | aggregatedPath.append("/")
75 | }
76 | val newParent = aggregatedPath.append(part).toString()
77 | add(Pair(newParent, entry))
78 | }
79 | }
80 | }
81 |
82 | // Will transform
83 | // a/b/c
84 | //
85 | // To
86 | //
87 | // a/b
88 | private fun getParentPath(path: String): String {
89 | return path.substringBeforeLast(delimiterRegex, "")
90 | }
91 |
92 | private fun buildNodeCache(pathsWithParents: List>): Map>> {
93 | val nodeCache = mutableMapOf>>()
94 | for ((parent, path, entry) in pathsWithParents) {
95 | if (!nodeCache.containsKey(parent)) {
96 | nodeCache[parent] = Pair(null, mutableListOf())
97 | }
98 |
99 | nodeCache[path] = Pair(entry, mutableListOf())
100 | nodeCache[parent]!!.second.add(path)
101 | }
102 |
103 | return nodeCache
104 | }
105 |
106 | private fun createNodeFrom(path: String, cache: Map>>): MgcbTreeNode {
107 | if (cache[path] == null) {
108 | // TODO
109 | throw Exception()
110 | }
111 |
112 | val cachedValue = cache[path]!!
113 | if (cachedValue.second.isEmpty()) {
114 | val name = path.substringAfterLast(delimiterRegex)
115 | val icon = IconLoader.findIcon(name, javaClass)
116 | return MgcbBuildEntryNode(path.substringAfterLast(delimiterRegex), icon, cachedValue.first!!)
117 | }
118 |
119 | val children = cachedValue.second.map { createNodeFrom(it, cache) }
120 | val folderNode = MgcbFolderNode(path.substringAfterLast(delimiterRegex))
121 | for (child in children) {
122 | folderNode.add(child)
123 | }
124 |
125 | return folderNode
126 | }
127 |
128 | // Nodes to remove - exist in old but not in new
129 | // private fun getNodesToRemove(oldModel: MgcbModel, newModel: MgcbModel): List {
130 | // val result = mutableListOf()
131 | //
132 | // val oldCount = oldModel.buildEntries.count()
133 | //
134 | // var oldCounter = 0
135 | // var newCounter = 0
136 | //
137 | // while (oldCounter != oldCount) {
138 | // val oldNode = oldModel.buildEntries[oldCounter]
139 | // val newNode = newModel.buildEntries.getOrNull(newCounter)
140 | //
141 | // if (oldNode.contentFilepath == newNode?.contentFilepath) {
142 | // oldCounter++
143 | // newCounter++
144 | // } else {
145 | // // At that point newNode always != oldNode
146 | // result.add(oldNode)
147 | // oldCounter++
148 | // }
149 | // }
150 | //
151 | // return result
152 | // }
153 | //
154 | // private fun getNodesToAdd(oldModel: MgcbModel, newModel: MgcbModel): List> {
155 | // val result = mutableListOf>()
156 | //
157 | // val newCount = newModel.buildEntries.count()
158 | //
159 | // var oldCounter = 0
160 | // var newCounter = 0
161 | //
162 | // var previousNewNode: BuildEntry? = null
163 | //
164 | // while (newCounter != newCount) {
165 | // val oldNode = oldModel.buildEntries.getOrNull(oldCounter)
166 | // val newNode = newModel.buildEntries[newCounter]
167 | //
168 | // if (oldNode?.contentFilepath == newNode.contentFilepath) {
169 | // oldCounter++
170 | // newCounter++
171 | // } else {
172 | // // At that point newNode always != oldNode
173 | // result.add(Pair(previousNewNode, newNode))
174 | // newCounter++
175 | // }
176 | //
177 | // previousNewNode = newNode
178 | // }
179 | //
180 | // return result
181 | // }
182 |
183 | companion object {
184 | private val delimiterRegex = Regex("[/\\\\]")
185 | }
186 | }
--------------------------------------------------------------------------------