Merge remote-tracking branch 'origin/master'

This commit is contained in:
Konstantin Bulenkov
2017-01-27 13:21:28 +03:00
28 changed files with 456 additions and 135 deletions
@@ -0,0 +1,72 @@
/*
* 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.intellij.openapi.editor.Document
import com.intellij.openapi.editor.RangeMarker
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.Segment
import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
import java.util.*
/**
* @author peter
*/
object CompletionMemory {
private val LAST_CHOSEN_METHODS = Key.create<LinkedList<RangeMarker>>("COMPLETED_METHODS")
private val CHOSEN_METHODS = Key.create<SmartPsiElementPointer<PsiMethod>>("CHOSEN_METHODS")
@JvmStatic
fun registerChosenMethod(method: PsiMethod, call: PsiCall) {
val nameRange = getAnchorRange(call) ?: return
val document = call.containingFile.viewProvider.document ?: return
addToMemory(document, createChosenMethodMarker(document, CompletionUtil.getOriginalOrSelf(method), nameRange))
}
private fun getAnchorRange(call: PsiCall) = when (call) {
is PsiMethodCallExpression -> call.methodExpression.referenceNameElement?.textRange
is PsiNewExpression -> call.classOrAnonymousClassReference?.referenceNameElement?.textRange
else -> null
}
private fun addToMemory(document: Document, marker: RangeMarker) {
val completedMethods = LinkedList<RangeMarker>()
document.getUserData(LAST_CHOSEN_METHODS)?.let { completedMethods.addAll(it.filter { it.isValid && !haveSameRange(it, marker) }) }
while (completedMethods.size > 10) {
completedMethods.removeAt(0)
}
document.putUserData(LAST_CHOSEN_METHODS, completedMethods)
completedMethods.add(marker)
}
private fun createChosenMethodMarker(document: Document, method: PsiMethod, nameRange: TextRange): RangeMarker {
val marker = document.createRangeMarker(nameRange.startOffset, nameRange.endOffset)
marker.putUserData(CHOSEN_METHODS, SmartPointerManager.getInstance(method.project).createSmartPsiElementPointer(method))
return marker
}
@JvmStatic
fun getChosenMethod(call: PsiCall): PsiMethod? {
val range = getAnchorRange(call) ?: return null
val completedMethods = call.containingFile.viewProvider.document?.getUserData(LAST_CHOSEN_METHODS)
val marker = completedMethods?.let { it.find { m -> haveSameRange(m, range) } }
return marker?.getUserData(CHOSEN_METHODS)?.element
}
private fun haveSameRange(s1: Segment, s2: Segment) = s1.startOffset == s2.startOffset && s1.endOffset == s2.endOffset
}
@@ -24,6 +24,7 @@ import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.editor.ScrollType;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
@@ -243,8 +244,17 @@ public class ConstructorInsertHandler implements InsertHandler<LookupElementDeco
assert place != null;
boolean hasParams = constructor != null ? constructor.getParameterList().getParametersCount() > 0 : hasConstructorParameters(psiClass, place);
RangeMarker refEnd = context.getDocument().createRangeMarker(context.getTailOffset(), context.getTailOffset());
JavaCompletionUtil.insertParentheses(context, delegate, false, hasParams, forAnonymous);
if (constructor != null) {
PsiCallExpression call = JavaMethodCallElement.findCallAtOffset(context, refEnd.getStartOffset());
if (call != null) {
CompletionMemory.registerChosenMethod(constructor, call);
}
}
return true;
}
@@ -168,21 +168,29 @@ public class JavaMethodCallElement extends LookupItem<PsiMethod> implements Type
importOrQualify(document, file, method, startOffset);
}
final PsiType type = method.getReturnType();
if (context.getCompletionChar() == '!' && type != null && PsiType.BOOLEAN.isAssignableFrom(type)) {
context.setAddCompletionChar(false);
context.commitDocument();
final int offset = context.getOffset(refStart);
final PsiMethodCallExpression methodCall = PsiTreeUtil.findElementOfClassAtOffset(file, offset, PsiMethodCallExpression.class, false);
if (methodCall != null) {
FeatureUsageTracker.getInstance().triggerFeatureUsed(CodeCompletionFeatures.EXCLAMATION_FINISH);
document.insertString(methodCall.getTextRange().getStartOffset(), "!");
}
PsiCallExpression methodCall = findCallAtOffset(context, context.getOffset(refStart));
if (methodCall != null) {
CompletionMemory.registerChosenMethod(method, methodCall);
handleNegation(context, document, method, methodCall);
}
startArgumentLiveTemplate(context, method);
}
static PsiCallExpression findCallAtOffset(InsertionContext context, int offset) {
context.commitDocument();
return PsiTreeUtil.findElementOfClassAtOffset(context.getFile(), offset, PsiCallExpression.class, false);
}
private static void handleNegation(InsertionContext context, Document document, PsiMethod method, PsiCallExpression methodCall) {
PsiType type = method.getReturnType();
if (context.getCompletionChar() == '!' && type != null && PsiType.BOOLEAN.isAssignableFrom(type)) {
context.setAddCompletionChar(false);
FeatureUsageTracker.getInstance().triggerFeatureUsed(CodeCompletionFeatures.EXCLAMATION_FINISH);
document.insertString(methodCall.getTextRange().getStartOffset(), "!");
}
}
private void importOrQualify(Document document, PsiFile file, PsiMethod method, int startOffset) {
if (willBeImported()) {
final PsiReferenceExpression ref = PsiTreeUtil.findElementOfClassAtOffset(file, startOffset, PsiReferenceExpression.class, false);
@@ -19,6 +19,7 @@ import com.intellij.codeInsight.AnnotationTargetUtil;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.completion.CompletionMemory;
import com.intellij.codeInsight.completion.JavaCompletionUtil;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.codeInsight.javadoc.JavaDocInfoGenerator;
@@ -37,7 +38,6 @@ import com.intellij.psi.scope.processor.MethodResolverProcessor;
import com.intellij.psi.scope.util.PsiScopesUtil;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.MethodSignatureUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
@@ -137,7 +137,12 @@ public class MethodParameterInfoHandler implements ParameterInfoHandlerWithTabAc
Object[] candidates = context.getObjectsToView();
PsiExpression[] args = o.getExpressions();
PsiElement realResolve = null;
PsiCall call = getCall(o);
PsiElement realResolve = call != null ? call.resolveMethod() : null;
PsiMethod chosenMethod = CompletionMemory.getChosenMethod(call);
CandidateInfo chosenInfo = null;
CandidateInfo completeMatch = null;
for (int i = 0; i < candidates.length; i++) {
CandidateInfo candidate = (CandidateInfo)candidates[i];
@@ -202,18 +207,24 @@ public class MethodParameterInfoHandler implements ParameterInfoHandlerWithTabAc
}
context.setUIComponentEnabled(i, enabled);
if (candidates.length > 1 &&
enabled &&
parms.length == args.length &&
isAssignableParametersBeforeGivenIndex(parms, args, args.length, substitutor)) {
if (realResolve == null) {
PsiCall call = getCall(o);
if (call != null) realResolve = call.resolveMethod();
if (realResolve == null) realResolve = PsiUtilCore.NULL_PSI_ELEMENT;
if (candidates.length > 1 && enabled) {
if (chosenMethod == method) {
chosenInfo = candidate;
}
if (parms.length == args.length && realResolve == method &&
isAssignableParametersBeforeGivenIndex(parms, args, args.length, substitutor)) {
completeMatch = candidate;
}
if (realResolve == PsiUtilCore.NULL_PSI_ELEMENT || realResolve == method) context.setHighlightedParameter(candidate);
}
}
if (chosenInfo != null) {
context.setHighlightedParameter(chosenInfo);
}
else if (completeMatch != null) {
context.setHighlightedParameter(completeMatch);
}
}
private static PsiSubstitutor getCandidateInfoSubstitutor(CandidateInfo candidate) {
@@ -19,8 +19,12 @@ import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.hint.ParameterInfoComponent;
import com.intellij.codeInsight.hint.api.impls.AnnotationParameterInfoHandler;
import com.intellij.codeInsight.hint.api.impls.MethodParameterInfoHandler;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementPresentation;
import com.intellij.lang.parameterInfo.CreateParameterInfoContext;
import com.intellij.lang.parameterInfo.ParameterInfoUIContextEx;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.infos.MethodCandidateInfo;
@@ -29,6 +33,7 @@ import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import com.intellij.testFramework.utils.parameterInfo.MockCreateParameterInfoContext;
import com.intellij.testFramework.utils.parameterInfo.MockParameterInfoUIContext;
import com.intellij.testFramework.utils.parameterInfo.MockUpdateParameterInfoContext;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
@@ -82,10 +87,18 @@ public class ParameterInfoTest extends LightCodeInsightFixtureTestCase {
assertEquals(2, itemsToShow.length);
assertTrue(itemsToShow[0] instanceof MethodCandidateInfo);
ParameterInfoComponent.createContext(itemsToShow, getEditor(), handler, -1);
MockUpdateParameterInfoContext updateParameterInfoContext = updateParameterInfo(handler, list, itemsToShow);
assertTrue(updateParameterInfoContext.isUIComponentEnabled(0) || updateParameterInfoContext.isUIComponentEnabled(1));
}
@NotNull
private MockUpdateParameterInfoContext updateParameterInfo(MethodParameterInfoHandler handler,
PsiExpressionList list,
Object[] itemsToShow) {
MockUpdateParameterInfoContext updateParameterInfoContext = new MockUpdateParameterInfoContext(getEditor(), getFile(), itemsToShow);
updateParameterInfoContext.setParameterOwner(list);
handler.updateParameterInfo(list, updateParameterInfoContext);
assertTrue(updateParameterInfoContext.isUIComponentEnabled(0) || updateParameterInfoContext.isUIComponentEnabled(1));
return updateParameterInfoContext;
}
public void testStopAtAccessibleStaticCorrectCandidate() {
@@ -132,20 +145,34 @@ public class ParameterInfoTest extends LightCodeInsightFixtureTestCase {
}
private String parameterPresentation(int parameterIndex) {
return parameterPresentation(0, parameterIndex);
}
private String parameterPresentation(int lineIndex, int parameterIndex) {
MethodParameterInfoHandler handler = new MethodParameterInfoHandler();
CreateParameterInfoContext context = new MockCreateParameterInfoContext(getEditor(), getFile());
CreateParameterInfoContext context = createContext();
PsiExpressionList list = handler.findElementForParameterInfo(context);
assertNotNull(list);
Object[] itemsToShow = context.getItemsToShow();
assertNotNull(itemsToShow);
assertEquals(1, itemsToShow.length);
assertTrue(itemsToShow[0] instanceof MethodCandidateInfo);
PsiMethod method = ((MethodCandidateInfo)itemsToShow[0]).getElement();
assertTrue(itemsToShow[lineIndex] instanceof MethodCandidateInfo);
PsiMethod method = ((MethodCandidateInfo)itemsToShow[lineIndex]).getElement();
ParameterInfoUIContextEx parameterContext = ParameterInfoComponent.createContext(itemsToShow, getEditor(), handler, parameterIndex);
PsiSubstitutor substitutor = ((MethodCandidateInfo)itemsToShow[0]).getSubstitutor();
PsiSubstitutor substitutor = ((MethodCandidateInfo)itemsToShow[lineIndex]).getSubstitutor();
return MethodParameterInfoHandler.updateMethodPresentation(method, substitutor, parameterContext);
}
private CreateParameterInfoContext createContext() {
int caretOffset = getEditor().getCaretModel().getOffset();
PsiExpressionList argList = PsiTreeUtil.findElementOfClassAtOffset(getFile(), caretOffset, PsiExpressionList.class, false);
return new MockCreateParameterInfoContext(getEditor(), getFile()) {
@Override
public int getParameterListStart() {
return argList == null ? caretOffset : argList.getTextRange().getStartOffset();
}
};
}
public void testAnnotationWithGenerics() {
myFixture.configureByFile(getTestName(false) + ".java");
String text = annoParameterPresentation();
@@ -189,4 +216,57 @@ public class ParameterInfoTest extends LightCodeInsightFixtureTestCase {
myFixture.configureByText("a.java", "class C {\n void m(@TA String s) { }\n void t() { m(<caret>\"test\"); }\n}");
assertEquals("<html>@TA String s</html>", parameterPresentation(-1));
}
public void testHighlightMethodJustChosenInCompletion() {
myFixture.configureByText("a.java", "class Foo {" +
"{ bar<caret> }" +
"void bar(boolean a);" +
"void bar(String a);" +
"void bar(int a);" +
"void bar2(int a);" +
"}");
LookupElement[] elements = myFixture.completeBasic();
assertEquals("(int a)", LookupElementPresentation.renderElement(elements[1]).getTailText());
myFixture.getLookup().setCurrentItem(elements[1]);
myFixture.type('\n');
assertEquals("<html>boolean a</html>", parameterPresentation(0, -1));
assertEquals("<html>String a</html>", parameterPresentation(1, -1));
assertEquals("<html>int a</html>", parameterPresentation(2, -1));
checkHighlighted(2);
}
public void testHighlightConstructorJustChosenInCompletion() {
Registry.get("java.completion.show.constructors").setValue(true);
Disposer.register(getTestRootDisposable(), () -> Registry.get("java.completion.show.constructors").setValue(false));
myFixture.addClass("class Bar {" +
"Bar(boolean a);" +
"Bar(String a);" +
"Bar(int a);" +
"}; " +
"class Bar2 {}");
myFixture.configureByText("a.java", "class Foo {{ new Bar<caret> }}");
LookupElement[] elements = myFixture.completeBasic();
assertEquals("(String a) (default package)", LookupElementPresentation.renderElement(elements[2]).getTailText());
myFixture.getLookup().setCurrentItem(elements[2]);
myFixture.type('\n');
myFixture.checkResult("class Foo {{ new Bar(<caret>) }}");
assertEquals("<html>boolean a</html>", parameterPresentation(0, -1));
assertEquals("<html>String a</html>", parameterPresentation(1, -1));
assertEquals("<html>int a</html>", parameterPresentation(2, -1));
checkHighlighted(1);
}
private void checkHighlighted(int lineIndex) {
MethodParameterInfoHandler handler = new MethodParameterInfoHandler();
CreateParameterInfoContext context = createContext();
PsiExpressionList list = handler.findElementForParameterInfo(context);
Object[] itemsToShow = context.getItemsToShow();
assertEquals(itemsToShow[lineIndex], updateParameterInfo(handler, list, itemsToShow).getHighlightedParameter());
}
}
@@ -201,17 +201,20 @@ class NormalCompletionOrderingTest extends CompletionSortingTestCase {
void testFqnStats() {
myFixture.addClass("public interface Baaaaaaar {}")
myFixture.addClass("package boo; public interface Baaaaaaar {}")
myFixture.addClass("package zoo; public interface Baaaaaaar {}")
configureSecondCompletion()
final LookupImpl lookup = getLookup()
assertEquals("Baaaaaaar", ((JavaPsiClassReferenceElement) lookup.getItems().get(0)).getQualifiedName())
assertEquals("zoo.Baaaaaaar", ((JavaPsiClassReferenceElement) lookup.getItems().get(1)).getQualifiedName())
incUseCount(lookup, 1)
assertEquals("Baaaaaaar", ((JavaPsiClassReferenceElement) lookup.items[0]).qualifiedName)
assertEquals("boo.Baaaaaaar", ((JavaPsiClassReferenceElement) lookup.items[1]).qualifiedName)
assertEquals("zoo.Baaaaaaar", ((JavaPsiClassReferenceElement) lookup.items[2]).qualifiedName)
incUseCount(lookup, 2)
assertEquals("zoo.Baaaaaaar", ((JavaPsiClassReferenceElement) lookup.getItems().get(0)).getQualifiedName())
assertEquals("Baaaaaaar", ((JavaPsiClassReferenceElement)lookup.getItems().get(1)).getQualifiedName())
assertEquals("Baaaaaaar", ((JavaPsiClassReferenceElement) lookup.items[0]).qualifiedName) // same package
assertEquals("zoo.Baaaaaaar", ((JavaPsiClassReferenceElement) lookup.items[1]).qualifiedName)
assertEquals("boo.Baaaaaaar", ((JavaPsiClassReferenceElement) lookup.items[2]).qualifiedName)
}
void testSkipLifted() {
@@ -738,4 +741,21 @@ class ContainerUtil extends ContainerUtilRt {
assertPreferredItems 0, 'someMethod'
}
void testPreferImportedClassesAmongstSameNamed() {
myFixture.addClass('package foo; public class String {}')
// use foo.String
myFixture.configureByText 'a.java', 'class Foo { Stri<caret> }'
myFixture.completeBasic()
myFixture.lookup.currentItem = myFixture.lookupElements.find { it.lookupString == 'String' && LookupElementPresentation.renderElement(it).tailText.contains('foo') }
myFixture.type('\n')
myFixture.checkResult 'import foo.String;\n\nclass Foo { String<caret>\n}'
// assert non-imported String is not preselected when completing in another file
myFixture.configureByText 'b.java', 'class Bar { Stri<caret> }'
myFixture.completeBasic()
myFixture.assertPreferredCompletionItems 0, 'String', 'String'
assert LookupElementPresentation.renderElement(myFixture.lookupElements[0]).tailText.contains('java.lang')
}
}
@@ -16,10 +16,33 @@
package com.intellij.codeInsight.daemon.inlays
import com.intellij.codeInsight.daemon.impl.ParameterHintsPresentationManager
import com.intellij.codeInsight.hints.settings.ParameterNameHintsSettings
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
import org.assertj.core.api.Assertions.assertThat
import org.jdom.Element
class BlackListMethodIntentionTest : LightCodeInsightFixtureTestCase() {
private var isParamHintsEnabledBefore = false
private lateinit var stateBefore: Element
override fun setUp() {
super.setUp()
val settings = EditorSettingsExternalizable.getInstance()
isParamHintsEnabledBefore = settings.isShowParameterNameHints
settings.isShowParameterNameHints = true
stateBefore = ParameterNameHintsSettings.getInstance().state
}
override fun tearDown() {
EditorSettingsExternalizable.getInstance().isShowParameterNameHints = isParamHintsEnabledBefore
ParameterNameHintsSettings.getInstance().loadState(stateBefore)
super.tearDown()
}
fun `test add to blacklist by alt enter`() {
myFixture.configureByText("a.java", """
@@ -19,7 +19,7 @@ import com.intellij.codeInsight.lookup.Classifier;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Factory;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.statistics.StatisticsInfo;
@@ -27,9 +27,8 @@ import com.intellij.psi.statistics.StatisticsManager;
import com.intellij.util.ProcessingContext;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.FlatteningIterator;
import gnu.trove.THashSet;
import gnu.trove.TObjectHashingStrategy;
import com.intellij.util.containers.JBIterable;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -81,25 +80,16 @@ public class StatisticsWeigher extends CompletionWeigher {
public Iterable<LookupElement> classify(@NotNull Iterable<LookupElement> source, @NotNull final ProcessingContext context) {
checkPrefixChanged(context);
final Collection<List<LookupElement>> byWeight = buildMapByWeight(source).descendingMap().values();
List<LookupElement> initialList = getInitialNoStatElements(source, context);
Iterable<LookupElement> rest = withoutInitial(source, initialList);
Collection<List<LookupElement>> byWeight = buildMapByWeight(rest).descendingMap().values();
//noinspection unchecked
final THashSet<LookupElement> initialSet = new THashSet<>(initialList, TObjectHashingStrategy.IDENTITY);
final Condition<LookupElement> notInInitialList = element -> !initialSet.contains(element);
return JBIterable.from(initialList).append(JBIterable.from(byWeight).flatten(group -> myNext.classify(group, context)));
}
return ContainerUtil.concat(initialList, new Iterable<LookupElement>() {
@Override
public Iterator<LookupElement> iterator() {
return new FlatteningIterator<List<LookupElement>, LookupElement>(byWeight.iterator()) {
@Override
protected Iterator<LookupElement> createValueIterator(List<LookupElement> group) {
return myNext.classify(ContainerUtil.findAll(group, notInInitialList), context).iterator();
}
};
}
});
private static Iterable<LookupElement> withoutInitial(Iterable<LookupElement> allItems, List<LookupElement> initial) {
Set<LookupElement> initialSet = ContainerUtil.newIdentityTroveSet(initial);
return JBIterable.from(allItems).filter(element -> !initialSet.contains(element));
}
private List<LookupElement> getInitialNoStatElements(Iterable<LookupElement> source, ProcessingContext context) {
@@ -116,18 +106,42 @@ public class StatisticsWeigher extends CompletionWeigher {
}
private TreeMap<Integer, List<LookupElement>> buildMapByWeight(Iterable<LookupElement> source) {
MultiMap<String, LookupElement> byName = buildMapByLookupString(source);
Set<List<LookupElement>> toSort = ContainerUtil.newIdentityTroveSet();
TreeMap<Integer, List<LookupElement>> map = new TreeMap<>();
for (LookupElement element : source) {
final int weight = getWeight(element).getScalar();
List<LookupElement> list = map.get(weight);
if (list == null) {
map.put(weight, list = new SmartList<>());
}
Collection<LookupElement> sameNamedGroup = byName.get(element.getLookupString());
int weight = getMaxWeight(sameNamedGroup);
List<LookupElement> list = ContainerUtil.getOrCreate(map, weight, (Factory<List<LookupElement>>)() -> new SmartList<>());
list.add(element);
if (sameNamedGroup.size() > 1 && weight != getScalarWeight(element)) {
toSort.add(list);
}
}
for (List<LookupElement> group : toSort) {
Collections.sort(group, Comparator.comparing(this::getScalarWeight).reversed());
}
return map;
}
@NotNull
private static MultiMap<String, LookupElement> buildMapByLookupString(Iterable<LookupElement> elements) {
MultiMap<String, LookupElement> byName = new MultiMap<>();
for (LookupElement element : elements) {
byName.putValue(element.getLookupString(), element);
}
return byName;
}
private int getMaxWeight(Collection<LookupElement> group) {
return group.stream().mapToInt(this::getScalarWeight).max().orElse(0);
}
private int getScalarWeight(LookupElement e) {
return getWeight(e).getScalar();
}
private StatisticsComparable getWeight(LookupElement t) {
StatisticsComparable w = myWeights.get(t);
if (w == null) {
@@ -105,12 +105,15 @@ public class ParameterHintsPassFactory extends AbstractProjectComponent implemen
if (isDebug) {
System.out.println(System.nanoTime() + ": [HintsPass] Traversing started");
System.out.println(System.nanoTime() + ": [HintsPass] Matchers:");
blackList.forEach(blackListItem -> System.out.println(" " + blackListItem));
}
SyntaxTraverser.psiTraverser(myFile).forEach(element -> process(element, provider, matchers));
if (isDebug) {
System.out.println(System.nanoTime() + ": [HintsPass] Traversing ended");
System.out.println(System.nanoTime() + ": [HintsPass] Traversing ended, hints to apply:");
myAnnotations.forEach((i, s) -> System.out.println(" " + i + " " + s));
}
}
@@ -139,12 +142,15 @@ public class ParameterHintsPassFactory extends AbstractProjectComponent implemen
if (info == null || !isMatchedByAny(info, blackListMatchers)) {
hints.forEach((h) -> myAnnotations.put(h.getOffset(), h.getText()));
}
else if (isDebug) {
System.out.println("Method Info : " + info + " is matched by something");
}
}
@Override
public void doApplyInformationToEditor() {
if (isDebug) {
System.out.println(System.nanoTime() + ": hints addition started");
System.out.println(System.nanoTime() + ": hints addition started, total hints: " + myAnnotations.size());
}
assert myDocument != null;
@@ -19,6 +19,7 @@ import com.intellij.openapi.diagnostic.Attachment;
import com.intellij.openapi.diagnostic.ExceptionWithAttachments;
import com.intellij.openapi.diagnostic.IdeaLoggingEvent;
import com.intellij.psi.impl.DebugUtil;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
/**
@@ -45,7 +46,9 @@ public class LogEventException extends RuntimeException implements ExceptionWith
public Attachment[] getAttachments() {
Object data = myLogMessage.getData();
if (data instanceof LogMessageEx) {
return ((LogMessageEx)data).getAllAttachments().toArray(Attachment.EMPTY_ARRAY);
Attachment[] attachments = ((LogMessageEx)data).getAllAttachments().toArray(Attachment.EMPTY_ARRAY);
Throwable throwable = myLogMessage.getThrowable();
return throwable == null ? attachments : ArrayUtil.prepend(new Attachment("details.trace", throwable), attachments);
}
return Attachment.EMPTY_ARRAY;
}
@@ -28,9 +28,9 @@ import com.intellij.openapi.command.UndoConfirmationPolicy;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.event.DocumentAdapter;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.ex.DocumentEx;
import com.intellij.openapi.editor.ex.PrioritizedDocumentListener;
import com.intellij.openapi.editor.impl.EditorFactoryImpl;
import com.intellij.openapi.editor.impl.TrailingSpacesStripper;
import com.intellij.openapi.extensions.Extensions;
@@ -179,24 +179,31 @@ public class FileDocumentManagerImpl extends FileDocumentManager implements Virt
}
if (!(file instanceof LightVirtualFile || file.getFileSystem() instanceof NonPhysicalFileSystem)) {
document.addDocumentListener(
new DocumentAdapter() {
@Override
public void documentChanged(DocumentEvent e) {
final Document document = e.getDocument();
myUnsavedDocuments.add(document);
final Runnable currentCommand = CommandProcessor.getInstance().getCurrentCommand();
Project project = currentCommand == null ? null : CommandProcessor.getInstance().getCurrentCommandProject();
String lineSeparator = CodeStyleFacade.getInstance(project).getLineSeparator();
document.putUserData(LINE_SEPARATOR_KEY, lineSeparator);
document.addDocumentListener(new PrioritizedDocumentListener() {
@Override
public void beforeDocumentChange(DocumentEvent event) {
}
// avoid documents piling up during batch processing
if (areTooManyDocumentsInTheQueue(myUnsavedDocuments)) {
saveAllDocumentsLater();
}
@Override
public int getPriority() {
return Integer.MIN_VALUE;
}
@Override
public void documentChanged(DocumentEvent e) {
final Document document = e.getDocument();
myUnsavedDocuments.add(document);
final Runnable currentCommand = CommandProcessor.getInstance().getCurrentCommand();
Project project = currentCommand == null ? null : CommandProcessor.getInstance().getCurrentCommandProject();
String lineSeparator = CodeStyleFacade.getInstance(project).getLineSeparator();
document.putUserData(LINE_SEPARATOR_KEY, lineSeparator);
// avoid documents piling up during batch processing
if (areTooManyDocumentsInTheQueue(myUnsavedDocuments)) {
saveAllDocumentsLater();
}
}
);
});
}
}
@@ -20,6 +20,9 @@ import com.intellij.mock.MockVirtualFile;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.event.DocumentListener;
import com.intellij.openapi.editor.ex.DocumentEx;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileDocumentManagerAdapter;
@@ -45,6 +48,7 @@ import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.atomic.AtomicInteger;
public class FileDocumentManagerImplTest extends PlatformTestCase {
private FileDocumentManagerImpl myDocumentManager;
@@ -593,4 +597,32 @@ public class FileDocumentManagerImplTest extends PlatformTestCase {
FileDocumentManager.getInstance().saveAllDocuments();
}
public void testDocumentUnsavedInsideChangeListener() throws IOException {
VirtualFile file = createFile("a.txt", "a");
FileDocumentManager manager = FileDocumentManager.getInstance();
Document document = manager.getDocument(file);
assertFalse(manager.isDocumentUnsaved(document));
AtomicInteger invoked = new AtomicInteger();
DocumentListener listener = new DocumentListener() {
@Override
public void beforeDocumentChange(DocumentEvent e) {
assertFalse(manager.isDocumentUnsaved(document));
}
@Override
public void documentChanged(DocumentEvent event) {
invoked.incrementAndGet();
assertTrue(manager.isDocumentUnsaved(document));
}
};
document.addDocumentListener(listener, getTestRootDisposable());
EditorFactory.getInstance().getEventMulticaster().addDocumentListener(listener, getTestRootDisposable());
WriteCommandAction.runWriteCommandAction(myProject, () -> document.insertString(0, "b"));
assertTrue(manager.isDocumentUnsaved(document));
assertEquals(2, invoked.get());
}
}
@@ -59,6 +59,10 @@ public class MockUpdateParameterInfoContext implements UpdateParameterInfoContex
myHighlightedParameter = parameter;
}
public Object getHighlightedParameter() {
return myHighlightedParameter;
}
public void setCurrentParameter(int index) {
myCurrentParameter = index;
}
@@ -41,6 +41,7 @@ import org.jdom.xpath.XPath;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
import org.junit.runner.RunWith;
@@ -94,9 +95,7 @@ public abstract class GuiTestBase {
@Before
public void setUp() throws Exception {
if (!canRunGuiTests()) {
return;
}
Assume.assumeTrue(canRunGuiTests());
Application application = ApplicationManager.getApplication();
assertNotNull(application); // verify that we are using the IDE's ClassLoader.
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* 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.
@@ -263,7 +263,10 @@ public abstract class TreeInplaceEditor implements AWTEventListener {
return;
}
}
cancelEditing();
if (id != MouseEvent.MOUSE_RELEASED) { // do not cancel on release outside of the component
cancelEditing();
}
}
@Nullable
@@ -27,6 +27,7 @@ import git4idea.validators.GitRefNameValidator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -77,23 +78,18 @@ public class GitTaskHandler extends DvcsTaskHandler<GitRepository> {
@Override
protected Iterable<TaskInfo> getAllBranches(@NotNull GitRepository repository) {
GitBranchesCollection branches = repository.getBranches();
List<TaskInfo> list = ContainerUtil.map(branches.getLocalBranches(), new Function<GitBranch, TaskInfo>() {
@Override
public TaskInfo fun(GitBranch branch) {
return new TaskInfo(branch.getName(), Collections.singleton(repository.getPresentableUrl()));
}
});
list.addAll(ContainerUtil.map(branches.getRemoteBranches(), new Function<GitBranch, TaskInfo>() {
@Override
public TaskInfo fun(GitBranch branch) {
return new TaskInfo(branch.getName(), Collections.singleton(repository.getPresentableUrl())) {
@Override
public boolean isRemote() {
return true;
}
};
}
}));
List<TaskInfo> list = new ArrayList<>(ContainerUtil.map(branches.getLocalBranches(),
(Function<GitBranch, TaskInfo>)branch -> new TaskInfo(branch.getName(),
Collections.singleton(
repository
.getPresentableUrl()))));
list.addAll(ContainerUtil.map(branches.getRemoteBranches(),
(Function<GitBranch, TaskInfo>)branch -> new TaskInfo(branch.getName(), Collections.singleton(repository.getPresentableUrl())) {
@Override
public boolean isRemote() {
return true;
}
}));
return list;
}
@@ -114,10 +114,6 @@ public class GradleExtensionsSettings implements PersistentStateComponent<Gradle
GradleProp gradleProp = new GradleProp();
gradleProp.name = property.getName();
gradleProp.typeFqn = property.getTypeFqn();
Serializable value = property.getValue();
if (value != null) {
gradleProp.value = value.toString();
}
extensionsData.properties.add(gradleProp);
}
for (ExternalTask task : gradleExtensions.getTasks()) {
@@ -1,9 +1,9 @@
<html>
<body>
This inspection reports any instances of Groovy statements witch attempts to put GString object as a key to map.
This inspection reports any instances of Groovy statements which attempts to put GString object as a key to map.
In a general way GString objects are mutable and probably should not be used as keys.
<p>
Also, GString entry could no be accessed with java.lang.String object with same value. Example:
Also, GString entry could not be accessed with java.lang.String object with same value. Example:
<br>
<pre>
def map = [:]
@@ -58,9 +58,11 @@ public class ChangeToOperatorInspection extends BaseInspection {
Transformation transformation = TRANSFORMATIONS.get(methodName);
if (transformation == null) return;
PsiElement highlightElement = getHighlightElement(methodCall);
if (highlightElement == null) return;
if (transformation.couldApply(methodCall, getOptions())) {
registerError(
methodCall,
highlightElement,
message("replace.with.operator.message", methodName),
new LocalQuickFix[]{getFix(transformation, methodName)},
GENERIC_ERROR_OR_WARNING
@@ -82,7 +84,9 @@ public class ChangeToOperatorInspection extends BaseInspection {
@Override
protected void doFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) throws IncorrectOperationException {
PsiElement call = descriptor.getPsiElement();
PsiElement call = descriptor.getPsiElement().getParent();
if (call == null) return;
call = call.getParent();
if (!(call instanceof GrMethodCall)) return;
GrMethodCall methodCall = (GrMethodCall) call;
GrExpression invokedExpression = methodCall.getInvokedExpression();
@@ -95,6 +99,14 @@ public class ChangeToOperatorInspection extends BaseInspection {
};
}
@Nullable
public PsiElement getHighlightElement(@NotNull GrMethodCall methodCall) {
GrExpression invokedExpression = methodCall.getInvokedExpression();
if (!(invokedExpression instanceof GrReferenceExpression)) return null;
return ((GrReferenceExpression)invokedExpression).getReferenceNameElement();
}
@Nullable
public String getMethodName(@NotNull GrMethodCall methodCall) {
GrExpression invokedExpression = methodCall.getInvokedExpression();
@@ -282,25 +282,27 @@ class Operators {
final String DECLARATIONS = 'def (Operators a, Operators b) = [null, null]\n'
private void doTest(String before, String after = null) {
fixture.with {
configureByText '_.groovy', "$DECLARATIONS$before"
moveCaret()
def intentions = filterAvailableIntentions('Replace ')
if (after) {
assert intentions: before
launchAction intentions.first()
checkResult "$DECLARATIONS$after"
}
else {
assert !intentions
Closeable closeCaret = {fixture.editor.caretModel.moveToOffset(0)}
closeCaret.withCloseable {
fixture.with {
configureByText '_.groovy', "$DECLARATIONS$before"
moveCaret()
def intentions = filterAvailableIntentions('Replace ')
if (after) {
assert intentions: before
launchAction intentions.first()
checkResult "$DECLARATIONS$after"
}
else {
assert !intentions
}
}
}
}
private void moveCaret() {
def statement =
(fixture.file as GroovyFile).statements.last()
def statement = (fixture.file as GroovyFile).statements.last()
GrMethodCall call = null
if (statement instanceof GrMethodCall) {
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* 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.
@@ -72,5 +72,5 @@ public interface Property {
* Get the return type of the property getter.
*/
@Nullable
PyType getType(@NotNull TypeEvalContext context);
PyType getType(@Nullable PyExpression receiver, @NotNull TypeEvalContext context);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* 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.
@@ -98,12 +98,15 @@ public class PyStatementEffectInspection extends PyInspection {
}
}
else if (expression instanceof PyBinaryExpression) {
PyBinaryExpression binary = (PyBinaryExpression)expression;
final PyBinaryExpression binary = (PyBinaryExpression)expression;
final PyElementType operator = binary.getOperator();
if (PyTokenTypes.COMPARISON_OPERATIONS.contains(operator)) return false;
final PyExpression leftExpression = binary.getLeftExpression();
final PyExpression rightExpression = binary.getRightExpression();
if (hasEffect(leftExpression) || hasEffect(rightExpression)) return true;
final PyElementType operator = binary.getOperator();
String method = operator == null ? null : operator.getSpecialMethodName();
if (method != null) {
// maybe the op is overridden and may produce side effects, like cout << "hello"
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* 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.
@@ -926,7 +926,7 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
@Nullable
@Override
public PyType getType(@NotNull TypeEvalContext context) {
public PyType getType(@Nullable PyExpression receiver, @NotNull TypeEvalContext context) {
if (mySite instanceof PyTargetExpressionImpl) {
final PyType targetDocStringType = ((PyTargetExpressionImpl)mySite).getTypeFromDocString();
if (targetDocStringType != null) {
@@ -939,7 +939,7 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
if (!(callable instanceof StubBasedPsiElement) && !context.maySwitchToAST(callable)) {
return null;
}
return context.getReturnType(callable);
return callable.getCallType(receiver, Collections.emptyMap(), context);
}
return null;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* 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.
@@ -289,10 +289,10 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
Property property = pyClass.findProperty(name, true, context);
if (property != null) {
if (classType.isDefinition()) {
return Ref.<PyType>create(PyBuiltinCache.getInstance(pyClass).getObjectType(PyNames.PROPERTY));
return Ref.create(PyBuiltinCache.getInstance(pyClass).getObjectType(PyNames.PROPERTY));
}
if (AccessDirection.of(this) == AccessDirection.READ) {
final PyType type = property.getType(context);
final PyType type = property.getType(getQualifier(), context);
if (type != null) {
return Ref.create(type);
}
@@ -0,0 +1,3 @@
<warning descr="Statement seems to have no effect">status == 1</warning>
<warning descr="Statement seems to have no effect">status == {}</warning>
<warning descr="Statement seems to have no effect">status == dict()</warning>
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* 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.
@@ -95,7 +95,7 @@ public class PyClassicPropertyTest extends PyTestCase {
accessor = p.getGetter();
assertFalse(accessor.isDefined());
final PyType codeInsightType = p.getType(TypeEvalContext.codeInsightFallback(myClass.getProject()));
final PyType codeInsightType = p.getType(null, TypeEvalContext.codeInsightFallback(myClass.getProject()));
assertNull(codeInsightType);
accessor = p.getSetter();
@@ -1621,6 +1621,19 @@ public class PyTypeTest extends PyTestCase {
" expr = foo");
}
// PY-22037
public void testAncestorPropertyReturnsSelf() {
doTest("Child",
"class Master(object):\n" +
" @property\n" +
" def me(self):\n" +
" return self\n" +
"class Child(Master):\n" +
" pass\n" +
"child = Child()\n" +
"expr = child.me");
}
private static List<TypeEvalContext> getTypeEvalContexts(@NotNull PyExpression element) {
return ImmutableList.of(TypeEvalContext.codeAnalysis(element.getProject(), element.getContainingFile()).withTracing(),
TypeEvalContext.userInitiated(element.getProject(), element.getContainingFile()).withTracing());
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* 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.
@@ -31,8 +31,12 @@ public class PyStatementEffectInspectionTest extends PyInspectionTestCase {
doTest(LanguageLevel.PYTHON35);
}
public void testComparison() {
doTest();
}
private void doTest(@NotNull LanguageLevel level) {
runWithLanguageLevel(level, () -> doTest());
runWithLanguageLevel(level, this::doTest);
}
@NotNull