IJPL-156647: merge: add statistics

GitOrigin-RevId: 8bb96df414312d166b070e9e90c874eef0b8c746
This commit is contained in:
Aleksandr Krasilnikov
2024-06-12 11:32:22 +02:00
committed by intellij-monorepo-bot
parent 12a30d5198
commit b6e7b4b06c
4 changed files with 113 additions and 2 deletions

View File

@@ -0,0 +1,36 @@
// 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.diff.merge
import org.jetbrains.annotations.ApiStatus
@ApiStatus.Internal
internal class MergeStatisticsAggregator(
val changes: Int,
val autoResolvable: Int,
val conflicts: Int,
) {
var unresolved: Int = -1
val initialTimestamp: Long = System.currentTimeMillis()
private val resolvedByAiChanges = mutableSetOf<Int>()
private val rolledBackAfterAI = mutableSetOf<Int>()
private val undoneAfterAi = mutableSetOf<Int>()
fun resolvedByAi(): Int = resolvedByAiChanges.size
fun rolledBackAfterAI(): Int = rolledBackAfterAI.size
fun undoneAfterAI(): Int = undoneAfterAi.size
fun wasResolvedByAi(index: Int) {
resolvedByAiChanges.add(index)
}
fun wasRolledBackAfterAI(index: Int) {
resolvedByAiChanges.remove(index)
rolledBackAfterAI.add(index)
}
fun wasUndoneAfterAI(index: Int) {
resolvedByAiChanges.remove(index)
undoneAfterAi.add(index)
}
}

View File

@@ -17,6 +17,7 @@ import com.intellij.diff.contents.DocumentContent;
import com.intellij.diff.fragments.MergeLineFragment;
import com.intellij.diff.requests.ContentDiffRequest;
import com.intellij.diff.requests.SimpleDiffRequest;
import com.intellij.diff.statistics.MergeStatisticsCollector;
import com.intellij.diff.tools.holders.EditorHolderFactory;
import com.intellij.diff.tools.holders.TextEditorHolder;
import com.intellij.diff.tools.simple.ThreesideTextDiffViewerEx;
@@ -118,6 +119,7 @@ public class MergeThreesideViewer extends ThreesideTextDiffViewerEx {
private final Action myLeftResolveAction;
private final Action myRightResolveAction;
protected final Action myAcceptResolveAction;
private MergeStatisticsAggregator myAggregator;
@NotNull protected final MergeContext myMergeContext;
@NotNull protected final TextMergeRequest myMergeRequest;
@@ -298,10 +300,17 @@ public class MergeThreesideViewer extends ThreesideTextDiffViewerEx {
}
protected void doFinishMerge(@NotNull final MergeResult result) {
if (result == MergeResult.RESOLVED) {
logMergeFinished(MergeResultSource.DIALOG_BUTTON);
}
destroyChangedBlocks();
myMergeContext.finishMerge(result);
}
public enum MergeResultSource {
DIALOG_BUTTON,
NOTIFICATION
}
//
// Diff
//
@@ -516,6 +525,15 @@ public class MergeThreesideViewer extends ThreesideTextDiffViewerEx {
myCurrentIgnorePolicy = ignorePolicy;
myResolveImportConflicts = getTextSettings().isAutoResolveImportConflicts();
// build initial statistics
int autoResolvableChanges = ContainerUtil.count(getAllChanges(), c -> canResolveChangeAutomatically(c, ThreeSide.BASE));
myAggregator = new MergeStatisticsAggregator(
getAllChanges().size(),
autoResolvableChanges,
getConflictsCount()
);
if (myResolveImportConflicts) {
List<TextMergeChange> importChanges = ContainerUtil.filter(getChanges(), change -> change.isImportChange());
if (importChanges.size() != fragmentsWithMetadata.getFragments().size()) {
@@ -729,6 +747,7 @@ public class MergeThreesideViewer extends ThreesideTextDiffViewerEx {
@NlsSafe String message = XmlStringUtil.wrapInHtmlTag(DiffBundle.message("merge.all.changes.processed.message.text"), "a");
DiffBalloons.showSuccessPopup(title, message, point, this, () -> {
if (isDisposed() || myLoadingPanel.isLoading()) return;
logMergeFinished(MergeResultSource.NOTIFICATION);
destroyChangedBlocks();
myMergeContext.finishMerge(MergeResult.RESOLVED);
});
@@ -825,6 +844,7 @@ public class MergeThreesideViewer extends ThreesideTextDiffViewerEx {
@ApiStatus.Internal
@RequiresEdt
public void markChangeResolvedWithAI(@NotNull TextMergeChange change) {
myAggregator.wasResolvedByAi(change.getIndex());
change.markChangeResolvedWithAI();
markChangeResolved(change);
}
@@ -889,7 +909,6 @@ public class MergeThreesideViewer extends ThreesideTextDiffViewerEx {
@RequiresWriteLock
void resetResolvedChange(TextMergeChange change) {
if (!change.isResolved()) return;
MergeLineFragment changeFragment = change.getFragment();
int startLine = changeFragment.getStartLine(ThreeSide.BASE);
int endLine = changeFragment.getEndLine(ThreeSide.BASE);
@@ -900,7 +919,9 @@ public class MergeThreesideViewer extends ThreesideTextDiffViewerEx {
myModel.replaceChange(change.getIndex(), baseContent);
change.resetState();
if (change.isResolvedWithAI()) {
myAggregator.wasRolledBackAfterAI(change.getIndex());
}
onChangeResolved(change);
myModel.invalidateHighlighters(change.getIndex());
}
@@ -979,6 +1000,9 @@ public class MergeThreesideViewer extends ThreesideTextDiffViewerEx {
TextMergeChange change = myAllMergeChanges.get(state.myIndex);
boolean wasResolved = change.isResolved();
if (change.isResolvedWithAI()) {
myAggregator.wasUndoneAfterAI(change.getIndex());
}
change.restoreState(state);
if (wasResolved != change.isResolved()) onChangeResolved(change);
}
@@ -1278,6 +1302,11 @@ public class MergeThreesideViewer extends ThreesideTextDiffViewerEx {
protected abstract boolean isEnabled(@NotNull TextMergeChange change);
}
private void logMergeFinished(MergeResultSource source) {
myAggregator.setUnresolved(getChanges().size());
MergeStatisticsCollector.INSTANCE.logMergeFinished(myProject, source, myAggregator);
}
private class IgnoreSelectedChangesSideAction extends ApplySelectedChangesActionBase {
@NotNull private final Side mySide;

View File

@@ -0,0 +1,45 @@
// 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.diff.statistics
import com.intellij.diff.merge.MergeStatisticsAggregator
import com.intellij.diff.merge.MergeThreesideViewer.MergeResultSource
import com.intellij.internal.statistic.eventLog.EventLogGroup
import com.intellij.internal.statistic.eventLog.events.EnumEventField
import com.intellij.internal.statistic.eventLog.events.EventFields
import com.intellij.internal.statistic.eventLog.events.IntEventField
import com.intellij.internal.statistic.eventLog.events.VarargEventId
import com.intellij.internal.statistic.service.fus.collectors.CounterUsagesCollector
import com.intellij.openapi.project.Project
import org.jetbrains.annotations.ApiStatus
@ApiStatus.Internal
internal object MergeStatisticsCollector : CounterUsagesCollector() {
private val GROUP: EventLogGroup = EventLogGroup("vcs.merge", 1)
private val SOURCE: EnumEventField<MergeResultSource> = EventFields.Enum("source", MergeResultSource::class.java)
private val CHANGES: IntEventField = EventFields.Int("changes")
private val AUTO_RESOLVABLE = EventFields.Int("autoResolvable")
private val CONFLICTS = EventFields.Int("conflicts")
private val UNRESOLVED = EventFields.Int("unresolved")
private val AI_RESOLVED = EventFields.Int("aiResolved")
private val AI_ROLLED_BACK = EventFields.Int("rolledBackAfterAi")
private val AI_UNDONE = EventFields.Int("undoneAfterAi")
private val FILE_MERGED_EVENT: VarargEventId = GROUP.registerVarargEvent("file.merged", CHANGES, EventFields.DurationMs, AUTO_RESOLVABLE, CONFLICTS, UNRESOLVED, AI_RESOLVED, AI_ROLLED_BACK, AI_UNDONE)
override fun getGroup(): EventLogGroup = GROUP
fun logMergeFinished(project: Project?, source: MergeResultSource, aggregator: MergeStatisticsAggregator) {
FILE_MERGED_EVENT.log(project) {
add(SOURCE.with(source))
add(CHANGES.with(aggregator.changes))
add(EventFields.DurationMs.with(System.currentTimeMillis() - aggregator.initialTimestamp))
add(AUTO_RESOLVABLE.with(aggregator.autoResolvable))
add(CONFLICTS.with(aggregator.conflicts))
add(UNRESOLVED.with(aggregator.unresolved))
add(AI_RESOLVED.with(aggregator.resolvedByAi()))
add(AI_ROLLED_BACK.with(aggregator.rolledBackAfterAI()))
add(AI_UNDONE.with(aggregator.undoneAfterAI()))
}
}
}

View File

@@ -955,6 +955,7 @@
implementation="com.intellij.ide.plugins.marketplace.statistics.validators.MarketplaceVendorsListValidator"/>
<statistics.counterUsagesCollector implementationClass="com.intellij.codeInsight.template.impl.LiveTemplateRunLogger"/>
<statistics.counterUsagesCollector implementationClass="com.intellij.openapi.diff.impl.DiffUsageTriggerCollector"/>
<statistics.counterUsagesCollector implementationClass="com.intellij.diff.statistics.MergeStatisticsCollector"/>
<statistics.counterUsagesCollector implementationClass="com.intellij.openapi.updateSettings.impl.IdeUpdateUsageTriggerCollector"/>
<statistics.counterUsagesCollector implementationClass="com.intellij.openapi.application.ImportOldConfigsUsagesCollector"/>
<statistics.counterUsagesCollector implementationClass="com.intellij.openapi.application.OldDirectoryCleaner$Stats"/>