Skip to content

Commit 3b8d23e

Browse files
authored
Merge pull request #3836 from joshuataylor/feature/fix-mix-deps
Fix settings for mix, add experimental settings, fix deps checking and improve mise SDK
2 parents d141407 + b0051c6 commit 3b8d23e

16 files changed

Lines changed: 476 additions & 140 deletions

.github/actions/setup-env/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ runs:
107107
cache-disabled: true
108108
add-job-summary-as-pr-comment: 'on-failure'
109109
build-scan-publish: ${{ inputs.build-scan-publish }}
110-
build-scan-terms-of-use-url: 'https://gradle.com/terms-of-service'
110+
build-scan-terms-of-use-url: 'https://gradle.com/help/legal-terms-of-use'
111111
build-scan-terms-of-use-agree: 'yes'
112112
github-token: ${{ inputs.github-token }}
113113

.github/workflows/shared-test.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,11 @@ jobs:
6262
otp-version: ${{ inputs.otp-version || '24.3.4.6' }}
6363
idea-version: ${{ matrix.idea-version }}
6464
github-token: ${{ secrets.GITHUB_TOKEN }}
65+
build-scan-publish: 'true'
6566

6667
- name: Run Tests
6768
id: test
68-
run: ./gradlew --stacktrace check --scan
69+
run: ./gradlew --stacktrace check
6970

7071
- name: Publish Test Results
7172
id: publish-test-results-linux
@@ -85,19 +86,20 @@ jobs:
8586
check_name: Test Results (${{ matrix.os }}, ${{ matrix.idea-version }})
8687
report_suite_logs: "error"
8788

88-
- name: Upload Test Reports
89+
- name: Upload Test Reports (on failure or when requested)
8990
id: upload-test-reports
9091
uses: actions/upload-artifact@v6
91-
if: ${{ always() && inputs.upload-reports }}
92+
if: ${{ always() && (failure() || inputs.upload-reports) }}
9293
with:
9394
name: test-reports-${{ matrix.os }}-${{ matrix.idea-version }}
9495
path: |
9596
**/build/reports/tests/**
9697
**/build/test-results/**
98+
**/build/reports/problems/**
9799
retention-days: 30
98100
include-hidden-files: true
99101
overwrite: true
100-
if-no-files-found: 'error'
102+
if-no-files-found: 'warn'
101103

102104
build-plugin:
103105
runs-on: ubuntu-22.04

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
# Changelog
22

3+
## v23.9.0
4+
5+
### Enhancements
6+
* Elixir settings consolidated -- renamed "Experimental Settings" to "Elixir Settings" and moved all settings into the top-level Elixir configurable (no more separate child page). - [@joshuataylor](https://github.com/joshuataylor)
7+
* Mix deps checker setting -- added an "Enable automatic Mix deps checking" toggle (default enabled) under Elixir Settings. When disabled, no deps check runs on project open or file changes. The checker also skips with a debug log when no Elixir SDK is configured, instead of showing the unhelpful "Mix deps check failed" notification. - [@joshuataylor](https://github.com/joshuataylor)
8+
* Erlang SDK prompt for mise Elixir SDKs -- when adding a mise-detected Elixir SDK without an Erlang SDK registered, a chooser dialog now lists valid mise-installed Erlang SDKs (sorted newest first, broken installations filtered out). The selected Erlang SDK is registered and linked automatically. - [@joshuataylor](https://github.com/joshuataylor)
9+
* Status bar widget -- when no Elixir SDK is configured, the widget popup now shows a "Detected Elixir SDKs" section listing valid mise installations. Clicking one registers the Elixir SDK, prompts for Erlang if needed, and sets it as the project SDK. - [@joshuataylor](https://github.com/joshuataylor)
10+
* When an Elixir SDK is added via Project Structure and no Erlang SDK is explicitly set, `configureInternalErlangSdk` now falls back to any Erlang SDK already registered in `ProjectJdkTable`. - [@joshuataylor](https://github.com/joshuataylor)
11+
12+
### Bug Fixes
13+
* [#3834](https://github.com/KronicDeth/intellij-elixir/pull/3834) - [@joshuataylor](https://github.com/joshuataylor)
14+
* Rethrow `ProcessCanceledException` in spell checking `Splitter` instead of swallowing it.
15+
* Fix intention preview for `ConvertMatchToTypeOperation`.
16+
* [#3835](https://github.com/KronicDeth/intellij-elixir/pull/3835) - [@joshuataylor](https://github.com/joshuataylor)
17+
* `ProgressManager.checkCanceled()` added to `while` loops in `@RequiresReadLock` methods.
18+
* Replace `Dispatchers.EDT` with `Dispatchers.UI` in `ElixirSdkStatusWidget` (EDT holds write-intent lock unnecessarily for pure UI updates).
19+
* Use no-arg `AnAction` constructor in `RefreshAllElixirSdksAction` to fix DevKit inspection.
20+
321
## v23.8.2
422

523
### Enhancements

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# --- Plugin Metadata ---
77
pluginGroup=org.elixir_lang
88
pluginName=Elixir
9-
pluginVersion=23.8.2
9+
pluginVersion=23.9.0
1010
pluginRepositoryUrl=https://github.com/KronicDeth/intellij-elixir/
1111
vendorName=Elle Imhoff
1212
vendorEmail=Kronic.Deth@gmail.com

resources/META-INF/changelog.html

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,28 @@
11
<html>
22
<body>
33

4+
<h1>v23.9.0</h1>
5+
<ul>
6+
<li>
7+
<p>Enhancements</p>
8+
<ul>
9+
<li>Elixir settings moved from a separate "Experimental Settings" child page into the main Elixir settings page for easier access.</li>
10+
<li>Mix deps checker is now a configurable setting -- disable it under Elixir Settings if you don't want automatic dependency checking. The checker also no longer shows a notification when no Elixir SDK is configured.</li>
11+
<li>When adding a mise Elixir SDK without an Erlang SDK, a dialog now prompts you to select from your mise-installed Erlang versions (sorted newest first).</li>
12+
<li>The status bar SDK widget now shows detected mise Elixir SDKs when no SDK is configured -- click one to set up Elixir and Erlang SDKs in one step.</li>
13+
<li>Adding an Elixir SDK now auto-links an existing Erlang SDK if one is already registered.</li>
14+
</ul>
15+
</li>
16+
<li>
17+
<p>Bug Fixes</p>
18+
<ul>
19+
<li>Fixed <code>ProcessCanceledException</code> being swallowed in spell checking, which could cause stale results.</li>
20+
<li>Fixed intention preview for <code>ConvertMatchToTypeOperation</code>.</li>
21+
<li>Added cancellation checks to loops in read-lock-annotated methods to improve IDE responsiveness during indexing.</li>
22+
</ul>
23+
</li>
24+
</ul>
25+
426
<h1>v23.8.2</h1>
527
<ul>
628
<li>

resources/META-INF/plugin.xml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,6 @@
9797
<fileType name="Elixir Script" implementationClass="org.elixir_lang.ElixirScriptFileType" fieldName="INSTANCE"
9898
language="Elixir" extensions="exs"/>
9999

100-
<projectConfigurable
101-
parentId="language.elixir"
102-
displayName="Experimental Settings"
103-
id="org.elixir_lang.settings.ElixirExperimentalSettingsConfigurable"
104-
instance="org.elixir_lang.settings.ElixirExperimentalSettingsConfigurable"/>
105-
106100
<!-- Facet -->
107101
<applicationConfigurable id="language.elixir.sdks.elixir"
108102
parentId="language.elixir"

settings.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ plugins {
22
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
33
}
44
rootProject.name = "intellij-elixir"
5-
include( "jps-shared")
6-
include( "jps-builder")
5+
include("jps-shared")
6+
include("jps-builder")

src/org/elixir_lang/mix/DepsCheckerService.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import com.intellij.util.Alarm
1919
import org.elixir_lang.notification.setup_sdk.Notifier
2020
import org.elixir_lang.package_manager.DepsStatusResult
2121
import org.elixir_lang.package_manager.virtualFile
22+
import org.elixir_lang.settings.ElixirExperimentalSettings
2223
import org.elixir_lang.sdk.elixir.Type as ElixirSdkType
2324
import java.util.concurrent.atomic.AtomicBoolean
2425

@@ -73,13 +74,24 @@ class DepsCheckerService(private val project: Project) : Disposable {
7374
if (project.isDisposed) {
7475
return
7576
}
77+
78+
if (!ElixirExperimentalSettings.instance.state.enableMixDepsCheck) {
79+
LOG.debug("DepsCheckerService: Mix deps check disabled in settings")
80+
return
81+
}
82+
7683
LOG.debug("DepsCheckerService: Checking Mix deps ($reason)")
7784
val (sdk, projectRoots) = ReadAction.nonBlocking(java.util.concurrent.Callable {
7885
val sdk = findElixirSdk(project)
7986
val roots = selectTopLevelMixRoots(ProjectRootManager.getInstance(project).contentRootsFromAllModules)
8087
sdk to roots
8188
}).executeSynchronously()
8289

90+
if (sdk == null) {
91+
LOG.debug("DepsCheckerService: Skipping mix deps check, no Elixir SDK configured")
92+
return
93+
}
94+
8395
var sawSupported = false
8496
var sawNonOk = false
8597

src/org/elixir_lang/mix/DepsCheckerStartupActivity.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.intellij.openapi.components.service
44
import com.intellij.openapi.project.DumbAware
55
import com.intellij.openapi.project.Project
66
import com.intellij.openapi.startup.ProjectActivity
7+
import org.elixir_lang.settings.ElixirExperimentalSettings
78

89
/**
910
* Checks if Mix dependencies are outdated when a project is opened.
@@ -12,6 +13,9 @@ import com.intellij.openapi.startup.ProjectActivity
1213
*/
1314
class DepsCheckerStartupActivity : ProjectActivity, DumbAware {
1415
override suspend fun execute(project: Project) {
16+
if (!ElixirExperimentalSettings.instance.state.enableMixDepsCheck) {
17+
return
18+
}
1519
project.service<DepsCheckerService>().scheduleInitialCheck()
1620
}
1721
}

src/org/elixir_lang/sdk/elixir/Type.kt

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ import org.elixir_lang.cli.getExecutableFilepathWslSafe
3434
import org.elixir_lang.jps.shared.ElixirSdkTypeId
3535
import org.elixir_lang.jps.shared.cli.CliTool
3636
import org.elixir_lang.jps.shared.sdk.SdkPaths
37+
import org.elixir_lang.sdk.SdkHomeKey
38+
import org.elixir_lang.sdk.SdkHomePaths
3739
import org.elixir_lang.sdk.*
3840
import org.elixir_lang.sdk.Type.ebinPathChainVirtualFile
3941
import org.elixir_lang.sdk.erlang_dependent.AdditionalDataConfigurable
@@ -230,6 +232,8 @@ ELIXIR_SDK_HOME
230232
}
231233

232234
companion object {
235+
val SKIP_ERLANG_SETTINGS_HINT_KEY = com.intellij.openapi.util.Key.create<Boolean>("SKIP_ERLANG_SETTINGS_HINT")
236+
233237
private const val LINUX_DEFAULT_HOME_PATH = SdkHomePaths.LINUX_DEFAULT_HOME_PATH + "/elixir"
234238
private const val LINUX_MINT_HOME_PATH = SdkHomePaths.LINUX_MINT_HOME_PATH + "/elixir"
235239
private val LOG = Logger.getInstance(Type::class.java)
@@ -633,9 +637,16 @@ ELIXIR_SDK_HOME
633637
// This happens when creating a new Elixir SDK right after creating its Erlang SDK
634638
val explicitErlangSdk = elixirSdk.getUserData(ERLANG_SDK_KEY)
635639

636-
val erlangSdk = explicitErlangSdk ?: existingErlangSdk
640+
val erlangSdk = explicitErlangSdk
641+
?: existingErlangSdk
642+
?: findRegisteredErlangSdk()
643+
?: promptForMiseErlangSdk(elixirSdk)
637644

638645
if (erlangSdk != null) {
646+
if (explicitErlangSdk == null && existingErlangSdk == null) {
647+
LOG.info("Auto-linked Erlang SDK '${erlangSdk.name}' to Elixir SDK '${elixirSdk.name}'")
648+
}
649+
639650
// Only set additional data if it's not already set
640651
if (existingAdditionalData == null || existingErlangSdk == null) {
641652
val sdkAdditionData: com.intellij.openapi.projectRoots.SdkAdditionalData =
@@ -653,6 +664,107 @@ ELIXIR_SDK_HOME
653664
return erlangSdk
654665
}
655666

667+
internal fun findRegisteredErlangSdk(): Sdk? {
668+
val erlangSdkType = org.elixir_lang.sdk.erlang.Type.instance
669+
return ProjectJdkTable.getInstance()
670+
.getSdksOfType(erlangSdkType)
671+
.firstOrNull { it.homePath != null }
672+
}
673+
674+
/**
675+
* For mise Elixir SDKs only: finds mise-installed Erlang SDK homes,
676+
* presents a chooser dialog, and registers the selected one.
677+
*/
678+
internal fun promptForMiseErlangSdk(elixirSdk: Sdk): Sdk? {
679+
val homePath = elixirSdk.homePath ?: return null
680+
if (SdkPaths.detectSource(homePath) != SdkPaths.SOURCE_NAME_MISE) return null
681+
682+
val erlangSdkType = org.elixir_lang.sdk.erlang.Type.instance
683+
val miseHomes = java.util.TreeMap<SdkHomeKey, String>()
684+
SdkHomePaths.mergeMise(miseHomes, "erlang")
685+
686+
// Filter to valid Erlang homes only (erl binary must exist and be executable)
687+
val validHomes = miseHomes.entries.filter { (_, path) ->
688+
erlangSdkType.isValidSdkHome(path)
689+
}
690+
if (validHomes.isEmpty()) return null
691+
692+
val displayNames = validHomes.map { (_, path) ->
693+
org.elixir_lang.sdk.erlang.Type.suggestSdkNameForHome(path, null)
694+
}.toTypedArray()
695+
696+
var selectedIndex = -1
697+
val app = ApplicationManager.getApplication()
698+
val showDialog = Runnable {
699+
selectedIndex = com.intellij.openapi.ui.Messages.showChooseDialog(
700+
"No Erlang SDK is configured. Select one to use with this Elixir SDK:",
701+
"Select Erlang SDK",
702+
displayNames,
703+
displayNames[0],
704+
null
705+
)
706+
}
707+
708+
if (app.isDispatchThread) {
709+
showDialog.run()
710+
} else {
711+
app.invokeAndWait(showDialog)
712+
}
713+
714+
if (selectedIndex < 0) return null
715+
716+
val erlangSdk = registerErlangSdk(validHomes[selectedIndex].value)
717+
if (erlangSdk != null && elixirSdk.getUserData(SKIP_ERLANG_SETTINGS_HINT_KEY) != true) {
718+
val sdkName = erlangSdk.name
719+
val showInfo = Runnable {
720+
com.intellij.openapi.ui.Messages.showInfoMessage(
721+
"Erlang SDK '$sdkName' has been registered.\n\n" +
722+
"Please save settings and reopen this dialog to see it in the Internal Erlang SDK dropdown.",
723+
"Erlang SDK Added"
724+
)
725+
}
726+
if (app.isDispatchThread) {
727+
showInfo.run()
728+
} else {
729+
app.invokeAndWait(showInfo)
730+
}
731+
}
732+
elixirSdk.putUserData(SKIP_ERLANG_SETTINGS_HINT_KEY, null)
733+
return erlangSdk
734+
}
735+
736+
internal fun registerErlangSdk(homePath: String): Sdk? {
737+
val erlangSdkType = org.elixir_lang.sdk.erlang.Type.instance
738+
val canonicalHome = wslCompat.canonicalizePath(homePath)
739+
val versionString = org.elixir_lang.sdk.erlang.Type.versionStringForHome(canonicalHome, null)
740+
?: return null
741+
val sdkName = org.elixir_lang.sdk.erlang.Type.suggestSdkNameForHome(canonicalHome, null)
742+
743+
val newSdk = com.intellij.openapi.projectRoots.impl.ProjectJdkImpl(
744+
sdkName, erlangSdkType, canonicalHome, versionString
745+
)
746+
747+
val app = ApplicationManager.getApplication()
748+
val table = ProjectJdkTable.getInstance()
749+
750+
val addSdk = Runnable {
751+
app.runWriteAction {
752+
table.addJdk(newSdk)
753+
}
754+
}
755+
756+
if (app.isDispatchThread) {
757+
addSdk.run()
758+
} else {
759+
app.invokeAndWait(addSdk)
760+
}
761+
762+
erlangSdkType.setupSdkPaths(newSdk)
763+
LOG.info("Registered Erlang SDK '${newSdk.name}' from $canonicalHome")
764+
return newSdk
765+
}
766+
767+
656768
@JvmStatic
657769
fun addNewCodePathsFromInternErlangSdk(
658770
elixirSdk: Sdk,

0 commit comments

Comments
 (0)