Skip to content

Commit e8eea9a

Browse files
committed
add installation location service
1 parent f2b13b9 commit e8eea9a

File tree

7 files changed

+195
-5
lines changed

7 files changed

+195
-5
lines changed

.github/workflows/skate-pr-notify.yml

Whitespace-only changes.

platforms/intellij/skate/src/main/kotlin/foundry/intellij/skate/SkatePluginSettings.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,16 @@ class SkatePluginSettings : SimplePersistentStateComponent<SkatePluginSettings.S
6969
}
7070

7171
var installationLocationPattern: String?
72-
get() = state.installationLocationPattern
72+
get() =
73+
state.installationLocationPattern ?: "${System.getProperty("user.home")}/.cache/bazel/**"
7374
set(value) {
7475
state.installationLocationPattern = value
7576
}
7677

7778
var installationInfoUrl: String?
78-
get() = state.installationInfoUrl
79+
get() =
80+
state.installationInfoUrl
81+
?: "https://slack-pde.slack.com/archives/C8EHYHTKP/p1746042447642229"
7982
set(value) {
8083
state.installationInfoUrl = value
8184
}
Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,89 @@
1+
/*
2+
* Copyright (C) 2025 Slack Technologies, LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
116
package foundry.intellij.skate.ideinstall
217

3-
class InstallationLocationService {
4-
}
18+
import androidx.annotation.VisibleForTesting
19+
import com.intellij.ide.BrowserUtil
20+
import com.intellij.notification.Notification
21+
import com.intellij.notification.NotificationAction
22+
import com.intellij.notification.NotificationGroupManager
23+
import com.intellij.notification.NotificationType
24+
import com.intellij.openapi.actionSystem.AnActionEvent
25+
import com.intellij.openapi.application.PathManager
26+
import com.intellij.openapi.components.Service
27+
import com.intellij.openapi.components.service
28+
import com.intellij.openapi.project.Project
29+
import foundry.intellij.skate.SkatePluginSettings
30+
import java.nio.file.FileSystems
31+
import java.nio.file.Path
32+
import java.nio.file.Paths
33+
import javax.inject.Inject
34+
35+
@Service(Service.Level.PROJECT)
36+
class InstallationLocationService @Inject constructor(private val project: Project) {
37+
fun checkInstallationLocation() {
38+
val settings = project.service<SkatePluginSettings>()
39+
val pattern = settings.installationLocationPattern ?: return
40+
val infoUrl = settings.installationInfoUrl ?: return
41+
42+
// print out what the current path is
43+
println("Running IDE from: ${PathManager.getHomePath()}")
44+
45+
if (pattern.isBlank()) return
46+
47+
val installationDir = PathManager.getHomePath()
48+
val installationPath = Paths.get(installationDir)
49+
50+
if (!matchesPattern(installationPath, pattern)) {
51+
showWarningNotification(infoUrl)
52+
}
53+
}
54+
55+
@VisibleForTesting
56+
internal fun matchesPattern(path: Path, pattern: String): Boolean {
57+
val expandedPattern = pattern.replace("\${user.home}", System.getProperty("user.home"))
58+
val matcher = FileSystems.getDefault().getPathMatcher("glob:$expandedPattern")
59+
return matcher.matches(path)
60+
}
61+
62+
private fun showWarningNotification(infoUrl: String) {
63+
val installationPath = PathManager.getHomePath()
64+
65+
val notification =
66+
NotificationGroupManager.getInstance()
67+
.getNotificationGroup("SkateInstallationLocation")
68+
.createNotification(
69+
"Move Android Studio for better performance",
70+
"""
71+
Your IDE isn't installed under the desired path, which means you're missing out on faster Gradle syncs and significantly improved IDE performance.
72+
73+
Detected path:
74+
`$installationPath`
75+
"""
76+
.trimIndent(),
77+
NotificationType.WARNING,
78+
)
79+
.addAction(
80+
object : NotificationAction("More info") {
81+
override fun actionPerformed(e: AnActionEvent, notification: Notification) {
82+
BrowserUtil.browse(infoUrl)
83+
}
84+
}
85+
)
86+
87+
notification.notify(project)
88+
}
89+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright (C) 2025 Slack Technologies, LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package foundry.intellij.skate.ideinstall
17+
18+
import com.intellij.openapi.components.service
19+
import com.intellij.openapi.project.Project
20+
import com.intellij.openapi.startup.ProjectActivity
21+
22+
class InstallationLocationStartupActivity : ProjectActivity {
23+
override suspend fun execute(project: Project) {
24+
val service = project.service<InstallationLocationService>()
25+
service.checkInstallationLocation()
26+
}
27+
}

platforms/intellij/skate/src/main/kotlin/foundry/intellij/skate/ui/SkateConfigUI.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,9 @@ internal class SkateConfigUI(
100100

101101
row {
102102
installationLocationEnabledButton =
103-
checkBox(SkateBundle.message("skate.configuration.installationLocation.enabledDescription"))
103+
checkBox(
104+
SkateBundle.message("skate.configuration.installationLocation.enabledDescription")
105+
)
104106
.bindSelected(
105107
getter = { settings.installationLocationPattern != null },
106108
setter = { enabled ->

platforms/intellij/skate/src/main/resources/META-INF/plugin.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
implementationClass="foundry.intellij.skate.featureflags.FeatureFlagAnnotator"/>
2323
<projectIndexingActivityHistoryListener implementation="foundry.intellij.skate.idemetrics.IndexingListener"/>
2424
<postStartupActivity implementation="foundry.intellij.skate.PostStartupActivityExtension"/>
25+
<postStartupActivity implementation="foundry.intellij.skate.ideinstall.InstallationLocationStartupActivity"/>
26+
<notificationGroup id="SkateInstallationLocation" displayType="BALLOON" isLogByDefault="true"/>
2527
<!-- <toolWindow factoryClass="foundry.intellij.skate.aibot.ChatBotToolWindow" id="DevXPAI" anchor="right"-->
2628
<!-- canCloseContents="true" secondary="false" icon="AllIcons.Actions.Lightning"/>-->
2729
</extensions>
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright (C) 2025 Slack Technologies, LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package foundry.intellij.skate.ideinstall
17+
18+
import com.google.common.truth.Truth.assertThat
19+
import com.intellij.testFramework.fixtures.BasePlatformTestCase
20+
import java.nio.file.FileSystems
21+
import java.nio.file.Paths
22+
import org.junit.Test
23+
import org.junit.runner.RunWith
24+
import org.junit.runners.JUnit4
25+
26+
@RunWith(JUnit4::class)
27+
class InstallationLocationServiceTest : BasePlatformTestCase() {
28+
private lateinit var service: InstallationLocationService
29+
30+
override fun setUp() {
31+
super.setUp()
32+
service = InstallationLocationService(project)
33+
}
34+
35+
@Test
36+
fun `path and pattern should match exactly`() {
37+
val path = Paths.get("/sample/test/path")
38+
val pattern = "/sample/test/path"
39+
40+
val result = service.matchesPattern(path, pattern)
41+
assertThat(result).isTrue()
42+
}
43+
44+
@Test
45+
fun `empty pattern should match`() {
46+
val path = Paths.get("")
47+
val pattern = ""
48+
val result = service.matchesPattern(path, pattern)
49+
assertThat(result).isTrue()
50+
}
51+
52+
@Test
53+
fun `user home expands properly`() {
54+
val userHome = System.getProperty("user.home")
55+
val path = Paths.get("$userHome/sample")
56+
val pattern = "\${user.home}/sample"
57+
val result = service.matchesPattern(path, pattern)
58+
assertTrue(result)
59+
}
60+
61+
@Test
62+
fun `expanded user home glob matches IDE path under bazel cache`() {
63+
val userHome = System.getProperty("user.home")
64+
val pattern = "${userHome}/.cache/bazel/**"
65+
val pathToMatch = Paths.get("$userHome/.cache/bazel/Android Studio.app/Contents")
66+
67+
val matcher = FileSystems.getDefault().getPathMatcher("glob:$pattern")
68+
69+
assertThat(matcher.matches(pathToMatch)).isTrue()
70+
}
71+
}

0 commit comments

Comments
 (0)