mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
LAB-29: Record file probability from change reminder as a feature for file prediction
GitOrigin-RevId: 6ecefbf4a296c2e18f59aaeee2a8d126bf8e1be1
This commit is contained in:
committed by
intellij-monorepo-bot
parent
1218c7af0e
commit
9d21e75fdf
+35
@@ -0,0 +1,35 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.jetbrains.changeReminder.predict
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vcs.FilePath
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.vcs.log.data.VcsLogData
|
||||
import com.jetbrains.changeReminder.getGitRootFiles
|
||||
import com.jetbrains.changeReminder.repository.Commit
|
||||
import com.jetbrains.changeReminder.repository.FilesHistoryProvider
|
||||
|
||||
class FileProbabilityRequest(private val project: Project,
|
||||
private val dataManager: VcsLogData,
|
||||
private val changeListFiles: Collection<FilePath>) {
|
||||
|
||||
private fun predict(candidate: FilePath, files: Collection<FilePath>, root: VirtualFile, history: FilesHistoryProvider): Double {
|
||||
val commit = Commit(-1, System.currentTimeMillis(), dataManager.currentUser[root]?.name ?: "", changeListFiles.toSet())
|
||||
return ProbabilityProvider.predict(commit, candidate, history.getFilesHistory(root, files).toSet())
|
||||
}
|
||||
|
||||
fun calculate(candidate: FilePath): Double {
|
||||
val history = dataManager.index.dataGetter?.let { FilesHistoryProvider(project, dataManager, it) } ?: return 0.0
|
||||
|
||||
val roots = getGitRootFiles(project, changeListFiles)
|
||||
return roots.mapNotNull { (root, files) ->
|
||||
if (dataManager.index.isIndexed(root)) {
|
||||
predict(candidate, files, root, history)
|
||||
}
|
||||
else {
|
||||
null
|
||||
}
|
||||
}.max() ?: 0.0
|
||||
}
|
||||
}
|
||||
|
||||
+1
-71
@@ -20,76 +20,6 @@ class PredictionProvider(private val minProb: Double = 0.55) {
|
||||
private const val MAX_HISTORY_COMMIT_SIZE = 15
|
||||
}
|
||||
|
||||
private class FactorCounter {
|
||||
var sum = 0.0
|
||||
private set
|
||||
var max = 0.0
|
||||
private set
|
||||
var min = Double.MAX_VALUE
|
||||
private set
|
||||
|
||||
fun append(newValue: Number): FactorCounter {
|
||||
return append(newValue.toDouble())
|
||||
}
|
||||
|
||||
private fun append(newValue: Double): FactorCounter {
|
||||
sum += newValue
|
||||
max = max(max, newValue)
|
||||
min = min(min, newValue)
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectFileFactors(newCommit: Commit, candidateFile: FilePath, candidateFileHistory: Set<Commit>): DoubleArray {
|
||||
val factors = DoubleArray(Factor.values().size)
|
||||
|
||||
val timeDistanceCounter = FactorCounter()
|
||||
val intersectionCounters = mutableMapOf<FilePath, FactorCounter>()
|
||||
val countFilesCounter = FactorCounter()
|
||||
val filePrefixCounter = FactorCounter()
|
||||
val pathPrefixCounter = FactorCounter()
|
||||
var authorCommitted = 0.0
|
||||
|
||||
candidateFileHistory.forEach { oldCommit ->
|
||||
val filesFromCommit = oldCommit.files.intersect(newCommit.files)
|
||||
|
||||
countFilesCounter.append(filesFromCommit.size)
|
||||
if (filesFromCommit.isNotEmpty()) {
|
||||
timeDistanceCounter.append(newCommit.time - oldCommit.time)
|
||||
}
|
||||
|
||||
if (authorCommitted != 1.0 && oldCommit.author.startsWith(newCommit.author)) {
|
||||
authorCommitted = 1.0
|
||||
}
|
||||
|
||||
filesFromCommit.forEach { file ->
|
||||
intersectionCounters.getOrPut(file) { FactorCounter() }.append(1)
|
||||
}
|
||||
}
|
||||
|
||||
val candidateFilePath = candidateFile.path
|
||||
val candidateFileName = candidateFile.name
|
||||
newCommit.files.forEach {
|
||||
pathPrefixCounter.append(StringUtil.commonPrefixLength(candidateFilePath, it.path))
|
||||
filePrefixCounter.append(StringUtil.commonPrefixLength(candidateFileName, it.name))
|
||||
}
|
||||
|
||||
factors[Factor.MAX_INTERSECTION.ordinal] = intersectionCounters.values.maxBy { it.sum }?.sum ?: 0.0
|
||||
factors[Factor.SUM_INTERSECTION.ordinal] = intersectionCounters.values.sumByDouble { it.sum }
|
||||
factors[Factor.MIN_DISTANCE_TIME.ordinal] = timeDistanceCounter.min
|
||||
factors[Factor.COMMIT_SIZE.ordinal] = newCommit.files.size.toDouble()
|
||||
factors[Factor.MAX_DISTANCE_TIME.ordinal] = timeDistanceCounter.max
|
||||
factors[Factor.AVG_DISTANCE_TIME.ordinal] = timeDistanceCounter.sum / candidateFileHistory.size.toDouble()
|
||||
factors[Factor.MAX_COUNT.ordinal] = countFilesCounter.max
|
||||
factors[Factor.MIN_COUNT.ordinal] = countFilesCounter.min
|
||||
factors[Factor.AUTHOR_COMMITTED_THE_FILE.ordinal] = authorCommitted
|
||||
factors[Factor.MAX_PREFIX_PATH.ordinal] = pathPrefixCounter.max
|
||||
factors[Factor.MAX_PREFIX_FILE_NAME.ordinal] = filePrefixCounter.max
|
||||
|
||||
return factors
|
||||
}
|
||||
|
||||
|
||||
private fun getRelatedFiles(commit: Commit, history: Collection<Commit>): Map<FilePath, Set<Commit>> {
|
||||
val sortedHistory = history.sortedByDescending { it.time }
|
||||
@@ -122,7 +52,7 @@ class PredictionProvider(private val minProb: Double = 0.55) {
|
||||
.asSequence()
|
||||
.map { (candidateFile, candidateFileHistory) ->
|
||||
ProgressManager.checkCanceled()
|
||||
val fileScore = PredictionModel.makePrediction(collectFileFactors(commit, candidateFile, candidateFileHistory))
|
||||
val fileScore = ProbabilityProvider.predict(commit, candidateFile, candidateFileHistory)
|
||||
candidateFile to fileScore
|
||||
}
|
||||
.filter { it.second > minProb }
|
||||
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.jetbrains.changeReminder.predict
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.openapi.vcs.FilePath
|
||||
import com.jetbrains.changeReminder.prediction.model.PredictionModel
|
||||
import com.jetbrains.changeReminder.repository.Commit
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
object ProbabilityProvider {
|
||||
private class FactorCounter {
|
||||
var sum = 0.0
|
||||
private set
|
||||
var max = 0.0
|
||||
private set
|
||||
var min = Double.MAX_VALUE
|
||||
private set
|
||||
|
||||
fun append(newValue: Number): FactorCounter {
|
||||
return append(newValue.toDouble())
|
||||
}
|
||||
|
||||
private fun append(newValue: Double): FactorCounter {
|
||||
sum += newValue
|
||||
max = max(max, newValue)
|
||||
min = min(min, newValue)
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectFileFactors(newCommit: Commit, candidateFile: FilePath, candidateFileHistory: Set<Commit>): DoubleArray {
|
||||
val factors = DoubleArray(Factor.values().size)
|
||||
|
||||
val timeDistanceCounter = FactorCounter()
|
||||
val intersectionCounters = mutableMapOf<FilePath, FactorCounter>()
|
||||
val countFilesCounter = FactorCounter()
|
||||
val filePrefixCounter = FactorCounter()
|
||||
val pathPrefixCounter = FactorCounter()
|
||||
var authorCommitted = 0.0
|
||||
|
||||
candidateFileHistory.forEach { oldCommit ->
|
||||
val filesFromCommit = oldCommit.files.intersect(newCommit.files)
|
||||
|
||||
countFilesCounter.append(filesFromCommit.size)
|
||||
if (filesFromCommit.isNotEmpty()) {
|
||||
timeDistanceCounter.append(newCommit.time - oldCommit.time)
|
||||
}
|
||||
|
||||
if (authorCommitted != 1.0 && oldCommit.author.startsWith(newCommit.author)) {
|
||||
authorCommitted = 1.0
|
||||
}
|
||||
|
||||
filesFromCommit.forEach { file ->
|
||||
intersectionCounters.getOrPut(file) { FactorCounter() }.append(1)
|
||||
}
|
||||
}
|
||||
|
||||
val candidateFilePath = candidateFile.path
|
||||
val candidateFileName = candidateFile.name
|
||||
newCommit.files.forEach {
|
||||
pathPrefixCounter.append(StringUtil.commonPrefixLength(candidateFilePath, it.path))
|
||||
filePrefixCounter.append(StringUtil.commonPrefixLength(candidateFileName, it.name))
|
||||
}
|
||||
|
||||
factors[Factor.MAX_INTERSECTION.ordinal] = intersectionCounters.values.maxBy { it.sum }?.sum ?: 0.0
|
||||
factors[Factor.SUM_INTERSECTION.ordinal] = intersectionCounters.values.sumByDouble { it.sum }
|
||||
factors[Factor.MIN_DISTANCE_TIME.ordinal] = timeDistanceCounter.min
|
||||
factors[Factor.COMMIT_SIZE.ordinal] = newCommit.files.size.toDouble()
|
||||
factors[Factor.MAX_DISTANCE_TIME.ordinal] = timeDistanceCounter.max
|
||||
factors[Factor.AVG_DISTANCE_TIME.ordinal] = timeDistanceCounter.sum / candidateFileHistory.size.toDouble()
|
||||
factors[Factor.MAX_COUNT.ordinal] = countFilesCounter.max
|
||||
factors[Factor.MIN_COUNT.ordinal] = countFilesCounter.min
|
||||
factors[Factor.AUTHOR_COMMITTED_THE_FILE.ordinal] = authorCommitted
|
||||
factors[Factor.MAX_PREFIX_PATH.ordinal] = pathPrefixCounter.max
|
||||
factors[Factor.MAX_PREFIX_FILE_NAME.ordinal] = filePrefixCounter.max
|
||||
|
||||
return factors
|
||||
}
|
||||
|
||||
fun predict(commit: Commit, candidateFile: FilePath, candidateFileHistory: Set<Commit>): Double {
|
||||
return PredictionModel.makePrediction(collectFileFactors(commit, candidateFile, candidateFileHistory))
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
<description><![CDATA[Predicts next file which will be open in IDE to start long running analysis and pre-load caches.]]></description>
|
||||
|
||||
<depends optional="true" config-file="file-prediction-java.xml">com.intellij.java</depends>
|
||||
<depends optional="true" config-file="file-prediction-vcs.xml">com.intellij.modules.vcs</depends>
|
||||
<depends optional="true" config-file="file-prediction-vcs.xml">com.jetbrains.changeReminder</depends>
|
||||
|
||||
<extensionPoints>
|
||||
<extensionPoint qualifiedName="com.intellij.filePrediction.featureProvider" interface="com.intellij.filePrediction.FilePredictionFeatureProvider" dynamic="true"/>
|
||||
@@ -16,7 +16,7 @@
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<registryKey key="filePrediction.calculate.features" defaultValue="true" description="Record opened files features to predict which file will be opened next and pre-load caches."/>
|
||||
|
||||
<statistics.counterUsagesCollector groupId="file.prediction" version="1"/>
|
||||
<statistics.counterUsagesCollector groupId="file.prediction" version="2"/>
|
||||
|
||||
<filePrediction.featureProvider implementation="com.intellij.filePrediction.FilePredictionGeneralFeatures"/>
|
||||
<filePrediction.featureProvider implementation="com.intellij.filePrediction.history.FilePredictionHistoryFeatures"/>
|
||||
|
||||
+17
@@ -5,8 +5,13 @@ import com.intellij.filePrediction.FilePredictionFeature
|
||||
import com.intellij.filePrediction.FilePredictionFeatureProvider
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vcs.ProjectLevelVcsManager
|
||||
import com.intellij.openapi.vcs.actions.VcsContextFactory
|
||||
import com.intellij.openapi.vcs.changes.ChangeListManager
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.vcs.log.impl.VcsProjectLog
|
||||
import com.jetbrains.changeReminder.predict.FileProbabilityRequest
|
||||
import java.util.*
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
class FilePredictionVcsFeatures : FilePredictionFeatureProvider {
|
||||
override fun getName(): String = "vcs"
|
||||
@@ -20,6 +25,18 @@ class FilePredictionVcsFeatures : FilePredictionFeatureProvider {
|
||||
result["prev_in_changelist"] = FilePredictionFeature.binary(changeListManager.isFileAffected(prevFile))
|
||||
}
|
||||
result["in_changelist"] = FilePredictionFeature.binary(changeListManager.isFileAffected(newFile))
|
||||
|
||||
if (prevFile != null) {
|
||||
val dataManager = VcsProjectLog.getInstance(project).dataManager
|
||||
if (dataManager != null) {
|
||||
val contextFactory = VcsContextFactory.SERVICE.getInstance()
|
||||
val newPath = contextFactory.createFilePath(newFile.path, false)
|
||||
|
||||
val recentFile = contextFactory.createFilePath(prevFile.path, false)
|
||||
val request = FileProbabilityRequest(project, dataManager, Collections.singletonList(recentFile))
|
||||
result["related_prob"] = FilePredictionFeature.numerical(request.calculate(newPath))
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user