Skip to content

Commit b698fda

Browse files
authored
Fix RubyMine umbrella apps, revert the reverted kotlin code change (#3643)
RubyMine is currently a bit wonky for new projects. This an interim fix until better support is added for custom languages.
1 parent f925de6 commit b698fda

29 files changed

+1221
-1295
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
* [#3640](https://github.com/KronicDeth/intellij-elixir/pull/3640) - [@joshuataylor](https://github.com/joshuataylor)
1010
* Bump intellij platform gradle to 2.0.1 and IdeaVIM to 2.16.0
1111

12+
* [#3643](https://github.com/KronicDeth/intellij-elixir/pull/3643) - [@joshuataylor](https://github.com/joshuataylor)
13+
* Fix RubyMine freezing for umbrella projects but showing the new project wizard as a temporary workaround.
14+
1215
## v19.0.0
1316

1417
### Breaking changes

build.gradle

+17
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import org.jetbrains.intellij.platform.gradle.IntelliJPlatformType
22
import org.jetbrains.intellij.platform.gradle.TestFrameworkType
3+
import org.jetbrains.intellij.platform.gradle.tasks.RunIdeTask
34

45
plugins {
56
id "org.jetbrains.intellij.platform" version "2.0.1"
@@ -125,6 +126,22 @@ intellijPlatform {
125126
}
126127
apply plugin: "kotlin"
127128

129+
tasks.withType(RunIdeTask) {
130+
// Set JVM arguments
131+
jvmArguments.addAll(["-Didea.debug.mode=true", "-Didea.is.internal=true", "-Dlog4j2.debug=true", "-Dlogger.org=TRACE", "-XX:+AllowEnhancedClassRedefinition"])
132+
133+
// Set system properties to debug log
134+
systemProperty "idea.log.debug.categories", "org.elixir_lang"
135+
136+
// Set the maximum heap size
137+
maxHeapSize = "7g"
138+
139+
// Optionally set the working directory if specified in the project properties
140+
if (project.hasProperty("runIdeWorkingDirectory") && !project.property("runIdeWorkingDirectory").isEmpty()) {
141+
workingDir = file(project.property("runIdeWorkingDirectory"))
142+
}
143+
}
144+
128145
//noinspection GroovyAssignabilityCheck,GrUnresolvedAccess
129146
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
130147
//noinspection GrUnresolvedAccess

resources/META-INF/changelog.html

+5-1
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@ <h1>v19.0.1</h1>
55
<li>
66
<p>Enhancements</p>
77
<ul>
8-
<li>
8+
<li>
99
<a href="https://github.com/KronicDeth/intellij-elixir/pull/3639">
1010
#3639 Support JetBrains Exception Analyzer
1111
</a> - <a href="https://github.com/joshuataylor">@joshuataylor</a>
12+
</li><li>
13+
<a href="https://github.com/KronicDeth/intellij-elixir/pull/3643">
14+
#3643 Fix RubyMine freezing for umbrella projects but showing the new project wizard as a temporary workaround.
15+
</a> - <a href="https://github.com/joshuataylor">@joshuataylor</a>
1216
</li>
1317
</ul>
1418
</li>

src/org/elixir_lang/Elixir.kt

+21-12
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,16 @@ object Elixir {
1414
environment: Map<String, String>,
1515
workingDirectory: String?,
1616
elixirSdk: Sdk,
17-
erlArgumentList: kotlin.collections.List<String> = emptyList()
17+
erlArgumentList: kotlin.collections.List<String> = emptyList(),
1818
): GeneralCommandLine {
1919
val erlangSdk = elixirSdkToEnsuredErlangSdk(elixirSdk)
20-
val commandLine = org.elixir_lang.Erl.commandLine(
21-
pty = false,
22-
environment = environment,
23-
workingDirectory = workingDirectory,
24-
erlangSdk = erlangSdk
25-
)
20+
val commandLine =
21+
org.elixir_lang.Erl.commandLine(
22+
pty = false,
23+
environment = environment,
24+
workingDirectory = workingDirectory,
25+
erlangSdk = erlangSdk,
26+
)
2627
// MUST be before `addElixir` because it ends with `-extra` which turns off argument parsing for `erl`
2728
commandLine.addParameters(erlArgumentList)
2829
addElixir(commandLine, elixirSdk, erlangSdk)
@@ -35,13 +36,17 @@ object Elixir {
3536
?: throw MissingErlangSdk(elixirSdk)
3637

3738
fun elixirSdkHasErlangSdk(elixirSdk: Sdk): Boolean = elixirSdkToErlangSdk(elixirSdk) != null
38-
fun elixirSdkToErlangSdk(elixirSdk: Sdk): Sdk? =
39-
elixirSdk.sdkAdditionalData?.let { it as SdkAdditionalData }?.erlangSdk
39+
40+
fun elixirSdkToErlangSdk(elixirSdk: Sdk): Sdk? = elixirSdk.sdkAdditionalData?.let { it as SdkAdditionalData }?.getErlangSdk()
4041

4142
/**
4243
* Adds `-pa ebinDirectory` for those in the `elixirSdk` that aren't in the `erlangSdk`
4344
*/
44-
fun prependNewCodePaths(commandLine: GeneralCommandLine, elixirSdk: Sdk, erlangSdk: Sdk) {
45+
fun prependNewCodePaths(
46+
commandLine: GeneralCommandLine,
47+
elixirSdk: Sdk,
48+
erlangSdk: Sdk,
49+
) {
4550
val elixirEbinDirectories = elixirSdk.ebinDirectories()
4651
val erlangEbinDirectories = erlangSdk.ebinDirectories()
4752
prependNewCodePaths(commandLine, elixirEbinDirectories, erlangEbinDirectories)
@@ -50,7 +55,11 @@ object Elixir {
5055
/**
5156
* Keep in-suync with [org.elixir_lang.jps.Builder.addElixir]
5257
*/
53-
private fun addElixir(commandLine: GeneralCommandLine, elixirSdk: Sdk, erlangSdk: Sdk) {
58+
private fun addElixir(
59+
commandLine: GeneralCommandLine,
60+
elixirSdk: Sdk,
61+
erlangSdk: Sdk,
62+
) {
5463
prependNewCodePaths(commandLine, elixirSdk, erlangSdk)
5564
commandLine.addParameters("-noshell", "-s", "elixir", "start_cli")
5665
commandLine.addParameters("-elixir", "ansi_enabled", "true")
@@ -60,7 +69,7 @@ object Elixir {
6069
private fun prependNewCodePaths(
6170
commandLine: GeneralCommandLine,
6271
elixirEbinDirectories: kotlin.collections.List<String>,
63-
erlangEbinDirectories: kotlin.collections.List<String>
72+
erlangEbinDirectories: kotlin.collections.List<String>,
6473
) {
6574
val newEbinDirectories = elixirEbinDirectories - erlangEbinDirectories
6675
prependCodePaths(commandLine, newEbinDirectories)

src/org/elixir_lang/facet/configurable/Provider.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ import com.intellij.openapi.options.ConfigurableProvider
55
import org.elixir_lang.sdk.ProcessOutput
66

77
class Provider(private val project: com.intellij.openapi.project.Project): ConfigurableProvider() {
8-
override fun canCreateConfigurable(): Boolean = ProcessOutput.isSmallIde()
8+
override fun canCreateConfigurable(): Boolean = ProcessOutput.isSmallIde
99
override fun createConfigurable(): Configurable = Project(project)
1010
}

src/org/elixir_lang/facet/sdks/Provider.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ import com.intellij.openapi.options.ConfigurableProvider
44
import org.elixir_lang.sdk.ProcessOutput
55

66
abstract class Provider : ConfigurableProvider() {
7-
override fun canCreateConfigurable(): Boolean = ProcessOutput.isSmallIde()
7+
override fun canCreateConfigurable(): Boolean = ProcessOutput.isSmallIde
88
}

src/org/elixir_lang/mix/Project.kt

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.elixir_lang.mix
22

33
import com.intellij.openapi.application.runWriteAction
4+
import com.intellij.openapi.diagnostic.Logger
45
import com.intellij.openapi.module.ModifiableModuleModel
56
import com.intellij.openapi.module.Module
67
import com.intellij.openapi.progress.ProgressIndicator
@@ -20,6 +21,8 @@ import java.io.EOFException
2021
import java.io.File
2122

2223
object Project {
24+
private val LOG = Logger.getInstance(Project::class.java)
25+
2326
fun addSourceDirToContent(
2427
content: ContentEntry,
2528
root: VirtualFile,
@@ -41,6 +44,7 @@ object Project {
4144

4245
VfsUtilCore.visitChildrenRecursively(root, object : VirtualFileVisitor<Any>() {
4346
override fun visitFile(file: VirtualFile): Boolean {
47+
LOG.debug("visiting $file")
4448
indicator.checkCanceled()
4549

4650
if (file.isDirectory) {

src/org/elixir_lang/mix/project/DirectoryConfigurator.kt

+53-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import com.intellij.facet.FacetManager
55
import com.intellij.facet.FacetType
66
import com.intellij.facet.impl.FacetUtil.addFacet
77
import com.intellij.ide.impl.OpenProjectTask
8+
import com.intellij.notification.NotificationGroupManager
9+
import com.intellij.notification.NotificationType
10+
import com.intellij.openapi.diagnostic.Logger
811
import com.intellij.openapi.module.Module
912
import com.intellij.openapi.module.ModuleManager
1013
import com.intellij.openapi.progress.ProgressIndicator
@@ -13,47 +16,82 @@ import com.intellij.openapi.progress.Task
1316
import com.intellij.openapi.project.Project
1417
import com.intellij.openapi.project.ex.ProjectManagerEx
1518
import com.intellij.openapi.roots.ModuleRootModificationUtil
19+
import com.intellij.openapi.roots.ProjectRootManager
20+
import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable
21+
import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel
1622
import com.intellij.openapi.util.Ref
1723
import com.intellij.openapi.util.io.FileUtil
1824
import com.intellij.openapi.vfs.VirtualFile
1925
import com.intellij.platform.PlatformProjectOpenProcessor.Companion.runDirectoryProjectConfigurators
2026
import com.intellij.projectImport.ProjectAttachProcessor
2127
import com.intellij.util.PlatformUtils
28+
import kotlinx.coroutines.TimeoutCancellationException
2229
import kotlinx.coroutines.launch
2330
import kotlinx.coroutines.runBlocking
31+
import kotlinx.coroutines.withTimeout
2432
import org.elixir_lang.DepsWatcher
2533
import org.elixir_lang.Facet
2634
import org.elixir_lang.mix.Project.addFolders
2735
import org.elixir_lang.mix.Watcher
2836
import java.nio.file.Path
2937
import java.nio.file.Paths
3038
import kotlin.io.path.exists
39+
import org.elixir_lang.sdk.elixir.Type
3140

3241
/**
3342
* Used in Small IDEs like Rubymine that don't support [OpenProcessor].
3443
*/
3544
class DirectoryConfigurator : com.intellij.platform.DirectoryProjectConfigurator {
45+
companion object {
46+
private val LOG = Logger.getInstance(DirectoryConfigurator::class.java)
47+
}
48+
3649
override fun configureProject(
3750
project: Project,
3851
baseDir: VirtualFile,
3952
moduleRef: Ref<Module>,
4053
isProjectCreatedWithWizard: Boolean
4154
) {
4255
var foundOtpApps: List<OtpApp> = emptyList()
56+
LOG.debug("configuring $baseDir for project $project, created with wizard: $isProjectCreatedWithWizard")
4357

4458
ProgressManager.getInstance().run(object : Task.Modal(project, "Scanning Mix Projects", true) {
4559
override fun run(indicator: ProgressIndicator) {
4660
foundOtpApps = org.elixir_lang.mix.Project.findOtpApps(baseDir, indicator)
4761
}
4862
})
4963

64+
// If this is an umbrella app, RubyMine currently freezes.
65+
// Instead, let's just show a notification that the user needs to use the Wizard.
66+
if (!isProjectCreatedWithWizard && foundOtpApps.isNotEmpty() && foundOtpApps.size > 1) {
67+
LOG.info("not configuring project $project because it is an umbrella app")
68+
NotificationGroupManager.getInstance()
69+
.getNotificationGroup("Elixir")
70+
.createNotification(
71+
"Umbrella App Detected",
72+
"Elixir Umbrella app detected, please use the Project Wizard to properly configure it when using an IDE like RubyMine.",
73+
NotificationType.WARNING
74+
)
75+
.notify(project)
76+
77+
return
78+
}
79+
5080
for (otpApp in foundOtpApps) {
81+
LOG.debug("configuring descendant otp app: ${otpApp.name}")
5182
if (otpApp.root == baseDir) {
83+
LOG.debug("configuring root otp app: ${otpApp.name}")
5284
configureRootOtpApp(project, otpApp)
5385
} else {
5486
runBlocking {
55-
launch(coroutineContext) {
56-
configureDescendantOtpApp(project, otpApp)
87+
try {
88+
withTimeout(2000L) {
89+
LOG.debug("Not otp app root: ${otpApp.name}, configuring descendant otp app.")
90+
configureDescendantOtpApp(project, otpApp)
91+
}
92+
} catch (e: TimeoutCancellationException) {
93+
// Handle the timeout exception, e.g., log a warning or notify the user
94+
LOG.error("Timeout while configuring descendant OTP app: ${otpApp.name}", e)
5795
}
5896
}
5997
}
@@ -82,8 +120,10 @@ class DirectoryConfigurator : com.intellij.platform.DirectoryProjectConfigurator
82120
private suspend fun configureDescendantOtpApp(rootProject: Project, otpApp: OtpApp) {
83121
if (!PlatformUtils.isGoIde() && ProjectAttachProcessor.canAttachToProject()) {
84122
newProject(otpApp)?.let { otpAppProject ->
123+
LOG.debug("attaching $otpAppProject to $rootProject")
85124
attachToProject(rootProject, Paths.get(otpApp.root.path))
86125

126+
LOG.debug("scanning libraries for newly attached project for OTP app ${otpApp.name}")
87127
ProgressManager.getInstance().run(object : Task.Modal(
88128
otpAppProject,
89129
"Scanning mix.exs to connect libraries for newly attached project for OTP app ${otpApp.name}",
@@ -92,8 +132,10 @@ class DirectoryConfigurator : com.intellij.platform.DirectoryProjectConfigurator
92132
override fun run(progressIndicator: ProgressIndicator) {
93133
for (module in ModuleManager.getInstance(otpAppProject).modules) {
94134
if (progressIndicator.isCanceled) {
135+
LOG.debug("canceled scanning libraries for newly attached project for OTP app ${otpApp.name}")
95136
break
96137
}
138+
LOG.debug("scanning libraries for newly attached project for OTP app ${otpApp.name} for module ${module.name}")
97139

98140
Watcher(otpAppProject).syncLibraries(module, progressIndicator)
99141
}
@@ -108,8 +150,10 @@ class DirectoryConfigurator : com.intellij.platform.DirectoryProjectConfigurator
108150
*/
109151
private suspend fun newProject(otpApp: OtpApp): Project? {
110152
val projectDir = Paths.get(FileUtil.toSystemDependentName(otpApp.root.path), Project.DIRECTORY_STORE_FOLDER)
153+
LOG.debug("Checking if $projectDir exists")
111154

112155
return if (projectDir.exists()) {
156+
LOG.debug("$projectDir already exists")
113157
null
114158
} else {
115159
val path = otpApp.root.path.let { Paths.get(it) }
@@ -119,16 +163,23 @@ class DirectoryConfigurator : com.intellij.platform.DirectoryProjectConfigurator
119163
projectName = otpApp.name
120164
}
121165

166+
LOG.debug("Creating new project at $path with isNewProject: ${openProjectTask.isNewProject} and useDefaultProjectAsTemplate: ${openProjectTask.useDefaultProjectAsTemplate} and projectName: ${openProjectTask.projectName}")
167+
122168
ProjectManagerEx
123169
.getInstanceEx()
124170
.newProject(
125171
path,
126172
openProjectTask
127173
)
128174
?.let { project ->
175+
LOG.debug("runDirectoryProjectConfigurators for project: $project at $path")
129176
runDirectoryProjectConfigurators(path, project, false)
177+
LOG.debug("runDirectoryProjectConfigurators complete for project: $project at $path")
178+
179+
LOG.debug("Saving settings for project: $project at $path")
130180

131181
StoreUtil.saveSettings(project, true)
182+
LOG.debug("Saved settings for project: $project at $path")
132183

133184
project
134185
}

src/org/elixir_lang/notification/setup_sdk/Action.kt

-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import com.intellij.openapi.actionSystem.AnActionEvent
66
import com.intellij.openapi.module.Module
77
import com.intellij.openapi.module.ModuleType
88
import com.intellij.openapi.project.Project
9-
import org.elixir_lang.notification.setup_sdk.Provider.Companion.showFacetSettings
10-
import org.elixir_lang.notification.setup_sdk.Provider.Companion.showModuleSettings
119

1210
/**
1311
* Created by zyuyou on 15/7/8.

0 commit comments

Comments
 (0)