Skip to content

Commit cc1de77

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

File tree

6 files changed

+178
-3
lines changed

6 files changed

+178
-3
lines changed

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

Whitespace-only changes.
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: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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.Paths
21+
import org.junit.Test
22+
import org.junit.runner.RunWith
23+
import org.junit.runners.JUnit4
24+
25+
@RunWith(JUnit4::class)
26+
class InstallationLocationServiceTest : BasePlatformTestCase() {
27+
private lateinit var service: InstallationLocationService
28+
29+
override fun setUp() {
30+
super.setUp()
31+
service = InstallationLocationService(project)
32+
}
33+
34+
@Test
35+
fun `path and pattern should match exactly`() {
36+
val path = Paths.get("/sample/test/path")
37+
val pattern = "/sample/test/path"
38+
39+
val result = service.matchesPattern(path, pattern)
40+
assertThat(result).isTrue()
41+
}
42+
43+
@Test
44+
fun `empty pattern should match`() {
45+
val path = Paths.get("")
46+
val pattern = ""
47+
val result = service.matchesPattern(path, pattern)
48+
assertThat(result).isTrue()
49+
}
50+
51+
@Test
52+
fun `user home expands properly`() {
53+
val userHome = System.getProperty("user.home")
54+
val path = Paths.get("$userHome/sample")
55+
val pattern = "\${user.home}/sample"
56+
val result = service.matchesPattern(path, pattern)
57+
assertTrue(result)
58+
}
59+
}

0 commit comments

Comments
 (0)