Fix RIDER-123585: detect VS on Windows

(cherry picked from commit 44eac219488fe51de076b8907f2956d9442211fa)

GitOrigin-RevId: 51c0ef9644fe5016f6c28d3c6081c6bf7793ed2b
This commit is contained in:
Ivan Migalev
2025-03-07 21:23:17 +01:00
committed by intellij-monorepo-bot
parent d85b962378
commit 9479c974fa
3 changed files with 84 additions and 2 deletions

View File

@@ -31,5 +31,6 @@
<orderEntry type="library" name="gson" level="project" />
<orderEntry type="library" name="kotlinx-serialization-core" level="project" />
<orderEntry type="library" name="kotlin-reflect" level="project" />
<orderEntry type="library" name="jna" level="project" />
</component>
</module>

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.internal.statistic.collectors.fus.environment
import com.intellij.internal.statistic.beans.MetricEvent
@@ -17,9 +17,10 @@ private const val CURSOR_ID = ".cursor"
private const val WINDSURF_ID = ".windsurf"
private const val ECLIPSE_ID = ".eclipse"
private const val ZED_ID = ".zed"
private const val VISUAL_STUDIO_ID = "VisualStudio"
internal class EditorsCollector : ApplicationUsagesCollector() {
private val EDITORS_GROUP: EventLogGroup = EventLogGroup("editors", 4)
private val EDITORS_GROUP: EventLogGroup = EventLogGroup("editors", 5)
override fun getGroup(): EventLogGroup = EDITORS_GROUP
@@ -46,6 +47,11 @@ internal class EditorsCollector : ApplicationUsagesCollector() {
EventFields.StringList("extension_ids", emptyList())
)
private val VISUAL_STUDIO_VERSIONS_INSTALLED: EventId1<List<String>> = EDITORS_GROUP.registerEvent(
"visual.studio.versions.installed",
EventFields.StringList("versions", emptyList())
)
override suspend fun getMetricsAsync(): Set<MetricEvent> {
val homeDir = System.getProperty("user.home")
return withContext(Dispatchers.IO) {
@@ -87,6 +93,12 @@ internal class EditorsCollector : ApplicationUsagesCollector() {
if (zedCollectionDataProvider.isZedDetected()) {
add(CONFIG_EXISTS.metric(ZED_ID))
}
val vsVersions = VisualStudioCollectionDataProvider().getInstalledVersions()
if (vsVersions.any()) {
add(CONFIG_EXISTS.metric(VISUAL_STUDIO_ID))
VISUAL_STUDIO_VERSIONS_INSTALLED.metric(vsVersions)
}
}
}
}

View File

@@ -0,0 +1,69 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.internal.statistic.collectors.fus.environment
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.diagnostic.runAndLogException
import com.intellij.openapi.util.SystemInfo
import com.sun.jna.platform.win32.Advapi32Util
import com.sun.jna.platform.win32.WinReg
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlin.io.path.Path
import kotlin.io.path.listDirectoryEntries
import kotlin.io.path.name
private val logger = logger<VisualStudioCollectionDataProvider>()
/**
* Collects anonymous data about the presense of Visual Studio to improve the "Import settings from external editor" feature and prioritize
* support for popular editors.
*
* This includes:
* - detecting if Visual Studio is installed on Windows via %LOCALAPPDATA%,
* - detecting if Visual Studio is installed on Windows via registry (legacy detection).
*
* The data is completely anonymized and no personally identifiable information is captured.
*/
internal class VisualStudioCollectionDataProvider : ExternalEditorCollectionDataProvider() {
suspend fun getInstalledVersions(): List<String> {
if (!SystemInfo.isWindows) return emptyList()
return (readVersionsFromAppData() + readVersionsFromRegistry()).distinct().sorted().toList()
}
private data class VsVersion(val major: Int, val minor: Int, val instanceId: String?) {
override fun toString(): String {
return "$major.$minor"
}
}
// Example: "15.0_a0848a47Exp", where VS version = "15.0", Instance Id = "a0848a47", Root Suffix = "Exp".
private val vsVersionWithHiveRegex = Regex("\\b([0-9]{1,2})\\.([0-9]{1,2})(?:_([a-fA-F0-9]{8}))?[a-zA-Z0-9]*\\b")
private fun matchVersion(hive: String): VsVersion? {
val match = vsVersionWithHiveRegex.find(hive) ?: return null
return VsVersion(
major = match.groupValues[1].toIntOrNull() ?: return null,
minor = match.groupValues[2].toIntOrNull() ?: return null,
instanceId = match.groupValues.elementAtOrNull(3)
)
}
private suspend fun readVersionsFromAppData(): Sequence<String> =
withContext(Dispatchers.IO) {
logger.runAndLogException {
val appData = System.getenv("LOCALAPPDATA") ?: return@withContext emptySequence()
val visualStudioHomeDir = Path(appData, "Microsoft", "VisualStudio")
visualStudioHomeDir.listDirectoryEntries().asSequence()
.mapNotNull { matchVersion(it.name) }
.filter { it.instanceId?.length == 8 }
.map { it.toString() }
} ?: emptySequence()
}
private fun readVersionsFromRegistry(): Sequence<String> = logger.runAndLogException {
val registry = Advapi32Util.registryGetKeys(WinReg.HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\VisualStudio")
registry.asSequence()
.mapNotNull { matchVersion(it) }
.filter { it.major == 11 || it.major == 12 || it.major == 14 }
.map { it.toString() }
} ?: emptySequence()
}