mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -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);
|
||||
|
||||
+22
-11
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
+25
-5
@@ -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) {
|
||||
|
||||
+8
-2
@@ -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;
|
||||
}
|
||||
|
||||
+23
-16
@@ -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();
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+32
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -59,6 +59,10 @@ public class MockUpdateParameterInfoContext implements UpdateParameterInfoContex
|
||||
myHighlightedParameter = parameter;
|
||||
}
|
||||
|
||||
public Object getHighlightedParameter() {
|
||||
return myHighlightedParameter;
|
||||
}
|
||||
|
||||
public void setCurrentParameter(int index) {
|
||||
myCurrentParameter = index;
|
||||
}
|
||||
|
||||
+2
-3
@@ -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.
|
||||
|
||||
+5
-2
@@ -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 = [:]
|
||||
|
||||
+14
-2
@@ -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();
|
||||
|
||||
+16
-14
@@ -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());
|
||||
|
||||
+6
-2
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user