mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 04:51:24 +07:00
extract delayed completion statistics logic out of CompletionLookupArranger
This commit is contained in:
@@ -91,7 +91,7 @@ public abstract class CompletionSortingTestCase extends LightFixtureCompletionTe
|
||||
protected static void imitateItemSelection(final LookupImpl lookup, final int index) {
|
||||
final LookupElement item = lookup.getItems().get(index);
|
||||
lookup.setCurrentItem(item);
|
||||
CompletionLookupArranger.collectStatisticChanges(item, lookup);
|
||||
CompletionLookupArranger.applyLastCompletionStatisticsUpdate();
|
||||
StatisticsUpdate.collectStatisticChanges(item);
|
||||
StatisticsUpdate.applyLastCompletionStatisticsUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ class SmartTypeCompletionOrderingTest extends CompletionSortingTestCase {
|
||||
assertPreferredItems(2, "Component", "String", "FooBean3", "JComponent", "Container")
|
||||
lookup.currentItem = lookup.items[4] //Container
|
||||
myFixture.type('\n\b')
|
||||
CompletionLookupArranger.applyLastCompletionStatisticsUpdate()
|
||||
StatisticsUpdate.applyLastCompletionStatisticsUpdate()
|
||||
FileDocumentManager.instance.saveAllDocuments()
|
||||
invokeCompletion("/JComponentAddNew.java")
|
||||
assertPreferredItems(2, "Component", "String", "FooBean3", "JComponent", "Container")
|
||||
|
||||
@@ -130,7 +130,7 @@ public class CodeCompletionHandlerBase {
|
||||
markCaretAsProcessed(caret);
|
||||
|
||||
if (invokedExplicitly) {
|
||||
CompletionLookupArranger.applyLastCompletionStatisticsUpdate();
|
||||
StatisticsUpdate.applyLastCompletionStatisticsUpdate();
|
||||
}
|
||||
|
||||
checkNoWriteAccess();
|
||||
@@ -564,10 +564,9 @@ public class CodeCompletionHandlerBase {
|
||||
|
||||
CompletionAssertions.WatchingInsertionContext context = null;
|
||||
try {
|
||||
Lookup lookup = indicator.getLookup();
|
||||
CompletionLookupArranger.StatisticsUpdate update = CompletionLookupArranger.collectStatisticChanges(item, lookup);
|
||||
StatisticsUpdate update = StatisticsUpdate.collectStatisticChanges(item);
|
||||
context = insertItemHonorBlockSelection(indicator, item, completionChar, items, update);
|
||||
CompletionLookupArranger.trackStatistics(context, update);
|
||||
update.trackStatistics(context);
|
||||
}
|
||||
finally {
|
||||
afterItemInsertion(indicator, context == null ? null : context.getLaterRunnable());
|
||||
@@ -579,7 +578,7 @@ public class CodeCompletionHandlerBase {
|
||||
final LookupElement item,
|
||||
final char completionChar,
|
||||
final List<LookupElement> items,
|
||||
final CompletionLookupArranger.StatisticsUpdate update) {
|
||||
final StatisticsUpdate update) {
|
||||
final Editor editor = indicator.getEditor();
|
||||
|
||||
final int caretOffset = indicator.getCaret().getOffset();
|
||||
@@ -657,7 +656,7 @@ public class CodeCompletionHandlerBase {
|
||||
final LookupElement item,
|
||||
final char completionChar,
|
||||
List<LookupElement> items,
|
||||
final CompletionLookupArranger.StatisticsUpdate update,
|
||||
final StatisticsUpdate update,
|
||||
final Editor editor,
|
||||
final PsiFile psiFile,
|
||||
final int caretOffset,
|
||||
@@ -694,7 +693,7 @@ public class CodeCompletionHandlerBase {
|
||||
}
|
||||
EditorModificationUtil.scrollToCaret(editor);
|
||||
});
|
||||
update.addSparedChars(indicator, item, context, completionChar);
|
||||
update.addSparedChars(indicator, item, context);
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,22 +22,16 @@ import com.intellij.codeInsight.lookup.*;
|
||||
import com.intellij.codeInsight.lookup.impl.EmptyLookupItem;
|
||||
import com.intellij.codeInsight.lookup.impl.LookupImpl;
|
||||
import com.intellij.codeInsight.template.impl.LiveTemplateLookupElement;
|
||||
import com.intellij.featureStatistics.FeatureUsageTracker;
|
||||
import com.intellij.featureStatistics.FeatureUsageTrackerImpl;
|
||||
import com.intellij.ide.ui.UISettings;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.RangeMarker;
|
||||
import com.intellij.openapi.editor.event.DocumentAdapter;
|
||||
import com.intellij.openapi.editor.event.DocumentEvent;
|
||||
import com.intellij.openapi.util.*;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.patterns.StandardPatterns;
|
||||
import com.intellij.psi.statistics.StatisticsInfo;
|
||||
import com.intellij.util.Alarm;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
@@ -52,8 +46,6 @@ import java.util.*;
|
||||
|
||||
public class CompletionLookupArranger extends LookupArranger {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.CompletionLookupArranger");
|
||||
@Nullable private static StatisticsUpdate ourPendingUpdate;
|
||||
private static final Alarm ourStatsAlarm = new Alarm(ApplicationManager.getApplication());
|
||||
private static final Key<String> GLOBAL_PRESENTATION_INVARIANT = Key.create("PRESENTATION_INVARIANT");
|
||||
private final Key<String> PRESENTATION_INVARIANT = Key.create("PRESENTATION_INVARIANT");
|
||||
private final Comparator<LookupElement> BY_PRESENTATION_COMPARATOR = (o1, o2) -> {
|
||||
@@ -67,14 +59,6 @@ public class CompletionLookupArranger extends LookupArranger {
|
||||
public static final Key<Integer> PREFIX_CHANGES = Key.create("PREFIX_CHANGES");
|
||||
private static final UISettings ourUISettings = UISettings.getInstance();
|
||||
private final List<LookupElement> myFrozenItems = new ArrayList<>();
|
||||
static {
|
||||
Disposer.register(ApplicationManager.getApplication(), new Disposable() {
|
||||
@Override
|
||||
public void dispose() {
|
||||
cancelLastCompletionStatisticsUpdate();
|
||||
}
|
||||
});
|
||||
}
|
||||
private final int myLimit = Registry.intValue("ide.completion.variant.limit");
|
||||
private boolean myOverflow;
|
||||
|
||||
@@ -456,86 +440,6 @@ public class CompletionLookupArranger extends LookupArranger {
|
||||
return element instanceof LiveTemplateLookupElement && ((LiveTemplateLookupElement)element).sudden;
|
||||
}
|
||||
|
||||
public static StatisticsUpdate collectStatisticChanges(LookupElement item, final Lookup lookup) {
|
||||
applyLastCompletionStatisticsUpdate();
|
||||
|
||||
final StatisticsInfo base = StatisticsWeigher.getBaseStatisticsInfo(item, null);
|
||||
if (base == StatisticsInfo.EMPTY) {
|
||||
return new StatisticsUpdate(StatisticsInfo.EMPTY);
|
||||
}
|
||||
|
||||
StatisticsUpdate update = new StatisticsUpdate(base);
|
||||
ourPendingUpdate = update;
|
||||
Disposer.register(update, new Disposable() {
|
||||
@Override
|
||||
public void dispose() {
|
||||
//noinspection AssignmentToStaticFieldFromInstanceMethod
|
||||
ourPendingUpdate = null;
|
||||
}
|
||||
});
|
||||
|
||||
return update;
|
||||
}
|
||||
|
||||
public static void trackStatistics(InsertionContext context, final StatisticsUpdate update) {
|
||||
if (ourPendingUpdate != update) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!context.getOffsetMap().containsOffset(CompletionInitializationContext.START_OFFSET)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Document document = context.getDocument();
|
||||
int startOffset = context.getStartOffset();
|
||||
int tailOffset = context.getEditor().getCaretModel().getOffset();
|
||||
if (startOffset < 0 || tailOffset <= startOffset) {
|
||||
return;
|
||||
}
|
||||
|
||||
final RangeMarker marker = document.createRangeMarker(startOffset, tailOffset);
|
||||
final DocumentAdapter listener = new DocumentAdapter() {
|
||||
@Override
|
||||
public void beforeDocumentChange(DocumentEvent e) {
|
||||
if (!marker.isValid() || e.getOffset() > marker.getStartOffset() && e.getOffset() < marker.getEndOffset()) {
|
||||
cancelLastCompletionStatisticsUpdate();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ourStatsAlarm.addRequest(() -> {
|
||||
if (ourPendingUpdate == update) {
|
||||
applyLastCompletionStatisticsUpdate();
|
||||
}
|
||||
}, 20 * 1000);
|
||||
|
||||
document.addDocumentListener(listener);
|
||||
Disposer.register(update, new Disposable() {
|
||||
@Override
|
||||
public void dispose() {
|
||||
document.removeDocumentListener(listener);
|
||||
marker.dispose();
|
||||
ourStatsAlarm.cancelAllRequests();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void cancelLastCompletionStatisticsUpdate() {
|
||||
if (ourPendingUpdate != null) {
|
||||
Disposer.dispose(ourPendingUpdate);
|
||||
assert ourPendingUpdate == null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void applyLastCompletionStatisticsUpdate() {
|
||||
StatisticsUpdate update = ourPendingUpdate;
|
||||
if (update != null) {
|
||||
update.performUpdate();
|
||||
Disposer.dispose(update);
|
||||
assert ourPendingUpdate == null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldSkip(CompletionPreselectSkipper[] skippers, LookupElement element) {
|
||||
for (final CompletionPreselectSkipper skipper : skippers) {
|
||||
if (skipper.skipElement(element, myLocation)) {
|
||||
@@ -555,43 +459,6 @@ public class CompletionLookupArranger extends LookupArranger {
|
||||
super.prefixChanged(lookup);
|
||||
}
|
||||
|
||||
static class StatisticsUpdate implements Disposable {
|
||||
private final StatisticsInfo myInfo;
|
||||
private int mySpared;
|
||||
|
||||
public StatisticsUpdate(StatisticsInfo info) {
|
||||
myInfo = info;
|
||||
}
|
||||
|
||||
void performUpdate() {
|
||||
myInfo.incUseCount();
|
||||
((FeatureUsageTrackerImpl)FeatureUsageTracker.getInstance()).getCompletionStatistics().registerInvocation(mySpared);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
public void addSparedChars(CompletionProgressIndicator indicator, LookupElement item, InsertionContext context, char completionChar) {
|
||||
String textInserted;
|
||||
if (context.getOffsetMap().containsOffset(CompletionInitializationContext.START_OFFSET) &&
|
||||
context.getOffsetMap().containsOffset(InsertionContext.TAIL_OFFSET) &&
|
||||
context.getTailOffset() >= context.getStartOffset()) {
|
||||
textInserted = context.getDocument().getImmutableCharSequence().subSequence(context.getStartOffset(), context.getTailOffset()).toString();
|
||||
} else {
|
||||
textInserted = item.getLookupString();
|
||||
}
|
||||
String withoutSpaces = StringUtil.replace(textInserted, new String[]{" ", "\t", "\n"}, new String[]{"", "", ""});
|
||||
int spared = withoutSpaces.length() - indicator.getLookup().itemPattern(item).length();
|
||||
if (!LookupEvent.isSpecialCompletionChar(completionChar) && withoutSpaces.contains(String.valueOf(completionChar))) {
|
||||
spared--;
|
||||
}
|
||||
if (spared > 0) {
|
||||
mySpared += spared;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class EmptyClassifier extends Classifier<LookupElement> {
|
||||
|
||||
private EmptyClassifier() {
|
||||
|
||||
@@ -487,7 +487,7 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement
|
||||
else {
|
||||
CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion);
|
||||
}
|
||||
CompletionLookupArranger.cancelLastCompletionStatisticsUpdate();
|
||||
StatisticsUpdate.cancelLastCompletionStatisticsUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInsight.completion
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupEvent
|
||||
import com.intellij.featureStatistics.FeatureUsageTracker
|
||||
import com.intellij.featureStatistics.FeatureUsageTrackerImpl
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.editor.event.DocumentAdapter
|
||||
import com.intellij.openapi.editor.event.DocumentEvent
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.psi.statistics.StatisticsInfo
|
||||
import com.intellij.util.Alarm
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
class StatisticsUpdate
|
||||
private constructor(private val myInfo: StatisticsInfo) : Disposable {
|
||||
private var mySpared: Int = 0
|
||||
|
||||
override fun dispose() {}
|
||||
|
||||
fun addSparedChars(indicator: CompletionProgressIndicator, item: LookupElement, context: InsertionContext) {
|
||||
val textInserted: String
|
||||
if (context.offsetMap.containsOffset(CompletionInitializationContext.START_OFFSET) &&
|
||||
context.offsetMap.containsOffset(InsertionContext.TAIL_OFFSET) &&
|
||||
context.tailOffset >= context.startOffset) {
|
||||
textInserted = context.document.immutableCharSequence.subSequence(context.startOffset, context.tailOffset).toString()
|
||||
}
|
||||
else {
|
||||
textInserted = item.lookupString
|
||||
}
|
||||
val withoutSpaces = StringUtil.replace(textInserted, arrayOf(" ", "\t", "\n"), arrayOf("", "", ""))
|
||||
var spared = withoutSpaces.length - indicator.lookup.itemPattern(item).length
|
||||
val completionChar = context.completionChar
|
||||
if (!LookupEvent.isSpecialCompletionChar(completionChar) && withoutSpaces.contains(completionChar.toString())) {
|
||||
spared--
|
||||
}
|
||||
if (spared > 0) {
|
||||
mySpared += spared
|
||||
}
|
||||
}
|
||||
|
||||
fun trackStatistics(context: InsertionContext) {
|
||||
if (ourPendingUpdate !== this) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!context.offsetMap.containsOffset(CompletionInitializationContext.START_OFFSET)) {
|
||||
return
|
||||
}
|
||||
|
||||
val document = context.document
|
||||
val startOffset = context.startOffset
|
||||
val tailOffset = context.editor.caretModel.offset
|
||||
if (startOffset < 0 || tailOffset <= startOffset) {
|
||||
return
|
||||
}
|
||||
|
||||
val marker = document.createRangeMarker(startOffset, tailOffset)
|
||||
val listener = object : DocumentAdapter() {
|
||||
override fun beforeDocumentChange(e: DocumentEvent) {
|
||||
if (!marker.isValid || e.offset > marker.startOffset && e.offset < marker.endOffset) {
|
||||
cancelLastCompletionStatisticsUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ourStatsAlarm.addRequest({
|
||||
if (ourPendingUpdate === this) {
|
||||
applyLastCompletionStatisticsUpdate()
|
||||
}
|
||||
}, 20 * 1000)
|
||||
|
||||
document.addDocumentListener(listener)
|
||||
Disposer.register(this, Disposable {
|
||||
document.removeDocumentListener(listener)
|
||||
marker.dispose()
|
||||
ourStatsAlarm.cancelAllRequests()
|
||||
})
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val ourStatsAlarm = Alarm(ApplicationManager.getApplication())
|
||||
private var ourPendingUpdate: StatisticsUpdate? = null
|
||||
|
||||
init {
|
||||
Disposer.register(ApplicationManager.getApplication(), Disposable { cancelLastCompletionStatisticsUpdate() })
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@JvmStatic
|
||||
fun collectStatisticChanges(item: LookupElement): StatisticsUpdate {
|
||||
applyLastCompletionStatisticsUpdate()
|
||||
|
||||
val base = StatisticsWeigher.getBaseStatisticsInfo(item, null)
|
||||
if (base === StatisticsInfo.EMPTY) {
|
||||
return StatisticsUpdate(StatisticsInfo.EMPTY)
|
||||
}
|
||||
|
||||
val update = StatisticsUpdate(base)
|
||||
ourPendingUpdate = update
|
||||
Disposer.register(update, Disposable { ourPendingUpdate = null })
|
||||
|
||||
return update
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun cancelLastCompletionStatisticsUpdate() {
|
||||
ourPendingUpdate?.let { Disposer.dispose(it) }
|
||||
assert(ourPendingUpdate == null)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun applyLastCompletionStatisticsUpdate() {
|
||||
ourPendingUpdate?.let {
|
||||
it.myInfo.incUseCount()
|
||||
(FeatureUsageTracker.getInstance() as FeatureUsageTrackerImpl).completionStatistics.registerInvocation(it.mySpared)
|
||||
}
|
||||
cancelLastCompletionStatisticsUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -500,7 +500,7 @@ public class XmlCompletionTest extends LightCodeInsightFixtureTestCase {
|
||||
selectItem(myFixture.getLookupElements()[4], '\t');
|
||||
checkResultByFile("CorrectSelectionInsertion_after.xml");
|
||||
|
||||
CompletionLookupArranger.applyLastCompletionStatisticsUpdate();
|
||||
StatisticsUpdate.applyLastCompletionStatisticsUpdate();
|
||||
|
||||
configureByFile("CorrectSelectionInsertion2.xml");
|
||||
myFixture.getEditor().getSelectionModel().removeSelection();
|
||||
|
||||
Reference in New Issue
Block a user