[IFT] Switch on a statistics collection for light suggester checkers

IJPL-159194

GitOrigin-RevId: 27a23c495d695c0b0059506efba50355279a635d
This commit is contained in:
Alexey Merkulov
2024-08-29 18:06:49 +02:00
committed by intellij-monorepo-bot
parent 4d8776250f
commit f395b23567
10 changed files with 50 additions and 21 deletions

View File

@@ -9,11 +9,11 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.EditorFactory
import com.intellij.openapi.editor.ex.EditorEventMulticasterEx
import com.intellij.openapi.editor.ex.FocusChangeListener
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.Project
import training.featuresSuggester.actions.Action
import training.featuresSuggester.actions.EditorFocusGainedAction
import training.featuresSuggester.settings.FeatureSuggesterSettings
import training.featuresSuggester.statistics.FeatureSuggesterStatistics
import training.featuresSuggester.suggesters.FeatureSuggester
import training.featuresSuggester.ui.NotificationSuggestionPresenter
import training.featuresSuggester.ui.SuggestionPresenter
@@ -32,6 +32,9 @@ internal class FeatureSuggestersManager(private val project: Project) : Disposab
try {
handleAction(action)
}
catch (e: ProcessCanceledException) {
throw e
}
catch (t: Throwable) {
thisLogger().error("An error occurred during action processing: $action", t)
}
@@ -39,19 +42,20 @@ internal class FeatureSuggestersManager(private val project: Project) : Disposab
private fun handleAction(action: Action) {
val language = action.language
val suggesters = FeatureSuggester.suggesters.filter { it.languages.find { id -> id == Language.ANY.id || id == language?.id } != null }
val needSendStatisticsForSwitchedOffCheckers = FeatureSuggesterSettings.instance().needSendStatisticsForSwitchedOffCheckers
val suggesters = FeatureSuggester.suggesters
.filter { (it.forceCheckForStatistics && needSendStatisticsForSwitchedOffCheckers) || it.isEnabled() }
.filter { it.languages.find { id -> id == Language.ANY.id || id == language?.id } != null }
for (suggester in suggesters) {
if (suggester.isEnabled()) {
processSuggester(suggester, action)
}
processSuggester(suggester, action)
}
}
private fun processSuggester(suggester: FeatureSuggester, action: Action) {
val suggestion = suggester.getSuggestion(action)
if (suggestion is PopupSuggestion) {
FeatureSuggesterStatistics.logSuggestionFound(suggester.id)
if (SuggestingUtils.forceShowSuggestions || suggester.isSuggestionNeeded()) {
suggester.logStatisticsThatSuggestionIsFound(suggestion)
if (suggester.isEnabled() && (SuggestingUtils.forceShowSuggestions || suggester.isSuggestionNeeded())) {
suggestionPresenter.showSuggestion(project, suggestion, disposable = this)
fireSuggestionFound(suggestion)
FeatureSuggesterSettings.instance().updateSuggestionShownTime(suggestion.suggesterId)

View File

@@ -32,7 +32,10 @@ class FeatureSuggesterSettings : PersistentStateComponent<FeatureSuggesterSettin
private var workingDays: MutableList<Long> = mutableListOf()
val isAnySuggesterEnabled: Boolean
get() = suggesters.any { it.value }
get() = needSendStatisticsForSwitchedOffCheckers || suggesters.any { it.value }
val needSendStatisticsForSwitchedOffCheckers: Boolean
get() = Registry.`is`("feature.suggester.send.statistics", false)
private val isSuggestersEnabledByDefault: Boolean
get() = Registry.`is`("feature.suggester.enable.suggesters", false)

View File

@@ -2,13 +2,11 @@ package training.featuresSuggester.statistics
import com.intellij.internal.statistic.eventLog.EventLogGroup
import com.intellij.internal.statistic.eventLog.events.EventFields
import com.intellij.internal.statistic.eventLog.events.EventId1
import com.intellij.internal.statistic.eventLog.validator.ValidationResultType
import com.intellij.internal.statistic.eventLog.validator.rules.EventContext
import com.intellij.internal.statistic.eventLog.validator.rules.impl.CustomValidationRule
import com.intellij.internal.statistic.service.fus.collectors.CounterUsagesCollector
import com.intellij.internal.statistic.utils.getPluginInfo
import com.intellij.openapi.util.registry.Registry
import training.featuresSuggester.suggesters.FeatureSuggester
object FeatureSuggesterStatistics : CounterUsagesCollector() {
@@ -20,26 +18,28 @@ object FeatureSuggesterStatistics : CounterUsagesCollector() {
private const val NOTIFICATION_LEARN_MORE_EVENT_ID = "notification.learn_more"
private const val SUGGESTION_FOUND = "suggestion_found"
private const val SUGGESTER_ID_FIELD = "suggester_id"
private const val SUGGESTION_WOULD_BE_SHOWN_FIELD = "suggestion_would_be_shown"
private const val DAYS_PASSED_LAST_USED_FIELD = "days_passed_last_used"
const val SUGGESTER_ID_VALIDATION_RULE = "feature_suggester_id"
private val GROUP = EventLogGroup(GROUP_ID, 4)
private val GROUP = EventLogGroup(GROUP_ID, 5)
private val suggesterIdField = EventFields.StringValidatedByCustomRule(SUGGESTER_ID_FIELD, FeatureSuggesterIdRuleValidator::class.java)
private val suggestionWouldBeShownField = EventFields.Boolean(SUGGESTION_WOULD_BE_SHOWN_FIELD)
private val daysPassedLastUsedField = EventFields.Int(DAYS_PASSED_LAST_USED_FIELD)
private val notificationShowedEvent = GROUP.registerEvent(NOTIFICATION_SHOWED_EVENT_ID, suggesterIdField)
private val notificationDontSuggestEvent = GROUP.registerEvent(NOTIFICATION_DONT_SUGGEST_EVENT_ID, suggesterIdField)
private val notificationLearnMoreEvent = GROUP.registerEvent(NOTIFICATION_LEARN_MORE_EVENT_ID, suggesterIdField)
private val suggestionFoundEvent = GROUP.registerEvent(SUGGESTION_FOUND, suggesterIdField)
fun logNotificationShowed(suggesterId: String) = sendStatistics(notificationShowedEvent, suggesterId)
fun logNotificationDontSuggest(suggesterId: String) = sendStatistics(notificationDontSuggestEvent, suggesterId)
fun logNotificationLearnMore(suggesterId: String) = sendStatistics(notificationLearnMoreEvent, suggesterId)
fun logSuggestionFound(suggesterId: String) = sendStatistics(suggestionFoundEvent, suggesterId)
private val suggestionFoundEvent = GROUP.registerEvent(SUGGESTION_FOUND, suggesterIdField, suggestionWouldBeShownField, daysPassedLastUsedField)
private fun sendStatistics(event: EventId1<String?>, suggesterId: String) {
if (Registry.`is`("feature.suggester.send.statistics", false)) {
event.log(suggesterId)
}
fun logNotificationShowed(suggesterId: String) = notificationShowedEvent.log(suggesterId)
fun logNotificationDontSuggest(suggesterId: String) = notificationDontSuggestEvent.log(suggesterId)
fun logNotificationLearnMore(suggesterId: String) = notificationLearnMoreEvent.log(suggesterId)
fun logSuggestionFound(suggesterId: String, suggestionWouldBeShown: Boolean, daysPassedFromLastUsage: Int) {
suggestionFoundEvent.log(suggesterId, suggestionWouldBeShown, daysPassedFromLastUsage)
}
}

View File

@@ -9,6 +9,7 @@ import com.intellij.openapi.keymap.KeymapUtil
import org.jetbrains.annotations.Nls
import training.featuresSuggester.*
import training.featuresSuggester.settings.FeatureSuggesterSettings
import training.featuresSuggester.statistics.FeatureSuggesterStatistics
import java.util.concurrent.TimeUnit
abstract class AbstractFeatureSuggester : FeatureSuggester {
@@ -25,13 +26,20 @@ abstract class AbstractFeatureSuggester : FeatureSuggester {
*/
override fun isSuggestionNeeded() = !isSuggestingActionUsedRecently() && !isSuggestionShownRecently()
override fun logStatisticsThatSuggestionIsFound(suggestion: Suggestion) {
val daysPassedFromLastUsage = actionSummary()?.lastUsedTimestamp?.let { (System.currentTimeMillis() - it) / TimeUnit.DAYS.toMillis(1L) } ?: -1
FeatureSuggesterStatistics.logSuggestionFound(id, !isSuggestingActionUsedRecently(), daysPassedFromLastUsage.toInt())
}
private fun isSuggestingActionUsedRecently(): Boolean {
val summary = service<ActionsLocalSummary>().getActionStatsById(suggestingActionId) ?: return false
val summary = actionSummary() ?: return false
val lastTimeUsed = summary.lastUsedTimestamp
val oldestWorkingDayStart = FeatureSuggesterSettings.instance().getOldestWorkingDayStartMillis(minSuggestingIntervalDays)
return lastTimeUsed > oldestWorkingDayStart
}
private fun actionSummary() = service<ActionsLocalSummary>().getActionStatsById(suggestingActionId)
private fun isSuggestionShownRecently(): Boolean {
val lastTimeShown = FeatureSuggesterSettings.instance().getSuggestionLastShownTime(id)
val delta = System.currentTimeMillis() - lastTimeShown

View File

@@ -16,6 +16,8 @@ class CompletionPopupSuggester : AbstractFeatureSuggester() {
override val languages = listOf("JAVA", "kotlin", "Python", "JavaScript", "ECMAScript 6")
override val forceCheckForStatistics = true
private object State {
var dotOffset: Int = -1
private set

View File

@@ -26,6 +26,8 @@ class EditBreakpointSuggester : AbstractFeatureSuggester() {
override val languages = listOf(Language.ANY.id)
override val forceCheckForStatistics = true
private val numOfPausesToGetSuggestion = 8
@Suppress("UnstableApiUsage")

View File

@@ -22,9 +22,13 @@ interface FeatureSuggester {
fun isSuggestionNeeded(): Boolean
fun logStatisticsThatSuggestionIsFound(suggestion: Suggestion)
val id: String
val suggestingActionDisplayName: String
val minSuggestingIntervalDays: Int
val forceCheckForStatistics: Boolean get() = false
}

View File

@@ -25,6 +25,8 @@ class FileStructureSuggester : AbstractFeatureSuggester() {
override val languages = listOf("JAVA", "kotlin", "Python", "JavaScript", "ECMAScript 6")
override val forceCheckForStatistics = true
private var prevActionIsEditorFindAction = false
override fun getSuggestion(action: Action): Suggestion {

View File

@@ -23,6 +23,8 @@ class MuteBreakpointsSuggester : AbstractFeatureSuggester() {
private val countOfRemovedBreakpointsToGetSuggestion = 3
override val forceCheckForStatistics = true
private object State {
var lastBreakpointPosition: XSourcePosition? = null
var lastPauseTimeMillis: Long = 0L

View File

@@ -26,6 +26,8 @@ class QuickEvaluateSuggester : AbstractFeatureSuggester() {
override val languages = listOf(Language.ANY.id)
override val forceCheckForStatistics = true
private var lastCopyFromFile = WeakReference<VirtualFile>(null)
private var lastCopiedText = ""