mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
LAB-29: record id to group candidates and opened file
GitOrigin-RevId: df6323afd3370c0a804202e7fd35986154e83960
This commit is contained in:
committed by
intellij-monorepo-bot
parent
d7fef2f225
commit
fd15f37929
@@ -23,6 +23,7 @@
|
||||
<filePrediction.featureProvider implementation="com.intellij.filePrediction.FilePredictionFileTypeFeatures"/>
|
||||
<filePrediction.featureProvider implementation="com.intellij.filePrediction.history.FilePredictionHistoryFeatures"/>
|
||||
|
||||
<applicationService serviceImplementation="com.intellij.filePrediction.FilePredictionHandler"/>
|
||||
<projectService serviceImplementation="com.intellij.filePrediction.history.FilePredictionHistory"/>
|
||||
</extensions>
|
||||
|
||||
|
||||
@@ -9,12 +9,16 @@ internal object FileNavigationLogger {
|
||||
|
||||
fun logEvent(project: Project,
|
||||
event: String,
|
||||
sessionId: Int,
|
||||
features: FileFeaturesComputationResult,
|
||||
filePath: String,
|
||||
prevFilePath: String?,
|
||||
refsComputation: Long,
|
||||
probability: Double? = null) {
|
||||
val data = FeatureUsageData().
|
||||
addData("session_id", sessionId).
|
||||
addAnonymizedPath(filePath).
|
||||
addAnonymizedValue("prev_file_path", prevFilePath).
|
||||
addData("refs_computation", refsComputation).
|
||||
addData("features_computation", features.duration)
|
||||
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ internal class FilePredictionEditorManagerListener : FileEditorManagerListener {
|
||||
override fun selectionChanged(event: FileEditorManagerEvent) {
|
||||
val newFile = event.newFile ?: return
|
||||
if (ApplicationManager.getApplication().isEAP && Registry.get("filePrediction.calculate.features").asBoolean()) {
|
||||
FilePredictionHandler.onFileOpened(event.manager.project, newFile, event.oldFile)
|
||||
FilePredictionHandler.getInstance()?.onFileOpened(event.manager.project, newFile, event.oldFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,18 +2,25 @@ package com.intellij.filePrediction
|
||||
|
||||
import com.intellij.filePrediction.history.FilePredictionHistory
|
||||
import com.intellij.filePrediction.predictor.FileUsagePredictor
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.progress.util.BackgroundTaskUtil
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.impl.ProjectManagerImpl
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.util.concurrency.NonUrgentExecutor
|
||||
|
||||
object FilePredictionHandler {
|
||||
private const val CALCULATE_OPEN_FILE_PROBABILITY: Double = 0.5
|
||||
class FilePredictionHandler {
|
||||
companion object {
|
||||
private const val CALCULATE_OPEN_FILE_PROBABILITY: Double = 0.5
|
||||
private const val CALCULATE_CANDIDATE_PROBABILITY: Double = 0.1
|
||||
|
||||
fun getInstance(): FilePredictionHandler? = ServiceManager.getService(FilePredictionHandler::class.java)
|
||||
}
|
||||
|
||||
private const val CALCULATE_CANDIDATE_PROBABILITY: Double = 0.1
|
||||
private val predictor: FileUsagePredictor = FileUsagePredictor(30, 5, 10)
|
||||
|
||||
private var session: FilePredictionSessionHolder = FilePredictionSessionHolder()
|
||||
|
||||
fun onFileOpened(project: Project, newFile: VirtualFile, prevFile: VirtualFile?) {
|
||||
if (ProjectManagerImpl.isLight(project)) {
|
||||
return
|
||||
@@ -21,21 +28,26 @@ object FilePredictionHandler {
|
||||
|
||||
NonUrgentExecutor.getInstance().execute {
|
||||
BackgroundTaskUtil.runUnderDisposeAwareIndicator(project, Runnable {
|
||||
if (Math.random() < CALCULATE_OPEN_FILE_PROBABILITY) {
|
||||
logOpenedFile(project, newFile, prevFile)
|
||||
val previousSession = session.getSession()
|
||||
if (previousSession != null && previousSession.shouldLog(CALCULATE_OPEN_FILE_PROBABILITY)) {
|
||||
logOpenedFile(project, previousSession.id, newFile, prevFile)
|
||||
}
|
||||
if (Math.random() < CALCULATE_CANDIDATE_PROBABILITY) {
|
||||
predictor.predictNextFile(project, newFile)
|
||||
val newSession = session.newSession()
|
||||
if (newSession != null && newSession.shouldLog(CALCULATE_CANDIDATE_PROBABILITY)) {
|
||||
predictor.predictNextFile(project, newSession.id, newFile)
|
||||
}
|
||||
FilePredictionHistory.getInstance(project).onFileOpened(newFile.url)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun logOpenedFile(project: Project, newFile: VirtualFile, prevFile: VirtualFile?) {
|
||||
private fun logOpenedFile(project: Project,
|
||||
sessionId: Int,
|
||||
newFile: VirtualFile,
|
||||
prevFile: VirtualFile?) {
|
||||
val result = FilePredictionFeaturesHelper.calculateExternalReferences(project, prevFile)
|
||||
|
||||
val features = FilePredictionFeaturesHelper.calculateFileFeatures(project, newFile, result.value, prevFile)
|
||||
FileNavigationLogger.logEvent(project, "file.opened", features, newFile.path, result.duration)
|
||||
FileNavigationLogger.logEvent(project, "file.opened", sessionId, features, newFile.path, prevFile?.path, result.duration)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.intellij.filePrediction
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
private val counter = AtomicInteger(0)
|
||||
|
||||
internal class FilePredictionSessionHolder {
|
||||
private var session: FilePredictionSession? = null
|
||||
|
||||
@Synchronized
|
||||
fun getSession(): FilePredictionSession? = session
|
||||
|
||||
@Synchronized
|
||||
fun newSession(): FilePredictionSession? {
|
||||
session = FilePredictionSession()
|
||||
return session
|
||||
}
|
||||
}
|
||||
|
||||
internal class FilePredictionSession {
|
||||
val id = counter.incrementAndGet()
|
||||
private val loggingProbability = Math.random()
|
||||
|
||||
fun shouldLog(threshold: Double): Boolean {
|
||||
return loggingProbability < threshold
|
||||
}
|
||||
}
|
||||
+22
-11
@@ -14,27 +14,28 @@ import com.intellij.openapi.vfs.VirtualFile
|
||||
internal class FileUsagePredictor(private val candidatesLimit: Int,
|
||||
private val logTopLimit: Int,
|
||||
private val logTotalLimit: Int) {
|
||||
fun predictNextFile(project: Project, file: VirtualFile) {
|
||||
fun predictNextFile(project: Project, sessionId: Int, file: VirtualFile) {
|
||||
val result = FilePredictionFeaturesHelper.calculateExternalReferences(project, file)
|
||||
|
||||
val model = getFilePredictionModel()
|
||||
if (model != null) {
|
||||
predictAndLogCandidates(project, model, file, result)
|
||||
predictAndLogCandidates(project, sessionId, model, file, result)
|
||||
}
|
||||
else {
|
||||
logCandidates(project, file, result)
|
||||
logCandidates(project, sessionId, file, result)
|
||||
}
|
||||
}
|
||||
|
||||
private fun logCandidates(project: Project, file: VirtualFile, refs: FileReferencesComputationResult) {
|
||||
private fun logCandidates(project: Project, sessionId: Int, file: VirtualFile, refs: FileReferencesComputationResult) {
|
||||
val candidateFiles = provideCandidates(project, file, refs.value.references, logTotalLimit)
|
||||
for (candidate in candidateFiles) {
|
||||
val features = FilePredictionFeaturesHelper.calculateFileFeatures(project, candidate, refs.value, file)
|
||||
FileNavigationLogger.logEvent(project, "candidate.calculated", features, candidate.path, refs.duration)
|
||||
FileNavigationLogger.logEvent(project, "candidate.calculated", sessionId, features, candidate.path, file.path, refs.duration)
|
||||
}
|
||||
}
|
||||
|
||||
private fun predictAndLogCandidates(project: Project,
|
||||
sessionId: Int,
|
||||
model: FilePredictionModel,
|
||||
file: VirtualFile,
|
||||
refs: FileReferencesComputationResult) {
|
||||
@@ -47,25 +48,35 @@ internal class FileUsagePredictor(private val candidatesLimit: Int,
|
||||
}
|
||||
|
||||
candidates.sortByDescending { it.probability }
|
||||
logCandidatesWithProbability(project, candidates, refs.duration)
|
||||
logCandidatesWithProbability(project, sessionId, file.path, candidates, refs.duration)
|
||||
}
|
||||
|
||||
private fun logCandidatesWithProbability(project: Project, candidates: MutableList<FilePredictionCandidate>, refsComputation: Long) {
|
||||
private fun logCandidatesWithProbability(project: Project,
|
||||
sessionId: Int,
|
||||
prevPath: String?,
|
||||
candidates: MutableList<FilePredictionCandidate>,
|
||||
refsComputation: Long) {
|
||||
val head = candidates.take(logTopLimit)
|
||||
logCalculatedCandidates(project, head, refsComputation)
|
||||
logCalculatedCandidates(project, sessionId, prevPath, head, refsComputation)
|
||||
|
||||
if (candidates.size > logTopLimit) {
|
||||
val tail = candidates.subList(logTopLimit, candidates.size)
|
||||
val randomToLog = tail.shuffled().take(logTotalLimit - logTopLimit)
|
||||
logCalculatedCandidates(project, randomToLog, refsComputation)
|
||||
logCalculatedCandidates(project, sessionId, prevPath, randomToLog, refsComputation)
|
||||
}
|
||||
}
|
||||
|
||||
private fun logCalculatedCandidates(project: Project, candidates: Collection<FilePredictionCandidate>, refsComputation: Long) {
|
||||
private fun logCalculatedCandidates(project: Project,
|
||||
sessionId: Int,
|
||||
prevPath:String?,
|
||||
candidates: Collection<FilePredictionCandidate>,
|
||||
refsComputation: Long) {
|
||||
for (candidate in candidates) {
|
||||
val probability = candidate.probability
|
||||
val features = candidate.features
|
||||
FileNavigationLogger.logEvent(project, "candidate.calculated", features, candidate.path, refsComputation, probability)
|
||||
FileNavigationLogger.logEvent(
|
||||
project, "candidate.calculated", sessionId, features, candidate.path, prevPath, refsComputation, probability
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -29,7 +29,7 @@ class FileUsagePredictorLoggerTest : CodeInsightFixtureTestCase<ModuleFixtureBui
|
||||
|
||||
val composite = TestStatisticsEventValidatorBuilder()
|
||||
.hasEventId("candidate.calculated")
|
||||
.contains("probability")
|
||||
.contains("probability", "session_id")
|
||||
.withCustom(validator).build()
|
||||
|
||||
doTest(builder, predictor, expectedEvents, composite) { setPredefinedProbabilityModel(it, probabilities) }
|
||||
@@ -39,7 +39,7 @@ class FileUsagePredictorLoggerTest : CodeInsightFixtureTestCase<ModuleFixtureBui
|
||||
val predictor = FileUsagePredictor(5, 1, 3)
|
||||
val validator = TestStatisticsEventValidatorBuilder()
|
||||
.hasEventId("candidate.calculated")
|
||||
.contains("probability").build()
|
||||
.contains("probability", "session_id").build()
|
||||
|
||||
doTest(builder, predictor, expectedEvents, validator) { setConstantFilePredictionModel(0.1, it) }
|
||||
}
|
||||
@@ -48,6 +48,7 @@ class FileUsagePredictorLoggerTest : CodeInsightFixtureTestCase<ModuleFixtureBui
|
||||
val predictor = FileUsagePredictor(5, 1, 3)
|
||||
val validator = TestStatisticsEventValidatorBuilder()
|
||||
.hasEventId("candidate.calculated")
|
||||
.contains("session_id")
|
||||
.notContains("probability").build()
|
||||
|
||||
doTest(builder, predictor, expectedEvents, validator) { disableFilePredictionModel() }
|
||||
@@ -66,7 +67,7 @@ class FileUsagePredictorLoggerTest : CodeInsightFixtureTestCase<ModuleFixtureBui
|
||||
|
||||
modelConfigurator.invoke(testRootDisposable)
|
||||
val events = collectLogEvents {
|
||||
predictor.predictNextFile(myFixture.project, file!!)
|
||||
predictor.predictNextFile(myFixture.project, 1, file!!)
|
||||
}
|
||||
val candidateEvents = events.filter { it.event.id == "candidate.calculated" }
|
||||
assertEquals(expectedEvents, candidateEvents.size)
|
||||
|
||||
Reference in New Issue
Block a user