mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 06:50:54 +07:00
[ab] IJPL-186222: Drop old implementation
(cherry picked from commit 7278d60df00844be60eaf39efc278e2a1738ff6b) (cherry picked from commit 09aeb31397b41611a0a3021fb807fd34b99ff3ea) IJ-CR-176469 GitOrigin-RevId: 4cab7a72b9a4d6dc9c7e3c24c1fba5e71737f1bc
This commit is contained in:
committed by
intellij-monorepo-bot
parent
2df6273b02
commit
e930319079
@@ -1,25 +1,9 @@
|
||||
<idea-plugin>
|
||||
<extensionPoints>
|
||||
<extensionPoint qualifiedName="com.intellij.experiment.abExperimentOption"
|
||||
beanClass="com.intellij.platform.experiment.ab.impl.experiment.ABExperimentOptionBean"
|
||||
dynamic="true">
|
||||
<with attribute="implementation"
|
||||
implements="com.intellij.platform.experiment.ab.impl.experiment.ABExperimentOption"/>
|
||||
</extensionPoint>
|
||||
</extensionPoints>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<experiment.abExperimentOption implementation="com.intellij.platform.experiment.ab.impl.option.ABExperimentControlOption"/>
|
||||
|
||||
<statistics.counterUsagesCollector
|
||||
implementationClass="com.intellij.platform.experiment.ab.impl.statistic.ABExperimentCountCollector"/>
|
||||
<statistics.validation.customValidationRule
|
||||
implementation="com.intellij.platform.experiment.ab.impl.statistic.ABExperimentOptionIdValidationRule"/>
|
||||
|
||||
<applicationService serviceInterface="com.intellij.platform.experiment.ab.impl.experiment.ABExperiment"
|
||||
serviceImplementation="com.intellij.platform.experiment.ab.impl.experiment.ABExperimentImpl"/>
|
||||
|
||||
<experiment.abExperimentOption implementation="com.intellij.platform.experiment.ab.temporary.ShowTrialSurveyOption"/>
|
||||
</extensions>
|
||||
|
||||
<actions>
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.experiment.ab.impl.experiment
|
||||
|
||||
import com.intellij.internal.statistic.eventLog.fus.MachineIdManager
|
||||
import com.intellij.internal.statistic.utils.getPluginInfoByDescriptor
|
||||
import com.intellij.openapi.application.ApplicationInfo.getInstance
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.diagnostic.debug
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.diagnostic.runAndLogException
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
import com.intellij.platform.experiment.ab.impl.option.ABExperimentControlOption
|
||||
import com.intellij.platform.experiment.ab.impl.statistic.ABExperimentCountCollector
|
||||
import com.intellij.util.MathUtil
|
||||
import com.intellij.util.PlatformUtils
|
||||
|
||||
fun getABExperimentInstance(): ABExperiment {
|
||||
return ABExperiment.getABExperimentInstance()
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a multi-optional A/B experiment for all IDEs and JetBrains plugins,
|
||||
* which affects IDE metrics like user retention in the IDE.
|
||||
*
|
||||
* Each feature is represented as an option.
|
||||
* An option defines the number of user groups which will be associated with this option.
|
||||
* There is a control option for default behavior.
|
||||
* You need to implement `ABExperimentOption` extension point to implement an option for your feature.
|
||||
*
|
||||
* The number of A/B experimental groups is limited.
|
||||
* It is necessary to keep a group audience enough to make statistically significant conclusions.
|
||||
* So it is crucial to choose group size judiciously.
|
||||
* If group capacity is exhausted for a specific IDE, there will be an error.
|
||||
* In such a case, you need to communicate with related people to handle such a case and rearrange option groups accordingly.
|
||||
*
|
||||
* A/B experiment supports the implemented options from JetBrains plugins.
|
||||
* Plugins can be installed/uninstalled or enabled/disabled.
|
||||
* Accordingly, the options defined in plugins may appear when the plugin is enabled or installed,
|
||||
* or disappear when the plugin is disabled or uninstalled.
|
||||
* The experiment uses special storage to be able to work with such conditions correctly.
|
||||
*
|
||||
* @see ABExperimentControlOption
|
||||
* @see ABExperimentGroupStorageService
|
||||
*/
|
||||
interface ABExperiment {
|
||||
companion object {
|
||||
fun getABExperimentInstance(): ABExperiment {
|
||||
val application = ApplicationManager.getApplication() ?: return DummyABExperiment
|
||||
return application.service<ABExperiment>()
|
||||
}
|
||||
}
|
||||
|
||||
fun isControlExperimentOptionEnabled() : Boolean
|
||||
|
||||
fun isExperimentOptionEnabled(experimentOptionClass: Class<out ABExperimentOption>): Boolean
|
||||
}
|
||||
|
||||
internal object DummyABExperiment : ABExperiment {
|
||||
// is never a control group
|
||||
override fun isControlExperimentOptionEnabled(): Boolean = false
|
||||
|
||||
// always turned on
|
||||
override fun isExperimentOptionEnabled(experimentOptionClass: Class<out ABExperimentOption>): Boolean = true
|
||||
}
|
||||
|
||||
private val AB_EXPERIMENTAL_OPTION_EP: ExtensionPointName<ABExperimentOptionBean> =
|
||||
ExtensionPointName("com.intellij.experiment.abExperimentOption")
|
||||
|
||||
private val LOG = logger<ABExperiment>()
|
||||
|
||||
private fun getDeviceIdPurpose(): String {
|
||||
return "A/B Experiment" + getInstance().shortVersion
|
||||
}
|
||||
|
||||
private const val TOTAL_NUMBER_OF_BUCKETS = 1024
|
||||
internal const val TOTAL_NUMBER_OF_GROUPS = 256
|
||||
|
||||
internal val OPTION_ID_FREE_GROUP = ABExperimentOptionId("free.option")
|
||||
|
||||
internal fun isPopularIDE(): Boolean = PlatformUtils.isIdeaUltimate() || PlatformUtils.isPyCharmPro()
|
||||
|
||||
internal fun getJbABExperimentOptionList(): List<ABExperimentOption> {
|
||||
return getJbABExperimentOptionBeanList().map { it.instance }
|
||||
}
|
||||
|
||||
internal fun getJbABExperimentOptionBeanList(): List<ABExperimentOptionBean> {
|
||||
return AB_EXPERIMENTAL_OPTION_EP.extensionList.filter {
|
||||
val pluginDescriptor = it.pluginDescriptor
|
||||
val pluginInfo = getPluginInfoByDescriptor(pluginDescriptor)
|
||||
pluginInfo.isDevelopedByJetBrains() && it.instance.isEnabled()
|
||||
}
|
||||
}
|
||||
|
||||
internal class ABExperimentImpl : ABExperiment {
|
||||
override fun isControlExperimentOptionEnabled(): Boolean {
|
||||
return isExperimentOptionEnabled(ABExperimentControlOption::class.java)
|
||||
}
|
||||
|
||||
override fun isExperimentOptionEnabled(experimentOptionClass: Class<out ABExperimentOption>): Boolean {
|
||||
return experimentOptionClass.isInstance(getUserExperimentOption())
|
||||
}
|
||||
|
||||
internal fun getUserExperimentOption(): ABExperimentOption? {
|
||||
val userOptionId = getUserExperimentOptionId()
|
||||
ABExperimentCountCollector.logABExperimentOptionUsed(userOptionId, getUserGroupNumber(), getUserBucketNumber())
|
||||
return getJbABExperimentOptionList().find { it.id.value == userOptionId?.value }
|
||||
}
|
||||
|
||||
internal fun getUserExperimentOptionId(): ABExperimentOptionId? {
|
||||
val manualOptionIdText = System.getProperty("platform.experiment.ab.manual.option", "")
|
||||
if (manualOptionIdText.isNotBlank()) {
|
||||
LOG.debug { "Use manual option id from Registry. Registry key value is: $manualOptionIdText" }
|
||||
|
||||
val manualOption = getJbABExperimentOptionList().find { it.id.value == manualOptionIdText }
|
||||
if (manualOption != null) {
|
||||
LOG.debug { "Found manual option is: $manualOption" }
|
||||
return manualOption.id
|
||||
}
|
||||
else if (manualOptionIdText == OPTION_ID_FREE_GROUP.value) {
|
||||
LOG.debug { "Found manual option is: $manualOptionIdText" }
|
||||
return ABExperimentOptionId(manualOptionIdText)
|
||||
}
|
||||
else {
|
||||
LOG.debug { "Manual option with id $manualOptionIdText not found." }
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
val userGroupNumber = getUserGroupNumber()
|
||||
val userOptionId = ABExperimentGroupStorageService.getUserExperimentOptionId(userGroupNumber)
|
||||
|
||||
LOG.debug { "User option id is: ${userOptionId.value}." }
|
||||
|
||||
return userOptionId
|
||||
}
|
||||
|
||||
private fun getUserGroupNumber(): Int {
|
||||
val bucket = getUserBucketNumber()
|
||||
if (TOTAL_NUMBER_OF_BUCKETS < TOTAL_NUMBER_OF_GROUPS) {
|
||||
LOG.error("Number of buckets is less than number of groups. " +
|
||||
"Please revise related experiment constants and adjust them accordingly.")
|
||||
}
|
||||
|
||||
val experimentGroup = bucket % TOTAL_NUMBER_OF_GROUPS
|
||||
LOG.debug { "User group number is: $experimentGroup." }
|
||||
return experimentGroup
|
||||
}
|
||||
|
||||
private fun getUserBucketNumber(): Int {
|
||||
val deviceId = LOG.runAndLogException {
|
||||
MachineIdManager.getAnonymizedMachineId(getDeviceIdPurpose())
|
||||
}
|
||||
|
||||
val bucketNumber = MathUtil.nonNegativeAbs(deviceId.hashCode()) % TOTAL_NUMBER_OF_BUCKETS
|
||||
LOG.debug { "User bucket number is: $bucketNumber." }
|
||||
return bucketNumber
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.experiment.ab.impl.experiment
|
||||
|
||||
import com.intellij.ide.plugins.DynamicPluginEnabler
|
||||
import com.intellij.ide.plugins.PluginStateManager
|
||||
import com.intellij.openapi.components.*
|
||||
import com.intellij.openapi.diagnostic.debug
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
|
||||
/**
|
||||
* This storage is used to ensure that option groups are assigned properly.
|
||||
* It maintains a map from a group's number to an assigned option id.
|
||||
* It uses a special id to mark free groups.
|
||||
*
|
||||
* At the start, the map is initialized with available options.
|
||||
* After that, for each event of plugin enabling/disabling/installing/uninstalling and after startup,
|
||||
* it checks new available options and puts its id into the map.
|
||||
* Ids of options that become not available are not removed from the map.
|
||||
* This is to avoid mixing different options with the user and
|
||||
* handle a case when a user enables and disables a plugin several times.
|
||||
*
|
||||
* @see com.intellij.platform.experiment.ab.impl.experiment.OPTION_ID_FREE_GROUP
|
||||
*/
|
||||
@Service(Service.Level.APP)
|
||||
@State(
|
||||
name = "ABExperimentGroupStorageService",
|
||||
storages = [Storage("ABExperimentGroupStorageService.xml", roamingType = RoamingType.DISABLED)]
|
||||
)
|
||||
internal class ABExperimentGroupStorageService : PersistentStateComponent<ABExperimentGroupStorage> {
|
||||
|
||||
companion object {
|
||||
private val LOG = logger<ABExperimentGroupStorageService>()
|
||||
|
||||
fun getUserExperimentOptionId(userGroupNumber: Int): ABExperimentOptionId {
|
||||
val experimentOptionIdText = service<ABExperimentGroupStorageService>().state.groupNumberToExperimentOptionId[userGroupNumber]!!
|
||||
return ABExperimentOptionId(experimentOptionIdText)
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var myState: ABExperimentGroupStorage
|
||||
|
||||
override fun getState(): ABExperimentGroupStorage {
|
||||
return myState
|
||||
}
|
||||
|
||||
override fun loadState(state: ABExperimentGroupStorage) {
|
||||
myState = state
|
||||
}
|
||||
|
||||
override fun noStateLoaded() {
|
||||
myState = ABExperimentGroupStorage(getInitialGroupToOptionState())
|
||||
}
|
||||
|
||||
override fun initializeComponent() {
|
||||
val tracker = ABExperimentPluginTracker()
|
||||
PluginStateManager.addStateListener(tracker)
|
||||
DynamicPluginEnabler.addPluginStateChangedListener(tracker)
|
||||
|
||||
setupNewPluginABExperimentOptions()
|
||||
}
|
||||
|
||||
internal fun setupNewPluginABExperimentOptions() {
|
||||
val groupNumberToExperimentOptionId = myState.groupNumberToExperimentOptionId
|
||||
LOG.debug { "State BEFORE update is: $groupNumberToExperimentOptionId" }
|
||||
|
||||
val optionBeans = getJbABExperimentOptionBeanList()
|
||||
val usedOptionIds = groupNumberToExperimentOptionId.values.toSet()
|
||||
val newOptionBeans = optionBeans.filter { it.instance.id.value !in usedOptionIds }
|
||||
|
||||
if (newOptionBeans.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
val isPopularIDE = isPopularIDE()
|
||||
|
||||
for (newOptionBean in newOptionBeans) {
|
||||
val newOption = newOptionBean.instance
|
||||
val groupCount = newOption.getGroupSizeForIde(isPopularIDE).groupCount
|
||||
for (i in 0 until groupCount) {
|
||||
val freeGroupKey = groupNumberToExperimentOptionId.entries.find { entry ->
|
||||
entry.value == OPTION_ID_FREE_GROUP.value
|
||||
}?.key
|
||||
|
||||
if (freeGroupKey == null) {
|
||||
LOG.error("There is no available groups for option ${newOption.id} from plugin " +
|
||||
newOptionBean.pluginDescriptor.pluginId.idString)
|
||||
return
|
||||
}
|
||||
|
||||
LOG.debug { "Assign experiment option ${newOption.id} to group $freeGroupKey." }
|
||||
|
||||
groupNumberToExperimentOptionId[freeGroupKey] = newOption.id.value
|
||||
}
|
||||
}
|
||||
LOG.debug { "State AFTER update is: $groupNumberToExperimentOptionId" }
|
||||
}
|
||||
|
||||
private fun getInitialGroupToOptionState(): MutableMap<Int, String> {
|
||||
val initialGroupNumberToExperimentOptionId = (0.rangeUntil(TOTAL_NUMBER_OF_GROUPS).associateWith {
|
||||
OPTION_ID_FREE_GROUP.value
|
||||
}).toMutableMap()
|
||||
|
||||
val isPopularIDE = isPopularIDE()
|
||||
val options = getJbABExperimentOptionList().sortedBy { it.id.value }
|
||||
var counter = 0
|
||||
|
||||
for (option in options) {
|
||||
val optionGroupsCount = option.getGroupSizeForIde(isPopularIDE).groupCount
|
||||
for (groupNumber in counter.rangeUntil(counter + optionGroupsCount)) {
|
||||
LOG.debug { "Assign experiment option ${option.id} to group $groupNumber." }
|
||||
initialGroupNumberToExperimentOptionId[groupNumber] = option.id.value
|
||||
}
|
||||
counter += optionGroupsCount
|
||||
}
|
||||
|
||||
LOG.debug { "Initial state of group to option map is: $initialGroupNumberToExperimentOptionId" }
|
||||
|
||||
return initialGroupNumberToExperimentOptionId
|
||||
}
|
||||
}
|
||||
|
||||
internal data class ABExperimentGroupStorage(val groupNumberToExperimentOptionId: MutableMap<Int, String>)
|
||||
@@ -1,55 +0,0 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.experiment.ab.impl.experiment
|
||||
|
||||
import com.intellij.platform.experiment.ab.impl.option.ABExperimentOptionGroupSize
|
||||
|
||||
/**
|
||||
* A/B Experiment option interface.
|
||||
*
|
||||
* Implement and register an option for your feature.
|
||||
*/
|
||||
interface ABExperimentOption {
|
||||
val id: ABExperimentOptionId
|
||||
|
||||
/**
|
||||
* Returns the size of an audience group for the option.
|
||||
*
|
||||
* The number of A/B experimental groups is limited.
|
||||
* The group size must be agreed with the analysts so that the result of the experiment is statistically significant.
|
||||
*
|
||||
* If group capacity is exhausted for a specific IDE, there will be an error in runtime.
|
||||
* In such a case, you need to communicate with related persons with other options
|
||||
* to handle such a case and rearrange option groups accordingly.
|
||||
*
|
||||
* @param isPopularIde true if the current IDE is popular.
|
||||
* It can be used to adjust the group size of the option accordingly.
|
||||
* Popular IDEs have more users, and the group size should be smaller than it is in other IDEs.
|
||||
*
|
||||
* @see com.intellij.platform.experiment.ab.impl.experiment.TOTAL_NUMBER_OF_GROUPS
|
||||
*/
|
||||
fun getGroupSizeForIde(isPopularIde: Boolean): ABExperimentOptionGroupSize
|
||||
|
||||
/**
|
||||
* Check if the option should be enabled in a certain IDE.
|
||||
*
|
||||
* Mostly useful for options in Intellij Platform to enable option only in certain IDEs.
|
||||
*/
|
||||
fun checkIdeIsSuitable(): Boolean
|
||||
|
||||
/**
|
||||
* Check if the option should be enabled in a certain IDE version.
|
||||
*
|
||||
* Must be agreed with analytics to do not spoil an experiment.
|
||||
*
|
||||
* Mostly useful for plugins that potentially can be installed to a different version of a certain IDE.
|
||||
* In this case, the plugin must specify the target version so as not to spoil the experiment
|
||||
* by overflowing the maximum number of user groups.
|
||||
*
|
||||
* For IDEs, it allows to control in what version of IDE what options are enabled.
|
||||
*/
|
||||
fun checkIdeVersionIsSuitable(): Boolean
|
||||
}
|
||||
|
||||
fun ABExperimentOption.isEnabled(): Boolean {
|
||||
return checkIdeIsSuitable() && checkIdeVersionIsSuitable()
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.experiment.ab.impl.experiment
|
||||
|
||||
import com.intellij.openapi.extensions.RequiredElement
|
||||
import com.intellij.serviceContainer.BaseKeyedLazyInstance
|
||||
import com.intellij.util.xmlb.annotations.Attribute
|
||||
|
||||
class ABExperimentOptionBean : BaseKeyedLazyInstance<ABExperimentOption>() {
|
||||
@Attribute("implementation")
|
||||
@JvmField
|
||||
@RequiredElement
|
||||
var implementationClass: String? = null
|
||||
|
||||
override fun getImplementationClassName(): String? {
|
||||
return implementationClass
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.experiment.ab.impl.experiment
|
||||
|
||||
@JvmInline
|
||||
value class ABExperimentOptionId(val value: String)
|
||||
@@ -1,25 +0,0 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.experiment.ab.impl.experiment
|
||||
|
||||
import com.intellij.ide.plugins.IdeaPluginDescriptor
|
||||
import com.intellij.ide.plugins.PluginEnableStateChangedListener
|
||||
import com.intellij.ide.plugins.PluginStateListener
|
||||
import com.intellij.openapi.components.service
|
||||
|
||||
internal class ABExperimentPluginTracker : PluginStateListener, PluginEnableStateChangedListener {
|
||||
override fun stateChanged(pluginDescriptors: Collection<IdeaPluginDescriptor>, enable: Boolean) {
|
||||
if (!enable) {
|
||||
return
|
||||
}
|
||||
|
||||
service<ABExperimentGroupStorageService>().setupNewPluginABExperimentOptions()
|
||||
}
|
||||
|
||||
override fun install(descriptor: IdeaPluginDescriptor) {
|
||||
service<ABExperimentGroupStorageService>().setupNewPluginABExperimentOptions()
|
||||
}
|
||||
|
||||
override fun uninstall(descriptor: IdeaPluginDescriptor) {
|
||||
service<ABExperimentGroupStorageService>().setupNewPluginABExperimentOptions()
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@ApiStatus.Internal
|
||||
package com.intellij.platform.experiment.ab.impl.experiment;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
@@ -17,9 +17,10 @@ import kotlin.math.absoluteValue
|
||||
* The plugins are welcome to use [ABExperimentOption.isEnabled] to check whether the experiment is enabled on the user's machine
|
||||
*/
|
||||
enum class ABExperimentOption {
|
||||
EXPERIMENT_1,
|
||||
EXPERIMENT_2,
|
||||
EXPERIMENT_3,
|
||||
KUBERNETES_SEPARATE_SERVICE_VIEW,
|
||||
FUZZY_FILE_SEARCH,
|
||||
SHOW_TRIAL_SURVEY,
|
||||
NEW_USERS_ONBOARDING,
|
||||
|
||||
/**
|
||||
* A group for users which are not assigned to any experiment.
|
||||
@@ -49,10 +50,9 @@ internal const val NUMBER_OF_BUCKETS: Int = 1024
|
||||
*/
|
||||
@VisibleForTesting
|
||||
internal val experimentsPartition: List<ExperimentAssignment> = listOf(
|
||||
ExperimentAssignment(experiment = EXPERIMENT_1, experimentBuckets = (0 until 180).toSet(), controlBuckets = (180 until 256).toSet()),
|
||||
ExperimentAssignment(experiment = EXPERIMENT_2, experimentBuckets = (256 until 384).toSet(), controlBuckets = (384 until 512).toSet()),
|
||||
ExperimentAssignment(experiment = EXPERIMENT_3, experimentBuckets = (512 until 640).toSet(), controlBuckets = (640 until 768).toSet()),
|
||||
// the rest belongs to the unassigned experiment
|
||||
ExperimentAssignment(experiment = KUBERNETES_SEPARATE_SERVICE_VIEW, experimentBuckets = (0 until 128).toSet(), controlBuckets = (128 until 256).toSet()),
|
||||
ExperimentAssignment(experiment = FUZZY_FILE_SEARCH, experimentBuckets = (256 until 512).toSet(), controlBuckets = (512 until 768).toSet()),
|
||||
// the rest belongs to the "unassigned" experiment
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.experiment.ab.impl.option
|
||||
|
||||
import com.intellij.platform.experiment.ab.impl.experiment.ABExperimentImpl
|
||||
import com.intellij.platform.experiment.ab.impl.experiment.ABExperimentOption
|
||||
import com.intellij.platform.experiment.ab.impl.experiment.ABExperimentOptionId
|
||||
import com.intellij.platform.experiment.ab.impl.experiment.getABExperimentInstance
|
||||
|
||||
|
||||
fun isControlOptionEnabled(): Boolean {
|
||||
val abImpl = getABExperimentInstance() as? ABExperimentImpl ?: return false
|
||||
return abImpl.getUserExperimentOption() is ABExperimentControlOption
|
||||
}
|
||||
|
||||
internal class ABExperimentControlOption : ABExperimentOption {
|
||||
|
||||
override val id: ABExperimentOptionId = ABExperimentOptionId("control.option")
|
||||
|
||||
override fun getGroupSizeForIde(isPopularIde: Boolean): ABExperimentOptionGroupSize {
|
||||
return ABExperimentOptionGroupSize(32)
|
||||
}
|
||||
|
||||
override fun checkIdeIsSuitable(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun checkIdeVersionIsSuitable(): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.experiment.ab.impl.option
|
||||
|
||||
/**
|
||||
* Represent experiment option user group size.
|
||||
*
|
||||
* Must be greater than 0 and less than the total number of user groups.
|
||||
*
|
||||
* The group size must be agreed with the analysts so that the result of the experiment is statistically significant.
|
||||
*
|
||||
* @see com.intellij.platform.experiment.ab.impl.experiment.TOTAL_NUMBER_OF_GROUPS
|
||||
*/
|
||||
@JvmInline
|
||||
value class ABExperimentOptionGroupSize(val groupCount: Int) {
|
||||
init {
|
||||
if (groupCount <= 0) {
|
||||
error("Size of option group should be greater then 0.")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@ApiStatus.Internal
|
||||
package com.intellij.platform.experiment.ab.impl.option;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
@@ -1,36 +1,15 @@
|
||||
// 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.platform.experiment.ab.temporary
|
||||
|
||||
import com.intellij.openapi.application.ApplicationInfo
|
||||
import com.intellij.platform.experiment.ab.impl.experiment.ABExperiment
|
||||
import com.intellij.platform.experiment.ab.impl.experiment.ABExperimentOption
|
||||
import com.intellij.platform.experiment.ab.impl.experiment.ABExperimentOptionId
|
||||
import com.intellij.platform.experiment.ab.impl.option.ABExperimentOptionGroupSize
|
||||
import com.intellij.util.PlatformUtils
|
||||
import com.intellij.platform.experiment.ab.impl.ABExperimentOption
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@ApiStatus.Internal
|
||||
class ShowTrialSurveyOption : ABExperimentOption {
|
||||
override val id: ABExperimentOptionId = ABExperimentOptionId("showTrialSurvey")
|
||||
|
||||
override fun getGroupSizeForIde(isPopularIde: Boolean): ABExperimentOptionGroupSize {
|
||||
return ABExperimentOptionGroupSize(192)
|
||||
}
|
||||
|
||||
override fun checkIdeIsSuitable(): Boolean = PlatformUtils.isIdeaUltimate()
|
||||
|
||||
/**
|
||||
* Experiment should be available only in 2024.3.5
|
||||
*/
|
||||
override fun checkIdeVersionIsSuitable(): Boolean {
|
||||
val appInfo = ApplicationInfo.getInstance()
|
||||
return appInfo.majorVersion == "2024" && appInfo.minorVersion.startsWith("3.5")
|
||||
}
|
||||
|
||||
class ShowTrialSurveyOption {
|
||||
@Suppress("CompanionObjectInExtension")
|
||||
companion object {
|
||||
@JvmStatic
|
||||
val isTrialSurveyEnabled: Boolean get() = System.getProperty("test.ide.trial.survey", "false").toBoolean() ||
|
||||
ABExperiment.getABExperimentInstance().isExperimentOptionEnabled(ShowTrialSurveyOption::class.java)
|
||||
ABExperimentOption.SHOW_TRIAL_SURVEY.isEnabled()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,11 @@
|
||||
// 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.ide.util.gotoByName
|
||||
|
||||
import com.intellij.openapi.application.ApplicationInfo
|
||||
import com.intellij.openapi.util.registry.Registry.Companion.`is`
|
||||
import com.intellij.platform.experiment.ab.impl.experiment.ABExperiment
|
||||
import com.intellij.platform.experiment.ab.impl.experiment.ABExperimentOption
|
||||
import com.intellij.platform.experiment.ab.impl.experiment.ABExperimentOptionId
|
||||
import com.intellij.platform.experiment.ab.impl.option.ABExperimentOptionGroupSize
|
||||
import com.intellij.platform.experiment.ab.impl.ABExperimentOption
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@ApiStatus.Internal
|
||||
internal fun isFuzzyFileSearchEnabled(): Boolean {
|
||||
return `is`("search.everywhere.fuzzy.file.search.ab.experiment.enabled", false) &&
|
||||
ABExperiment.getABExperimentInstance().isExperimentOptionEnabled(FuzzyFileSearchExperimentOption::class.java)
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
internal class FuzzyFileSearchExperimentOption : ABExperimentOption {
|
||||
override val id: ABExperimentOptionId = ABExperimentOptionId(FUZZY_FILE_SEARCH_EXPERIMENT_OPTION_ID)
|
||||
|
||||
override fun getGroupSizeForIde(isPopularIde: Boolean): ABExperimentOptionGroupSize {
|
||||
return ABExperimentOptionGroupSize(128)
|
||||
}
|
||||
|
||||
override fun checkIdeIsSuitable(): Boolean = true
|
||||
|
||||
override fun checkIdeVersionIsSuitable(): Boolean {
|
||||
val appInfo = ApplicationInfo.getInstance()
|
||||
return appInfo.isEAP && appInfo.majorVersion == "2025" && appInfo.minorVersionMainPart == "2"
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val FUZZY_FILE_SEARCH_EXPERIMENT_OPTION_ID = "fuzzyFileSearch"
|
||||
}
|
||||
return `is`("search.everywhere.fuzzy.file.search.enabled", false) || ABExperimentOption.FUZZY_FILE_SEARCH.isEnabled()
|
||||
}
|
||||
@@ -17,8 +17,6 @@
|
||||
<notificationGroup displayType="BALLOON" id="newUsersOnboarding" bundle="messages.NewUsersOnboardingBundle"
|
||||
key="notification.group"/>
|
||||
|
||||
<experiment.abExperimentOption implementation="com.intellij.platform.ide.newUsersOnboarding.NewUsersOnboardingExperimentOption"/>
|
||||
|
||||
<statistics.counterUsagesCollector implementationClass="com.intellij.platform.ide.newUsersOnboarding.NewUsersOnboardingStatistics"/>
|
||||
</extensions>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.serviceAsync
|
||||
import com.intellij.openapi.extensions.ExtensionNotApplicableException
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.platform.experiment.ab.impl.experiment.getABExperimentInstance
|
||||
import com.intellij.platform.experiment.ab.impl.ABExperimentOption
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
@@ -20,7 +20,7 @@ class NewUsersOnboardingExperiment {
|
||||
* Since it is not changed during IDE session, better to calculate it once.
|
||||
*/
|
||||
private val isExperimentEnabled: Boolean by lazy {
|
||||
getABExperimentInstance().isExperimentOptionEnabled(NewUsersOnboardingExperimentOption::class.java)
|
||||
ABExperimentOption.NEW_USERS_ONBOARDING.isEnabled()
|
||||
}
|
||||
|
||||
fun isEnabled(): Boolean {
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.ide.newUsersOnboarding
|
||||
|
||||
import com.intellij.openapi.application.ApplicationInfo
|
||||
import com.intellij.platform.experiment.ab.impl.experiment.ABExperimentOption
|
||||
import com.intellij.platform.experiment.ab.impl.experiment.ABExperimentOptionId
|
||||
import com.intellij.platform.experiment.ab.impl.option.ABExperimentOptionGroupSize
|
||||
|
||||
internal class NewUsersOnboardingExperimentOption : ABExperimentOption {
|
||||
override val id: ABExperimentOptionId = ABExperimentOptionId("newUsersOnboarding")
|
||||
|
||||
override fun getGroupSizeForIde(isPopularIde: Boolean): ABExperimentOptionGroupSize {
|
||||
return ABExperimentOptionGroupSize(128)
|
||||
}
|
||||
|
||||
override fun checkIdeIsSuitable(): Boolean = true
|
||||
|
||||
/**
|
||||
* Experiment should be available only in 2024.2.1
|
||||
*/
|
||||
override fun checkIdeVersionIsSuitable(): Boolean {
|
||||
val appInfo = ApplicationInfo.getInstance()
|
||||
return appInfo.majorVersion == "2024" && appInfo.minorVersion == "2.1"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user