From db891c3f24abf9662d7b6033863c57c64db3165d Mon Sep 17 00:00:00 2001 From: "Roman.Ivanov" Date: Tue, 10 Apr 2018 15:08:41 +0700 Subject: [PATCH 01/53] SortContentAction: place \n correctly if comment inside node IDEA-189822 --- .../intention/impl/SortContentAction.java | 10 ++++++++++ .../quickFix/sortContent/afterEnumComments.java | 13 +++++++++++++ .../quickFix/sortContent/beforeEnumComments.java | 9 +++++++++ 3 files changed, 32 insertions(+) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterEnumComments.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeEnumComments.java diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/SortContentAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/SortContentAction.java index 3df133ee6e19..5449b419e0b1 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/SortContentAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/SortContentAction.java @@ -14,6 +14,7 @@ import com.intellij.util.IncorrectOperationException; import com.intellij.util.SmartList; import com.siyeh.ig.psiutils.ExpressionUtils; import gnu.trove.TIntArrayList; +import one.util.streamex.StreamEx; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -821,6 +822,15 @@ public class SortContentAction extends PsiElementBaseIntentionAction { PsiElement lBrace = aClass.getLBrace(); PsiElement rBrace = aClass.getRBrace(); if (lBrace == null || rBrace == null) return; + + //PsiEnumConstant holds comments inside, we need codegen to know about this comments to place \n correctly + for (SortableEntry entry : sortableList.myEntries) { + List comments = StreamEx.ofTree(entry.myElement, el -> StreamEx.of(el.getChildren())).select(PsiComment.class).toList(); + for (PsiComment comment : comments) { + entry.myBeforeSeparator.add((PsiComment)comment.copy()); + comment.delete(); + } + } StringBuilder sb = new StringBuilder(); sortableList.generate(sb); PsiElement elementToPreserve = lastElement.getNextSibling(); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterEnumComments.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterEnumComments.java new file mode 100644 index 000000000000..7831bf6ccaf2 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/afterEnumComments.java @@ -0,0 +1,13 @@ +// "Sort content" "true" + +public enum OSType { + LINUX// linux + , + + MAC//mac + , + WIN//after win +// win + + ; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeEnumComments.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeEnumComments.java new file mode 100644 index 000000000000..2639bf0ed280 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/sortContent/beforeEnumComments.java @@ -0,0 +1,9 @@ +// "Sort content" "true" + +public enum OSType { + WIN// win + //after win + , LINUX // linux + ,MAC //mac + ; +} \ No newline at end of file From 5e0d5a8c16f66ca6651d3d5c8d6f165ee7a6f862 Mon Sep 17 00:00:00 2001 From: "Roman.Ivanov" Date: Tue, 10 Apr 2018 15:49:24 +0700 Subject: [PATCH 02/53] AddExceptionToExistingCatch: disable fix for local classes: IDEA-189828 --- .../AddExceptionToExistingCatchFix.java | 3 ++- .../afterInAnnonimousClass.java | 18 ++++++++++++++++++ .../beforeInAnnonimousClass.java | 17 +++++++++++++++++ .../beforeInLocalClass.java | 17 +++++++++++++++++ .../src/com/intellij/psi/util/PsiTreeUtil.java | 5 +++-- 5 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addExceptionToExistingCatch/afterInAnnonimousClass.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addExceptionToExistingCatch/beforeInAnnonimousClass.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addExceptionToExistingCatch/beforeInLocalClass.java diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddExceptionToExistingCatchFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddExceptionToExistingCatchFix.java index e8bbf73f8026..000a3781c41e 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddExceptionToExistingCatchFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddExceptionToExistingCatchFix.java @@ -137,7 +137,8 @@ public class AddExceptionToExistingCatchFix extends PsiElementBaseIntentionActio if (unhandledExceptions.isEmpty()) return null; boolean containsInCatchOrFinally = containsInCatchOrFinally(element); List tryStatements = - PsiTreeUtil.collectParentsOfType(element, PsiTryStatement.class, PsiLambdaExpression.class, PsiClass.class); + PsiTreeUtil.collectParents(element, PsiTryStatement.class, el -> + el instanceof PsiLambdaExpression || el instanceof PsiClass && !(el instanceof PsiAnonymousClass)); if (containsInCatchOrFinally) { tryStatements.remove(0); } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addExceptionToExistingCatch/afterInAnnonimousClass.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addExceptionToExistingCatch/afterInAnnonimousClass.java new file mode 100644 index 000000000000..e1c94f5728ab --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addExceptionToExistingCatch/afterInAnnonimousClass.java @@ -0,0 +1,18 @@ +// "Add exception to existing catch clause" "true" +import java.io.FileInputStream; +import java.io.FileNotFoundException; + +class Test { + void m() { + try { + new Runnable() { + @Override + public void run() { } + + InputStream in = new FileInputStream(""); + }; + } catch (RuntimeException | FileNotFoundException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addExceptionToExistingCatch/beforeInAnnonimousClass.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addExceptionToExistingCatch/beforeInAnnonimousClass.java new file mode 100644 index 000000000000..6f57aecfe6e1 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addExceptionToExistingCatch/beforeInAnnonimousClass.java @@ -0,0 +1,17 @@ +// "Add exception to existing catch clause" "true" +import java.io.FileInputStream; + +class Test { + void m() { + try { + new Runnable() { + @Override + public void run() { } + + InputStream in = new FileInputStream(""); + }; + } catch (RuntimeException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addExceptionToExistingCatch/beforeInLocalClass.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addExceptionToExistingCatch/beforeInLocalClass.java new file mode 100644 index 000000000000..e72a780e217c --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addExceptionToExistingCatch/beforeInLocalClass.java @@ -0,0 +1,17 @@ +// "Add exception to existing catch clause" "false" +import java.io.FileInputStream; + +class Test { + void m() { + try { + class A implements Runnable { + @Override + public void run() { } + + InputStream in = new FileInputStream(""); + }; + } catch (RuntimeException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/platform/core-api/src/com/intellij/psi/util/PsiTreeUtil.java b/platform/core-api/src/com/intellij/psi/util/PsiTreeUtil.java index 7385787975ee..0c97059a93e6 100644 --- a/platform/core-api/src/com/intellij/psi/util/PsiTreeUtil.java +++ b/platform/core-api/src/com/intellij/psi/util/PsiTreeUtil.java @@ -28,6 +28,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; +import java.util.function.Predicate; import static com.intellij.psi.SyntaxTraverser.psiTraverser; @@ -697,11 +698,11 @@ public class PsiTreeUtil { return aClass.cast(element); } - public static List collectParentsOfType(PsiElement element, Class parent, Class... stopClasses) { + public static List collectParents(PsiElement element, Class parent, Predicate stopCondition) { element = element.getParent(); List parents = new SmartList<>(); while (element != null) { - if (instanceOf(element, stopClasses)) break; + if (stopCondition.test(element)) break; if (parent.isInstance(element)) { parents.add(parent.cast(element)); } From a8d9728c9ad7767dfd95ee2b5c6e04283cac09a5 Mon Sep 17 00:00:00 2001 From: Anton Bragin Date: Tue, 27 Mar 2018 14:28:35 +0300 Subject: [PATCH 03/53] getVariants() in PyReferenceImpl signature changed to prettify API PyReferenceImpl.getVariants() changed to return LookupElement[] instead of Object[] since it's effectively returns array of LookupElement's in all practical cases. --- .../plugins/ipnb/psi/IpnbPyReference.java | 7 ++-- .../jetbrains/python/psi/types/PyType.java | 3 +- .../com/jetbrains/python/PyCustomType.java | 7 ++-- .../codeInsight/stdlib/PyNamedTupleType.java | 8 ++--- .../documentation/doctest/PyDocReference.java | 12 +++---- .../impl/references/PyImportReference.java | 23 ++++++------ .../impl/references/PyQualifiedReference.java | 25 ++++++------- .../psi/impl/references/PyReferenceImpl.java | 6 ++-- .../impl/references/PyTargetReference.java | 6 ++-- .../python/psi/types/PyCallableTypeImpl.java | 6 ++-- .../python/psi/types/PyClassTypeImpl.java | 35 +++++++++---------- .../python/psi/types/PyFunctionTypeImpl.java | 6 ++-- .../python/psi/types/PyGenericType.java | 6 ++-- .../psi/types/PyImportedModuleType.java | 5 ++- .../python/psi/types/PyModuleType.java | 4 +-- .../python/psi/types/PyNoneType.java | 6 ++-- .../python/psi/types/PyStructuralType.java | 12 ++++--- .../python/psi/types/PyUnionType.java | 8 ++--- 18 files changed, 93 insertions(+), 92 deletions(-) diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/psi/IpnbPyReference.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/psi/IpnbPyReference.java index f943cc6dac1e..792c511334dc 100644 --- a/python/ipnb/src/org/jetbrains/plugins/ipnb/psi/IpnbPyReference.java +++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/psi/IpnbPyReference.java @@ -1,6 +1,7 @@ package org.jetbrains.plugins.ipnb.psi; import com.google.common.collect.Lists; +import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.editor.Editor; import com.intellij.psi.PsiDocumentManager; @@ -31,8 +32,8 @@ public class IpnbPyReference extends PyReferenceImpl { @NotNull @Override - public Object[] getVariants() { - final List variants = Lists.newArrayList(); + public LookupElement[] getVariants() { + final List variants = Lists.newArrayList(); Collections.addAll(variants, super.getVariants()); PsiFile file = myElement.getContainingFile(); if (file instanceof IpnbPyFragment) { @@ -50,7 +51,7 @@ public class IpnbPyReference extends PyReferenceImpl { variants.addAll(getOriginalElements(processor)); } } - return variants.toArray(); + return variants.toArray(LookupElement.EMPTY_ARRAY); } @NotNull diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyType.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyType.java index 4cb8dd4a10a2..77cecdfe81e8 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/PyType.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyType.java @@ -15,6 +15,7 @@ */ package com.jetbrains.python.psi.types; +import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.openapi.util.Key; import com.intellij.psi.PsiElement; import com.intellij.util.ProcessingContext; @@ -71,7 +72,7 @@ public interface PyType { * @param context to share state between nested invocations * @return completion variants good for {@link com.intellij.psi.PsiReference#getVariants} return value. */ - Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context); + LookupElement[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context); /** * Context key for access to a set of names already found by variant search. diff --git a/python/src/com/jetbrains/python/PyCustomType.java b/python/src/com/jetbrains/python/PyCustomType.java index ecf36c9e4a69..d35dafeea14d 100644 --- a/python/src/com/jetbrains/python/PyCustomType.java +++ b/python/src/com/jetbrains/python/PyCustomType.java @@ -21,7 +21,6 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.util.QualifiedName; -import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; import com.intellij.util.Processor; import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider; @@ -200,14 +199,14 @@ public final class PyCustomType implements PyClassLikeType { } @Override - public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { - final List lookupElements = new ArrayList<>(); + public LookupElement[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { + final List lookupElements = new ArrayList<>(); for (PyClassLikeType parentType : myTypesToMimic) { lookupElements.addAll(Collections2.filter(Arrays.asList(parentType.getCompletionVariants(completionPrefix, location, context)), new CompletionFilter())); } - return ArrayUtil.toObjectArray(lookupElements); + return lookupElements.toArray(LookupElement.EMPTY_ARRAY); } diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java index 186f513fdcdf..f3f486f2780f 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java @@ -1,9 +1,9 @@ // Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.jetbrains.python.codeInsight.stdlib; +import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.psi.PsiElement; -import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.psi.*; @@ -64,13 +64,13 @@ public class PyNamedTupleType extends PyTupleType implements PyCallableType { } @Override - public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { - final List result = new ArrayList<>(); + public LookupElement[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { + final List result = new ArrayList<>(); Collections.addAll(result, super.getCompletionVariants(completionPrefix, location, context)); for (String field : myFields.keySet()) { result.add(LookupElementBuilder.create(field)); } - return ArrayUtil.toObjectArray(result); + return result.toArray(LookupElement.EMPTY_ARRAY); } @NotNull diff --git a/python/src/com/jetbrains/python/documentation/doctest/PyDocReference.java b/python/src/com/jetbrains/python/documentation/doctest/PyDocReference.java index 9f7d40d9df21..3f1ccf4695c7 100644 --- a/python/src/com/jetbrains/python/documentation/doctest/PyDocReference.java +++ b/python/src/com/jetbrains/python/documentation/doctest/PyDocReference.java @@ -17,6 +17,7 @@ package com.jetbrains.python.documentation.doctest; import com.google.common.collect.Lists; import com.intellij.codeInsight.completion.CompletionUtil; +import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.lang.ASTNode; import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.lang.injection.InjectedLanguageManager; @@ -149,11 +150,10 @@ public class PyDocReference extends PyReferenceImpl { } @NotNull - public Object[] getVariants() { - final ArrayList ret = Lists.newArrayList(super.getVariants()); - final PsiElement originalElement = CompletionUtil.getOriginalElement(myElement); - final PyQualifiedExpression element = originalElement instanceof PyQualifiedExpression ? - (PyQualifiedExpression)originalElement : myElement; + public LookupElement[] getVariants() { + final ArrayList ret = Lists.newArrayList(super.getVariants()); + final PyQualifiedExpression originalElement = CompletionUtil.getOriginalElement(myElement); + final PyQualifiedExpression element = originalElement != null ? originalElement : myElement; final ScopeOwner scopeOwner = getHostScopeOwner(); if (scopeOwner != null) { @@ -161,7 +161,7 @@ public class PyDocReference extends PyReferenceImpl { PyResolveUtil.scopeCrawlUp(processor, scopeOwner, null, null); ret.addAll(processor.getResultList()); } - return ret.toArray(); + return ret.toArray(LookupElement.EMPTY_ARRAY); } diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyImportReference.java b/python/src/com/jetbrains/python/psi/impl/references/PyImportReference.java index 3c3612c54bfc..45540305cba0 100644 --- a/python/src/com/jetbrains/python/psi/impl/references/PyImportReference.java +++ b/python/src/com/jetbrains/python/psi/impl/references/PyImportReference.java @@ -27,7 +27,6 @@ import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.QualifiedName; -import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; import com.jetbrains.python.PyNames; import com.jetbrains.python.PyTokenTypes; @@ -83,18 +82,18 @@ public class PyImportReference extends PyReferenceImpl { protected List resolveInner() { final PyImportElement parent = PsiTreeUtil.getParentOfType(myElement, PyImportElement.class); //importRef.getParent(); final QualifiedName qname = myElement.asQualifiedName(); - return qname == null ? Collections.emptyList() : ResolveImportUtil.resolveNameInImportStatement(parent, qname); + return qname == null || parent == null ? Collections.emptyList() : ResolveImportUtil.resolveNameInImportStatement(parent, qname); } @NotNull @Override - public Object[] getVariants() { + public LookupElement[] getVariants() { // no completion in invalid import statements PyImportElement importElement = PsiTreeUtil.getParentOfType(myElement, PyImportElement.class); if (importElement != null) { PsiErrorElement prevError = PsiTreeUtil.getPrevSiblingOfType(importElement, PsiErrorElement.class); if (prevError != null) { - return ArrayUtil.EMPTY_OBJECT_ARRAY; + return LookupElement.EMPTY_ARRAY; } } @@ -105,14 +104,14 @@ public class PyImportReference extends PyReferenceImpl { // qualifier's type must be module, it should know how to complete PyType type = context.getType(qualifier); if (type != null) { - Object[] variants = getTypeCompletionVariants(myElement, type); + LookupElement[] variants = getTypeCompletionVariants(myElement, type); if (!alreadyHasImportKeyword()) { replaceInsertHandler(variants, ImportKeywordHandler.INSTANCE); } return variants; } else { - return ArrayUtil.EMPTY_OBJECT_ARRAY; + return LookupElement.EMPTY_ARRAY; } } else { @@ -173,7 +172,7 @@ public class PyImportReference extends PyReferenceImpl { class ImportVariantCollector { private final PsiFile myCurrentFile; private final Set myNamesAlready; - private final List myObjects; + private final List myObjects; @NotNull private final TypeEvalContext myContext; public ImportVariantCollector(@NotNull TypeEvalContext context) { @@ -185,7 +184,7 @@ public class PyImportReference extends PyReferenceImpl { myObjects = new ArrayList<>(); } - public Object[] execute() { + public LookupElement[] execute() { int relativeLevel = -1; InsertHandler insertHandler = null; @@ -206,11 +205,11 @@ public class PyImportReference extends PyReferenceImpl { ctx.put(PyType.CTX_NAMES, myNamesAlready); Collections.addAll(myObjects, qualifierType.getCompletionVariants(myElement.getName(), myElement, ctx)); } - return myObjects.toArray(); + return myObjects.toArray(LookupElement.EMPTY_ARRAY); } else if (modCandidate instanceof PsiDirectory) { fillFromDir((PsiDirectory)modCandidate, ImportKeywordHandler.INSTANCE); - return myObjects.toArray(); + return myObjects.toArray(LookupElement.EMPTY_ARRAY); } } else { // null source, must be a "from ... import" @@ -262,7 +261,7 @@ public class PyImportReference extends PyReferenceImpl { fillFromQName(QualifiedName.fromComponents(), insertHandler); } - return ArrayUtil.toObjectArray(myObjects); + return myObjects.toArray(LookupElement.EMPTY_ARRAY); } private void fillFromQName(QualifiedName thisQName, InsertHandler insertHandler) { @@ -291,7 +290,7 @@ public class PyImportReference extends PyReferenceImpl { PyModuleType moduleType = new PyModuleType((PyFile)initPy); ProcessingContext context = new ProcessingContext(); context.put(PyType.CTX_NAMES, myNamesAlready); - Object[] completionVariants = moduleType.getCompletionVariants("", getElement(), context); + LookupElement[] completionVariants = moduleType.getCompletionVariants("", getElement(), context); if (insertHandler != null) { replaceInsertHandler(completionVariants, insertHandler); } diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyQualifiedReference.java b/python/src/com/jetbrains/python/psi/impl/references/PyQualifiedReference.java index b7576cdec096..b6a06449adb5 100644 --- a/python/src/com/jetbrains/python/psi/impl/references/PyQualifiedReference.java +++ b/python/src/com/jetbrains/python/psi/impl/references/PyQualifiedReference.java @@ -30,7 +30,6 @@ import com.intellij.psi.search.ProjectScope; import com.intellij.psi.stubs.StubUpdatingIndex; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.QualifiedName; -import com.intellij.util.ArrayUtil; import com.intellij.util.PlatformIcons; import com.intellij.util.ProcessingContext; import com.intellij.util.indexing.FileBasedIndex; @@ -255,13 +254,13 @@ public class PyQualifiedReference extends PyReferenceImpl { @NotNull @Override - public Object[] getVariants() { + public LookupElement[] getVariants() { PyExpression qualifier = myElement.getQualifier(); if (qualifier != null) { qualifier = CompletionUtil.getOriginalOrSelf(qualifier); } if (qualifier == null) { - return EMPTY_ARRAY; + return LookupElement.EMPTY_ARRAY; } final PyQualifiedExpression element = CompletionUtil.getOriginalOrSelf(myElement); @@ -269,7 +268,7 @@ public class PyQualifiedReference extends PyReferenceImpl { ProcessingContext ctx = new ProcessingContext(); final Set namesAlready = new HashSet<>(); ctx.put(PyType.CTX_NAMES, namesAlready); - final Collection variants = new ArrayList<>(); + final Collection variants = new ArrayList<>(); if (qualifierType != null) { Collections.addAll(variants, getVariantFromHasAttr(qualifier)); if (qualifierType instanceof PyStructuralType && ((PyStructuralType)qualifierType).isInferredFromUsages()) { @@ -304,7 +303,7 @@ public class PyQualifiedReference extends PyReferenceImpl { } } Collections.addAll(variants, qualifierType.getCompletionVariants(element.getName(), element, ctx)); - return variants.toArray(); + return variants.toArray(LookupElement.EMPTY_ARRAY); } else { return qualifierType.getCompletionVariants(element.getName(), element, ctx); @@ -318,12 +317,12 @@ public class PyQualifiedReference extends PyReferenceImpl { if (qualifier instanceof PyReferenceExpression) { Collections.addAll(variants, collectSeenMembers(qualifier.getText())); } - return variants.toArray(); + return variants.toArray(LookupElement.EMPTY_ARRAY); } } - private Object[] getVariantFromHasAttr(PyExpression qualifier) { - Collection variants = new ArrayList<>(); + private LookupElement[] getVariantFromHasAttr(PyExpression qualifier) { + Collection variants = new ArrayList<>(); PyIfStatement ifStatement = PsiTreeUtil.getParentOfType(myElement, PyIfStatement.class); while (ifStatement != null) { PyExpression condition = ifStatement.getIfPart().getCondition(); @@ -331,12 +330,14 @@ public class PyQualifiedReference extends PyReferenceImpl { PyCallExpression call = (PyCallExpression)condition; if (call.getArguments().length > 1 && call.getArguments()[0].getText().equals(qualifier.getText())) { PyStringLiteralExpression string = call.getArgument(1, PyStringLiteralExpression.class); - if (string != null && StringUtil.isJavaIdentifier(string.getStringValue())) variants.add(string.getStringValue()); + if (string != null && StringUtil.isJavaIdentifier(string.getStringValue())) { + variants.add(LookupElementBuilder.create(string.getStringValue())); + } } } ifStatement = PsiTreeUtil.getParentOfType(ifStatement, PyIfStatement.class); } - return variants.toArray(); + return variants.toArray(LookupElement.EMPTY_ARRAY); } @Nullable @@ -375,7 +376,7 @@ public class PyQualifiedReference extends PyReferenceImpl { return result; } - private Object[] collectSeenMembers(final String text) { + private LookupElement[] collectSeenMembers(final String text) { final Set members = new HashSet<>(); myElement.getContainingFile().accept(new PyRecursiveElementVisitor() { @Override @@ -406,7 +407,7 @@ public class PyQualifiedReference extends PyReferenceImpl { for (String member : members) { results.add(AutoCompletionPolicy.NEVER_AUTOCOMPLETE.applyPolicy(LookupElementBuilder.create(member))); } - return ArrayUtil.toObjectArray(results); + return results.toArray(LookupElement.EMPTY_ARRAY); } /** diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java index a229b7aa3bdc..c83160e165b8 100644 --- a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java @@ -671,7 +671,7 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference @Override @NotNull - public Object[] getVariants() { + public LookupElement[] getVariants() { final List ret = Lists.newArrayList(); // Use real context here to enable correct completion and resolve in case of PyExpressionCodeFragment!!! @@ -734,7 +734,7 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference } ret.addAll(getOriginalElements(processor)); - return ret.toArray(); + return ret.toArray(LookupElement.EMPTY_ARRAY); } /** @@ -816,7 +816,7 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference return myElement.hashCode(); } - protected static Object[] getTypeCompletionVariants(PyExpression pyExpression, PyType type) { + protected static LookupElement[] getTypeCompletionVariants(PyExpression pyExpression, PyType type) { ProcessingContext context = new ProcessingContext(); context.put(PyType.CTX_NAMES, new HashSet<>()); return type.getCompletionVariants(pyExpression.getName(), pyExpression, context); diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyTargetReference.java b/python/src/com/jetbrains/python/psi/impl/references/PyTargetReference.java index 1afc36971114..0014abd95c1a 100644 --- a/python/src/com/jetbrains/python/psi/impl/references/PyTargetReference.java +++ b/python/src/com/jetbrains/python/psi/impl/references/PyTargetReference.java @@ -15,11 +15,11 @@ */ package com.jetbrains.python.psi.impl.references; +import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementResolveResult; import com.intellij.psi.ResolveResult; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.util.ArrayUtil; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyFunction; import com.jetbrains.python.psi.PyImportElement; @@ -57,11 +57,11 @@ public class PyTargetReference extends PyReferenceImpl { @NotNull @Override - public Object[] getVariants() { + public LookupElement[] getVariants() { final PyImportElement importElement = PsiTreeUtil.getParentOfType(myElement, PyImportElement.class); // reference completion is useless in 'as' part of import statement (PY-2384) if (importElement != null && myElement == importElement.getAsNameElement()) { - return ArrayUtil.EMPTY_OBJECT_ARRAY; + return LookupElement.EMPTY_ARRAY; } return super.getVariants(); } diff --git a/python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java index bffa3cc6cc4c..b47166d7515f 100644 --- a/python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java @@ -15,9 +15,9 @@ */ package com.jetbrains.python.psi.types; +import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; -import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; import com.jetbrains.python.PyNames; import com.jetbrains.python.psi.AccessDirection; @@ -70,8 +70,8 @@ public class PyCallableTypeImpl implements PyCallableType { } @Override - public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { - return ArrayUtil.EMPTY_OBJECT_ARRAY; + public LookupElement[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { + return LookupElement.EMPTY_ARRAY; } @Nullable diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index ff8d8aa71f42..6b7b497712ec 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -11,7 +11,6 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiInvalidElementAccessException; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.QualifiedName; -import com.intellij.util.ArrayUtil; import com.intellij.util.ObjectUtils; import com.intellij.util.ProcessingContext; import com.intellij.util.Processor; @@ -542,21 +541,21 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { public static final Key CTX_SUPPRESS_PARENTHESES = Key.create("PyFunction.SuppressParentheses"); @Override - public Object[] getCompletionVariants(String prefix, PsiElement location, ProcessingContext context) { + public LookupElement[] getCompletionVariants(String prefix, PsiElement location, ProcessingContext context) { Set visited = context.get(CTX_VISITED); if (visited == null) { visited = new HashSet<>(); context.put(CTX_VISITED, visited); } if (visited.contains(this)) { - return ArrayUtil.EMPTY_OBJECT_ARRAY; + return LookupElement.EMPTY_ARRAY; } visited.add(this); Set namesAlready = context.get(CTX_NAMES); if (namesAlready == null) { namesAlready = new HashSet<>(); } - List ret = new ArrayList<>(); + List ret = new ArrayList<>(); boolean suppressParentheses = context.get(CTX_SUPPRESS_PARENTHESES) != null; addOwnClassMembers(location, namesAlready, suppressParentheses, ret, prefix); @@ -590,32 +589,33 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { Collections.addAll(ret, getMetaClassCompletionVariants(prefix, location, context, typeEvalContext)); - return ret.toArray(); + return ret.toArray(LookupElement.EMPTY_ARRAY); } @NotNull - private Object[] getMetaClassCompletionVariants(@Nullable String prefix, + private LookupElement[] getMetaClassCompletionVariants(@Nullable String prefix, @Nullable PsiElement location, @NotNull ProcessingContext processingContext, @NotNull TypeEvalContext typeEvalContext) { if (!myClass.isNewStyleClass(typeEvalContext)) { - return ArrayUtil.EMPTY_OBJECT_ARRAY; + return LookupElement.EMPTY_ARRAY; } final PyClassLikeType typeType = getMetaClassType(typeEvalContext, true); if (typeType == null) { - return ArrayUtil.EMPTY_OBJECT_ARRAY; + return LookupElement.EMPTY_ARRAY; } if (isDefinition()) { return typeType.getCompletionVariants(prefix, location, processingContext); } else if (typeType instanceof PyClassType) { - final List typeInstanceAttributes = ((PyClassType)typeType).getPyClass().getInstanceAttributes(); - return ContainerUtil.map2Array(typeInstanceAttributes, LookupElementBuilder::create); + return StreamEx.of(((PyClassType)typeType).getPyClass().getInstanceAttributes()) + .map(LookupElementBuilder::create) + .toArray(LookupElement.EMPTY_ARRAY); } - return ArrayUtil.EMPTY_OBJECT_ARRAY; + return LookupElement.EMPTY_ARRAY; } @Override @@ -729,7 +729,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { private void addOwnClassMembers(PsiElement expressionHook, Set namesAlready, boolean suppressParentheses, - List ret, + List ret, @Nullable final String prefix) { PyClass containingClass = PsiTreeUtil.getParentOfType(expressionHook, PyClass.class); if (containingClass != null) { @@ -781,7 +781,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { PsiElement expressionHook, Set namesAlready, ProcessingContext context, - List ret, + List ret, @NotNull TypeEvalContext typeEvalContext) { for (PyType type : myClass.getSuperClassTypes(typeEvalContext)) { if (!(type instanceof PyClassLikeType)) { @@ -793,8 +793,8 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { type = classLikeType.toInstance(); } - Object[] ancestry = type.getCompletionVariants(name, expressionHook, context); - for (Object ob : ancestry) { + LookupElement[] ancestry = type.getCompletionVariants(name, expressionHook, context); + for (LookupElement ob : ancestry) { String inheritedName = ob.toString(); if (!namesAlready.contains(inheritedName) && !PyUtil.isClassPrivateName(inheritedName)) { ret.add(ob); @@ -848,10 +848,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { } public static boolean is(@NotNull String qName, PyType type) { - if (type instanceof PyClassType) { - return qName.equals(((PyClassType)type).getClassQName()); - } - return false; + return type instanceof PyClassType && qName.equals(((PyClassType)type).getClassQName()); } @Override diff --git a/python/src/com/jetbrains/python/psi/types/PyFunctionTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyFunctionTypeImpl.java index 737332cd0bb7..608e0eb62a6b 100644 --- a/python/src/com/jetbrains/python/psi/types/PyFunctionTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyFunctionTypeImpl.java @@ -15,9 +15,9 @@ */ package com.jetbrains.python.psi.types; +import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.psi.PsiElement; import com.intellij.psi.util.QualifiedName; -import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.PyNames; @@ -83,7 +83,7 @@ public class PyFunctionTypeImpl implements PyFunctionType { } @Override - public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { + public LookupElement[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { final TypeEvalContext typeEvalContext = TypeEvalContext.codeCompletion(location.getProject(), location.getContainingFile()); final PyClassType delegate; if (location instanceof PyReferenceExpression) { @@ -95,7 +95,7 @@ public class PyFunctionTypeImpl implements PyFunctionType { delegate = cls != null ? new PyClassTypeImpl(cls, false) : null; } if (delegate == null) { - return ArrayUtil.EMPTY_OBJECT_ARRAY; + return LookupElement.EMPTY_ARRAY; } return delegate.getCompletionVariants(completionPrefix, location, context); } diff --git a/python/src/com/jetbrains/python/psi/types/PyGenericType.java b/python/src/com/jetbrains/python/psi/types/PyGenericType.java index c3d0b0bba176..be61c55a0444 100644 --- a/python/src/com/jetbrains/python/psi/types/PyGenericType.java +++ b/python/src/com/jetbrains/python/psi/types/PyGenericType.java @@ -15,8 +15,8 @@ */ package com.jetbrains.python.psi.types; +import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.psi.PsiElement; -import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; import com.jetbrains.python.psi.AccessDirection; import com.jetbrains.python.psi.PyExpression; @@ -68,8 +68,8 @@ public class PyGenericType implements PyType, PyInstantiableType } @Override - public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { - return ArrayUtil.EMPTY_OBJECT_ARRAY; + public LookupElement[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { + return LookupElement.EMPTY_ARRAY; } @NotNull diff --git a/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java b/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java index 457447b92668..623a03f3c39e 100644 --- a/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java +++ b/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java @@ -6,7 +6,6 @@ import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileSystemItem; -import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; import com.intellij.util.SmartList; import com.jetbrains.python.psi.AccessDirection; @@ -80,7 +79,7 @@ public class PyImportedModuleType implements PyType { } @Override - public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { + public LookupElement[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { final List result = new ArrayList<>(); for (PsiElement resolveResult : ResolveResultList.getElements(myImportedModule.multiResolve())) { if (resolveResult instanceof PyFile) { @@ -101,7 +100,7 @@ public class PyImportedModuleType implements PyType { } } } - return ArrayUtil.toObjectArray(result); + return result.toArray(LookupElement.EMPTY_ARRAY); } @Override diff --git a/python/src/com/jetbrains/python/psi/types/PyModuleType.java b/python/src/com/jetbrains/python/psi/types/PyModuleType.java index 5e8e178ef89e..b7b98a2a2684 100644 --- a/python/src/com/jetbrains/python/psi/types/PyModuleType.java +++ b/python/src/com/jetbrains/python/psi/types/PyModuleType.java @@ -399,10 +399,10 @@ public class PyModuleType implements PyType { // Modules don't descend from obje } @Override - public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { + public LookupElement[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { final TypeEvalContext typeEvalContext = TypeEvalContext.codeCompletion(location.getProject(), location.getContainingFile()); final List result = getCompletionVariantsAsLookupElements(location, context, false, false, typeEvalContext); - return result.toArray(); + return result.toArray(LookupElement.EMPTY_ARRAY); } @NotNull diff --git a/python/src/com/jetbrains/python/psi/types/PyNoneType.java b/python/src/com/jetbrains/python/psi/types/PyNoneType.java index 18e83a86dd8f..d9cdf2c15f69 100644 --- a/python/src/com/jetbrains/python/psi/types/PyNoneType.java +++ b/python/src/com/jetbrains/python/psi/types/PyNoneType.java @@ -15,8 +15,8 @@ */ package com.jetbrains.python.psi.types; +import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.psi.PsiElement; -import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; import com.jetbrains.python.psi.AccessDirection; import com.jetbrains.python.psi.PyExpression; @@ -44,8 +44,8 @@ public class PyNoneType implements PyType { // TODO must extend ClassType. It's return null; } - public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { - return ArrayUtil.EMPTY_OBJECT_ARRAY; + public LookupElement[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { + return LookupElement.EMPTY_ARRAY; } public String getName() { diff --git a/python/src/com/jetbrains/python/psi/types/PyStructuralType.java b/python/src/com/jetbrains/python/psi/types/PyStructuralType.java index 67de94c2f3d8..908586063328 100644 --- a/python/src/com/jetbrains/python/psi/types/PyStructuralType.java +++ b/python/src/com/jetbrains/python/psi/types/PyStructuralType.java @@ -15,6 +15,7 @@ */ package com.jetbrains.python.psi.types; +import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; @@ -27,7 +28,10 @@ import com.jetbrains.python.psi.resolve.RatedResolveResult; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; /** * @author vlan @@ -51,14 +55,14 @@ public class PyStructuralType implements PyType { } @Override - public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { - final List variants = new ArrayList<>(); + public LookupElement[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { + final List variants = new ArrayList<>(); for (String attribute : myAttributes) { if (!attribute.equals(completionPrefix)) { variants.add(LookupElementBuilder.create(attribute).withIcon(PlatformIcons.FIELD_ICON)); } } - return variants.toArray(); + return variants.toArray(LookupElement.EMPTY_ARRAY); } @Nullable diff --git a/python/src/com/jetbrains/python/psi/types/PyUnionType.java b/python/src/com/jetbrains/python/psi/types/PyUnionType.java index 2677bd1ba357..fbeaf8063fc4 100644 --- a/python/src/com/jetbrains/python/psi/types/PyUnionType.java +++ b/python/src/com/jetbrains/python/psi/types/PyUnionType.java @@ -15,9 +15,9 @@ */ package com.jetbrains.python.psi.types; +import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; -import com.intellij.util.ArrayUtil; import com.intellij.util.NullableFunction; import com.intellij.util.ProcessingContext; import com.intellij.util.SmartList; @@ -59,14 +59,14 @@ public class PyUnionType implements PyType { return allNulls ? null : ret; } - public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { - Set variants = new HashSet<>(); + public LookupElement[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { + Set variants = new HashSet<>(); for (PyType member : myMembers) { if (member != null) { Collections.addAll(variants, member.getCompletionVariants(completionPrefix, location, context)); } } - return ArrayUtil.toObjectArray(variants); + return variants.toArray(LookupElement.EMPTY_ARRAY); } public String getName() { From 2f6b7e275e1646765164c75da1bd54e6c5d38707 Mon Sep 17 00:00:00 2001 From: Anton Bragin Date: Tue, 10 Apr 2018 12:10:10 +0300 Subject: [PATCH 04/53] getVariants() module dependency fix --- python/psi-api/intellij.python.psi.iml | 1 + 1 file changed, 1 insertion(+) diff --git a/python/psi-api/intellij.python.psi.iml b/python/psi-api/intellij.python.psi.iml index 55b34ece4ab2..9516d9f8b92d 100644 --- a/python/psi-api/intellij.python.psi.iml +++ b/python/psi-api/intellij.python.psi.iml @@ -12,5 +12,6 @@ + \ No newline at end of file From 6cbc1826ef9c92069ebf70088d8816a0ae3a0d01 Mon Sep 17 00:00:00 2001 From: Anton Bragin Date: Tue, 10 Apr 2018 12:22:11 +0300 Subject: [PATCH 05/53] Revert getVariants() in PyReferenceImpl signature This reverts commit a8d9728 --- .../plugins/ipnb/psi/IpnbPyReference.java | 7 ++-- python/psi-api/intellij.python.psi.iml | 1 - .../jetbrains/python/psi/types/PyType.java | 3 +- .../com/jetbrains/python/PyCustomType.java | 7 ++-- .../codeInsight/stdlib/PyNamedTupleType.java | 8 ++--- .../documentation/doctest/PyDocReference.java | 12 +++---- .../impl/references/PyImportReference.java | 23 ++++++------ .../impl/references/PyQualifiedReference.java | 25 +++++++------ .../psi/impl/references/PyReferenceImpl.java | 6 ++-- .../impl/references/PyTargetReference.java | 6 ++-- .../python/psi/types/PyCallableTypeImpl.java | 6 ++-- .../python/psi/types/PyClassTypeImpl.java | 35 ++++++++++--------- .../python/psi/types/PyFunctionTypeImpl.java | 6 ++-- .../python/psi/types/PyGenericType.java | 6 ++-- .../psi/types/PyImportedModuleType.java | 5 +-- .../python/psi/types/PyModuleType.java | 4 +-- .../python/psi/types/PyNoneType.java | 6 ++-- .../python/psi/types/PyStructuralType.java | 12 +++---- .../python/psi/types/PyUnionType.java | 8 ++--- 19 files changed, 92 insertions(+), 94 deletions(-) diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/psi/IpnbPyReference.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/psi/IpnbPyReference.java index 792c511334dc..f943cc6dac1e 100644 --- a/python/ipnb/src/org/jetbrains/plugins/ipnb/psi/IpnbPyReference.java +++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/psi/IpnbPyReference.java @@ -1,7 +1,6 @@ package org.jetbrains.plugins.ipnb.psi; import com.google.common.collect.Lists; -import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.editor.Editor; import com.intellij.psi.PsiDocumentManager; @@ -32,8 +31,8 @@ public class IpnbPyReference extends PyReferenceImpl { @NotNull @Override - public LookupElement[] getVariants() { - final List variants = Lists.newArrayList(); + public Object[] getVariants() { + final List variants = Lists.newArrayList(); Collections.addAll(variants, super.getVariants()); PsiFile file = myElement.getContainingFile(); if (file instanceof IpnbPyFragment) { @@ -51,7 +50,7 @@ public class IpnbPyReference extends PyReferenceImpl { variants.addAll(getOriginalElements(processor)); } } - return variants.toArray(LookupElement.EMPTY_ARRAY); + return variants.toArray(); } @NotNull diff --git a/python/psi-api/intellij.python.psi.iml b/python/psi-api/intellij.python.psi.iml index 9516d9f8b92d..55b34ece4ab2 100644 --- a/python/psi-api/intellij.python.psi.iml +++ b/python/psi-api/intellij.python.psi.iml @@ -12,6 +12,5 @@ - \ No newline at end of file diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyType.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyType.java index 77cecdfe81e8..4cb8dd4a10a2 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/PyType.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyType.java @@ -15,7 +15,6 @@ */ package com.jetbrains.python.psi.types; -import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.openapi.util.Key; import com.intellij.psi.PsiElement; import com.intellij.util.ProcessingContext; @@ -72,7 +71,7 @@ public interface PyType { * @param context to share state between nested invocations * @return completion variants good for {@link com.intellij.psi.PsiReference#getVariants} return value. */ - LookupElement[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context); + Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context); /** * Context key for access to a set of names already found by variant search. diff --git a/python/src/com/jetbrains/python/PyCustomType.java b/python/src/com/jetbrains/python/PyCustomType.java index d35dafeea14d..ecf36c9e4a69 100644 --- a/python/src/com/jetbrains/python/PyCustomType.java +++ b/python/src/com/jetbrains/python/PyCustomType.java @@ -21,6 +21,7 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.util.QualifiedName; +import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; import com.intellij.util.Processor; import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider; @@ -199,14 +200,14 @@ public final class PyCustomType implements PyClassLikeType { } @Override - public LookupElement[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { - final List lookupElements = new ArrayList<>(); + public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { + final List lookupElements = new ArrayList<>(); for (PyClassLikeType parentType : myTypesToMimic) { lookupElements.addAll(Collections2.filter(Arrays.asList(parentType.getCompletionVariants(completionPrefix, location, context)), new CompletionFilter())); } - return lookupElements.toArray(LookupElement.EMPTY_ARRAY); + return ArrayUtil.toObjectArray(lookupElements); } diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java index f3f486f2780f..186f513fdcdf 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java @@ -1,9 +1,9 @@ // Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.jetbrains.python.codeInsight.stdlib; -import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.psi.PsiElement; +import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.psi.*; @@ -64,13 +64,13 @@ public class PyNamedTupleType extends PyTupleType implements PyCallableType { } @Override - public LookupElement[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { - final List result = new ArrayList<>(); + public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { + final List result = new ArrayList<>(); Collections.addAll(result, super.getCompletionVariants(completionPrefix, location, context)); for (String field : myFields.keySet()) { result.add(LookupElementBuilder.create(field)); } - return result.toArray(LookupElement.EMPTY_ARRAY); + return ArrayUtil.toObjectArray(result); } @NotNull diff --git a/python/src/com/jetbrains/python/documentation/doctest/PyDocReference.java b/python/src/com/jetbrains/python/documentation/doctest/PyDocReference.java index 3f1ccf4695c7..9f7d40d9df21 100644 --- a/python/src/com/jetbrains/python/documentation/doctest/PyDocReference.java +++ b/python/src/com/jetbrains/python/documentation/doctest/PyDocReference.java @@ -17,7 +17,6 @@ package com.jetbrains.python.documentation.doctest; import com.google.common.collect.Lists; import com.intellij.codeInsight.completion.CompletionUtil; -import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.lang.ASTNode; import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.lang.injection.InjectedLanguageManager; @@ -150,10 +149,11 @@ public class PyDocReference extends PyReferenceImpl { } @NotNull - public LookupElement[] getVariants() { - final ArrayList ret = Lists.newArrayList(super.getVariants()); - final PyQualifiedExpression originalElement = CompletionUtil.getOriginalElement(myElement); - final PyQualifiedExpression element = originalElement != null ? originalElement : myElement; + public Object[] getVariants() { + final ArrayList ret = Lists.newArrayList(super.getVariants()); + final PsiElement originalElement = CompletionUtil.getOriginalElement(myElement); + final PyQualifiedExpression element = originalElement instanceof PyQualifiedExpression ? + (PyQualifiedExpression)originalElement : myElement; final ScopeOwner scopeOwner = getHostScopeOwner(); if (scopeOwner != null) { @@ -161,7 +161,7 @@ public class PyDocReference extends PyReferenceImpl { PyResolveUtil.scopeCrawlUp(processor, scopeOwner, null, null); ret.addAll(processor.getResultList()); } - return ret.toArray(LookupElement.EMPTY_ARRAY); + return ret.toArray(); } diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyImportReference.java b/python/src/com/jetbrains/python/psi/impl/references/PyImportReference.java index 45540305cba0..3c3612c54bfc 100644 --- a/python/src/com/jetbrains/python/psi/impl/references/PyImportReference.java +++ b/python/src/com/jetbrains/python/psi/impl/references/PyImportReference.java @@ -27,6 +27,7 @@ import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.QualifiedName; +import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; import com.jetbrains.python.PyNames; import com.jetbrains.python.PyTokenTypes; @@ -82,18 +83,18 @@ public class PyImportReference extends PyReferenceImpl { protected List resolveInner() { final PyImportElement parent = PsiTreeUtil.getParentOfType(myElement, PyImportElement.class); //importRef.getParent(); final QualifiedName qname = myElement.asQualifiedName(); - return qname == null || parent == null ? Collections.emptyList() : ResolveImportUtil.resolveNameInImportStatement(parent, qname); + return qname == null ? Collections.emptyList() : ResolveImportUtil.resolveNameInImportStatement(parent, qname); } @NotNull @Override - public LookupElement[] getVariants() { + public Object[] getVariants() { // no completion in invalid import statements PyImportElement importElement = PsiTreeUtil.getParentOfType(myElement, PyImportElement.class); if (importElement != null) { PsiErrorElement prevError = PsiTreeUtil.getPrevSiblingOfType(importElement, PsiErrorElement.class); if (prevError != null) { - return LookupElement.EMPTY_ARRAY; + return ArrayUtil.EMPTY_OBJECT_ARRAY; } } @@ -104,14 +105,14 @@ public class PyImportReference extends PyReferenceImpl { // qualifier's type must be module, it should know how to complete PyType type = context.getType(qualifier); if (type != null) { - LookupElement[] variants = getTypeCompletionVariants(myElement, type); + Object[] variants = getTypeCompletionVariants(myElement, type); if (!alreadyHasImportKeyword()) { replaceInsertHandler(variants, ImportKeywordHandler.INSTANCE); } return variants; } else { - return LookupElement.EMPTY_ARRAY; + return ArrayUtil.EMPTY_OBJECT_ARRAY; } } else { @@ -172,7 +173,7 @@ public class PyImportReference extends PyReferenceImpl { class ImportVariantCollector { private final PsiFile myCurrentFile; private final Set myNamesAlready; - private final List myObjects; + private final List myObjects; @NotNull private final TypeEvalContext myContext; public ImportVariantCollector(@NotNull TypeEvalContext context) { @@ -184,7 +185,7 @@ public class PyImportReference extends PyReferenceImpl { myObjects = new ArrayList<>(); } - public LookupElement[] execute() { + public Object[] execute() { int relativeLevel = -1; InsertHandler insertHandler = null; @@ -205,11 +206,11 @@ public class PyImportReference extends PyReferenceImpl { ctx.put(PyType.CTX_NAMES, myNamesAlready); Collections.addAll(myObjects, qualifierType.getCompletionVariants(myElement.getName(), myElement, ctx)); } - return myObjects.toArray(LookupElement.EMPTY_ARRAY); + return myObjects.toArray(); } else if (modCandidate instanceof PsiDirectory) { fillFromDir((PsiDirectory)modCandidate, ImportKeywordHandler.INSTANCE); - return myObjects.toArray(LookupElement.EMPTY_ARRAY); + return myObjects.toArray(); } } else { // null source, must be a "from ... import" @@ -261,7 +262,7 @@ public class PyImportReference extends PyReferenceImpl { fillFromQName(QualifiedName.fromComponents(), insertHandler); } - return myObjects.toArray(LookupElement.EMPTY_ARRAY); + return ArrayUtil.toObjectArray(myObjects); } private void fillFromQName(QualifiedName thisQName, InsertHandler insertHandler) { @@ -290,7 +291,7 @@ public class PyImportReference extends PyReferenceImpl { PyModuleType moduleType = new PyModuleType((PyFile)initPy); ProcessingContext context = new ProcessingContext(); context.put(PyType.CTX_NAMES, myNamesAlready); - LookupElement[] completionVariants = moduleType.getCompletionVariants("", getElement(), context); + Object[] completionVariants = moduleType.getCompletionVariants("", getElement(), context); if (insertHandler != null) { replaceInsertHandler(completionVariants, insertHandler); } diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyQualifiedReference.java b/python/src/com/jetbrains/python/psi/impl/references/PyQualifiedReference.java index b6a06449adb5..b7576cdec096 100644 --- a/python/src/com/jetbrains/python/psi/impl/references/PyQualifiedReference.java +++ b/python/src/com/jetbrains/python/psi/impl/references/PyQualifiedReference.java @@ -30,6 +30,7 @@ import com.intellij.psi.search.ProjectScope; import com.intellij.psi.stubs.StubUpdatingIndex; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.QualifiedName; +import com.intellij.util.ArrayUtil; import com.intellij.util.PlatformIcons; import com.intellij.util.ProcessingContext; import com.intellij.util.indexing.FileBasedIndex; @@ -254,13 +255,13 @@ public class PyQualifiedReference extends PyReferenceImpl { @NotNull @Override - public LookupElement[] getVariants() { + public Object[] getVariants() { PyExpression qualifier = myElement.getQualifier(); if (qualifier != null) { qualifier = CompletionUtil.getOriginalOrSelf(qualifier); } if (qualifier == null) { - return LookupElement.EMPTY_ARRAY; + return EMPTY_ARRAY; } final PyQualifiedExpression element = CompletionUtil.getOriginalOrSelf(myElement); @@ -268,7 +269,7 @@ public class PyQualifiedReference extends PyReferenceImpl { ProcessingContext ctx = new ProcessingContext(); final Set namesAlready = new HashSet<>(); ctx.put(PyType.CTX_NAMES, namesAlready); - final Collection variants = new ArrayList<>(); + final Collection variants = new ArrayList<>(); if (qualifierType != null) { Collections.addAll(variants, getVariantFromHasAttr(qualifier)); if (qualifierType instanceof PyStructuralType && ((PyStructuralType)qualifierType).isInferredFromUsages()) { @@ -303,7 +304,7 @@ public class PyQualifiedReference extends PyReferenceImpl { } } Collections.addAll(variants, qualifierType.getCompletionVariants(element.getName(), element, ctx)); - return variants.toArray(LookupElement.EMPTY_ARRAY); + return variants.toArray(); } else { return qualifierType.getCompletionVariants(element.getName(), element, ctx); @@ -317,12 +318,12 @@ public class PyQualifiedReference extends PyReferenceImpl { if (qualifier instanceof PyReferenceExpression) { Collections.addAll(variants, collectSeenMembers(qualifier.getText())); } - return variants.toArray(LookupElement.EMPTY_ARRAY); + return variants.toArray(); } } - private LookupElement[] getVariantFromHasAttr(PyExpression qualifier) { - Collection variants = new ArrayList<>(); + private Object[] getVariantFromHasAttr(PyExpression qualifier) { + Collection variants = new ArrayList<>(); PyIfStatement ifStatement = PsiTreeUtil.getParentOfType(myElement, PyIfStatement.class); while (ifStatement != null) { PyExpression condition = ifStatement.getIfPart().getCondition(); @@ -330,14 +331,12 @@ public class PyQualifiedReference extends PyReferenceImpl { PyCallExpression call = (PyCallExpression)condition; if (call.getArguments().length > 1 && call.getArguments()[0].getText().equals(qualifier.getText())) { PyStringLiteralExpression string = call.getArgument(1, PyStringLiteralExpression.class); - if (string != null && StringUtil.isJavaIdentifier(string.getStringValue())) { - variants.add(LookupElementBuilder.create(string.getStringValue())); - } + if (string != null && StringUtil.isJavaIdentifier(string.getStringValue())) variants.add(string.getStringValue()); } } ifStatement = PsiTreeUtil.getParentOfType(ifStatement, PyIfStatement.class); } - return variants.toArray(LookupElement.EMPTY_ARRAY); + return variants.toArray(); } @Nullable @@ -376,7 +375,7 @@ public class PyQualifiedReference extends PyReferenceImpl { return result; } - private LookupElement[] collectSeenMembers(final String text) { + private Object[] collectSeenMembers(final String text) { final Set members = new HashSet<>(); myElement.getContainingFile().accept(new PyRecursiveElementVisitor() { @Override @@ -407,7 +406,7 @@ public class PyQualifiedReference extends PyReferenceImpl { for (String member : members) { results.add(AutoCompletionPolicy.NEVER_AUTOCOMPLETE.applyPolicy(LookupElementBuilder.create(member))); } - return results.toArray(LookupElement.EMPTY_ARRAY); + return ArrayUtil.toObjectArray(results); } /** diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java index c83160e165b8..a229b7aa3bdc 100644 --- a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java @@ -671,7 +671,7 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference @Override @NotNull - public LookupElement[] getVariants() { + public Object[] getVariants() { final List ret = Lists.newArrayList(); // Use real context here to enable correct completion and resolve in case of PyExpressionCodeFragment!!! @@ -734,7 +734,7 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference } ret.addAll(getOriginalElements(processor)); - return ret.toArray(LookupElement.EMPTY_ARRAY); + return ret.toArray(); } /** @@ -816,7 +816,7 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference return myElement.hashCode(); } - protected static LookupElement[] getTypeCompletionVariants(PyExpression pyExpression, PyType type) { + protected static Object[] getTypeCompletionVariants(PyExpression pyExpression, PyType type) { ProcessingContext context = new ProcessingContext(); context.put(PyType.CTX_NAMES, new HashSet<>()); return type.getCompletionVariants(pyExpression.getName(), pyExpression, context); diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyTargetReference.java b/python/src/com/jetbrains/python/psi/impl/references/PyTargetReference.java index 0014abd95c1a..1afc36971114 100644 --- a/python/src/com/jetbrains/python/psi/impl/references/PyTargetReference.java +++ b/python/src/com/jetbrains/python/psi/impl/references/PyTargetReference.java @@ -15,11 +15,11 @@ */ package com.jetbrains.python.psi.impl.references; -import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementResolveResult; import com.intellij.psi.ResolveResult; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.ArrayUtil; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyFunction; import com.jetbrains.python.psi.PyImportElement; @@ -57,11 +57,11 @@ public class PyTargetReference extends PyReferenceImpl { @NotNull @Override - public LookupElement[] getVariants() { + public Object[] getVariants() { final PyImportElement importElement = PsiTreeUtil.getParentOfType(myElement, PyImportElement.class); // reference completion is useless in 'as' part of import statement (PY-2384) if (importElement != null && myElement == importElement.getAsNameElement()) { - return LookupElement.EMPTY_ARRAY; + return ArrayUtil.EMPTY_OBJECT_ARRAY; } return super.getVariants(); } diff --git a/python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java index b47166d7515f..bffa3cc6cc4c 100644 --- a/python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java @@ -15,9 +15,9 @@ */ package com.jetbrains.python.psi.types; -import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; +import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; import com.jetbrains.python.PyNames; import com.jetbrains.python.psi.AccessDirection; @@ -70,8 +70,8 @@ public class PyCallableTypeImpl implements PyCallableType { } @Override - public LookupElement[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { - return LookupElement.EMPTY_ARRAY; + public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { + return ArrayUtil.EMPTY_OBJECT_ARRAY; } @Nullable diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index 6b7b497712ec..ff8d8aa71f42 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -11,6 +11,7 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiInvalidElementAccessException; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.QualifiedName; +import com.intellij.util.ArrayUtil; import com.intellij.util.ObjectUtils; import com.intellij.util.ProcessingContext; import com.intellij.util.Processor; @@ -541,21 +542,21 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { public static final Key CTX_SUPPRESS_PARENTHESES = Key.create("PyFunction.SuppressParentheses"); @Override - public LookupElement[] getCompletionVariants(String prefix, PsiElement location, ProcessingContext context) { + public Object[] getCompletionVariants(String prefix, PsiElement location, ProcessingContext context) { Set visited = context.get(CTX_VISITED); if (visited == null) { visited = new HashSet<>(); context.put(CTX_VISITED, visited); } if (visited.contains(this)) { - return LookupElement.EMPTY_ARRAY; + return ArrayUtil.EMPTY_OBJECT_ARRAY; } visited.add(this); Set namesAlready = context.get(CTX_NAMES); if (namesAlready == null) { namesAlready = new HashSet<>(); } - List ret = new ArrayList<>(); + List ret = new ArrayList<>(); boolean suppressParentheses = context.get(CTX_SUPPRESS_PARENTHESES) != null; addOwnClassMembers(location, namesAlready, suppressParentheses, ret, prefix); @@ -589,33 +590,32 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { Collections.addAll(ret, getMetaClassCompletionVariants(prefix, location, context, typeEvalContext)); - return ret.toArray(LookupElement.EMPTY_ARRAY); + return ret.toArray(); } @NotNull - private LookupElement[] getMetaClassCompletionVariants(@Nullable String prefix, + private Object[] getMetaClassCompletionVariants(@Nullable String prefix, @Nullable PsiElement location, @NotNull ProcessingContext processingContext, @NotNull TypeEvalContext typeEvalContext) { if (!myClass.isNewStyleClass(typeEvalContext)) { - return LookupElement.EMPTY_ARRAY; + return ArrayUtil.EMPTY_OBJECT_ARRAY; } final PyClassLikeType typeType = getMetaClassType(typeEvalContext, true); if (typeType == null) { - return LookupElement.EMPTY_ARRAY; + return ArrayUtil.EMPTY_OBJECT_ARRAY; } if (isDefinition()) { return typeType.getCompletionVariants(prefix, location, processingContext); } else if (typeType instanceof PyClassType) { - return StreamEx.of(((PyClassType)typeType).getPyClass().getInstanceAttributes()) - .map(LookupElementBuilder::create) - .toArray(LookupElement.EMPTY_ARRAY); + final List typeInstanceAttributes = ((PyClassType)typeType).getPyClass().getInstanceAttributes(); + return ContainerUtil.map2Array(typeInstanceAttributes, LookupElementBuilder::create); } - return LookupElement.EMPTY_ARRAY; + return ArrayUtil.EMPTY_OBJECT_ARRAY; } @Override @@ -729,7 +729,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { private void addOwnClassMembers(PsiElement expressionHook, Set namesAlready, boolean suppressParentheses, - List ret, + List ret, @Nullable final String prefix) { PyClass containingClass = PsiTreeUtil.getParentOfType(expressionHook, PyClass.class); if (containingClass != null) { @@ -781,7 +781,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { PsiElement expressionHook, Set namesAlready, ProcessingContext context, - List ret, + List ret, @NotNull TypeEvalContext typeEvalContext) { for (PyType type : myClass.getSuperClassTypes(typeEvalContext)) { if (!(type instanceof PyClassLikeType)) { @@ -793,8 +793,8 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { type = classLikeType.toInstance(); } - LookupElement[] ancestry = type.getCompletionVariants(name, expressionHook, context); - for (LookupElement ob : ancestry) { + Object[] ancestry = type.getCompletionVariants(name, expressionHook, context); + for (Object ob : ancestry) { String inheritedName = ob.toString(); if (!namesAlready.contains(inheritedName) && !PyUtil.isClassPrivateName(inheritedName)) { ret.add(ob); @@ -848,7 +848,10 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { } public static boolean is(@NotNull String qName, PyType type) { - return type instanceof PyClassType && qName.equals(((PyClassType)type).getClassQName()); + if (type instanceof PyClassType) { + return qName.equals(((PyClassType)type).getClassQName()); + } + return false; } @Override diff --git a/python/src/com/jetbrains/python/psi/types/PyFunctionTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyFunctionTypeImpl.java index 608e0eb62a6b..737332cd0bb7 100644 --- a/python/src/com/jetbrains/python/psi/types/PyFunctionTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyFunctionTypeImpl.java @@ -15,9 +15,9 @@ */ package com.jetbrains.python.psi.types; -import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.psi.PsiElement; import com.intellij.psi.util.QualifiedName; +import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.PyNames; @@ -83,7 +83,7 @@ public class PyFunctionTypeImpl implements PyFunctionType { } @Override - public LookupElement[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { + public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { final TypeEvalContext typeEvalContext = TypeEvalContext.codeCompletion(location.getProject(), location.getContainingFile()); final PyClassType delegate; if (location instanceof PyReferenceExpression) { @@ -95,7 +95,7 @@ public class PyFunctionTypeImpl implements PyFunctionType { delegate = cls != null ? new PyClassTypeImpl(cls, false) : null; } if (delegate == null) { - return LookupElement.EMPTY_ARRAY; + return ArrayUtil.EMPTY_OBJECT_ARRAY; } return delegate.getCompletionVariants(completionPrefix, location, context); } diff --git a/python/src/com/jetbrains/python/psi/types/PyGenericType.java b/python/src/com/jetbrains/python/psi/types/PyGenericType.java index be61c55a0444..c3d0b0bba176 100644 --- a/python/src/com/jetbrains/python/psi/types/PyGenericType.java +++ b/python/src/com/jetbrains/python/psi/types/PyGenericType.java @@ -15,8 +15,8 @@ */ package com.jetbrains.python.psi.types; -import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.psi.PsiElement; +import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; import com.jetbrains.python.psi.AccessDirection; import com.jetbrains.python.psi.PyExpression; @@ -68,8 +68,8 @@ public class PyGenericType implements PyType, PyInstantiableType } @Override - public LookupElement[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { - return LookupElement.EMPTY_ARRAY; + public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { + return ArrayUtil.EMPTY_OBJECT_ARRAY; } @NotNull diff --git a/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java b/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java index 623a03f3c39e..457447b92668 100644 --- a/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java +++ b/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java @@ -6,6 +6,7 @@ import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileSystemItem; +import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; import com.intellij.util.SmartList; import com.jetbrains.python.psi.AccessDirection; @@ -79,7 +80,7 @@ public class PyImportedModuleType implements PyType { } @Override - public LookupElement[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { + public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { final List result = new ArrayList<>(); for (PsiElement resolveResult : ResolveResultList.getElements(myImportedModule.multiResolve())) { if (resolveResult instanceof PyFile) { @@ -100,7 +101,7 @@ public class PyImportedModuleType implements PyType { } } } - return result.toArray(LookupElement.EMPTY_ARRAY); + return ArrayUtil.toObjectArray(result); } @Override diff --git a/python/src/com/jetbrains/python/psi/types/PyModuleType.java b/python/src/com/jetbrains/python/psi/types/PyModuleType.java index b7b98a2a2684..5e8e178ef89e 100644 --- a/python/src/com/jetbrains/python/psi/types/PyModuleType.java +++ b/python/src/com/jetbrains/python/psi/types/PyModuleType.java @@ -399,10 +399,10 @@ public class PyModuleType implements PyType { // Modules don't descend from obje } @Override - public LookupElement[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { + public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { final TypeEvalContext typeEvalContext = TypeEvalContext.codeCompletion(location.getProject(), location.getContainingFile()); final List result = getCompletionVariantsAsLookupElements(location, context, false, false, typeEvalContext); - return result.toArray(LookupElement.EMPTY_ARRAY); + return result.toArray(); } @NotNull diff --git a/python/src/com/jetbrains/python/psi/types/PyNoneType.java b/python/src/com/jetbrains/python/psi/types/PyNoneType.java index d9cdf2c15f69..18e83a86dd8f 100644 --- a/python/src/com/jetbrains/python/psi/types/PyNoneType.java +++ b/python/src/com/jetbrains/python/psi/types/PyNoneType.java @@ -15,8 +15,8 @@ */ package com.jetbrains.python.psi.types; -import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.psi.PsiElement; +import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; import com.jetbrains.python.psi.AccessDirection; import com.jetbrains.python.psi.PyExpression; @@ -44,8 +44,8 @@ public class PyNoneType implements PyType { // TODO must extend ClassType. It's return null; } - public LookupElement[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { - return LookupElement.EMPTY_ARRAY; + public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { + return ArrayUtil.EMPTY_OBJECT_ARRAY; } public String getName() { diff --git a/python/src/com/jetbrains/python/psi/types/PyStructuralType.java b/python/src/com/jetbrains/python/psi/types/PyStructuralType.java index 908586063328..67de94c2f3d8 100644 --- a/python/src/com/jetbrains/python/psi/types/PyStructuralType.java +++ b/python/src/com/jetbrains/python/psi/types/PyStructuralType.java @@ -15,7 +15,6 @@ */ package com.jetbrains.python.psi.types; -import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; @@ -28,10 +27,7 @@ import com.jetbrains.python.psi.resolve.RatedResolveResult; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; +import java.util.*; /** * @author vlan @@ -55,14 +51,14 @@ public class PyStructuralType implements PyType { } @Override - public LookupElement[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { - final List variants = new ArrayList<>(); + public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { + final List variants = new ArrayList<>(); for (String attribute : myAttributes) { if (!attribute.equals(completionPrefix)) { variants.add(LookupElementBuilder.create(attribute).withIcon(PlatformIcons.FIELD_ICON)); } } - return variants.toArray(LookupElement.EMPTY_ARRAY); + return variants.toArray(); } @Nullable diff --git a/python/src/com/jetbrains/python/psi/types/PyUnionType.java b/python/src/com/jetbrains/python/psi/types/PyUnionType.java index fbeaf8063fc4..2677bd1ba357 100644 --- a/python/src/com/jetbrains/python/psi/types/PyUnionType.java +++ b/python/src/com/jetbrains/python/psi/types/PyUnionType.java @@ -15,9 +15,9 @@ */ package com.jetbrains.python.psi.types; -import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; +import com.intellij.util.ArrayUtil; import com.intellij.util.NullableFunction; import com.intellij.util.ProcessingContext; import com.intellij.util.SmartList; @@ -59,14 +59,14 @@ public class PyUnionType implements PyType { return allNulls ? null : ret; } - public LookupElement[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { - Set variants = new HashSet<>(); + public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { + Set variants = new HashSet<>(); for (PyType member : myMembers) { if (member != null) { Collections.addAll(variants, member.getCompletionVariants(completionPrefix, location, context)); } } - return variants.toArray(LookupElement.EMPTY_ARRAY); + return ArrayUtil.toObjectArray(variants); } public String getName() { From faa922da99ea3aa06c0d88f39305352ff1915474 Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Tue, 10 Apr 2018 12:43:50 +0300 Subject: [PATCH 06/53] Gradle: skip trace progress messages by default (IDEA-185472) --- .../gradle/service/execution/GradleProgressListener.java | 6 ++++++ .../jetbrains/plugins/gradle/util/GradleEnvironment.java | 1 + 2 files changed, 7 insertions(+) diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleProgressListener.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleProgressListener.java index 33b422d3e9a6..32fb4e9e0e8b 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleProgressListener.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/execution/GradleProgressListener.java @@ -25,6 +25,7 @@ import org.gradle.tooling.ProgressEvent; import org.gradle.tooling.ProgressListener; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.gradle.util.GradleEnvironment; import java.util.HashMap; import java.util.Map; @@ -68,6 +69,11 @@ public class GradleProgressListener implements ProgressListener, org.gradle.tool @Override public void statusChanged(org.gradle.tooling.events.ProgressEvent event) { + if (!GradleEnvironment.GRADLE_PROGRESS_VERBOSE_EVENTS) { + if (event.getDisplayName().startsWith("Resolve ")) return; + if (event.getDisplayName().startsWith("Apply plugin ")) return; + } + ExternalSystemTaskNotificationEvent notificationEvent = GradleProgressEventConverter.convert(myTaskId, event, myOperationId + "_"); if (notificationEvent instanceof ExternalSystemTaskExecutionEvent) { ExternalSystemProgressEvent progressEvent = ((ExternalSystemTaskExecutionEvent)notificationEvent).getProgressEvent(); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleEnvironment.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleEnvironment.java index 43d4638d40d5..0152c743cfba 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleEnvironment.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleEnvironment.java @@ -11,6 +11,7 @@ public class GradleEnvironment { @NonNls public static final boolean DEBUG_GRADLE_HOME_PROCESSING = Boolean.getBoolean("gradle.debug.home.processing"); @NonNls public static final boolean ADJUST_USER_DIR = Boolean.getBoolean("gradle.adjust.userdir"); + @NonNls public static final boolean GRADLE_PROGRESS_VERBOSE_EVENTS = Boolean.getBoolean("gradle.progress.verbose.events"); public static class Headless { @NonNls public static final String GRADLE_DISTRIBUTION_TYPE = System.getProperty("idea.gradle.distributionType"); From 04fcd7bd526ec76ac109f5a9f07118d010a35bfd Mon Sep 17 00:00:00 2001 From: Denis Fokin Date: Tue, 10 Apr 2018 13:02:07 +0300 Subject: [PATCH 07/53] Assign popup window type to movable popups IDEA-112015 "Go to class" popup loses focus in focus follow mouse (sloppy focus mode) mode --- .../platform-impl/src/com/intellij/ui/popup/MovablePopup.java | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/platform-impl/src/com/intellij/ui/popup/MovablePopup.java b/platform/platform-impl/src/com/intellij/ui/popup/MovablePopup.java index 636f9b56b3b2..0e019d2a409c 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/MovablePopup.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/MovablePopup.java @@ -177,6 +177,7 @@ public class MovablePopup { if (owner != null) { if (myHeavyWeight) { Window view = new JWindow(owner); + view.setType(Window.Type.POPUP); setAlwaysOnTop(view, myAlwaysOnTop); setWindowFocusable(view, myWindowFocusable); setWindowShadow(view, myWindowShadow); From 5ffb694af73e1ec566ee1f969bb48eba7673c6de Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Tue, 10 Apr 2018 13:06:49 +0300 Subject: [PATCH 08/53] Gradle: skip trace progress messages by default (IDEA-185472) --- .../org/jetbrains/plugins/gradle/util/GradleEnvironment.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleEnvironment.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleEnvironment.java index 0152c743cfba..47a0b699a858 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleEnvironment.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleEnvironment.java @@ -1,5 +1,6 @@ package org.jetbrains.plugins.gradle.util; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NonNls; /** @@ -11,6 +12,7 @@ public class GradleEnvironment { @NonNls public static final boolean DEBUG_GRADLE_HOME_PROCESSING = Boolean.getBoolean("gradle.debug.home.processing"); @NonNls public static final boolean ADJUST_USER_DIR = Boolean.getBoolean("gradle.adjust.userdir"); + @ApiStatus.Experimental // todo should be replaced with the related event level setting at the gradle tooling api @NonNls public static final boolean GRADLE_PROGRESS_VERBOSE_EVENTS = Boolean.getBoolean("gradle.progress.verbose.events"); public static class Headless { From 134786ba6cbd3991ac40822c6e0e8911df0e75e5 Mon Sep 17 00:00:00 2001 From: nik Date: Tue, 10 Apr 2018 13:36:20 +0300 Subject: [PATCH 09/53] build scripts: log free disk space (for IJI-8) --- .../jetbrains/intellij/build/impl/BuildTasksImpl.groovy | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy index 75bab3b8c6d7..19665d85fd53 100644 --- a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy +++ b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy @@ -48,6 +48,7 @@ class BuildTasksImpl extends BuildTasks { } buildContext.notifyArtifactBuilt(targetFile) } + logFreeDiskSpace("after building sources archive") } @Override @@ -299,7 +300,9 @@ idea.fatal.error.notification=disabled copyDependenciesFile() def patchedApplicationInfo = patchApplicationInfo() + logFreeDiskSpace("before compilation") def distributionJARsBuilder = compileModulesForDistribution(patchedApplicationInfo) + logFreeDiskSpace("after compilation") def mavenArtifacts = buildContext.productProperties.mavenArtifacts if (mavenArtifacts.forIdeModules || !mavenArtifacts.additionalModules.isEmpty()) { buildContext.executeStep("Generate Maven artifacts", BuildOptions.MAVEN_ARTIFACTS_STEP) { @@ -354,6 +357,12 @@ idea.fatal.error.notification=disabled } } } + logFreeDiskSpace("after building distributions") + } + + private void logFreeDiskSpace(String phase) { + def dir = new File(buildContext.paths.buildOutputRoot) + buildContext.messages.debug("Free disk space $phase: ${StringUtil.formatFileSize(dir.freeSpace)} (on disk containing $dir)") } private def copyDependenciesFile() { From be945601b6f90667271e11ad7c85b8438d524555 Mon Sep 17 00:00:00 2001 From: Egor Ushakov Date: Tue, 10 Apr 2018 13:55:33 +0300 Subject: [PATCH 10/53] sort locations before matching for IDEA-127045 --- .../debugger/jdi/MethodBytecodeUtil.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/jdi/MethodBytecodeUtil.java b/java/debugger/impl/src/com/intellij/debugger/jdi/MethodBytecodeUtil.java index f5ac8a8104fc..36c7bb99d8f9 100644 --- a/java/debugger/impl/src/com/intellij/debugger/jdi/MethodBytecodeUtil.java +++ b/java/debugger/impl/src/com/intellij/debugger/jdi/MethodBytecodeUtil.java @@ -289,17 +289,19 @@ public class MethodBytecodeUtil { List res = new ArrayList<>(); for (Map.Entry> entry : byMethod.entrySet()) { - res.addAll(removeMethodSameLineLocations(entry.getKey(), entry.getValue())); + res.addAll(removeMethodSameLineLocations(entry.getKey(), (List)entry.getValue())); } return res; } - private static Collection removeMethodSameLineLocations(@NotNull Method method, @NotNull Collection locations) { - if (locations.size() < 2) { + private static Collection removeMethodSameLineLocations(@NotNull Method method, @NotNull List locations) { + int locationsSize = locations.size(); + if (locationsSize < 2) { return locations; } + //noinspection ConstantConditions int lineNumber = ContainerUtil.getFirstItem(locations).lineNumber(); - List mask = new ArrayList<>(); + List mask = new ArrayList<>(locationsSize); visit(method, new MethodVisitor(Opcodes.API_VERSION) { boolean myNewBlock = true; @Override @@ -322,8 +324,10 @@ public class MethodBytecodeUtil { myNewBlock = true; } }, true); - if (mask.size() == locations.size()) { - List res = new ArrayList<>(); + + if (mask.size() == locationsSize) { + locations.sort(Comparator.comparing(Location::codeIndex)); + List res = new ArrayList<>(locationsSize); int pos = 0; for (Location location : locations) { if (mask.get(pos++)) { From 1d0bb28d91fb96a4a20cf023141c6075f6478b66 Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 10 Apr 2018 13:11:34 +0200 Subject: [PATCH 11/53] no dom inside .idea (IDEA-189915) --- .../FileDescriptionCachedValueProvider.java | 41 ++++++------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/FileDescriptionCachedValueProvider.java b/xml/dom-impl/src/com/intellij/util/xml/impl/FileDescriptionCachedValueProvider.java index c3693eaab3ca..1802cfcad4b1 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/FileDescriptionCachedValueProvider.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/FileDescriptionCachedValueProvider.java @@ -18,6 +18,7 @@ package com.intellij.util.xml.impl; import com.intellij.ide.highlighter.DomSupportEnabled; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.ProjectCoreUtil; import com.intellij.openapi.util.Condition; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileWithId; @@ -26,19 +27,14 @@ import com.intellij.psi.stubs.ObjectStubTree; import com.intellij.psi.stubs.StubTreeLoader; import com.intellij.psi.xml.XmlFile; import com.intellij.semantic.SemElement; -import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; -import java.util.HashSet; import com.intellij.util.xml.*; -import com.intellij.util.xml.events.DomEvent; import com.intellij.util.xml.stubs.FileStub; import com.intellij.xml.util.XmlUtil; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.lang.ref.WeakReference; -import java.util.Collections; -import java.util.List; +import java.util.HashSet; import java.util.Set; /** @@ -65,7 +61,7 @@ class FileDescriptionCachedValueProvider implements SemEle public final DomFileElementImpl getFileElement() { if (myComputed) return myLastResult; - DomFileElementImpl result = _computeFileElement(false, myDomService.getXmlFileHeader(myXmlFile), null); + DomFileElementImpl result = _computeFileElement(null); synchronized (myCondition) { if (myComputed) return myLastResult; @@ -79,12 +75,7 @@ class FileDescriptionCachedValueProvider implements SemEle } @Nullable - private DomFileElementImpl _computeFileElement(final boolean fireEvents, - @NotNull final XmlFileHeader rootTagName, @Nullable StringBuilder sb) { - if (sb != null) { - sb.append(rootTagName).append("\n"); - } - + private DomFileElementImpl _computeFileElement(@Nullable StringBuilder sb) { if (!myXmlFile.isValid()) { return null; } @@ -92,12 +83,14 @@ class FileDescriptionCachedValueProvider implements SemEle sb.append("File is valid\n"); } - if (!(myXmlFile.getFileType() instanceof DomSupportEnabled)) { + VirtualFile file = myXmlFile.getVirtualFile(); + if (!(myXmlFile.getFileType() instanceof DomSupportEnabled) || file != null && ProjectCoreUtil.isProjectOrWorkspaceFile(file)) { return null; } + XmlFileHeader rootTagName = myDomService.getXmlFileHeader(myXmlFile); if (sb != null) { - sb.append("File is of dom file type\n"); + sb.append(rootTagName).append(", file is of dom file type\n"); } final DomFileDescription description = findFileDescription(rootTagName, sb); @@ -106,12 +99,6 @@ class FileDescriptionCachedValueProvider implements SemEle if (sb != null) { sb.append("last " + oldValue + "\n"); } - final List events = fireEvents ? new SmartList<>() : Collections.emptyList(); - if (oldValue != null) { - if (fireEvents) { - events.add(new DomEvent(oldValue, false)); - } - } if (description == null) { return null; @@ -122,7 +109,6 @@ class FileDescriptionCachedValueProvider implements SemEle assert xmlName != null; final EvaluatedXmlNameImpl rootTagName1 = EvaluatedXmlNameImpl.createEvaluatedXmlName(xmlName, xmlName.getNamespaceKey(), false); - VirtualFile file = myXmlFile.getVirtualFile(); FileStub stub = null; if (description.hasStubs() && file instanceof VirtualFileWithId && !isFileParsed()) { ApplicationManager.getApplication().assertReadAccessAllowed(); @@ -139,9 +125,6 @@ class FileDescriptionCachedValueProvider implements SemEle sb.append("success " + result + "\n"); } - if (fireEvents) { - events.add(new DomEvent(result, true)); - } return result; } @@ -151,6 +134,7 @@ class FileDescriptionCachedValueProvider implements SemEle @Nullable private DomFileDescription findFileDescription(final XmlFileHeader xmlFileHeader, @Nullable StringBuilder sb) { + //noinspection unchecked final DomFileDescription mockDescription = myXmlFile.getUserData(DomManagerImpl.MOCK_DESCRIPTION); if (mockDescription != null) return mockDescription; @@ -176,9 +160,11 @@ class FileDescriptionCachedValueProvider implements SemEle if (sb != null) { sb.append("named " + new HashSet<>(namedDescriptions) + "\n"); } + //noinspection unchecked DomFileDescription description = ContainerUtil.find(namedDescriptions, myCondition); if (description == null) { final Set unnamed = myDomManager.getAcceptingOtherRootTagNameDescriptions(); + //noinspection unchecked description = ContainerUtil.find(unnamed, myCondition); } if (sb != null) { @@ -193,9 +179,8 @@ class FileDescriptionCachedValueProvider implements SemEle } public String getFileElementWithLogging() { - final XmlFileHeader rootTagName = myDomService.getXmlFileHeader(myXmlFile); - final StringBuilder log = new StringBuilder(); - myLastResult = _computeFileElement(false, rootTagName, log); + StringBuilder log = new StringBuilder(); + myLastResult = _computeFileElement(log); return log.toString(); } From d2061e69f7d9e6a1a458254b4ae38b93f5c865dd Mon Sep 17 00:00:00 2001 From: Anton Bragin Date: Wed, 28 Mar 2018 19:15:59 +0300 Subject: [PATCH 12/53] PY-28341 Already used entries don't removed from completion list Entries that were used in code remain in the completion list. Return type of PyQualifiedReference updated to reflect the fact that it returns PyTargetExpression's only. --- .../impl/references/PyQualifiedReference.java | 23 +++++++------------ .../completionForUsedAttribute/a.after.py | 4 ++++ .../completionForUsedAttribute/a.py | 4 ++++ .../completionForUsedAttribute/m1.py | 1 + .../python/PythonCompletionTest.java | 5 ++++ 5 files changed, 22 insertions(+), 15 deletions(-) create mode 100644 python/testData/completion/completionForUsedAttribute/a.after.py create mode 100644 python/testData/completion/completionForUsedAttribute/a.py create mode 100644 python/testData/completion/completionForUsedAttribute/m1.py diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyQualifiedReference.java b/python/src/com/jetbrains/python/psi/impl/references/PyQualifiedReference.java index b7576cdec096..789b90373dfc 100644 --- a/python/src/com/jetbrains/python/psi/impl/references/PyQualifiedReference.java +++ b/python/src/com/jetbrains/python/psi/impl/references/PyQualifiedReference.java @@ -282,24 +282,17 @@ public class PyQualifiedReference extends PyReferenceImpl { final PyQualifiedExpression qualifierExpression = (PyQualifiedExpression)qualifier; final QualifiedName qualifiedName = qualifierExpression.asQualifiedName(); if (qualifiedName != null) { - final Collection attrs = collectAssignedAttributes(qualifiedName, qualifier); - for (PyExpression ex : attrs) { - final String name = ex.getName(); + final Collection attrs = collectAssignedAttributes(qualifiedName, qualifier); + for (PyTargetExpression expression : attrs) { + final String name = expression.getName(); if (name != null && name.endsWith(CompletionUtil.DUMMY_IDENTIFIER_TRIMMED)) { continue; } - if (ex instanceof PsiNamedElement && qualifierType instanceof PyClassType && name != null) { - variants.add(LookupElementBuilder.createWithSmartPointer(name, ex) + if (qualifierType instanceof PyClassType && name != null) { + variants.add(LookupElementBuilder.createWithSmartPointer(name, expression) .withTypeText(qualifierType.getName()) .withIcon(PlatformIcons.FIELD_ICON)); - } - if (ex instanceof PyReferenceExpression) { - PyReferenceExpression refExpr = (PyReferenceExpression)ex; - namesAlready.add(refExpr.getReferencedName()); - } - else if (ex instanceof PyTargetExpression) { - PyTargetExpression targetExpr = (PyTargetExpression)ex; - namesAlready.add(targetExpr.getName()); + namesAlready.add(name); } } } @@ -414,10 +407,10 @@ public class PyQualifiedReference extends PyReferenceImpl { * Can be used for completion. */ @NotNull - public static Collection collectAssignedAttributes(@NotNull final QualifiedName qualifierQName, + public static Collection collectAssignedAttributes(@NotNull final QualifiedName qualifierQName, @NotNull final PsiElement anchor) { final Set names = new HashSet<>(); - final List results = new ArrayList<>(); + final List results = new ArrayList<>(); for (ScopeOwner owner = ScopeUtil.getScopeOwner(anchor); owner != null; owner = ScopeUtil.getScopeOwner(owner)) { final Scope scope = ControlFlowCache.getScope(owner); for (final PyTargetExpression target : scope.getTargetExpressions()) { diff --git a/python/testData/completion/completionForUsedAttribute/a.after.py b/python/testData/completion/completionForUsedAttribute/a.after.py new file mode 100644 index 000000000000..2eb90c203e55 --- /dev/null +++ b/python/testData/completion/completionForUsedAttribute/a.after.py @@ -0,0 +1,4 @@ +import m1 + +m1.a +m1.a = 0 diff --git a/python/testData/completion/completionForUsedAttribute/a.py b/python/testData/completion/completionForUsedAttribute/a.py new file mode 100644 index 000000000000..08c782d2f2e2 --- /dev/null +++ b/python/testData/completion/completionForUsedAttribute/a.py @@ -0,0 +1,4 @@ +import m1 + +m1. +m1.a = 0 diff --git a/python/testData/completion/completionForUsedAttribute/m1.py b/python/testData/completion/completionForUsedAttribute/m1.py new file mode 100644 index 000000000000..d25d49e0ffc3 --- /dev/null +++ b/python/testData/completion/completionForUsedAttribute/m1.py @@ -0,0 +1 @@ +a = 1 \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java index 9f13f9fd1052..f1d3ee3c173a 100644 --- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java @@ -1335,6 +1335,11 @@ public class PythonCompletionTest extends PyTestCase { runWithLanguageLevel(LanguageLevel.PYTHON34, this::assertSingleVariantInExtendedCompletion); } + // PY-28341 + public void testCompletionForUsedAttribute() { + doMultiFileTest(); + } + // PY-28103 public void testPrintFunctionWithoutFuture() { final List suggested = doTestByText("pr"); From f514017b0ac9b9351b0ba218cc3d5f9662ecdcf8 Mon Sep 17 00:00:00 2001 From: "Vladimir.Orlov" Date: Tue, 10 Apr 2018 14:34:11 +0300 Subject: [PATCH 13/53] win launcher: updated create JVM error message. IDEA-189436 IDEA fails to launch with JDK 10 set as IDEA_JDK_64. --- bin/WinLauncher/WinLauncher.exe | Bin 1303552 -> 1303552 bytes bin/WinLauncher/WinLauncher64.exe | Bin 1333248 -> 1333248 bytes .../WinLauncher/WinLauncher/WinLauncher.cpp | 3 +-- 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/bin/WinLauncher/WinLauncher.exe b/bin/WinLauncher/WinLauncher.exe index c40b7a0f135225fc9b94426a02821633e991b0a1..3596a30377f407d91dae937e1cd64a177e07465a 100644 GIT binary patch delta 29517 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 delta 29494 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 S5IU)iCyqC919IK!ZT}16_RV?# diff --git a/bin/WinLauncher/WinLauncher64.exe b/bin/WinLauncher/WinLauncher64.exe index 1c305dfbe73b8b417f56b477a75433e4d22b694e..c4b0a9b56068ef946373279a2ed40b3d7a822b9e 100644 GIT binary patch delta 40924 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 C=+Qa= delta 40711 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 b6s#y*5n55a!Z5&SUr$uHapXkbM+5&4G(XfW diff --git a/native/WinLauncher/WinLauncher/WinLauncher.cpp b/native/WinLauncher/WinLauncher/WinLauncher.cpp index 476a3dc75415..a58ce368ff91 100644 --- a/native/WinLauncher/WinLauncher/WinLauncher.cpp +++ b/native/WinLauncher/WinLauncher/WinLauncher.cpp @@ -674,8 +674,7 @@ bool CreateJVM() buf << "Failed to create JVM: error code " << result << ".\n"; buf << "JVM Path: " << jvmPath << "\n"; - buf << "If you already have a " BITS_STR " JDK installed, define a JAVA_HOME variable in \n"; - buf << "Computer > System Properties > System Settings > Environment Variables."; + buf << "Most likely the cause of the problem is incorrect settings in the VM options file."; std::string error = LoadStdString(IDS_ERROR_LAUNCHING_APP); MessageBoxA(NULL, buf.str().c_str(), error.c_str(), MB_OK); } From 42a95a9f8ae288f67ad4861535252973d876eb71 Mon Sep 17 00:00:00 2001 From: nik Date: Tue, 10 Apr 2018 14:39:31 +0300 Subject: [PATCH 14/53] build scripts: log free disk space when build is started (for IJI-8) --- .../jetbrains/intellij/build/impl/BuildTasksImpl.groovy | 4 ++-- .../intellij/build/impl/CompilationContextImpl.groovy | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy index 19665d85fd53..90bbfb43ab4b 100644 --- a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy +++ b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy @@ -361,10 +361,10 @@ idea.fatal.error.notification=disabled } private void logFreeDiskSpace(String phase) { - def dir = new File(buildContext.paths.buildOutputRoot) - buildContext.messages.debug("Free disk space $phase: ${StringUtil.formatFileSize(dir.freeSpace)} (on disk containing $dir)") + CompilationContextImpl.logFreeDiskSpace(buildContext.messages, buildContext.paths.buildOutputRoot, phase) } + private def copyDependenciesFile() { if (buildContext.gradle.forceRun('Preparing dependencies file', 'dependenciesFile')) { def outputFile = "$buildContext.paths.artifacts/dependencies.txt" diff --git a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/CompilationContextImpl.groovy b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/CompilationContextImpl.groovy index ec233782bbd1..9fed638528f3 100644 --- a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/CompilationContextImpl.groovy +++ b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/CompilationContextImpl.groovy @@ -16,6 +16,7 @@ package org.jetbrains.intellij.build.impl import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.util.text.StringUtil import com.intellij.util.PathUtilRt import com.intellij.util.SystemProperties import groovy.transform.CompileDynamic @@ -73,6 +74,7 @@ class CompilationContextImpl implements CompilationContext { } def dependenciesProjectDir = new File(communityHome, 'build/dependencies') + logFreeDiskSpace(messages, projectHome, "before dwonloading dependencies") GradleRunner gradle = new GradleRunner(dependenciesProjectDir, messages, SystemProperties.getJavaHome()) if (!options.isInDevelopmentMode) { setupCompilationDependencies(gradle) @@ -369,6 +371,11 @@ class CompilationContextImpl implements CompilationContext { private static String toCanonicalPath(String path) { FileUtil.toSystemIndependentName(new File(path).canonicalPath) } + + static void logFreeDiskSpace(BuildMessages buildMessages, String directoryPath, String phase) { + def dir = new File(directoryPath) + buildMessages.debug("Free disk space $phase: ${StringUtil.formatFileSize(dir.freeSpace)} (on disk containing $dir)") + } } class BuildPathsImpl extends BuildPaths { From 799dc4473d74a708d8c182c5a65bc80e83aa8264 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 10 Apr 2018 14:19:19 +0200 Subject: [PATCH 15/53] API for replacing term "project" used in the UI --- .../ide/actions/SearchEverywhereAction.java | 2 +- .../ide/util/gotoByName/GotoClassModel2.java | 5 +- .../ide/util/gotoByName/GotoFileModel.java | 3 +- .../ide/util/gotoByName/GotoSymbolModel2.java | 5 +- .../intellij/ui/ProjectUICustomization.java | 49 +++++++++++++++++++ .../ide/GeneralSettingsConfigurable.java | 10 +++- .../intellij/ide/GeneralSettingsPanel.form | 2 +- .../ide/actions/CloseProjectAction.java | 5 +- .../src/META-INF/PlatformExtensions.xml | 2 + 9 files changed, 73 insertions(+), 10 deletions(-) create mode 100644 platform/platform-api/src/com/intellij/ui/ProjectUICustomization.java diff --git a/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java b/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java index b7aa8e9e6f48..4a53907e931d 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java @@ -672,7 +672,7 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA UIManager.getColor("SearchEverywhere.shortcutForeground") : foregroundColor; StringBuilder cbText = new StringBuilder(""); - cbText.append(IdeBundle.message("checkbox.include.non.project.items")); + cbText.append(ProjectUICustomization.replaceProjectConceptName(IdeBundle.message("checkbox.include.non.project.items"))); cbText.append(" "); if (!UIUtil.isUnderWin10LookAndFeel()) cbText.append(""); cbText.append("").append(getShortcut()).append(""); diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoClassModel2.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoClassModel2.java index b252baf65934..2e858a5424e5 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoClassModel2.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoClassModel2.java @@ -27,6 +27,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; +import com.intellij.ui.ProjectUICustomization; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.indexing.FileBasedIndex; @@ -68,12 +69,12 @@ public class GotoClassModel2 extends FilteringGotoByModel { @Override public String getCheckBoxName() { - return IdeBundle.message("checkbox.include.non.project.classes"); + return ProjectUICustomization.replaceProjectConceptName(IdeBundle.message("checkbox.include.non.project.classes")); } @Override public String getNotInMessage() { - return IdeBundle.message("label.no.matches.found.in.project"); + return ProjectUICustomization.replaceProjectConceptName(IdeBundle.message("label.no.matches.found.in.project")); } @Override diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoFileModel.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoFileModel.java index 4f07e8823786..53644dffe9d8 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoFileModel.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoFileModel.java @@ -24,6 +24,7 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileSystemItem; import com.intellij.psi.codeStyle.MinusculeMatcher; import com.intellij.psi.codeStyle.NameUtil; +import com.intellij.ui.ProjectUICustomization; import com.intellij.util.containers.JBIterable; import com.intellij.util.indexing.FileBasedIndex; import org.jetbrains.annotations.NotNull; @@ -94,7 +95,7 @@ public class GotoFileModel extends FilteringGotoByModel implements Dum if (NonProjectScopeDisablerEP.isSearchInNonProjectDisabled()) { return null; } - return IdeBundle.message("checkbox.include.non.project.files"); + return ProjectUICustomization.replaceProjectConceptName(IdeBundle.message("checkbox.include.non.project.files")); } @Override diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoSymbolModel2.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoSymbolModel2.java index e878553c7f2c..0456b46e4717 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoSymbolModel2.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoSymbolModel2.java @@ -26,6 +26,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.SystemInfo; import com.intellij.psi.PsiElement; import com.intellij.psi.presentation.java.SymbolPresentationUtil; +import com.intellij.ui.ProjectUICustomization; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -66,12 +67,12 @@ public class GotoSymbolModel2 extends FilteringGotoByModel { @Override public String getCheckBoxName() { - return IdeBundle.message("checkbox.include.non.project.symbols"); + return ProjectUICustomization.replaceProjectConceptName(IdeBundle.message("checkbox.include.non.project.symbols")); } @Override public String getNotInMessage() { - return IdeBundle.message("label.no.matches.found.in.project"); + return ProjectUICustomization.replaceProjectConceptName(IdeBundle.message("label.no.matches.found.in.project")); } @Override diff --git a/platform/platform-api/src/com/intellij/ui/ProjectUICustomization.java b/platform/platform-api/src/com/intellij/ui/ProjectUICustomization.java new file mode 100644 index 000000000000..c0f22db40146 --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/ProjectUICustomization.java @@ -0,0 +1,49 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.ui; + +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.util.text.StringUtil; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.TitledBorder; + +/** + * Allows to apply IDE-specific customizations to the terms used in platform UI features. + */ +public class ProjectUICustomization { + public static ProjectUICustomization getInstance() { + return ServiceManager.getService(ProjectUICustomization.class); + } + + /** + * Returns the name to be displayed in the UI for the "project" concept (Rider changes this to "solution"). + */ + public String getProjectConceptName() { + return "project"; + } + + public static String replaceProjectConceptName(String text) { + return text.replace("project", getInstance().getProjectConceptName()); + } + + + public static void replaceProjectConceptName(AbstractButton component) { + component.setText(component.getText().replace("project", getInstance().getProjectConceptName())); + } + + public static void replaceProjectConceptName(Border border) { + if (border instanceof TitledBorder) { + TitledBorder titledBorder = (TitledBorder)border; + titledBorder.setTitle(titledBorder.getTitle().replace("project", getInstance().getProjectConceptName())); + } + } + + public static String replaceProjectConceptNameForAction(String actionName) { + String name = getInstance().getProjectConceptName(); + if (name.equals("project")) { + return actionName; + } + return actionName.replace("_", "").replace("project", name).replace("Project", StringUtil.capitalize(name)); + } +} diff --git a/platform/platform-impl/src/com/intellij/ide/GeneralSettingsConfigurable.java b/platform/platform-impl/src/com/intellij/ide/GeneralSettingsConfigurable.java index 9c15304ae9f6..2c1526d046bf 100644 --- a/platform/platform-impl/src/com/intellij/ide/GeneralSettingsConfigurable.java +++ b/platform/platform-impl/src/com/intellij/ide/GeneralSettingsConfigurable.java @@ -25,6 +25,7 @@ import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.SearchableConfigurable; import com.intellij.openapi.options.ex.ConfigurableWrapper; import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import com.intellij.ui.ProjectUICustomization; import com.intellij.ui.components.JBRadioButton; import com.intellij.util.PlatformUtils; import org.jetbrains.annotations.NotNull; @@ -216,8 +217,15 @@ public class GeneralSettingsConfigurable extends CompositeConfigurable - + diff --git a/platform/platform-impl/src/com/intellij/ide/actions/CloseProjectAction.java b/platform/platform-impl/src/com/intellij/ide/actions/CloseProjectAction.java index 60ae40bc804b..3a014b3e197e 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/CloseProjectAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/CloseProjectAction.java @@ -26,6 +26,7 @@ import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeFrame; import com.intellij.projectImport.ProjectAttachProcessor; +import com.intellij.ui.ProjectUICustomization; public class CloseProjectAction extends AnAction implements DumbAware { @Override @@ -43,10 +44,10 @@ public class CloseProjectAction extends AnAction implements DumbAware { Project project = event.getData(CommonDataKeys.PROJECT); presentation.setEnabled(project != null); if (ProjectAttachProcessor.canAttachToProject() && project != null && ModuleManager.getInstance(project).getModules().length > 1) { - presentation.setText(IdeBundle.message("action.close.projects.in.current.window")); + presentation.setText(ProjectUICustomization.replaceProjectConceptNameForAction(IdeBundle.message("action.close.projects.in.current.window"))); } else { - presentation.setText(IdeBundle.message("action.close.project")); + presentation.setText(ProjectUICustomization.replaceProjectConceptNameForAction(IdeBundle.message("action.close.project"))); } } } diff --git a/platform/platform-resources/src/META-INF/PlatformExtensions.xml b/platform/platform-resources/src/META-INF/PlatformExtensions.xml index 4646adc31c9a..cb0d52aa9ae8 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensions.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensions.xml @@ -294,6 +294,8 @@ + + From f4ad85bde6c5241850ea8c0ad18e277e2df1f16e Mon Sep 17 00:00:00 2001 From: Anton Tarasov Date: Tue, 10 Apr 2018 15:27:31 +0300 Subject: [PATCH 16/53] IDEA-189859 provide JVM property for the screen reader option --- .../src/com/intellij/ide/GeneralSettings.java | 19 ++++++++++++++++++- .../ide/GeneralSettingsConfigurable.java | 5 +++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/platform/platform-api/src/com/intellij/ide/GeneralSettings.java b/platform/platform-api/src/com/intellij/ide/GeneralSettings.java index ef23b4e61994..33f1c067a908 100644 --- a/platform/platform-api/src/com/intellij/ide/GeneralSettings.java +++ b/platform/platform-api/src/com/intellij/ide/GeneralSettings.java @@ -8,6 +8,7 @@ import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.ObjectUtils; import com.intellij.util.PlatformUtils; import com.intellij.util.xmlb.XmlSerializerUtil; import com.intellij.util.xmlb.annotations.OptionTag; @@ -33,12 +34,15 @@ public class GeneralSettings implements PersistentStateComponent Date: Tue, 10 Apr 2018 15:51:45 +0300 Subject: [PATCH 17/53] allow clients to omit sending 'testFinished' messages after 'testFailed'/'testIgnored' messages (WEB-32144) Previously, it was possible to skip sending 'testFinished' messages after 'testFailed' messages only. Now it's possible for 'testIgnored' too. --- ...eralIdBasedToSMTRunnerEventsConvertor.java | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralIdBasedToSMTRunnerEventsConvertor.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralIdBasedToSMTRunnerEventsConvertor.java index e8faf435e3ac..35ab1ca57bae 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralIdBasedToSMTRunnerEventsConvertor.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralIdBasedToSMTRunnerEventsConvertor.java @@ -209,21 +209,25 @@ public class GeneralIdBasedToSMTRunnerEventsConvertor extends GeneralTestEventsP } testProxy.setFrameworkOutputFile(testFinishedEvent.getOutputFile()); testProxy.setFinished(); - if (node.getState() != State.FAILED) { - LOG.debug("onTestFinished: node.getState() != State.FAILED"); - - // Don't count the same test twice if 'testFailed' message is followed by 'testFinished' message - // which may happen if generated TeamCity messages adhere rules from - // https://confluence.jetbrains.com/display/TCD10/Build+Script+Interaction+with+TeamCity - // Anyway, this id-based converter already breaks TeamCity protocol by expecting messages with - // non-standard TeamCity attributes: 'nodeId'/'parentNodeId' instead of 'name'. - fireOnTestFinished(testProxy); - } + fireOnTestFinishedIfNeeded(testProxy, node); terminateNode(node, State.FINISHED); } }); } + private void fireOnTestFinishedIfNeeded(@NotNull SMTestProxy testProxy, @NotNull Node node) { + // allow clients to omit sending 'testFinished' messages after 'testFailed'/'testIgnored' messages + if (node.getState() != State.FINISHED && node.getState() != State.FAILED && node.getState() != State.IGNORED) { + LOG.debug("onTestFinished: state != FINISHED && state != FAILED && state != IGNORED"); + // Don't count the same test twice if 'testFailed' or 'testIgnored' message is followed by 'testFinished' message + // which may happen if generated TeamCity messages adhere rules from + // https://confluence.jetbrains.com/display/TCD10/Build+Script+Interaction+with+TeamCity + // Anyway, this id-based converter already breaks TeamCity protocol by expecting messages with + // non-standard TeamCity attributes: 'nodeId'/'parentNodeId' instead of 'name'. + fireOnTestFinished(testProxy); + } + } + public void onSuiteFinished(@NotNull final TestSuiteFinishedEvent suiteFinishedEvent) { if (LOG.isDebugEnabled()) { LOG.debug("onSuiteFinished " + suiteFinishedEvent.getId()); @@ -328,10 +332,9 @@ public class GeneralIdBasedToSMTRunnerEventsConvertor extends GeneralTestEventsP if (duration >= 0) { testProxy.setDuration(duration); } - fireOnTestFinished(testProxy); - // fire event fireOnTestFailed(testProxy); + fireOnTestFinishedIfNeeded(testProxy, node); terminateNode(node, State.FAILED); }); @@ -349,8 +352,10 @@ public class GeneralIdBasedToSMTRunnerEventsConvertor extends GeneralTestEventsP } SMTestProxy testProxy = node.getProxy(); testProxy.setTestIgnored(testIgnoredEvent.getIgnoreComment(), testIgnoredEvent.getStacktrace()); - // fire event + fireOnTestIgnored(testProxy); + fireOnTestFinishedIfNeeded(testProxy, node); + terminateNode(node, State.IGNORED); } }); From bce8646f7ebbc7cba3ef017cbe46fed114aff12f Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 10 Apr 2018 14:53:36 +0200 Subject: [PATCH 18/53] API for replacing "Log to console" text in the UI Rider needs to show "Log to debug output" instead. --- .../com/intellij/xdebugger/breakpoints/XBreakpointType.java | 5 +++++ .../impl/breakpoints/ui/XBreakpointActionsPanel.form | 2 +- .../impl/breakpoints/ui/XBreakpointActionsPanel.java | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/XBreakpointType.java b/platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/XBreakpointType.java index 9a52d7416316..bf1cd555af5b 100644 --- a/platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/XBreakpointType.java +++ b/platform/xdebugger-api/src/com/intellij/xdebugger/breakpoints/XBreakpointType.java @@ -19,6 +19,7 @@ package com.intellij.xdebugger.breakpoints; import com.intellij.icons.AllIcons; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.project.Project; +import com.intellij.xdebugger.XDebuggerBundle; import com.intellij.xdebugger.XSourcePosition; import com.intellij.xdebugger.breakpoints.ui.XBreakpointCustomPropertiesPanel; import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingRule; @@ -245,6 +246,10 @@ public abstract class XBreakpointType, P extends XBreak return getDisplayText(breakpoint); } + public String getLogMessageLabelText() { + return XDebuggerBundle.message("xbreakpoints.log.message.label"); + } + public interface XBreakpointCreator

{ @NotNull XBreakpoint

createBreakpoint(@Nullable P properties); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XBreakpointActionsPanel.form b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XBreakpointActionsPanel.form index 68c68f0276ae..9fceb61ee1cc 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XBreakpointActionsPanel.form +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XBreakpointActionsPanel.form @@ -77,7 +77,7 @@ - + diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XBreakpointActionsPanel.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XBreakpointActionsPanel.java index eb41796c0d24..cba96840add8 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XBreakpointActionsPanel.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/ui/XBreakpointActionsPanel.java @@ -34,10 +34,12 @@ public class XBreakpointActionsPanel extends XBreakpointPropertiesSubPanel { private JPanel myExpressionPanel; private JPanel myLanguageChooserPanel; private JCheckBox myLogStack; + private JLabel myLogToConsoleLabel; private XDebuggerExpressionComboBox myLogExpressionComboBox; public void init(Project project, XBreakpointManager breakpointManager, @NotNull XBreakpointBase breakpoint, @Nullable XDebuggerEditorsProvider debuggerEditorsProvider) { init(project, breakpointManager, breakpoint); + myLogToConsoleLabel.setText(breakpoint.getType().getLogMessageLabelText()); if (debuggerEditorsProvider != null) { ActionListener listener = new ActionListener() { public void actionPerformed(final ActionEvent e) { From 7a6a412b291cff796877c4eb65061b506ef5cec6 Mon Sep 17 00:00:00 2001 From: Anton Lobov Date: Tue, 10 Apr 2018 15:01:34 +0200 Subject: [PATCH 19/53] don't use schema catalog in unit tests - 2 --- .../jetbrains/jsonSchema/remote/JsonSchemaCatalogManager.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/json/src/com/jetbrains/jsonSchema/remote/JsonSchemaCatalogManager.java b/json/src/com/jetbrains/jsonSchema/remote/JsonSchemaCatalogManager.java index 6285e2216960..8c6ffe8ee285 100644 --- a/json/src/com/jetbrains/jsonSchema/remote/JsonSchemaCatalogManager.java +++ b/json/src/com/jetbrains/jsonSchema/remote/JsonSchemaCatalogManager.java @@ -25,7 +25,7 @@ public class JsonSchemaCatalogManager { @NotNull private final ConcurrentMap myResolvedMappings = ContainerUtil.newConcurrentMap(); private static final String NO_CACHE = "$_$_WS_NO_CACHE_$_$"; private static final String EMPTY = "$_$_WS_EMPTY_$_$"; - private static final AtomicBoolean myIsEnabled = new AtomicBoolean(!ApplicationManager.getApplication().isUnitTestMode()); + private static final AtomicBoolean myIsEnabled = new AtomicBoolean(true); public JsonSchemaCatalogManager(@NotNull Project project) { myProject = project; @@ -37,6 +37,7 @@ public class JsonSchemaCatalogManager { } public void startUpdates() { + if (ApplicationManager.getApplication().isUnitTestMode()) return; RemoteFileManager instance = RemoteFileManager.getInstance(); instance.addRemoteContentProvider(myRemoteContentProvider); myCatalog = JsonFileResolver.urlToFile(DEFAULT_CATALOG); @@ -44,6 +45,7 @@ public class JsonSchemaCatalogManager { @Nullable public VirtualFile getSchemaFileForFile(@NotNull VirtualFile file) { + if (ApplicationManager.getApplication().isUnitTestMode()) return null; if (!myIsEnabled.get()) return null; String name = file.getName(); From e1536c54e51a3b76b9b5ce98501914f7f8a06a2f Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Wed, 4 Apr 2018 14:03:19 +0300 Subject: [PATCH 20/53] vcs: cleanup - make method static --- .../vcs/changes/ui/CommitChangeListDialog.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java index 1ded9b521741..327e7ec21326 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java @@ -352,7 +352,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj setComment(initialSelection, comment); } - initCheckinHandlers(project); + myHandlers.addAll(createCheckinHandlers(project, this, myCommitContext)); myCommitOptions = new CommitOptionsPanel(this, myHandlers, getAffectedVcses()); restoreState(); @@ -444,13 +444,18 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj showDetailsIfSaved(); } - private void initCheckinHandlers(@NotNull Project project) { + @NotNull + private static List createCheckinHandlers(@NotNull Project project, + @NotNull CheckinProjectPanel checkinPanel, + @NotNull CommitContext commitContext) { + List handlers = new ArrayList<>(); for (BaseCheckinHandlerFactory factory : getCheckInFactories(project)) { - CheckinHandler handler = factory.createHandler(this, myCommitContext); + CheckinHandler handler = factory.createHandler(checkinPanel, commitContext); if (!CheckinHandler.DUMMY.equals(handler)) { - myHandlers.add(handler); + handlers.add(handler); } } + return handlers; } @NotNull From 53d621eb92eecd7f4d6dd1b81e1cbdf955bbf75a Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Wed, 4 Apr 2018 14:04:52 +0300 Subject: [PATCH 21/53] vcs: cleanup - make method static --- .../vcs/changes/ui/CommitChangeListDialog.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java index 327e7ec21326..7caf73c09b36 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java @@ -99,6 +99,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj @NotNull private final List myExecutors; @NotNull private final List myHandlers = newArrayList(); private final boolean myAllOfDefaultChangeListChangesIncluded; + @NotNull private final String myCommitActionName; @NotNull private final Map myListComments; @NotNull private final List myExecutorActions; @@ -357,7 +358,8 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj restoreState(); setTitle(myShowVcsCommit ? TITLE : trimEllipsis(executors.get(0).getActionText())); - myCommitAction = myShowVcsCommit ? new CommitAction(getCommitActionName()) : null; + myCommitActionName = getCommitActionName(myAffectedVcses); + myCommitAction = myShowVcsCommit ? new CommitAction(myCommitActionName) : null; myExecutorActions = createExecutorActions(executors); if (myCommitAction != null) { myCommitAction.setOptions(myExecutorActions); @@ -751,10 +753,16 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj PropertiesComponent.getInstance().setValue(DETAILS_SHOW_OPTION, myDetailsSplitter.isOn(), DETAILS_SHOW_OPTION_DEFAULT); } + @NotNull @Override public String getCommitActionName() { + return myCommitActionName; + } + + @NotNull + private static String getCommitActionName(@NotNull Collection affectedVcses) { String name = null; - for (AbstractVcs vcs : myAffectedVcses) { + for (AbstractVcs vcs : affectedVcses) { CheckinEnvironment checkinEnvironment = vcs.getCheckinEnvironment(); if (name == null && checkinEnvironment != null) { name = checkinEnvironment.getCheckinOperationName(); From 5d5e35cae6338ff62b53a4e39186041ebd81677e Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Wed, 4 Apr 2018 14:07:29 +0300 Subject: [PATCH 22/53] vcs: commit action text should not depend on order of affected vcses * fix case, when one of affected vcses doesn't have a CheckinEnvironment --- .../vcs/changes/ui/CommitChangeListDialog.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java index 7caf73c09b36..e647d68c5d2a 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java @@ -761,17 +761,14 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj @NotNull private static String getCommitActionName(@NotNull Collection affectedVcses) { - String name = null; - for (AbstractVcs vcs : affectedVcses) { + Set names = map2SetNotNull(affectedVcses, vcs -> { CheckinEnvironment checkinEnvironment = vcs.getCheckinEnvironment(); - if (name == null && checkinEnvironment != null) { - name = checkinEnvironment.getCheckinOperationName(); - } - else { - name = VcsBundle.getString("commit.dialog.default.commit.operation.name"); - } + return checkinEnvironment != null ? checkinEnvironment.getCheckinOperationName() : null; + }); + if (names.size() == 1) { + return notNull(ContainerUtil.getFirstItem(names)); } - return name != null ? name : VcsBundle.getString("commit.dialog.default.commit.operation.name"); + return VcsBundle.getString("commit.dialog.default.commit.operation.name"); } private boolean checkComment() { From 50de525618e7d7c0c3cd4ac38439de40e5c8c0ea Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Wed, 4 Apr 2018 14:12:35 +0300 Subject: [PATCH 23/53] vcs: cleanup - unify similar if branches --- .../openapi/vcs/changes/ui/CommitChangeListDialog.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java index e647d68c5d2a..278ba689c742 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java @@ -359,15 +359,16 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj setTitle(myShowVcsCommit ? TITLE : trimEllipsis(executors.get(0).getActionText())); myCommitActionName = getCommitActionName(myAffectedVcses); - myCommitAction = myShowVcsCommit ? new CommitAction(myCommitActionName) : null; myExecutorActions = createExecutorActions(executors); - if (myCommitAction != null) { + if (myShowVcsCommit) { + myCommitAction = new CommitAction(myCommitActionName); myCommitAction.setOptions(myExecutorActions); } else { + myCommitAction = null; myExecutorActions.get(0).putValue(DEFAULT_ACTION, Boolean.TRUE); } - myHelpId = myCommitAction != null ? HELP_ID : getHelpId(executors); + myHelpId = myShowVcsCommit ? HELP_ID : getHelpId(executors); myWarningLabel = new JBLabel(); myWarningLabel.setForeground(JBColor.RED); From f069b27e4778878512612cc78f1063cf0f33f317 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Sat, 7 Apr 2018 14:43:13 +0300 Subject: [PATCH 24/53] vcs: move executors handling earlier in commit dialog constructor --- .../changes/ui/CommitChangeListDialog.java | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java index 278ba689c742..f9122b0984af 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java @@ -298,6 +298,22 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj myAllOfDefaultChangeListChangesIncluded = newHashSet(changes).containsAll(newHashSet(defaultChangeList.getChanges())); + myHandlers.addAll(createCheckinHandlers(project, this, myCommitContext)); + + setTitle(myShowVcsCommit ? TITLE : trimEllipsis(executors.get(0).getActionText())); + myCommitActionName = getCommitActionName(myAffectedVcses); + myExecutorActions = createExecutorActions(executors); + if (myShowVcsCommit) { + myCommitAction = new CommitAction(myCommitActionName); + myCommitAction.setOptions(myExecutorActions); + } + else { + myCommitAction = null; + myExecutorActions.get(0).putValue(DEFAULT_ACTION, Boolean.TRUE); + } + myHelpId = myShowVcsCommit ? HELP_ID : getHelpId(executors); + + myCommitMessageArea = new CommitMessage(project, true, true, myShowVcsCommit); if (myIsAlien) { @@ -353,23 +369,9 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj setComment(initialSelection, comment); } - myHandlers.addAll(createCheckinHandlers(project, this, myCommitContext)); myCommitOptions = new CommitOptionsPanel(this, myHandlers, getAffectedVcses()); restoreState(); - setTitle(myShowVcsCommit ? TITLE : trimEllipsis(executors.get(0).getActionText())); - myCommitActionName = getCommitActionName(myAffectedVcses); - myExecutorActions = createExecutorActions(executors); - if (myShowVcsCommit) { - myCommitAction = new CommitAction(myCommitActionName); - myCommitAction.setOptions(myExecutorActions); - } - else { - myCommitAction = null; - myExecutorActions.get(0).putValue(DEFAULT_ACTION, Boolean.TRUE); - } - myHelpId = myShowVcsCommit ? HELP_ID : getHelpId(executors); - myWarningLabel = new JBLabel(); myWarningLabel.setForeground(JBColor.RED); myWarningLabel.setBorder(JBUI.Borders.empty(5, 5, 0, 5)); From 7e7d8a30c53d2c388226f256d4c98db8e9b0c779 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Wed, 4 Apr 2018 14:40:38 +0300 Subject: [PATCH 25/53] vcs: strip mnemonics before using executor action text in commit dialog title --- .../vcs/changes/ui/CommitChangeListDialog.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java index f9122b0984af..aced2e9c8efa 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java @@ -300,7 +300,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj myHandlers.addAll(createCheckinHandlers(project, this, myCommitContext)); - setTitle(myShowVcsCommit ? TITLE : trimEllipsis(executors.get(0).getActionText())); + setTitle(myShowVcsCommit ? TITLE : getExecutorPresentableText(executors.get(0))); myCommitActionName = getCommitActionName(myAffectedVcses); myExecutorActions = createExecutorActions(executors); if (myShowVcsCommit) { @@ -638,7 +638,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj JComponent configurationUI = SessionDialog.createConfigurationUI(session, getIncludedChanges(), getCommitMessage()); if (configurationUI != null) { DialogWrapper sessionDialog = - new SessionDialog(commitExecutor.getActionText(), getProject(), session, getIncludedChanges(), getCommitMessage(), + new SessionDialog(getExecutorPresentableText(commitExecutor), getProject(), session, getIncludedChanges(), getCommitMessage(), configurationUI); if (!sessionDialog.showAndGet()) { session.executionCanceled(); @@ -1084,9 +1084,14 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj return myBrowser.getData(dataId); } + @NotNull + static String getExecutorPresentableText(@NotNull CommitExecutor executor) { + return trimEllipsis(removeMnemonic(executor.getActionText())); + } + @NotNull static String trimEllipsis(@NotNull String title) { - return StringUtil.trimEnd(title, "..."); + return StringUtil.trimEnd(StringUtil.trimEnd(title, "..."), "\u2026"); } private void ensureDataIsActual(@NotNull Runnable runnable) { From e486d8e883c2b3f0cb986c9259b088ba75a43286 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Wed, 4 Apr 2018 14:43:41 +0300 Subject: [PATCH 26/53] vcs: cleanup --- .../openapi/vcs/changes/ui/CommitChangeListDialog.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java index aced2e9c8efa..0dd234e3406b 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java @@ -513,7 +513,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj private void updateOnListSelection() { updateComment(); - myCommitOptions.onChangeListSelected((LocalChangeList)myBrowser.getSelectedChangeList(), + myCommitOptions.onChangeListSelected(myBrowser.getSelectedChangeList(), ChangeListManagerImpl.getInstanceImpl(myProject).getUnversionedFiles()); } @@ -713,8 +713,8 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj private void updateComment() { if (myVcsConfiguration.CLEAR_INITIAL_COMMIT_MESSAGE) return; - LocalChangeList list = (LocalChangeList)myBrowser.getSelectedChangeList(); - if (list == null || list.getName().equals(myLastSelectedListName)) { + LocalChangeList list = myBrowser.getSelectedChangeList(); + if (list.getName().equals(myLastSelectedListName)) { return; } else if (myLastSelectedListName != null) { saveCommentIntoChangeList(); @@ -1178,7 +1178,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj @Override protected void selectChange(@NotNull Wrapper change) { //noinspection unchecked - myBrowser.selectEntries((List)singletonList(change.getUserObject())); + myBrowser.selectEntries(singletonList(change.getUserObject())); } @NotNull From c23a84f0a929718ab4bf868efe63b6bb09c4d27a Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Sun, 1 Apr 2018 21:19:05 +0300 Subject: [PATCH 27/53] TW-54380 vcs: warn if partial changes are not supported by CommitExecutor * do not show checkboxes if none of executors supports partial commit --- .../src/messages/VcsBundle.properties | 2 ++ .../openapi/vcs/changes/CommitExecutor.java | 4 +++ .../patch/CreatePatchCommitExecutor.java | 5 +++ .../shelf/ShelveChangesCommitExecutor.java | 5 +++ .../changes/ui/CommitChangeListDialog.java | 34 ++++++++++++++++--- .../ui/MultipleLocalChangeListsBrowser.java | 7 ++-- .../util/CloudGitDeploymentRuntime.java | 5 +++ .../checkin/GitCommitAndPushExecutor.java | 5 +++ 8 files changed, 61 insertions(+), 6 deletions(-) diff --git a/platform/platform-resources-en/src/messages/VcsBundle.properties b/platform/platform-resources-en/src/messages/VcsBundle.properties index b5a355ea541c..ce2d0ed46d3b 100644 --- a/platform/platform-resources-en/src/messages/VcsBundle.properties +++ b/platform/platform-resources-en/src/messages/VcsBundle.properties @@ -274,6 +274,8 @@ commit.dialog.refresh.files=Synchronizing files... commit.dialog.rejected.commit.template=Rejected commit: {0} commit.dialog.changelist.label=Changelis&t: commit.dialog.changed.files.label=Changed &Files +commit.dialog.partial.commit.warning.title=Partial Commit Is Not Supported +commit.dialog.partial.commit.warning.body=Partial commit is not supported for ''{0}''.\nAll changes in the selected files will be committed. commit.wait.util.synced.message=Please wait until VCS synchronization is finished. commit.wait.util.synced.title=Finishing VCS refresh change.list.manager.wait.lists.synchronization={0}: Local Changes Refresh diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/CommitExecutor.java b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/CommitExecutor.java index 30a9a4e2716d..a0c811989fc9 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/CommitExecutor.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/CommitExecutor.java @@ -22,6 +22,10 @@ public interface CommitExecutor { return null; } + default boolean supportsPartialCommit() { + return false; + } + @NotNull CommitSession createCommitSession(); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchCommitExecutor.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchCommitExecutor.java index 818cecfa0471..a87540a5586e 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchCommitExecutor.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/CreatePatchCommitExecutor.java @@ -76,6 +76,11 @@ public class CreatePatchCommitExecutor extends LocalCommitExecutor implements Pr return "reference.dialogs.vcs.patch.create"; } + @Override + public boolean supportsPartialCommit() { + return true; + } + @Override @NotNull public CommitSession createCommitSession() { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesCommitExecutor.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesCommitExecutor.java index 62703650c920..3b95f0315b15 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesCommitExecutor.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesCommitExecutor.java @@ -55,6 +55,11 @@ public class ShelveChangesCommitExecutor extends LocalCommitExecutor { return "reference.dialogs.vcs.shelve"; } + @Override + public boolean supportsPartialCommit() { + return true; + } + private class ShelveChangesCommitSession implements CommitSession, CommitSessionContextAware { @Override public void setContext(CommitContext context) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java index 0dd234e3406b..d3dce30db30c 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java @@ -1,6 +1,7 @@ // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.openapi.vcs.changes.ui; +import com.intellij.CommonBundle; import com.intellij.diff.util.DiffPlaces; import com.intellij.diff.util.DiffUserDataKeysEx; import com.intellij.diff.util.DiffUtil; @@ -29,6 +30,7 @@ import com.intellij.openapi.vcs.changes.*; import com.intellij.openapi.vcs.changes.actions.ScheduleForAdditionAction; import com.intellij.openapi.vcs.changes.actions.diff.lst.LocalChangeListDiffTool; import com.intellij.openapi.vcs.checkin.*; +import com.intellij.openapi.vcs.ex.PartialLocalLineStatusTracker; import com.intellij.openapi.vcs.impl.CheckinHandlersManager; import com.intellij.openapi.vcs.impl.LineStatusTrackerManager; import com.intellij.openapi.vcs.impl.PartialChangesUtil; @@ -100,6 +102,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj @NotNull private final List myHandlers = newArrayList(); private final boolean myAllOfDefaultChangeListChangesIncluded; @NotNull private final String myCommitActionName; + private final boolean myEnablePartialCommit; @NotNull private final Map myListComments; @NotNull private final List myExecutorActions; @@ -290,7 +293,6 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj myIsAlien = isAlien; myResultHandler = customResultHandler; myListComments = newHashMap(); - myDiffDetails = new MyChangeProcessor(myProject); if (!myShowVcsCommit && isEmpty(executors)) { throw new IllegalArgumentException("nothing found to execute commit with"); @@ -313,7 +315,10 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj } myHelpId = myShowVcsCommit ? HELP_ID : getHelpId(executors); + myEnablePartialCommit = ContainerUtil.exists(getAffectedVcses(), AbstractVcs::arePartialChangelistsSupported) && + (myShowVcsCommit || ContainerUtil.exists(myExecutors, executor -> executor.supportsPartialCommit())); + myDiffDetails = new MyChangeProcessor(myProject, myEnablePartialCommit); myCommitMessageArea = new CommitMessage(project, true, true, myShowVcsCommit); if (myIsAlien) { @@ -329,7 +334,8 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj else { LineStatusTrackerManager.getInstanceImpl(myProject).resetExcludedFromCommitMarkers(); - MultipleLocalChangeListsBrowser browser = new MultipleLocalChangeListsBrowser(project, true, true, myShowVcsCommit); + MultipleLocalChangeListsBrowser browser = new MultipleLocalChangeListsBrowser(project, true, true, + myShowVcsCommit, myEnablePartialCommit); myBrowser = browser; if (initialSelection != null) browser.setSelectedChangeList(initialSelection); @@ -627,6 +633,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj return; } + if (!checkCommitOptionsSupported(commitExecutor)) return; if (!saveDialogState()) return; saveComments(true); @@ -896,6 +903,25 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj myListComments.forEach((changeListName, comment) -> changeListManager.editComment(changeListName, comment)); } + private boolean checkCommitOptionsSupported(@NotNull CommitExecutor commitExecutor) { + if (myIsAlien) return true; + + if (!commitExecutor.supportsPartialCommit()) { + boolean hasPartialChanges = ContainerUtil.exists(getIncludedChanges(), change -> { + PartialLocalLineStatusTracker tracker = PartialChangesUtil.getPartialTracker(myProject, change); + return tracker != null && tracker.hasPartialChangesToCommit(); + }); + if (hasPartialChanges) { + return Messages.YES == + Messages.showYesNoDialog(myProject, + message("commit.dialog.partial.commit.warning.body", getExecutorPresentableText(commitExecutor)), + message("commit.dialog.partial.commit.warning.title"), + commitExecutor.getActionText(), CommonBundle.getCancelButtonText(), Messages.getWarningIcon()); + } + } + return true; + } + @Override public void doCancelAction() { myCommitOptions.saveChangeListComponentsState(); @@ -1151,11 +1177,11 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj } private class MyChangeProcessor extends ChangeViewDiffRequestProcessor { - public MyChangeProcessor(@NotNull Project project) { + public MyChangeProcessor(@NotNull Project project, boolean enablePartialCommit) { super(project, DiffPlaces.COMMIT_DIALOG); putContextUserData(DiffUserDataKeysEx.SHOW_READ_ONLY_LOCK, true); - putContextUserData(LocalChangeListDiffTool.ALLOW_EXCLUDE_FROM_COMMIT, true); + putContextUserData(LocalChangeListDiffTool.ALLOW_EXCLUDE_FROM_COMMIT, enablePartialCommit); } @Override diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/MultipleLocalChangeListsBrowser.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/MultipleLocalChangeListsBrowser.java index ca6786f188a3..8bd99c188f97 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/MultipleLocalChangeListsBrowser.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/MultipleLocalChangeListsBrowser.java @@ -62,6 +62,7 @@ public class MultipleLocalChangeListsBrowser extends CommitDialogChangesBrowser new MergingUpdateQueue("MultipleLocalChangeListsBrowser", 300, true, ANY_COMPONENT, this); private final boolean myEnableUnversioned; + private final boolean myEnablePartialCommit; @Nullable private JComponent myBottomDiffComponent; @NotNull private final ChangeListChooser myChangeListChooser; @@ -78,9 +79,11 @@ public class MultipleLocalChangeListsBrowser extends CommitDialogChangesBrowser public MultipleLocalChangeListsBrowser(@NotNull Project project, boolean showCheckboxes, boolean highlightProblems, - boolean enableUnversioned) { + boolean enableUnversioned, + boolean enablePartialCommit) { super(project, showCheckboxes, highlightProblems); myEnableUnversioned = enableUnversioned; + myEnablePartialCommit = enablePartialCommit; myChangeList = ChangeListManager.getInstance(project).getDefaultChangeList(); myChangeListChooser = new ChangeListChooser(); @@ -151,7 +154,7 @@ public class MultipleLocalChangeListsBrowser extends CommitDialogChangesBrowser protected void updateDiffContext(@NotNull DiffRequestChain chain) { super.updateDiffContext(chain); chain.putUserData(DiffUserDataKeysEx.BOTTOM_PANEL, myBottomDiffComponent); - chain.putUserData(LocalChangeListDiffTool.ALLOW_EXCLUDE_FROM_COMMIT, true); + chain.putUserData(LocalChangeListDiffTool.ALLOW_EXCLUDE_FROM_COMMIT, myEnablePartialCommit); } diff --git a/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/CloudGitDeploymentRuntime.java b/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/CloudGitDeploymentRuntime.java index a0f1ac27d937..2b95602922ac 100644 --- a/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/CloudGitDeploymentRuntime.java +++ b/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/CloudGitDeploymentRuntime.java @@ -81,6 +81,11 @@ public class CloudGitDeploymentRuntime extends CloudDeploymentRuntime { return "Commit and Push"; } + @Override + public boolean supportsPartialCommit() { + return true; + } + @NotNull @Override public CommitSession createCommitSession() { diff --git a/plugins/git4idea/src/git4idea/checkin/GitCommitAndPushExecutor.java b/plugins/git4idea/src/git4idea/checkin/GitCommitAndPushExecutor.java index 89f8869b4255..8988d0cbfc90 100644 --- a/plugins/git4idea/src/git4idea/checkin/GitCommitAndPushExecutor.java +++ b/plugins/git4idea/src/git4idea/checkin/GitCommitAndPushExecutor.java @@ -35,6 +35,11 @@ public class GitCommitAndPushExecutor implements CommitExecutor { return ID; } + @Override + public boolean supportsPartialCommit() { + return true; + } + @NotNull public CommitSession createCommitSession() { myCheckinEnvironment.setNextCommitIsPushed(true); From c3b534b28c7cf36d04cd9d724366d40fbb3b290d Mon Sep 17 00:00:00 2001 From: Artem Bukhonov Date: Tue, 10 Apr 2018 15:21:45 +0200 Subject: [PATCH 28/53] Add API for restarting IDE with elevation This is used in Rider to allow debugging processes which require extra privileges --- .../application/impl/ApplicationImpl.java | 12 ++++++++---- .../src/com/intellij/util/Restarter.java | 16 +++++++++++++--- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java index 0781a3fc32d1..1f2a2d8277d5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java @@ -744,6 +744,10 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App } public void exit(boolean force, boolean exitConfirmed, boolean restart, @NotNull String[] beforeRestart) { + exit(force, exitConfirmed, restart, false, beforeRestart); + } + + public void exit(boolean force, boolean exitConfirmed, boolean restart, boolean elevate, @NotNull String[] beforeRestart) { if (!force) { if (myExitInProgress) return; if (!exitConfirmed && getDefaultModalityState() != ModalityState.NON_MODAL) return; @@ -751,14 +755,14 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App myExitInProgress = true; if (isDispatchThread()) { - doExit(force, exitConfirmed, restart, beforeRestart); + doExit(force, exitConfirmed, restart, elevate, beforeRestart); } else { - invokeLater(() -> doExit(force, exitConfirmed, restart, beforeRestart), ModalityState.NON_MODAL); + invokeLater(() -> doExit(force, exitConfirmed, restart, elevate, beforeRestart), ModalityState.NON_MODAL); } } - private void doExit(boolean force, boolean exitConfirmed, boolean restart, String[] beforeRestart) { + private void doExit(boolean force, boolean exitConfirmed, boolean restart, boolean elevate, String[] beforeRestart) { try { if (!force && !confirmExitIfNeeded(exitConfirmed)) { return; @@ -786,7 +790,7 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App int exitCode = 0; if (restart && Restarter.isSupported()) { try { - Restarter.scheduleRestart(beforeRestart); + Restarter.scheduleRestart(elevate, beforeRestart); } catch (Throwable t) { LOG.error("Restart failed", t); diff --git a/platform/platform-impl/src/com/intellij/util/Restarter.java b/platform/platform-impl/src/com/intellij/util/Restarter.java index dbb46f73d590..a2f2029f7277 100644 --- a/platform/platform-impl/src/com/intellij/util/Restarter.java +++ b/platform/platform-impl/src/com/intellij/util/Restarter.java @@ -104,9 +104,13 @@ public class Restarter { } public static void scheduleRestart(@NotNull String... beforeRestart) throws IOException { + scheduleRestart(false, beforeRestart); + } + + public static void scheduleRestart(Boolean elevate, @NotNull String... beforeRestart) throws IOException { Logger.getInstance(Restarter.class).info("restart: " + Arrays.toString(beforeRestart)); if (SystemInfo.isWindows) { - restartOnWindows(beforeRestart); + restartOnWindows(elevate, beforeRestart); } else if (SystemInfo.isMac) { restartOnMac(beforeRestart); @@ -119,7 +123,7 @@ public class Restarter { } } - private static void restartOnWindows(String... beforeRestart) throws IOException { + private static void restartOnWindows(Boolean elevate, String... beforeRestart) throws IOException { Kernel32 kernel32 = Native.loadLibrary("kernel32", Kernel32.class); Shell32 shell32 = Native.loadLibrary("shell32", Shell32.class); @@ -146,7 +150,13 @@ public class Restarter { args.add(String.valueOf(pid)); args.add(String.valueOf(beforeRestart.length)); Collections.addAll(args, beforeRestart); - args.add(String.valueOf(argv.length)); + if (elevate) { + args.add(String.valueOf(argv.length + 1)); + args.add(new File(PathManager.getBinPath(), "launcher.exe").getPath()); + } + else { + args.add(String.valueOf(argv.length)); + } Collections.addAll(args, argv); runRestarter(new File(PathManager.getBinPath(), "restarter.exe"), args); From 17db82279dac6d2b0d5c689391cecf5032bd8523 Mon Sep 17 00:00:00 2001 From: Maxim Kropotov Date: Tue, 10 Apr 2018 14:24:57 +0300 Subject: [PATCH 29/53] json schema: resolve array pointers, validate additional items Fixes WEB-32242 --- .../impl/JsonSchemaAnnotatorChecker.java | 3 + .../jsonSchema/impl/JsonSchemaObject.java | 47 +++++++++++- .../jsonSchema/impl/JsonSchemaReader.java | 4 +- .../JsonSchemaHighlightingTest.java | 72 +++++++++++++++++++ 4 files changed, 123 insertions(+), 3 deletions(-) diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaAnnotatorChecker.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaAnnotatorChecker.java index d0dcce6997dd..90fef6f27e4e 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaAnnotatorChecker.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaAnnotatorChecker.java @@ -457,6 +457,9 @@ class JsonSchemaAnnotatorChecker { if (!Boolean.TRUE.equals(schema.getAdditionalItemsAllowed())) { error("Additional items are not allowed", arrayValue.getDelegate()); } + else if (schema.getAdditionalItemsSchema() != null) { + checkObjectBySchemaRecordErrors(schema.getAdditionalItemsSchema(), arrayValue); + } } } } diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java index 35309b44feff..2feb02fe389d 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java @@ -9,6 +9,7 @@ import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -28,6 +29,8 @@ import java.util.stream.Collectors; public class JsonSchemaObject { @NonNls public static final String DEFINITIONS = "definitions"; @NonNls public static final String PROPERTIES = "properties"; + @NonNls public static final String ITEMS = "items"; + @NonNls public static final String ADDITIONAL_ITEMS = "additionalItems"; @NonNls public static final String X_INTELLIJ_HTML_DESCRIPTION = "x-intellij-html-description"; @NotNull private final JsonObject myJsonObject; @Nullable private Map myDefinitionsMap; @@ -641,12 +644,54 @@ public class JsonSchemaObject { current = current.getProperties().get(parts.get(++i)); continue; } - + Pair arrayResult = ObjectUtils.coalesce( + handleArrayItem(ITEMS, parts, i, current.getItemsSchema(), current.getItemsSchemaList()), + handleArrayItem(ADDITIONAL_ITEMS, parts, i, current.getAdditionalItemsSchema(), ContainerUtil.emptyList())); + if (arrayResult != null) { + //noinspection AssignmentToForLoopParameter + i = arrayResult.second; + current = arrayResult.first; + continue; + } current = current.getDefinitionsMap() == null ? null : current.getDefinitionsMap().get(part); } return current; } + @Nullable + private static Pair handleArrayItem(@NotNull String name, + List parts, + int i, + @Nullable JsonSchemaObject arraySchema, + @Nullable List arraySchemaList) { + final String part = parts.get(i); + if (!name.equals(part)) { + return null; + } + if (i == (parts.size() - 1)) { + if (arraySchema != null) { + return Pair.create(arraySchema, i); + } + } + else { + Integer next = tryParseInt(parts.get(++i)); + if (arraySchemaList != null && next != null && next >= 0 && next < arraySchemaList.size()) { + return Pair.create(arraySchemaList.get(next), i); + } + } + return null; + } + + @Nullable + private static Integer tryParseInt(String s) { + try { + return Integer.parseInt(s); + } + catch (Exception __) { + return null; + } + } + @Override public boolean equals(@Nullable Object o) { if (this == o) return true; diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaReader.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaReader.java index cd2bd5f4cdfa..72d8e2c613cb 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaReader.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaReader.java @@ -155,8 +155,8 @@ public class JsonSchemaReader { READERS_MAP.put("pattern", (element, object, queue) -> { if (element instanceof JsonStringLiteral) object.setPattern(StringUtil.unquoteString(element.getText())); }); - READERS_MAP.put("additionalItems", createAdditionalItems()); - READERS_MAP.put("items", createItems()); + READERS_MAP.put(JsonSchemaObject.ADDITIONAL_ITEMS, createAdditionalItems()); + READERS_MAP.put(JsonSchemaObject.ITEMS, createItems()); READERS_MAP.put("contains", createContains()); READERS_MAP.put("maxItems", (element, object, queue) -> { if (element instanceof JsonNumberLiteral) object.setMaxItems((int)((JsonNumberLiteral)element).getValue()); diff --git a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java index e8f915d04896..dc254502917a 100644 --- a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java +++ b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java @@ -546,6 +546,78 @@ public class JsonSchemaHighlightingTest extends DaemonAnalyzerTestCase { doTest(schema, "{\"withFormat\": \"localhost\"}"); } + public void testArrayItemReference() throws Exception { + @Language("JSON") final String schema = "{\n" + + " \"items\": [\n" + + " {\n" + + " \"type\": \"integer\"\n" + + " },\n" + + " {\n" + + " \"$ref\": \"#/items/0\"\n" + + " }\n" + + " ]\n" + + "}"; + doTest(schema, "[1, 2]"); + doTest(schema, "[1, \"foo\"]"); + } + + public void testArrayReference() throws Exception { + @Language("JSON") final String schema = "{\n" + + " \"definitions\": {\n" + + " \"options\": {\n" + + " \"type\": \"array\",\n" + + " \"items\": {\n" + + " \"type\": \"number\"\n" + + " }\n" + + " }\n" + + " },\n" + + " \"items\":{\n" + + " \"$ref\": \"#/definitions/options/items\"\n" + + " }\n" + + " \n" + + "}"; + doTest(schema, "[2, 3 ,4]"); + doTest(schema, "[2, \"3\"]"); + } + + public void testSelfArrayReferenceDoesNotThrowSOE() throws Exception { + @Language("JSON") final String schema = "{\n" + + " \"items\": [\n" + + " {\n" + + " \"$ref\": \"#/items/0\"\n" + + " }\n" + + " ]\n" + + "}"; + doTest(schema, "[]"); + } + + public void testValidateAdditionalItems() throws Exception { + @Language("JSON") final String schema = "{\n" + + " \"definitions\": {\n" + + " \"options\": {\n" + + " \"type\": \"array\",\n" + + " \"items\": {\n" + + " \"type\": \"number\"\n" + + " }\n" + + " }\n" + + " },\n" + + " \"items\": [\n" + + " {\n" + + " \"type\": \"boolean\"\n" + + " },\n" + + " {\n" + + " \"type\": \"boolean\"\n" + + " }\n" + + " ],\n" + + " \"additionalItems\": {\n" + + " \"$ref\": \"#/definitions/options/items\"\n" + + " }\n" + + "}"; + doTest(schema, "[true, true]"); + doTest(schema, "[true, true, 1, 2, 3]"); + doTest(schema, "[true, true, 1, \"2\"]"); + } + public static String rootObjectRedefinedSchema() { return "{\n" + " \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n" + From a8cf7a3f2f0e823db0d7e8860f5b4cf72dc9be1f Mon Sep 17 00:00:00 2001 From: Rustam Vishnyakov Date: Tue, 10 Apr 2018 17:19:56 +0300 Subject: [PATCH 30/53] Migration to PsiFile-based Code Style API --- .../generation/CommentByLineCommentHandler.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java index ee6517d9d009..9f54c79003b8 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java @@ -27,8 +27,6 @@ import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; -import com.intellij.psi.codeStyle.CodeStyleSettings; -import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import com.intellij.psi.impl.source.tree.injected.InjectedCaret; import com.intellij.psi.util.PsiUtilBase; @@ -46,14 +44,12 @@ import java.util.List; import java.util.Map; public class CommentByLineCommentHandler extends MultiCaretCodeInsightActionHandler { - private Project myProject; private final List myBlocks = new ArrayList<>(); @Override // first pass - adjacent carets are grouped into blocks public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull Caret caret, @NotNull PsiFile file) { - myProject = project; file = file.getViewProvider().getPsi(file.getViewProvider().getBaseLanguage()); PsiElement context = InjectedLanguageManager.getInstance(file.getProject()).getInjectionHost(file); @@ -135,8 +131,6 @@ public class CommentByLineCommentHandler extends MultiCaretCodeInsightActionHand public void postInvoke() { FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.comment.line"); - CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getSettings(myProject); - // second pass - determining whether we need to comment or to uncomment boolean allLinesCommented = true; for (Block block : myBlocks) { @@ -158,7 +152,7 @@ public class CommentByLineCommentHandler extends MultiCaretCodeInsightActionHand block.blockSuitableCommenter = getBlockSuitableCommenter(psiFile, offset, endOffset); Language lineStartLanguage = getLineStartLanguage(block.editor, psiFile, startLine); - CommonCodeStyleSettings languageSettings = codeStyleSettings.getCommonSettings(lineStartLanguage); + CommonCodeStyleSettings languageSettings = CodeStyle.getLanguageSettings(psiFile, lineStartLanguage); block.commentWithIndent = !languageSettings.LINE_COMMENT_AT_FIRST_COLUMN; block.addSpace = languageSettings.LINE_COMMENT_ADD_SPACE; From df8bef8395784970ae7c28eef970374d430f2a06 Mon Sep 17 00:00:00 2001 From: Rustam Vishnyakov Date: Tue, 10 Apr 2018 17:30:41 +0300 Subject: [PATCH 31/53] Migration to PsiFile-based Code Style API --- .../com/intellij/ide/fileTemplates/FileTemplateUtil.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/ide/fileTemplates/FileTemplateUtil.java b/platform/lang-impl/src/com/intellij/ide/fileTemplates/FileTemplateUtil.java index 1ba53923ecfd..4754ca04d3c5 100644 --- a/platform/lang-impl/src/com/intellij/ide/fileTemplates/FileTemplateUtil.java +++ b/platform/lang-impl/src/com/intellij/ide/fileTemplates/FileTemplateUtil.java @@ -15,6 +15,7 @@ */ package com.intellij.ide.fileTemplates; +import com.intellij.application.options.CodeStyle; import com.intellij.ide.IdeBundle; import com.intellij.ide.fileTemplates.impl.CustomFileTemplate; import com.intellij.openapi.application.ApplicationManager; @@ -27,7 +28,6 @@ import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.fileTypes.ex.FileTypeManagerEx; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; -import com.intellij.openapi.project.ex.ProjectManagerEx; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.ClassLoaderUtil; import com.intellij.openapi.util.ThrowableComputable; @@ -240,7 +240,7 @@ public class FileTemplateUtil { final String result = stringWriter.toString(); if (useSystemLineSeparators) { - final String newSeparator = CodeStyleSettingsManager.getSettings(ProjectManagerEx.getInstanceEx().getDefaultProject()).getLineSeparator(); + final String newSeparator = CodeStyle.getDefaultSettings().getLineSeparator(); if (!"\n".equals(newSeparator)) { return StringUtil.convertLineSeparators(result, newSeparator); } @@ -363,7 +363,7 @@ public class FileTemplateUtil { } public static String indent(String methodText, Project project, FileType fileType) { - int indent = CodeStyleSettingsManager.getSettings(project).getIndentSize(fileType); + int indent = CodeStyle.getSettings(project).getIndentSize(fileType); return methodText.replaceAll("\n", "\n" + StringUtil.repeatSymbol(' ', indent)); } From fe1ff8a4def2b66962481d9d173a00ac7600a881 Mon Sep 17 00:00:00 2001 From: Rustam Vishnyakov Date: Tue, 10 Apr 2018 17:33:04 +0300 Subject: [PATCH 32/53] Migration to PsiFile-based Code Style API --- .../java/src/service/project/wizard/GradleModuleBuilder.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/gradle/java/src/service/project/wizard/GradleModuleBuilder.java b/plugins/gradle/java/src/service/project/wizard/GradleModuleBuilder.java index caf915e6f114..19cadb3fa2a4 100644 --- a/plugins/gradle/java/src/service/project/wizard/GradleModuleBuilder.java +++ b/plugins/gradle/java/src/service/project/wizard/GradleModuleBuilder.java @@ -1,6 +1,7 @@ // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.plugins.gradle.service.project.wizard; +import com.intellij.application.options.CodeStyle; import com.intellij.ide.fileTemplates.FileTemplate; import com.intellij.ide.fileTemplates.FileTemplateManager; import com.intellij.ide.highlighter.ModuleFileType; @@ -29,7 +30,6 @@ import com.intellij.openapi.fileEditor.impl.LoadTextUtil; import com.intellij.openapi.module.*; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; -import com.intellij.openapi.project.ex.ProjectManagerEx; import com.intellij.openapi.projectRoots.JavaSdkType; import com.intellij.openapi.projectRoots.SdkTypeId; import com.intellij.openapi.roots.ModifiableRootModel; @@ -46,7 +46,6 @@ import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; -import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.util.ThreeState; import com.intellij.util.containers.ContainerUtil; import org.gradle.util.GradleVersion; @@ -476,7 +475,7 @@ public class GradleModuleBuilder extends AbstractExternalModuleBuilder Date: Tue, 10 Apr 2018 17:45:55 +0300 Subject: [PATCH 33/53] IDEABKL-6984 Add Eclipse Style Paren/Bracket/Quote Completiton simple solution, that makes Tab always (if option is enabled) move caret to the right of parenthesis/quote if caret was located before it --- .../editor/EditorSmartKeysConfigurable.form | 23 ++++-- .../editor/EditorSmartKeysConfigurable.java | 22 ++---- .../codeInsight/CodeInsightSettings.java | 2 + .../editorActions/BraceOrQuoteOutAction.java | 71 +++++++++++++++++++ .../editorActions/TabActionsPromoter.java | 24 +++++++ .../editorActions/TypedHandler.java | 18 +---- .../EditorSettingsStatisticsCollector.java | 1 + ...gacyEditorSettingsStatisticsCollector.java | 1 + .../openapi/actionSystem/IdeActions.java | 18 +---- .../src/messages/ActionsBundle.properties | 4 +- .../src/messages/ApplicationBundle.properties | 1 + .../src/META-INF/LangExtensions.xml | 1 + .../src/idea/LangActions.xml | 2 + .../src/keymaps/$default.xml | 4 ++ .../editor/actions/EditorTabOutTest.java | 30 ++++++++ 15 files changed, 167 insertions(+), 55 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/codeInsight/editorActions/BraceOrQuoteOutAction.java create mode 100644 platform/lang-impl/src/com/intellij/codeInsight/editorActions/TabActionsPromoter.java create mode 100644 platform/platform-tests/testSrc/com/intellij/openapi/editor/actions/EditorTabOutTest.java diff --git a/platform/lang-impl/src/com/intellij/application/options/editor/EditorSmartKeysConfigurable.form b/platform/lang-impl/src/com/intellij/application/options/editor/EditorSmartKeysConfigurable.form index 309450650b52..a458b1bdb4c7 100644 --- a/platform/lang-impl/src/com/intellij/application/options/editor/EditorSmartKeysConfigurable.form +++ b/platform/lang-impl/src/com/intellij/application/options/editor/EditorSmartKeysConfigurable.form @@ -1,6 +1,6 @@

- + @@ -10,7 +10,7 @@ - + @@ -60,7 +60,7 @@ - + @@ -88,7 +88,7 @@ - + @@ -114,7 +114,7 @@ - + @@ -146,7 +146,7 @@ - + @@ -184,7 +184,7 @@ - + @@ -196,6 +196,15 @@ + + + + + + + + + diff --git a/platform/lang-impl/src/com/intellij/application/options/editor/EditorSmartKeysConfigurable.java b/platform/lang-impl/src/com/intellij/application/options/editor/EditorSmartKeysConfigurable.java index 6b61d4a78666..d5e031be92f5 100644 --- a/platform/lang-impl/src/com/intellij/application/options/editor/EditorSmartKeysConfigurable.java +++ b/platform/lang-impl/src/com/intellij/application/options/editor/EditorSmartKeysConfigurable.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2016 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. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.application.options.editor; @@ -71,6 +57,7 @@ public class EditorSmartKeysConfigurable extends CompositeConfigurable, C public boolean ADD_MEMBER_IMPORTS_ON_THE_FLY = true; public boolean JSP_ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY; + public boolean TAB_EXITS_BRACKETS_AND_QUOTES; + /** * Names of classes and packages excluded from (Java) auto-import and completion. These are only IDE-specific settings * and don't take project-specific settings into account. diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/BraceOrQuoteOutAction.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/BraceOrQuoteOutAction.java new file mode 100644 index 000000000000..8803e80f4760 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/BraceOrQuoteOutAction.java @@ -0,0 +1,71 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.codeInsight.editorActions; + +import com.intellij.codeInsight.CodeInsightSettings; +import com.intellij.codeInsight.highlighting.BraceMatcher; +import com.intellij.codeInsight.highlighting.BraceMatchingUtil; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.editor.Caret; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.actionSystem.EditorAction; +import com.intellij.openapi.editor.actionSystem.EditorActionHandler; +import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.editor.highlighter.HighlighterIterator; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiUtilBase; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class BraceOrQuoteOutAction extends EditorAction { + public BraceOrQuoteOutAction() { + super(new Handler()); + } + + private static class Handler extends EditorActionHandler { + private Handler() { + super(true); + } + + @Override + protected boolean isEnabledForCaret(@NotNull Editor editor, @NotNull Caret caret, DataContext dataContext) { + return getCaretShift(editor, caret) != 0; + } + + @Override + protected void doExecute(@NotNull Editor editor, @Nullable Caret caret, DataContext dataContext) { + assert caret != null; + int caretShift = getCaretShift(editor, caret); + if (caretShift != 0) { + caret.moveToOffset(caret.getOffset() + caretShift); + } + } + + private static int getCaretShift(@NotNull Editor editor, @NotNull Caret caret) { + if (!CodeInsightSettings.getInstance().TAB_EXITS_BRACKETS_AND_QUOTES || !(editor instanceof EditorEx)) return 0; + + Project project = editor.getProject(); + if (project == null) return 0; + PsiFile psiFile = PsiUtilBase.getPsiFileInEditor(editor, project); + if (psiFile == null) return 0; + FileType fileType = TypedHandler.getFileType(psiFile, editor); + + int caretOffset = caret.getOffset(); + HighlighterIterator iterator = ((EditorEx)editor).getHighlighter().createIterator(caretOffset); + + BraceMatcher braceMatcher = BraceMatchingUtil.getBraceMatcher(fileType, iterator); + if (caretOffset == iterator.getStart() && + braceMatcher.isRBraceToken(iterator, editor.getDocument().getImmutableCharSequence(), fileType)) { + return iterator.getEnd() - caretOffset; + } + else { + QuoteHandler quoteHandler = TypedHandler.getQuoteHandler(psiFile, editor); + if (quoteHandler != null && quoteHandler.isClosingQuote(iterator, caretOffset)) { + return 1; + } + } + return 0; + } + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/TabActionsPromoter.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/TabActionsPromoter.java new file mode 100644 index 000000000000..2b668dd8a7d6 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/TabActionsPromoter.java @@ -0,0 +1,24 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.codeInsight.editorActions; + +import com.intellij.openapi.actionSystem.ActionPromoter; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.editor.actions.TabAction; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +public class TabActionsPromoter implements ActionPromoter { + @Override + public List promote(List actions, DataContext context) { + List newList = new ArrayList<>(actions); + newList.sort(Comparator.comparingInt(action -> { + if (action instanceof BraceOrQuoteOutAction) return 0; + else if (action instanceof TabAction) return 1; + else return -1; + })); + return newList; + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/TypedHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/TypedHandler.java index 6d6238817dac..4ac3eb262618 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/TypedHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/TypedHandler.java @@ -1,18 +1,4 @@ -/* - * 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. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.codeInsight.editorActions; @@ -101,7 +87,7 @@ public class TypedHandler extends TypedActionHandlerBase { } @NotNull - private static FileType getFileType(@NotNull PsiFile file, @NotNull Editor editor) { + static FileType getFileType(@NotNull PsiFile file, @NotNull Editor editor) { FileType fileType = file.getFileType(); Language language = PsiUtilBase.getLanguageInEditor(editor, file.getProject()); if (language != null && language != PlainTextLanguage.INSTANCE) { diff --git a/platform/lang-impl/src/com/intellij/internal/statistic/editor/EditorSettingsStatisticsCollector.java b/platform/lang-impl/src/com/intellij/internal/statistic/editor/EditorSettingsStatisticsCollector.java index 3b786032f582..eb86ccfd524b 100644 --- a/platform/lang-impl/src/com/intellij/internal/statistic/editor/EditorSettingsStatisticsCollector.java +++ b/platform/lang-impl/src/com/intellij/internal/statistic/editor/EditorSettingsStatisticsCollector.java @@ -106,6 +106,7 @@ class EditorSettingsStatisticsCollector extends ApplicationUsagesCollector { addBoolIfDiffers(set, cis, cisDefault, s -> s.SHOW_PARAMETER_NAME_HINTS_ON_COMPLETION, "completionHints"); addBoolIfDiffers(set, cis, cisDefault, s -> s.SHOW_EXTERNAL_ANNOTATIONS_INLINE, "externalAnnotationsInline"); addBoolIfDiffers(set, cis, cisDefault, s -> s.SHOW_INFERRED_ANNOTATIONS_INLINE, "inferredAnnotationsInline"); + addBoolIfDiffers(set, cis, cisDefault, s -> s.TAB_EXITS_BRACKETS_AND_QUOTES, "tabExitsBracketsAndQuotes"); return set; } diff --git a/platform/lang-impl/src/com/intellij/internal/statistic/editor/LegacyEditorSettingsStatisticsCollector.java b/platform/lang-impl/src/com/intellij/internal/statistic/editor/LegacyEditorSettingsStatisticsCollector.java index 6dcd974dc87a..6fe70ef9af53 100644 --- a/platform/lang-impl/src/com/intellij/internal/statistic/editor/LegacyEditorSettingsStatisticsCollector.java +++ b/platform/lang-impl/src/com/intellij/internal/statistic/editor/LegacyEditorSettingsStatisticsCollector.java @@ -113,6 +113,7 @@ class LegacyEditorSettingsStatisticsCollector extends UsagesCollector { addBoolIfDiffers(set, cis, cisDefault, s -> s.SHOW_PARAMETER_NAME_HINTS_ON_COMPLETION, "completionHints"); addBoolIfDiffers(set, cis, cisDefault, s -> s.SHOW_EXTERNAL_ANNOTATIONS_INLINE, "externalAnnotationsInline"); addBoolIfDiffers(set, cis, cisDefault, s -> s.SHOW_INFERRED_ANNOTATIONS_INLINE, "inferredAnnotationsInline"); + addBoolIfDiffers(set, cis, cisDefault, s -> s.TAB_EXITS_BRACKETS_AND_QUOTES, "tabExitsBracketsAndQuotes"); return set; } diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java index 5ab81d83bbdf..3032a2add668 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java @@ -1,18 +1,4 @@ -/* - * 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. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.openapi.actionSystem; import org.jetbrains.annotations.NonNls; @@ -344,4 +330,6 @@ public interface IdeActions { @NonNls String ACTION_METHOD_OVERLOAD_SWITCH_DOWN = "MethodOverloadSwitchDown"; @NonNls String ACTION_UPDATE_RUNNING_APPLICATION = "UpdateRunningApplication"; + + @NonNls String ACTION_BRACE_OR_QUOTE_OUT = "BraceOrQuoteOut"; } diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index f8813c379645..6e11fc34ff13 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -1630,4 +1630,6 @@ action.TogglePresentationMode.enter=Enter Presentation Mode action.EmojiAndSymbols.description=Input special characters using macOS system palette action.MethodOverloadSwitchUp.text=Previous Method Overload -action.MethodOverloadSwitchDown.text=Next Method Overload \ No newline at end of file +action.MethodOverloadSwitchDown.text=Next Method Overload + +action.BraceOrQuoteOut.text=Exit Current Braces/Quotes \ No newline at end of file diff --git a/platform/platform-resources-en/src/messages/ApplicationBundle.properties b/platform/platform-resources-en/src/messages/ApplicationBundle.properties index baf97471b6b8..9950df3ae907 100644 --- a/platform/platform-resources-en/src/messages/ApplicationBundle.properties +++ b/platform/platform-resources-en/src/messages/ApplicationBundle.properties @@ -453,6 +453,7 @@ checkbox.honor.camelhumps.words.settings.on.double.click=Honor "CamelHumps group.smart.keys=Smart Keys checkbox.use.camelhumps.words=Use "CamelHumps" words checkbox.enable.double.ctrl=Add multiple carets on double {0} with arrow keys +checkbox.tab.exists.brackets.and.quotes=Tab exits brackets/quotes checkbox.insert.pair.quote=Insert pair quote checkbox.insert.pair.bracket=Insert paired brackets (), [], {}, <> checkbox.javadoc.stub.after.slash.star.star=Insert documentation comment stub diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index 96c8202a69e9..741ef9864f2b 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -991,6 +991,7 @@ + diff --git a/platform/platform-resources/src/idea/LangActions.xml b/platform/platform-resources/src/idea/LangActions.xml index 33537b677256..089c670aebf9 100644 --- a/platform/platform-resources/src/idea/LangActions.xml +++ b/platform/platform-resources/src/idea/LangActions.xml @@ -48,6 +48,8 @@ + + diff --git a/platform/platform-resources/src/keymaps/$default.xml b/platform/platform-resources/src/keymaps/$default.xml index 573556aefd61..edafc4f5c7d3 100644 --- a/platform/platform-resources/src/keymaps/$default.xml +++ b/platform/platform-resources/src/keymaps/$default.xml @@ -1081,4 +1081,8 @@ + + + + \ No newline at end of file diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/actions/EditorTabOutTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/editor/actions/EditorTabOutTest.java new file mode 100644 index 000000000000..f60ea0a93b25 --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/actions/EditorTabOutTest.java @@ -0,0 +1,30 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.openapi.editor.actions; + +import com.intellij.codeInsight.CodeInsightSettings; +import com.intellij.openapi.actionSystem.IdeActions; +import com.intellij.openapi.editor.impl.AbstractEditorTest; + +public class EditorTabOutTest extends AbstractEditorTest { + public void testMethodCall() { + doTest("class C { void m() { System.getenv(\"a\") } }", 1); + } + + public void testStringLiteral() { + doTest("class C { void m() { String s = \"a\" } }", 1); + } + + private void doTest(String fileText, int expectedCaretShift) { + boolean savedSetting = CodeInsightSettings.getInstance().TAB_EXITS_BRACKETS_AND_QUOTES; + CodeInsightSettings.getInstance().TAB_EXITS_BRACKETS_AND_QUOTES = true; + try { + configureFromFileText(getTestName(false) + ".java", fileText); + int originalCaretOffset = myEditor.getCaretModel().getOffset(); + executeAction(IdeActions.ACTION_BRACE_OR_QUOTE_OUT); + assertEquals("Unexpected caret offset", originalCaretOffset + expectedCaretShift, myEditor.getCaretModel().getOffset()); + } + finally { + CodeInsightSettings.getInstance().TAB_EXITS_BRACKETS_AND_QUOTES = savedSetting; + } + } +} From d8181953c3e8abb19bc6e7d3235c058b95b4e76a Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 10 Apr 2018 15:33:06 +0200 Subject: [PATCH 34/53] [rider] allow to pass postRunnable argument for test purpose --- .../codeInspection/actions/SilentCodeCleanupAction.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/codeInspection/actions/SilentCodeCleanupAction.java b/platform/lang-impl/src/com/intellij/codeInspection/actions/SilentCodeCleanupAction.java index 1454dc3acff8..af10ffee881a 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/actions/SilentCodeCleanupAction.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/actions/SilentCodeCleanupAction.java @@ -52,6 +52,10 @@ public class SilentCodeCleanupAction extends AnAction { runInspections(project, analysisScope); } + @SuppressWarnings("WeakerAccess") + @Nullable + protected Runnable getPostRunnable() { return null; } + @SuppressWarnings("WeakerAccess") protected void runInspections(@NotNull Project project, @NotNull AnalysisScope scope) { InspectionProfile profile = getProfileForSilentCleanup(project); @@ -60,7 +64,7 @@ public class SilentCodeCleanupAction extends AnAction { } InspectionManager managerEx = InspectionManager.getInstance(project); GlobalInspectionContextBase globalContext = (GlobalInspectionContextBase) managerEx.createNewGlobalContext(false); - globalContext.codeCleanup(scope, profile, getTemplatePresentation().getText(), null, false); + globalContext.codeCleanup(scope, profile, getTemplatePresentation().getText(), getPostRunnable(), false); } @SuppressWarnings("WeakerAccess") From 96d06b47a82b7a25a5432d1edaa279d7eed01327 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 10 Apr 2018 15:43:48 +0200 Subject: [PATCH 35/53] check that FileEditor is valid. Fix RIDER-13812. --- .../intellij/codeInsight/daemon/impl/PassExecutorService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PassExecutorService.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PassExecutorService.java index 1e300fc9dcc2..de0a661d4c25 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PassExecutorService.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PassExecutorService.java @@ -479,7 +479,7 @@ class PassExecutorService implements Disposable { @NotNull final AtomicInteger threadsToStartCountdown, @NotNull Runnable callbackOnApplied) { ApplicationManager.getApplication().invokeLater((DumbAwareRunnable)() -> { - if (isDisposed() || myProject.isDisposed()) { + if (isDisposed() || myProject.isDisposed() || !fileEditor.isValid()) { updateProgress.cancel(); } if (updateProgress.isCanceled()) { From f0fdfa517f30ddc0142a2d75232e6e6b2250d335 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 10 Apr 2018 15:46:51 +0200 Subject: [PATCH 36/53] Make it possible to customize default TODO patterns --- .../src/com/intellij/ide/todo/TodoConfiguration.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/platform/editor-ui-ex/src/com/intellij/ide/todo/TodoConfiguration.java b/platform/editor-ui-ex/src/com/intellij/ide/todo/TodoConfiguration.java index 4218fe0601c6..dd84905cd542 100644 --- a/platform/editor-ui-ex/src/com/intellij/ide/todo/TodoConfiguration.java +++ b/platform/editor-ui-ex/src/com/intellij/ide/todo/TodoConfiguration.java @@ -64,8 +64,11 @@ public class TodoConfiguration implements PersistentStateComponent { buildIndexPatterns(); } + /** + * Returns the list of default TO_DO patterns. Can be customized in other IDEs (and is customized in Rider). + */ @NotNull - private static TodoPattern[] getDefaultPatterns() { + protected TodoPattern[] getDefaultPatterns() { //noinspection SpellCheckingInspection return new TodoPattern[]{ new TodoPattern("\\btodo\\b.*", TodoAttributesUtil.createDefault(), false), From 9522f9962da6d0f57d129a83bd4d012bfd899032 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 10 Apr 2018 15:50:08 +0200 Subject: [PATCH 37/53] Allow customizing color of run configuration indicator icon Used in Rider's Unity integration --- .../src/com/intellij/execution/runners/ExecutionUtil.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/platform/lang-api/src/com/intellij/execution/runners/ExecutionUtil.java b/platform/lang-api/src/com/intellij/execution/runners/ExecutionUtil.java index 1fd4ffa96dda..c0197e0a53c4 100644 --- a/platform/lang-api/src/com/intellij/execution/runners/ExecutionUtil.java +++ b/platform/lang-api/src/com/intellij/execution/runners/ExecutionUtil.java @@ -229,7 +229,12 @@ public class ExecutionUtil { return getLiveIndicator(base, 13, 13); } + @SuppressWarnings("UseJBColor") public static Icon getLiveIndicator(@Nullable final Icon base, int emptyIconWidth, int emptyIconHeight) { + return getIndicator(base, emptyIconWidth, emptyIconHeight, Color.GREEN); + } + + public static Icon getIndicator(@Nullable final Icon base, int emptyIconWidth, int emptyIconHeight, Color color) { return new LayeredIcon(base, new Icon() { @SuppressWarnings("UseJBColor") @Override @@ -238,7 +243,7 @@ public class ExecutionUtil { Graphics2D g2d = (Graphics2D)g.create(); try { GraphicsUtil.setupAAPainting(g2d); - g2d.setColor(Color.GREEN); + g2d.setColor(color); Ellipse2D.Double shape = new Ellipse2D.Double(x + getIconWidth() - iSize, y + getIconHeight() - iSize, iSize, iSize); g2d.fill(shape); From f27e8d2aafd38412d1fb901f73b74ae1830e9edc Mon Sep 17 00:00:00 2001 From: Artem Bukhonov Date: Tue, 10 Apr 2018 16:25:46 +0200 Subject: [PATCH 38/53] XDebuggerTestUtil overloads which take custom waiter function as parameter --- .../intellij/xdebugger/XDebuggerTestUtil.java | 101 +++++++++++++++--- .../intellij/xdebugger/XTestContainer.java | 7 +- .../xdebugger/XTestEvaluationCallback.java | 7 +- .../intellij/xdebugger/XTestValueNode.java | 6 +- 4 files changed, 104 insertions(+), 17 deletions(-) diff --git a/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XDebuggerTestUtil.java b/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XDebuggerTestUtil.java index db43e9fcbe07..afbddb77ce00 100644 --- a/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XDebuggerTestUtil.java +++ b/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XDebuggerTestUtil.java @@ -38,6 +38,7 @@ import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; +import java.util.function.BiFunction; import java.util.concurrent.TimeoutException; import static org.junit.Assert.*; @@ -122,9 +123,13 @@ public class XDebuggerTestUtil { } public static Pair, String> collectThreadsWithErrors(@NotNull XDebugSession session) { + return collectThreadsWithErrors(session, XDebuggerTestUtil::waitFor); + } + + public static Pair, String> collectThreadsWithErrors(@NotNull XDebugSession session, @NotNull BiFunction waitFunction) { XTestExecutionStackContainer container = new XTestExecutionStackContainer(); session.getSuspendContext().computeExecutionStacks(container); - return container.waitFor(TIMEOUT_MS); + return container.waitFor(TIMEOUT_MS, waitFunction); } public static List collectFrames(@NotNull XDebugSession session) { @@ -146,13 +151,21 @@ public class XDebuggerTestUtil { } public static List collectFrames(XExecutionStack thread, long timeout) { - return collectFramesWithError(thread, timeout).first; + return collectFrames(thread, timeout, XDebuggerTestUtil::waitFor); + } + + public static List collectFrames(XExecutionStack thread, long timeout, BiFunction waitFunction) { + return collectFramesWithError(thread, timeout, waitFunction).first; } public static Pair, String> collectFramesWithError(XExecutionStack thread, long timeout) { + return collectFramesWithError(thread, timeout, XDebuggerTestUtil::waitFor); + } + + public static Pair, String> collectFramesWithError(XExecutionStack thread, long timeout, BiFunction waitFunction) { XTestStackFrameContainer container = new XTestStackFrameContainer(); thread.computeStackFrames(0, container); - return container.waitFor(timeout); + return container.waitFor(timeout, waitFunction); } public static Pair, XStackFrame> collectFramesWithSelected(@NotNull XDebugSession session, long timeout) { @@ -160,9 +173,13 @@ public class XDebuggerTestUtil { } public static Pair, XStackFrame> collectFramesWithSelected(XExecutionStack thread, long timeout) { + return collectFramesWithSelected(thread, timeout, XDebuggerTestUtil::waitFor); + } + + public static Pair, XStackFrame> collectFramesWithSelected(XExecutionStack thread, long timeout, BiFunction waitFunction) { XTestStackFrameContainer container = new XTestStackFrameContainer(); thread.computeStackFrames(0, container); - List all = container.waitFor(timeout).first; + List all = container.waitFor(timeout, waitFunction).first; return Pair.create(all, container.frameToSelect); } @@ -175,31 +192,50 @@ public class XDebuggerTestUtil { } public static List collectChildren(XValueContainer value) { + return collectChildren(value, XDebuggerTestUtil::waitFor); + } + + public static List collectChildren(XValueContainer value, BiFunction waitFunction) { XTestCompositeNode container = new XTestCompositeNode(); value.computeChildren(container); - return container.waitFor(TIMEOUT_MS).first; + + return container.waitFor(TIMEOUT_MS, waitFunction).first; } public static Pair evaluate(XDebugSession session, XExpression expression) { return evaluate(session, expression, TIMEOUT_MS); } + public static Pair evaluate(XDebugSession session, XExpression expression, BiFunction waitFunction) { + return evaluate(session, expression, TIMEOUT_MS, waitFunction); + } + public static Pair evaluate(XDebugSession session, String expression) { - return evaluate(session, XExpressionImpl.fromText(expression), TIMEOUT_MS); + return evaluate(session, expression, XDebuggerTestUtil::waitFor); + } + public static Pair evaluate(XDebugSession session, String expression, BiFunction waitFunction) { + return evaluate(session, XExpressionImpl.fromText(expression), TIMEOUT_MS, waitFunction); } public static Pair evaluate(XDebugSession session, String expression, long timeout) { - return evaluate(session, XExpressionImpl.fromText(expression), timeout); + return evaluate(session, expression, timeout, XDebuggerTestUtil::waitFor); + } + public static Pair evaluate(XDebugSession session, String expression, long timeout, BiFunction waitFunction) { + return evaluate(session, XExpressionImpl.fromText(expression), timeout, waitFunction); } private static Pair evaluate(XDebugSession session, XExpression expression, long timeout) { + return evaluate(session, expression, timeout, XDebuggerTestUtil::waitFor); + } + + private static Pair evaluate(XDebugSession session, XExpression expression, long timeout, BiFunction waitFunction) { XStackFrame frame = session.getCurrentStackFrame(); assertNotNull(frame); XDebuggerEvaluator evaluator = frame.getEvaluator(); assertNotNull(evaluator); XTestEvaluationCallback callback = new XTestEvaluationCallback(); evaluator.evaluate(expression, callback, session.getCurrentPosition()); - return callback.waitFor(timeout); + return callback.waitFor(timeout, waitFunction); } public static void waitForSwing() throws InterruptedException { @@ -226,16 +262,22 @@ public class XDebuggerTestUtil { } public static XTestValueNode computePresentation(@NotNull XValue value) { - return computePresentation(value, TIMEOUT_MS); + return computePresentation(value, XDebuggerTestUtil::waitFor); + } + public static XTestValueNode computePresentation(@NotNull XValue value, BiFunction waitFunction) { + return computePresentation(value, TIMEOUT_MS, waitFunction); } public static XTestValueNode computePresentation(XValue value, long timeout) { + return computePresentation(value, timeout, XDebuggerTestUtil::waitFor); + } + public static XTestValueNode computePresentation(XValue value, long timeout, BiFunction waitFunction) { XTestValueNode node = new XTestValueNode(); if (value instanceof XNamedValue) { node.myName = ((XNamedValue)value).getName(); } value.computePresentation(node, XValuePlace.TREE); - node.waitFor(timeout); + node.waitFor(timeout, waitFunction); return node; } @@ -244,7 +286,16 @@ public class XDebuggerTestUtil { @Nullable String type, @Nullable String value, @Nullable Boolean hasChildren) { - XTestValueNode node = computePresentation(var); + assertVariable(var, name, type, value, hasChildren, XDebuggerTestUtil::waitFor); + } + + public static void assertVariable(XValue var, + @Nullable String name, + @Nullable String type, + @Nullable String value, + @Nullable Boolean hasChildren, + BiFunction waitFunction) { + XTestValueNode node = computePresentation(var, waitFunction); if (name != null) assertEquals(name, node.myName); if (type != null) assertEquals(type, node.myType); @@ -299,7 +350,16 @@ public class XDebuggerTestUtil { @Nullable String type, @Nullable @Language("RegExp") String valuePattern, @Nullable Boolean hasChildren) { - XTestValueNode node = computePresentation(var); + assertVariableValueMatches(var, name, type, valuePattern, hasChildren, XDebuggerTestUtil::waitFor); + } + + public static void assertVariableValueMatches(@NotNull XValue var, + @Nullable String name, + @Nullable String type, + @Nullable @Language("RegExp") String valuePattern, + @Nullable Boolean hasChildren, + BiFunction waitFunction) { + XTestValueNode node = computePresentation(var, waitFunction); if (name != null) assertEquals(name, node.myName); if (type != null) assertEquals(type, node.myType); if (valuePattern != null) { @@ -317,7 +377,14 @@ public class XDebuggerTestUtil { public static void assertVariableTypeMatches(@NotNull XValue var, @Nullable String name, @Nullable @Language("RegExp") String typePattern) { - XTestValueNode node = computePresentation(var); + assertVariableTypeMatches(var, name, typePattern, XDebuggerTestUtil::waitFor); + } + + public static void assertVariableTypeMatches(@NotNull XValue var, + @Nullable String name, + @Nullable @Language("RegExp") String typePattern, + @NotNull BiFunction waitFunction) { + XTestValueNode node = computePresentation(var, waitFunction); if (name != null) { assertEquals(name, node.myName); } @@ -328,7 +395,13 @@ public class XDebuggerTestUtil { public static void assertVariableFullValue(@NotNull XValue var, @Nullable String value) throws Exception { - XTestValueNode node = computePresentation(var); + assertVariableFullValue(var, value, XDebuggerTestUtil::waitFor); + } + + public static void assertVariableFullValue(@NotNull XValue var, + @Nullable String value, + @NotNull BiFunction waitFunction) throws Exception { + XTestValueNode node = computePresentation(var, waitFunction); if (value == null) { assertNull("full value evaluator should be null", node.myFullValueEvaluator); diff --git a/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestContainer.java b/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestContainer.java index 57ac8996651c..9f3c86b5afc6 100644 --- a/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestContainer.java +++ b/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestContainer.java @@ -13,6 +13,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.List; import java.util.concurrent.Semaphore; +import java.util.function.BiFunction; public class XTestContainer { private final List myChildren = new SmartList<>(); @@ -41,7 +42,11 @@ public class XTestContainer { } public Pair, String> waitFor(long timeoutMs) { - if (!XDebuggerTestUtil.waitFor(myFinished, timeoutMs)) { + return waitFor(timeoutMs, (semaphore, timeout) -> XDebuggerTestUtil.waitFor(myFinished, timeout)); + } + + public Pair, String> waitFor(long timeoutMs, BiFunction waitFunction) { + if (!waitFunction.apply(myFinished, timeoutMs)) { throw new AssertionError("Waiting timed out"); } diff --git a/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestEvaluationCallback.java b/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestEvaluationCallback.java index 6425a685a604..0d298ad9a945 100644 --- a/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestEvaluationCallback.java +++ b/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestEvaluationCallback.java @@ -21,6 +21,7 @@ import com.intellij.xdebugger.impl.ui.tree.nodes.XEvaluationCallbackBase; import org.jetbrains.annotations.NotNull; import java.util.concurrent.Semaphore; +import java.util.function.BiFunction; import static org.junit.Assert.*; @@ -42,7 +43,11 @@ public class XTestEvaluationCallback extends XEvaluationCallbackBase { } public Pair waitFor(long timeoutInMilliseconds) { - assertTrue("timed out", XDebuggerTestUtil.waitFor(myFinished, timeoutInMilliseconds)); + return waitFor(timeoutInMilliseconds, XDebuggerTestUtil::waitFor); + } + + public Pair waitFor(long timeoutInMilliseconds, BiFunction waitFunction) { + assertTrue("timed out", waitFunction.apply(myFinished, timeoutInMilliseconds)); return Pair.create(myResult, myErrorMessage); } } diff --git a/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestValueNode.java b/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestValueNode.java index 32c78f2533c9..95d5b2e5e0eb 100644 --- a/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestValueNode.java +++ b/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestValueNode.java @@ -12,6 +12,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.concurrent.Semaphore; +import java.util.function.BiFunction; public class XTestValueNode extends XValueNodePresentationConfigurator.ConfigurableXValueNodeImpl { public Icon myIcon; @@ -42,7 +43,10 @@ public class XTestValueNode extends XValueNodePresentationConfigurator.Configura } public void waitFor(long timeoutInMillis) { - if (!XDebuggerTestUtil.waitFor(myFinished, timeoutInMillis)) { + waitFor(timeoutInMillis, XDebuggerTestUtil::waitFor); + } + public void waitFor(long timeoutInMillis, BiFunction waitFunction) { + if (!waitFunction.apply(myFinished, timeoutInMillis)) { throw new AssertionError("Waiting timed out"); } } From 0ae64dccb4aac30c33f9b082dfadf7992a4e6d0a Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 10 Apr 2018 16:28:02 +0200 Subject: [PATCH 39/53] Allow specifying our PID when sending signal to process tree This allows to send signals to processes which weren't started as direct children of the IDE process. Used in Rider. --- .../intellij/execution/process/UnixProcessManager.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/platform/util/src/com/intellij/execution/process/UnixProcessManager.java b/platform/util/src/com/intellij/execution/process/UnixProcessManager.java index 0507c03ebe93..e90f4b2611a6 100644 --- a/platform/util/src/com/intellij/execution/process/UnixProcessManager.java +++ b/platform/util/src/com/intellij/execution/process/UnixProcessManager.java @@ -115,7 +115,11 @@ public class UnixProcessManager { public static boolean sendSignalToProcessTree(int processId, int signal) { checkCLib(); - final int our_pid = C_LIB.getpid(); + final int ourPid = C_LIB.getpid(); + return sendSignalToProcessTree(processId, signal, ourPid); + } + + public static boolean sendSignalToProcessTree(int processId, int signal, int ourPid) { if (LOG.isDebugEnabled()) { LOG.debug("Sending signal " + signal + " to process tree with root PID " + processId); } @@ -124,7 +128,7 @@ public class UnixProcessManager { final ProcessInfo processInfo = new ProcessInfo(); final List childrenPids = new ArrayList(); - findChildProcesses(our_pid, processId, foundPid, processInfo, childrenPids); + findChildProcesses(ourPid, processId, foundPid, processInfo, childrenPids); // result is true if signal was sent to at least one process final boolean result; From 8292a6d3e5b0ba3d4cd093753b000521ae063c40 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 10 Apr 2018 16:33:49 +0200 Subject: [PATCH 40/53] Notify listeners when virtualSpace setting is changed Used in Rider. --- .../openapi/editor/ex/EditorSettingsExternalizable.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java index f92bb0896681..a107ff7abd40 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java @@ -24,6 +24,9 @@ import java.util.Set; @State(name = "EditorSettings", storages = @Storage("editor.xml")) public class EditorSettingsExternalizable implements PersistentStateComponent { + @NonNls + public static String PROP_VIRTUAL_SPACE = "VirtualSpace"; + public static final UINumericRange BLINKING_RANGE = new UINumericRange(500, 10, 1500); public static final UINumericRange QUICK_DOC_DELAY_RANGE = new UINumericRange(500, 1, 5000); @@ -374,7 +377,9 @@ public class EditorSettingsExternalizable implements PersistentStateComponent Date: Tue, 10 Apr 2018 16:36:12 +0200 Subject: [PATCH 41/53] Fix race condition when killing processes (RIDER-11849) --- .../src/com/intellij/execution/process/OSProcessUtil.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/execution/process/OSProcessUtil.java b/platform/platform-impl/src/com/intellij/execution/process/OSProcessUtil.java index cbc5d704eca1..d4535d10e982 100644 --- a/platform/platform-impl/src/com/intellij/execution/process/OSProcessUtil.java +++ b/platform/platform-impl/src/com/intellij/execution/process/OSProcessUtil.java @@ -28,7 +28,9 @@ public class OSProcessUtil { if (SystemInfo.isWindows) { try { if (process instanceof WinPtyProcess) { - boolean res = WinProcessManager.kill(((WinPtyProcess)process).getChildProcessId(), true); + int pid = ((WinPtyProcess) process).getChildProcessId(); + if (pid == -1) return true; + boolean res = WinProcessManager.kill(pid, true); process.destroy(); return res; } From 9b6fe53b3f46b5c5cf90a66241f9b636cd6dfd4a Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 10 Apr 2018 16:38:47 +0200 Subject: [PATCH 42/53] Don't write error to log when unable to create process. The exception may be properly handled outside (e.g. when process creation failed due to required elevation -> we handle this error and show elevation dialog to user). Needed for Rider. --- .../com/intellij/execution/configurations/PtyCommandLine.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/platform-api/src/com/intellij/execution/configurations/PtyCommandLine.java b/platform/platform-api/src/com/intellij/execution/configurations/PtyCommandLine.java index 7f793f7ff2ee..65f2198f6246 100644 --- a/platform/platform-api/src/com/intellij/execution/configurations/PtyCommandLine.java +++ b/platform/platform-api/src/com/intellij/execution/configurations/PtyCommandLine.java @@ -70,10 +70,10 @@ public class PtyCommandLine extends GeneralCommandLine { logContent = "Unable to retrieve log: " + e.getMessage(); } - LOG.error("Couldn't run process with PTY", t, logContent); + LOG.debug("Couldn't run process with PTY", t, logContent); } else { - LOG.error("Couldn't run process with PTY", t); + LOG.debug("Couldn't run process with PTY", t); } } From c70e2fbf2896eba2117899c71e8627f2b484f00e Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 10 Apr 2018 16:47:29 +0200 Subject: [PATCH 43/53] Allow to customize IdeDocumentHistory behavior in tests (for Rider) --- .../openapi/fileEditor/impl/IdeDocumentHistoryImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.java index c836b40f34b5..757a793d7a46 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.java @@ -446,7 +446,7 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec return file != null ? myEditorManager.getSelectedEditorWithProvider(file) : null; } - private PlaceInfo createPlaceInfo(@NotNull final FileEditor fileEditor, final FileEditorProvider fileProvider) { + protected PlaceInfo createPlaceInfo(@NotNull final FileEditor fileEditor, final FileEditorProvider fileProvider) { if (!fileEditor.isValid()) return null; final VirtualFile file = myEditorManager.getFile(fileEditor); @@ -485,13 +485,13 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec return myFileDocumentManager; } - private static final class PlaceInfo { + protected static final class PlaceInfo { private final VirtualFile myFile; private final FileEditorState myNavigationState; private final String myEditorTypeId; private final Reference myWindow; - PlaceInfo(@NotNull VirtualFile file, + public PlaceInfo(@NotNull VirtualFile file, @NotNull FileEditorState navigationState, @NotNull String editorTypeId, @Nullable EditorWindow window) { From 4d30fdf28dc570da86df0d19f1955796050d1fc7 Mon Sep 17 00:00:00 2001 From: "Gregory.Shrago" Date: Tue, 10 Apr 2018 17:51:20 +0300 Subject: [PATCH 44/53] WI-41555, WI-41328 fix empty scratch folders and db-console guids --- .../ide/projectView/impl/AbstractProjectViewPane.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectViewPane.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectViewPane.java index f175133d3566..7eddd57f0f2c 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectViewPane.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectViewPane.java @@ -297,6 +297,12 @@ public abstract class AbstractProjectViewPane implements DataProvider, Disposabl @Override public Object getData(String dataId) { + Object data = + myTreeStructure instanceof AbstractTreeStructureBase ? + ((AbstractTreeStructureBase)myTreeStructure).getDataFromProviders(getSelectedNodes(AbstractTreeNode.class), dataId) : null; + if (data != null) { + return data; + } if (CommonDataKeys.NAVIGATABLE_ARRAY.is(dataId)) { TreePath[] paths = getSelectionPaths(); if (paths == null) return null; @@ -318,9 +324,6 @@ public abstract class AbstractProjectViewPane implements DataProvider, Disposabl return navigatables.toArray(new Navigatable[0]); } } - if (myTreeStructure instanceof AbstractTreeStructureBase) { - return ((AbstractTreeStructureBase) myTreeStructure).getDataFromProviders(getSelectedNodes(AbstractTreeNode.class), dataId); - } return null; } From b4ea6a6b83632d6b1d75edced3180f443aad7d26 Mon Sep 17 00:00:00 2001 From: "Gregory.Shrago" Date: Tue, 10 Apr 2018 17:53:18 +0300 Subject: [PATCH 45/53] notnull --- .../projectView/TreeStructureProvider.java | 2 +- .../impl/CompoundTreeStructureProvider.java | 4 +-- .../projectView/ResourceBundleGrouper.java | 3 +- .../FormMergerTreeStructureProvider.java | 30 +++++++++---------- 4 files changed, 18 insertions(+), 21 deletions(-) diff --git a/platform/editor-ui-api/src/com/intellij/ide/projectView/TreeStructureProvider.java b/platform/editor-ui-api/src/com/intellij/ide/projectView/TreeStructureProvider.java index 5d13dc167f6c..98ae81917bb8 100644 --- a/platform/editor-ui-api/src/com/intellij/ide/projectView/TreeStructureProvider.java +++ b/platform/editor-ui-api/src/com/intellij/ide/projectView/TreeStructureProvider.java @@ -53,7 +53,7 @@ public interface TreeStructureProvider { * @see com.intellij.openapi.actionSystem.DataProvider */ @Nullable - default Object getData(Collection selected, String dataName) { + default Object getData(@NotNull Collection selected, String dataName) { return null; } } diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/CompoundTreeStructureProvider.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/CompoundTreeStructureProvider.java index 7b8ae5015e42..89f3c960c1d3 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/CompoundTreeStructureProvider.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/CompoundTreeStructureProvider.java @@ -73,8 +73,8 @@ public final class CompoundTreeStructureProvider implements TreeStructureProvide @Nullable @Override - public Object getData(Collection selection, String id) { - if (id != null && selection != null && !selection.isEmpty()) { + public Object getData(@NotNull Collection selection, String id) { + if (id != null && !selection.isEmpty()) { for (TreeStructureProvider provider : providers) { try { Object data = provider.getData(selection, id); diff --git a/plugins/properties/src/com/intellij/lang/properties/projectView/ResourceBundleGrouper.java b/plugins/properties/src/com/intellij/lang/properties/projectView/ResourceBundleGrouper.java index 535eb22c8e15..f7187008dc34 100644 --- a/plugins/properties/src/com/intellij/lang/properties/projectView/ResourceBundleGrouper.java +++ b/plugins/properties/src/com/intellij/lang/properties/projectView/ResourceBundleGrouper.java @@ -100,8 +100,7 @@ public class ResourceBundleGrouper implements TreeStructureProvider, DumbAware { } @Override - public Object getData(Collection selected, String dataName) { - if (selected == null) return null; + public Object getData(@NotNull Collection selected, String dataName) { if (PlatformDataKeys.DELETE_ELEMENT_PROVIDER.is(dataName)) { for (AbstractTreeNode selectedElement : selected) { Object element = selectedElement.getValue(); diff --git a/plugins/ui-designer/src/com/intellij/uiDesigner/projectView/FormMergerTreeStructureProvider.java b/plugins/ui-designer/src/com/intellij/uiDesigner/projectView/FormMergerTreeStructureProvider.java index ba705feb7c23..3538450139d7 100644 --- a/plugins/ui-designer/src/com/intellij/uiDesigner/projectView/FormMergerTreeStructureProvider.java +++ b/plugins/ui-designer/src/com/intellij/uiDesigner/projectView/FormMergerTreeStructureProvider.java @@ -102,24 +102,22 @@ public class FormMergerTreeStructureProvider implements TreeStructureProvider { return result; } - public Object getData(Collection selected, String dataId) { - if (selected != null) { - if (Form.DATA_KEY.is(dataId)) { - List
result = new ArrayList<>(); - for(AbstractTreeNode node: selected) { - if (node.getValue() instanceof Form) { - result.add((Form) node.getValue()); - } - } - if (!result.isEmpty()) { - return result.toArray(new Form[0]); + public Object getData(@NotNull Collection selected, String dataId) { + if (Form.DATA_KEY.is(dataId)) { + List result = new ArrayList<>(); + for(AbstractTreeNode node: selected) { + if (node.getValue() instanceof Form) { + result.add((Form) node.getValue()); } } - else if (PlatformDataKeys.DELETE_ELEMENT_PROVIDER.is(dataId)) { - for(AbstractTreeNode node: selected) { - if (node.getValue() instanceof Form) { - return new MyDeleteProvider(selected); - } + if (!result.isEmpty()) { + return result.toArray(new Form[0]); + } + } + else if (PlatformDataKeys.DELETE_ELEMENT_PROVIDER.is(dataId)) { + for(AbstractTreeNode node: selected) { + if (node.getValue() instanceof Form) { + return new MyDeleteProvider(selected); } } } From 3caf6bba8b571b60c1ba2879abd7bae7c99672d4 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Tue, 10 Apr 2018 16:54:34 +0200 Subject: [PATCH 46/53] =?UTF-8?q?UpdateOptionsTest=20=E2=80=94=20add=20cas?= =?UTF-8?q?e=20`auto=20check=20is=20enabled`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../intellij/openapi/updateSettings/UpdateOptionsTest.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/updateSettings/UpdateOptionsTest.kt b/platform/platform-tests/testSrc/com/intellij/openapi/updateSettings/UpdateOptionsTest.kt index 56bfcc6b6759..3e13a31fb9c5 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/updateSettings/UpdateOptionsTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/openapi/updateSettings/UpdateOptionsTest.kt @@ -4,6 +4,7 @@ package com.intellij.openapi.updateSettings import com.intellij.configurationStore.deserialize import com.intellij.openapi.updateSettings.impl.UpdateOptions import com.intellij.testFramework.ProjectRule +import com.intellij.testFramework.assertions.Assertions.assertThat import com.intellij.util.loadElement import org.junit.ClassRule import org.junit.Test @@ -15,6 +16,12 @@ class UpdateOptionsTest { val projectRule = ProjectRule() } + @Test + fun `auto check is enabled`() { + // it is quite important default value (true), so, ensure that it is not modified without changing test expectation (as an additional check) + assertThat(UpdateOptions().isCheckNeeded).isTrue() + } + @Test fun test() { loadElement(""" From c4cfcee2514ee3c47317eb19cda3db4c3e738af1 Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 10 Apr 2018 16:54:29 +0200 Subject: [PATCH 47/53] simplify stub-AST-PSI relationships replace AstPath with index-based SpineRef, take coarser locks, load multi-root stubs independently What we lose: * an ability to gc AST of stubbed PSI loaded via AST-only * an ability to gc AST of non-stubbed StubBasedPsiElementBase * partial chameleon expansion during findElementAt after AST has been gc-ed but stubbed PSI is still referenced ** probably not a major regression: full parsing has been happening during stub-AST switch for ages What we retain: * we can hold references to PSI received from stub index queries without worrying about retaining their AST (even if loaded, it will be gc-ed) What we gain: * simpler code with less mutable state * outdated AstPath assertion is now nonexistent (WEB-32120, EA-101911) * no more cases when stub has been gc-ed but can't be reloaded due to some cached PSI with unknown stub index (IDEA-189948) * other stub-related exceptions are also likely to be fixed: ** EA-97101 - REWA: StubBasedPsiElementBase.failedToBindStubToAst ** EA-102022 - CCE: PsiFieldImpl.findFirstFieldInDeclaration ** EA-101452 - assert: StubBasedPsiElementBase.getRequiredStubOrPsiChild ** EA-107208 - ISE: StubBasedPsiElementBase.$$$reportNull$$$ ** EA-110161 - PIEAE: SubstrateRef$StubRef.reportError ** EA-113453 - CCE: PsiClassImpl.getContainingClass ** EA-117888 - CCE: PsiClassImpl.getContextStub --- .../psi/impl/source/PsiJavaFileBaseImpl.java | 6 +- .../java/psi/StubAstSwitchTest.groovy | 63 ++-- .../src/com/intellij/psi/stubs/StubBase.java | 3 +- .../extapi/psi/StubBasedPsiElementBase.java | 28 +- .../psi/impl/source/AstPathPsiMap.java | 153 --------- .../intellij/psi/impl/source/FileTrees.java | 178 ++++++++-- .../intellij/psi/impl/source/PsiFileImpl.java | 278 +++------------- .../intellij/psi/impl/source/SpineRef.java | 53 +++ .../psi/impl/source/SubstrateRef.java | 8 +- .../psi/impl/source/tree/AstPath.java | 311 ------------------ .../psi/impl/source/tree/AstSpine.java | 3 +- .../impl/source/tree/CompositeElement.java | 30 +- .../psi/impl/source/tree/FileElement.java | 18 +- .../source/tree/LazyParseableElement.java | 6 +- .../psi/impl/source/tree/TreeUtil.java | 50 +-- .../intellij/psi/stubs/StubTreeLoader.java | 4 - .../psi/stubs/StubTreeLoaderImpl.java | 5 - 17 files changed, 315 insertions(+), 882 deletions(-) delete mode 100644 platform/core-impl/src/com/intellij/psi/impl/source/AstPathPsiMap.java create mode 100644 platform/core-impl/src/com/intellij/psi/impl/source/SpineRef.java delete mode 100644 platform/core-impl/src/com/intellij/psi/impl/source/tree/AstPath.java diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaFileBaseImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaFileBaseImpl.java index 0ccc3be41edb..5e97247a95e0 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaFileBaseImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaFileBaseImpl.java @@ -40,7 +40,6 @@ import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.*; import com.intellij.util.IncorrectOperationException; import com.intellij.util.Processor; -import java.util.HashSet; import com.intellij.util.containers.MostlySingularMultiMap; import com.intellij.util.indexing.IndexingDataKeys; import org.jetbrains.annotations.NotNull; @@ -48,6 +47,7 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.List; public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJavaFile { @@ -137,8 +137,8 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava if (stub != null) { PsiImportList[] nodes = stub.getChildrenByType(JavaStubElementTypes.IMPORT_LIST, PsiImportList.ARRAY_FACTORY); if (nodes.length == 1) return nodes[0]; - if (nodes.length == 0) return null; - reportStubAstMismatch(stub + "; " + stub.getChildrenStubs(), getStubTree()); + assert nodes.length == 0; + return null; } ASTNode node = calcTreeElement().findChildByType(JavaElementType.IMPORT_LIST); diff --git a/java/java-tests/testSrc/com/intellij/java/psi/StubAstSwitchTest.groovy b/java/java-tests/testSrc/com/intellij/java/psi/StubAstSwitchTest.groovy index 0c7d2fdc5519..af7a14b8c969 100644 --- a/java/java-tests/testSrc/com/intellij/java/psi/StubAstSwitchTest.groovy +++ b/java/java-tests/testSrc/com/intellij/java/psi/StubAstSwitchTest.groovy @@ -34,15 +34,13 @@ import com.intellij.psi.stubs.StubTree import com.intellij.psi.util.PsiTreeUtil import com.intellij.reference.SoftReference import com.intellij.testFramework.LeakHunter -import com.intellij.testFramework.PlatformTestUtil import com.intellij.testFramework.SkipSlowTestLocally import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase import com.intellij.util.ref.GCUtil -import groovy.transform.CompileStatic import java.util.concurrent.Callable import java.util.concurrent.CountDownLatch -import java.util.concurrent.Future +import java.util.concurrent.Future /** * @author peter */ @@ -236,7 +234,7 @@ class B { LeakHunter.checkLeak(file, StubTree) { candidate -> candidate.root.psi == file } } - void "test node is not deeply parsed when loaded in green stub presence"() { + void "test node has same PSI when loaded in green stub presence"() { PsiFileImpl file = (PsiFileImpl)myFixture.addFileToProject("a.java", "class A{}") def stubTree = file.stubTree PsiClass psiClass = ((PsiJavaFile)file).classes[0] @@ -244,7 +242,7 @@ class B { GCUtil.tryGcSoftlyReachableObjects() assert stubTree.is(file.greenStubTree) - assert !file.node.parsed + assert file.node.lastChildNode.psi.is(psiClass) } void "test load stub from non-file PSI after AST is unloaded"() { @@ -258,26 +256,22 @@ class B { assert ((PsiClassImpl) cls).stub } - void "test load PSI via stub when AST is gc-ed but PSI exists that has never known stub"() { + void "test load PSI via stub when AST is gc-ed but PSI exists that was loaded via AST but knows its stub index"() { PsiJavaFileImpl file = (PsiJavaFileImpl)myFixture.addFileToProject("a.java", "class A{}") def cls = file.lastChild assert cls instanceof PsiClass GCUtil.tryGcSoftlyReachableObjects() - assert !file.treeElement - - assert cls == myFixture.findClass('A') - } - - void "test load PSI via stub when AST is gc-ed and PSI remains that never knew stub"() { - PsiJavaFileImpl file = (PsiJavaFileImpl)myFixture.addFileToProject("a.java", "class A{}") - def cls = file.lastChild - assert cls instanceof PsiClass + assert file.treeElement // we still hold a strong reference to AST + assert cls == myFixture.findClass('A') + + // now we know stub index and can GC AST GCUtil.tryGcSoftlyReachableObjects() assert !file.treeElement assert cls == myFixture.findClass('A') + assert !file.treeElement } void "test bind stubs to AST after AST has been loaded and gc-ed"() { @@ -335,30 +329,27 @@ class B { assert stubTree.plainList.find { it.stubType == JavaStubElementTypes.ANONYMOUS_CLASS } } - @CompileStatic - void "test getStub performance with cached PSI"() { - def text = "class Foo { " + "void bar(int a, int b, int c, int d, int e) { int x = null; }\n" * 1000 + "}" - def file = myFixture.addFileToProject "a.java", text - - PsiMethod[] methods = ((PsiJavaFile) file).classes[0].methods - def params = methods.collect { PsiMethod method -> method.parameterList.parameters } - def literal = file.findElementAt(text.indexOf('null')).parent as PsiLiteralExpression // the only cached PSI without stubIndex + void "test stub index is cleared on AST change"() { + def clazz = myFixture.addClass("class Foo { int a; }") + def field = clazz.fields[0] + def file = clazz.containingFile as PsiFileImpl + WriteCommandAction.runWriteCommandAction(project, { + file.viewProvider.document.insertString(0, ' ') + PsiDocumentManager.getInstance(project).commitAllDocuments() + }) + + assert file.calcStubTree() + WriteCommandAction.runWriteCommandAction(project, { + file.viewProvider.document.insertString(file.text.indexOf('int'), 'void foo();') + PsiDocumentManager.getInstance(project).commitAllDocuments() + }) + GCUtil.tryGcSoftlyReachableObjects() - def fileImpl = (PsiFileImpl)file - assert !fileImpl.treeElement - assert !fileImpl.stub + assert file.calcStubTree() - PlatformTestUtil.startPerformanceTest('getStub performance', 100, { - 10_000.times { - if (fileImpl.stub != null) { - throw new IllegalStateException("has stub") - } - } - }).assertTiming() - - assert params - assert literal + assert field.valid + assert field.name == 'a' } } diff --git a/platform/core-api/src/com/intellij/psi/stubs/StubBase.java b/platform/core-api/src/com/intellij/psi/stubs/StubBase.java index 14453b447f15..2f2f7a7fc586 100644 --- a/platform/core-api/src/com/intellij/psi/stubs/StubBase.java +++ b/platform/core-api/src/com/intellij/psi/stubs/StubBase.java @@ -51,7 +51,8 @@ public abstract class StubBase extends ObjectStubBase extends ASTDelegateP public static final Key CREATION_TRACE = Key.create("CREATION_TRACE"); public static final boolean ourTraceStubAstBinding = "true".equals(System.getProperty("trace.stub.ast.binding", "false")); private volatile SubstrateRef mySubstrateRef; - private volatile int myStubIndex = -1; private final IElementType myElementType; public StubBasedPsiElementBase(@NotNull T stub, @NotNull IStubElementType nodeType) { @@ -238,29 +237,6 @@ public class StubBasedPsiElementBase extends ASTDelegateP */ public final void setSubstrateRef(@NotNull SubstrateRef substrateRef) { mySubstrateRef = substrateRef; - myStubIndex = -1; - } - - /** - * Don't invoke this method, it's public for implementation reasons. - */ - public final void setStubIndex(int stubIndex) { - myStubIndex = stubIndex; - } - - /** - * Don't invoke this method, it's public for implementation reasons. - */ - public int getStubIndex() { - return myStubIndex; - } - - /** - * Don't invoke this method, it's public for implementation reasons. - */ - @NotNull - public final SubstrateRef getSubstrateRef() { - return mySubstrateRef; } @NotNull @@ -383,7 +359,7 @@ public class StubBasedPsiElementBase extends ASTDelegateP public T getStub() { ProgressIndicatorProvider.checkCanceled(); // Hope, this is called often //noinspection unchecked - return (T)mySubstrateRef.getStub(myStubIndex); + return (T)mySubstrateRef.getStub(); } /** @@ -395,7 +371,7 @@ public class StubBasedPsiElementBase extends ASTDelegateP public final T getGreenStub() { ProgressIndicatorProvider.checkCanceled(); // Hope, this is called often //noinspection unchecked - return (T)mySubstrateRef.getGreenStub(myStubIndex); + return (T)mySubstrateRef.getGreenStub(); } /** diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/AstPathPsiMap.java b/platform/core-impl/src/com/intellij/psi/impl/source/AstPathPsiMap.java deleted file mode 100644 index 87e300b5a03b..000000000000 --- a/platform/core-impl/src/com/intellij/psi/impl/source/AstPathPsiMap.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2000-2016 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.psi.impl.source; - -import com.intellij.extapi.psi.StubBasedPsiElementBase; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.LowMemoryWatcher; -import com.intellij.openapi.util.UserDataHolderEx; -import com.intellij.psi.impl.DebugUtil; -import com.intellij.psi.impl.source.tree.AstPath; -import com.intellij.psi.impl.source.tree.CompositeElement; -import com.intellij.reference.SoftReference; -import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.ref.Reference; -import java.lang.ref.ReferenceQueue; -import java.lang.ref.WeakReference; -import java.util.Objects; -import java.util.concurrent.ConcurrentMap; -import java.util.stream.Stream; - -/** - * A weak cache for all instantiated stub-based PSI to allow {@link CompositeElement#getPsi()} return it when AST is reloaded.

- * - * All methods should be called under an external lock (view provider's PsiLock), except for - * ({@link #getCachedPsi(AstPath)} which can be called without lock. - * - * @author peter - */ -class AstPathPsiMap { - /** - * Not using ConcurrentWeakValueMap because we need to clean each of them up separately, when ASTs in thousands of files are created and gc-ed. - * So we have a per-project single shared reference queue {@link #myQueue} for that. - * Otherwise the files end up retaining lots of maps with all-gc-ed stuff inside, but the maps are still very large. - */ - private final ConcurrentMap myMap = ContainerUtil.newConcurrentMap(); - private volatile Boolean myHasUnbindableCachedPsi = null; - - private static final Key STUB_PSI_REFS = Key.create("STUB_PSI_REFS"); - private final MyReferenceQueue myQueue; - - AstPathPsiMap(@NotNull Project project) { - MyReferenceQueue queue = project.getUserData(STUB_PSI_REFS); - myQueue = queue != null ? queue : ((UserDataHolderEx)project).putUserDataIfAbsent(STUB_PSI_REFS, new MyReferenceQueue(project)); - } - - void invalidatePsi() { - myQueue.cleanupStaleReferences(); - getAllCachedPsi().forEach(psi -> { - DebugUtil.onInvalidated(psi); - psi.setSubstrateRef(SubstrateRef.createInvalidRef(psi)); - }); - myMap.clear(); - myHasUnbindableCachedPsi = false; - } - - void switchToStrongRefs() { - myQueue.cleanupStaleReferences(); - getAllCachedPsi().forEach(psi -> { - CompositeElement node = (CompositeElement)psi.getNode(); - node.setPsi(psi); - psi.setSubstrateRef(SubstrateRef.createAstStrongRef(node)); - }); - myMap.clear(); - myHasUnbindableCachedPsi = false; - } - - @Nullable - StubBasedPsiElementBase getCachedPsi(@NotNull AstPath ref) { - return SoftReference.dereference(myMap.get(ref)); - } - - @NotNull - StubBasedPsiElementBase cachePsi(@NotNull AstPath key, @NotNull StubBasedPsiElementBase psi) { - myQueue.cleanupStaleReferences(); - // ensure PSI will use AST path before making it available to other threads - // otherwise another thread could invoke StubRef.getNode and fail since file's AST isn't set yet - psi.setSubstrateRef(key); - myMap.put(key, new MyReference(psi, key, myQueue)); - clearStubIndexCache(); - return psi; - } - - Stream> getAllCachedPsi() { - myQueue.cleanupStaleReferences(); - if (myMap.isEmpty()) return Stream.empty(); - - return myMap.values().stream().map(Reference::get).filter(Objects::nonNull); - } - - boolean hasUnbindableCachedPsi() { - Boolean answer = myHasUnbindableCachedPsi; - if (answer == null) { - myHasUnbindableCachedPsi = answer = calcHasUnbindableCachedPsi(); - } - return answer; - } - - private boolean calcHasUnbindableCachedPsi() { - myQueue.cleanupStaleReferences(); - if (myMap.isEmpty()) return false; - - return getAllCachedPsi().anyMatch(e -> e.getStubIndex() < 0); - } - - void clearStubIndexCache() { - myHasUnbindableCachedPsi = null; - } - - private static class MyReference extends WeakReference> { - final AstPath pathRef; - - MyReference(StubBasedPsiElementBase psi, AstPath ref, ReferenceQueue> queue) { - super(psi, queue); - pathRef = ref; - } - } - - private static class MyReferenceQueue extends ReferenceQueue> { - MyReferenceQueue(Project project) { - LowMemoryWatcher.register(() -> cleanupStaleReferences(), project); - } - - void cleanupStaleReferences() { - while (true) { - MyReference reference = (MyReference)poll(); - if (reference == null) break; - - AstPath key = reference.pathRef; - AstPathPsiMap refToPsi = key.getContainingFile().getRefToPsi(); - refToPsi.myMap.remove(key, reference); - refToPsi.clearStubIndexCache(); - } - } - - } -} diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/FileTrees.java b/platform/core-impl/src/com/intellij/psi/impl/source/FileTrees.java index 031d99778975..6bf2b1d06ebe 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/FileTrees.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/FileTrees.java @@ -15,32 +15,48 @@ */ package com.intellij.psi.impl.source; +import com.intellij.extapi.psi.StubBasedPsiElementBase; +import com.intellij.lang.ASTNode; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Getter; +import com.intellij.psi.PsiElement; +import com.intellij.psi.impl.DebugUtil; +import com.intellij.psi.impl.source.tree.CompositeElement; import com.intellij.psi.impl.source.tree.FileElement; -import com.intellij.psi.stubs.PsiFileStubImpl; -import com.intellij.psi.stubs.StubTree; +import com.intellij.psi.stubs.*; import com.intellij.reference.SoftReference; -import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.lang.ref.Reference; -import java.util.Set; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; /** * @author peter */ final class FileTrees { + private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.FileTrees"); + private static final int firstNonFilePsiIndex = 1; + private final PsiFileImpl myFile; private final Reference myStub; private final Getter myTreeElementPointer; // SoftReference/WeakReference to ASTNode or a strong reference to a tree if the file is a DummyHolder - final boolean astLoaded; - final boolean useStrongRefs; + + /** Keeps references to all alive stubbed PSI (using {@link SpineRef}) to ensure PSI identity is preserved after AST/stubs are gc-ed and reloaded */ + @Nullable private final List> myRefToPsi; - private FileTrees(@Nullable Reference stub, @Nullable Getter ast, boolean astLoaded, boolean useStrongRefs) { + private FileTrees(@NotNull PsiFileImpl file, + @Nullable Reference stub, + @Nullable Getter ast, + @Nullable List> refToPsi) { + this.myFile = file; this.myStub = stub; this.myTreeElementPointer = ast; - this.astLoaded = astLoaded; - this.useStrongRefs = useStrongRefs; + this.myRefToPsi = refToPsi; } @Nullable @@ -54,7 +70,41 @@ final class FileTrees { } FileTrees switchToStrongRefs() { - return new FileTrees(myStub, myTreeElementPointer, astLoaded, true); + if (myRefToPsi == null) return this; + + getAllCachedPsi(myRefToPsi).forEach(psi -> { + ASTNode node = psi.getNode(); + LOG.assertTrue(node.getPsi() == psi); + psi.setSubstrateRef(SubstrateRef.createAstStrongRef(node)); + }); + + return new FileTrees(myFile, myStub, myTreeElementPointer, null); + } + + private static Stream getAllCachedPsi(@NotNull List> refToPsi) { + return refToPsi.stream().map(SoftReference::dereference).filter(Objects::nonNull); + } + + boolean useSpineRefs() { + return myRefToPsi != null; + } + + FileTrees switchToSpineRefs(@NotNull StubbedSpine srcSpine) { + List> refToPsi = myRefToPsi; + if (refToPsi == null) refToPsi = new ArrayList<>(Collections.nCopies(srcSpine.getStubCount(), null)); + + for (int i = firstNonFilePsiIndex; i < refToPsi.size(); i++) { + StubBasedPsiElementBase psi = (StubBasedPsiElementBase)srcSpine.getStubPsi(i); + assert psi != null; + psi.setSubstrateRef(new SpineRef(myFile, i)); + StubBasedPsiElementBase existing = SoftReference.dereference(refToPsi.get(i)); + if (existing != null) { + assert existing == psi : "Duplicate PSI found"; + } else { + refToPsi.set(i, new WeakReference<>(psi)); + } + } + return new FileTrees(myFile, myStub, myTreeElementPointer, refToPsi); } FileTrees clearStub(@NotNull String reason) { @@ -62,29 +112,104 @@ final class FileTrees { if (stubHolder != null) { ((PsiFileStubImpl)stubHolder.getRoot()).clearPsi(reason); } - return new FileTrees(null, myTreeElementPointer, astLoaded, useStrongRefs); + + if (myRefToPsi != null) { + DebugUtil.performPsiModification("clearStub", () -> getAllCachedPsi(myRefToPsi).forEach(psi -> { + DebugUtil.onInvalidated(psi); + psi.setSubstrateRef(SubstrateRef.createInvalidRef(psi)); + })); + } + + return new FileTrees(myFile, null, myTreeElementPointer, null); } FileTrees withAst(@NotNull Getter ast) { - return new FileTrees(myStub, ast, true, useStrongRefs); + return new FileTrees(myFile, myStub, ast, myRefToPsi).reconcilePsi(derefStub(), ast.get(), true); } - FileTrees withExclusiveStub(@NotNull StubTree stub, Set allRoots) { - if (derefTreeElement() != null || useStrongRefs) { - throw new RuntimeException(toString() + "; roots=" + allRoots + "; root trees=" + ContainerUtil.map(allRoots, PsiFileImpl::getFileTrees)); - } - return new FileTrees(new SoftReference<>(stub), null, false, false); - } - - FileTrees withGreenStub(@NotNull StubTree stub, @NotNull PsiFileImpl file) { - if (derefTreeElement() == null || !astLoaded) { - throw new RuntimeException("No AST in file " + file + " of " + file.getClass() + "; " + this); - } - return new FileTrees(new SoftReference<>(stub), myTreeElementPointer, true, useStrongRefs); + FileTrees withStub(@NotNull StubTree stub, @Nullable FileElement ast) { + assert derefTreeElement() == ast; + return new FileTrees(myFile, new SoftReference<>(stub), myTreeElementPointer, myRefToPsi) + .reconcilePsi(stub, ast, false); } static FileTrees noStub(@Nullable FileElement ast, @NotNull PsiFileImpl file) { - return new FileTrees(null, ast, ast != null, file instanceof DummyHolder); + return new FileTrees(file, null, ast, null); + } + + /** + * Ensures {@link #myRefToPsi}, stubs and AST all have the same PSI at corresponding indices. + * In case several sources already have PSI (e.g. created during AST parsing), overwrites them with the "correct" one, + * which is taken from {@link #myRefToPsi} if exists, otherwise from either stubs or AST depending on {@code takePsiFromStubs}. + */ + private FileTrees reconcilePsi(@Nullable StubTree stubTree, @Nullable FileElement astRoot, boolean takePsiFromStubs) { + assert stubTree != null || astRoot != null; + + if ((stubTree == null || astRoot == null) && (myRefToPsi == null || !getAllCachedPsi(myRefToPsi).findFirst().isPresent())) { + // there's only one source of PSI, nothing to reconcile + return new FileTrees(myFile, myStub, myTreeElementPointer, null); + } + + List> stubList = stubTree == null ? null : stubTree.getPlainList(); + List nodeList = astRoot == null ? null : astRoot.getStubbedSpine().getSpineNodes(); + + try { + return DebugUtil.performPsiModification("reconcilePsi", () -> { + if (myRefToPsi != null) { + assert myRefToPsi.size() == (stubList != null ? stubList.size() : nodeList.size()) : "Cached PSI count doesn't match actual one"; + bindSubstratesToCachedPsi(stubList, nodeList); + } + + if (stubList != null && nodeList != null) { + assert stubList.size() == nodeList.size() : "Stub count doesn't match stubbed node length"; + + StubbedSpine srcSpine = takePsiFromStubs ? stubTree.getSpine() : astRoot.getStubbedSpine(); + FileTrees result = switchToSpineRefs(srcSpine); + bindStubsWithAst(srcSpine, stubList, nodeList, takePsiFromStubs); + return result; + } + return this; + }); + } + catch (Throwable e) { + LOG.error(e); + myFile.onContentReload(); + myFile.rebuildStub(); + throw StubTreeLoader.getInstance().stubTreeAndIndexDoNotMatch(e.getMessage(), stubTree, myFile); + } + } + + private void bindSubstratesToCachedPsi(List> stubList, List nodeList) { + assert myRefToPsi != null; + for (int i = firstNonFilePsiIndex; i < myRefToPsi.size(); i++) { + StubBasedPsiElementBase cachedPsi = SoftReference.dereference(myRefToPsi.get(i)); + if (cachedPsi != null) { + if (stubList != null) { + //noinspection unchecked + ((StubBase)stubList.get(i)).setPsi(cachedPsi); + } + if (nodeList != null) { + nodeList.get(i).setPsi(cachedPsi); + } + } + } + } + + private static void bindStubsWithAst(StubbedSpine srcSpine, List> stubList, List nodeList, boolean takePsiFromStubs) { + for (int i = firstNonFilePsiIndex; i < stubList.size(); i++) { + StubElement stub = stubList.get(i); + CompositeElement node = nodeList.get(i); + assert stub.getStubType() == node.getElementType() : "Stub type mismatch"; + + PsiElement psi = srcSpine.getStubPsi(i); + assert psi != null; + if (takePsiFromStubs) { + node.setPsi(psi); + } else { + //noinspection unchecked + ((StubBase)stub).setPsi(psi); + } + } } @Override @@ -92,8 +217,7 @@ final class FileTrees { return "FileTrees{" + "stub=" + (myStub == null ? "noRef" : derefStub()) + ", AST=" + (myTreeElementPointer == null ? "noRef" : derefTreeElement()) + - ", astLoaded=" + astLoaded + - ", useStrongRefs=" + useStrongRefs + + ", useSpineRefs=" + useSpineRefs() + '}' ; } } diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java b/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java index 9d2507511d12..eedee4bc13f0 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java @@ -2,7 +2,6 @@ package com.intellij.psi.impl.source; -import com.intellij.extapi.psi.StubBasedPsiElementBase; import com.intellij.ide.util.PsiNavigationSupport; import com.intellij.lang.*; import com.intellij.navigation.ItemPresentation; @@ -14,8 +13,10 @@ import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Queryable; -import com.intellij.openapi.util.Factory; -import com.intellij.openapi.util.*; +import com.intellij.openapi.util.Getter; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileWithId; @@ -40,7 +41,6 @@ import com.intellij.util.Function; import com.intellij.util.IncorrectOperationException; import com.intellij.util.PatchedWeakReference; import com.intellij.util.concurrency.AtomicFieldUpdater; -import com.intellij.util.containers.ContainerUtil; import com.intellij.util.text.CharArrayUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -66,8 +66,6 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF private volatile FileTrees myTrees = FileTrees.noStub(null, this); private boolean myInvalidated; @SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized") - private AstPathPsiMap myRefToPsi; - private final ThreadLocal myFileElementBeingLoaded = new ThreadLocal<>(); protected final PsiManagerEx myManager; public static final Key BUILDING_STUB = new Key<>("Don't use stubs mark!"); private final PsiLock myPsiLock; @@ -80,7 +78,6 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF protected PsiFileImpl(@NotNull FileViewProvider provider ) { myManager = (PsiManagerEx)provider.getManager(); myViewProvider = provider; - myRefToPsi = new AstPathPsiMap(getProject()); myPsiLock = ((AbstractFileViewProvider) provider).getFilePsiLock(); } @@ -179,43 +176,16 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF } } - FileElement treeElement = createFileElement(viewProvider.getContents()); - treeElement.setPsi(this); - - myFileElementBeingLoaded.set(treeElement); - try { - while (true) { - FileTrees trees = myTrees; - List> bindings = calcStubAstBindings(treeElement, trees); - - FileElement savedTree = ensureTreeElement(viewProvider, treeElement, trees, bindings); - if (savedTree != null) { - return savedTree; - } - } - } - finally { - myFileElementBeingLoaded.remove(); - } - } - - @Nullable - private FileElement ensureTreeElement(@NotNull FileViewProvider viewProvider, - @NotNull FileElement treeElement, - @NotNull FileTrees trees, - @NotNull List> bindings) { synchronized (myPsiLock) { - FileElement existing = derefTreeElement(); - if (existing != null) { - return existing; + FileElement treeElement = derefTreeElement(); + if (treeElement != null) { + return treeElement; } - if (trees != myTrees) { - return null; // try again - } + treeElement = createFileElement(viewProvider.getContents()); + treeElement.setPsi(this); - switchFromStubToAst(bindings, trees); - updateTrees(trees.withAst(createTreeElementPointer(treeElement))); + updateTrees(myTrees.withAst(createTreeElementPointer(treeElement))); if (LOG.isDebugEnabled() && viewProvider.isPhysical()) { LOG.debug("Loaded text for file " + viewProvider.getVirtualFile().getPresentableUrl()); @@ -229,49 +199,15 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF @Override public StubbedSpine getStubbedSpine() { StubTree tree = getGreenStubTree(); - return tree != null ? tree.getSpine() : calcTreeElement().getStubbedSpine(); - } - - private void switchFromStubToAst(List> bindings, FileTrees trees) { - if (!bindings.isEmpty() && trees.useStrongRefs) { - List psiStrings = ContainerUtil.map(bindings, pair -> pair.first.getClass().getName()); - LOG.error(this + " of " + getClass() + "; " + psiStrings); - } - - for (int i = 0; i < bindings.size(); i++) { - Pair pair = bindings.get(i); - StubBasedPsiElementBase psi = pair.first; - AstPath path = pair.second; - path.getNode().setPsi(psi); - myRefToPsi.cachePsi(path, psi); - psi.setStubIndex(i + 1); - } - myRefToPsi.clearStubIndexCache(); - } - - private List> calcStubAstBindings(@NotNull FileElement root, FileTrees trees) { - final StubTree stubTree = trees.derefStub(); - if (stubTree == null || trees.astLoaded) { // don't bind green stub to AST: the PSI should already be cached in myRefToPsi - return Collections.emptyList(); - } - - try { - List> result = TreeUtil.calcStubAstBindings(stubTree, root); + if (tree != null) return tree.getSpine(); + + AstSpine astSpine = calcTreeElement().getStubbedSpine(); + if (!myTrees.useSpineRefs()) { synchronized (myPsiLock) { - return ContainerUtil.map(result, pair -> { - StubElement stub = pair.first; - PsiElement psi = stub.getPsi(); - assert psi != null : "Stub " + stub + " (" + stub.getClass() + ") has returned null PSI"; - AstPath path = AstPath.getNodePath((CompositeElement)pair.second); - assert path != null : "Null path"; - return Pair.create((StubBasedPsiElementBase)psi, path); - }); + updateTrees(myTrees.switchToSpineRefs(astSpine)); } } - catch (TreeUtil.StubBindingException e) { - reportStubAstMismatch(e.getMessage(), stubTree); - return Collections.emptyList(); - } + return astSpine; } @Nullable @@ -281,15 +217,6 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF return type instanceof IStubFileElementType ? (IStubFileElementType)type : null; } - void reportStubAstMismatch(String message, StubTree stubTree) { - rebuildStub(); - synchronized (myPsiLock) { - updateTrees(myTrees.clearStub(STUB_PSI_MISMATCH)); - } - - throw StubTreeLoader.getInstance().stubTreeAndIndexDoNotMatch(message, stubTree, this); - } - @NotNull protected FileElement createFileElement(CharSequence docText) { final FileElement treeElement; @@ -396,12 +323,13 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF PsiFileImpl clone = BlockSupportImpl.getFileCopy(this, providerCopy); copyCopyableDataTo(clone); - clone.myRefToPsi = new AstPathPsiMap(getProject()); if (getTreeElement() != null) { // not set by provider in clone final FileElement treeClone = (FileElement)calcTreeElement().clone(); clone.setTreeElementPointer(treeClone); // should not use setTreeElement here because cloned file still have VirtualFile (SCR17963) treeClone.setPsi(clone); + } else { + clone.setTreeElementPointer(null); } if (viewProvider.isEventSystemEnabled()) { @@ -583,8 +511,6 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF DebugUtil.performPsiModification("onContentReload", () -> { synchronized (myPsiLock) { - myRefToPsi.invalidatePsi(); - FileElement treeElement = derefTreeElement(); if (treeElement != null) { treeElement.detachFromFile(); @@ -628,13 +554,12 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF public StubTree getStubTree() { assertReadAccessAllowed(); - if (myTrees.astLoaded && !mayReloadStub()) return null; - if (Boolean.TRUE.equals(getUserData(BUILDING_STUB))) return null; + if (getTreeElement() != null) return null; final StubTree derefd = derefStub(); if (derefd != null) return derefd; - if (getElementTypeForStubBuilder() == null) return null; + if (Boolean.TRUE.equals(getUserData(BUILDING_STUB)) || getElementTypeForStubBuilder() == null) return null; final VirtualFile vFile = getVirtualFile(); if (!(vFile instanceof VirtualFileWithId) || !vFile.isValid()) return null; @@ -645,7 +570,7 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF final List> roots = StubTreeBuilder.getStubbedRoots(viewProvider); synchronized (myPsiLock) { - if (!mayLoadExclusiveStub()) return null; + if (getTreeElement() != null) return null; final StubTree derefdOnLock = derefStub(); if (derefdOnLock != null) return derefdOnLock; @@ -664,66 +589,31 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF return null; } - // first, set all references from stubs to existing PSI (in AST or AstPathPsiMap) - Map bindings = prepareAllStubTrees(roots, stubRoots); - StubTree result = bindings.get(this); - assert result != null : "Current file not in root list: " + roots + ", vp=" + viewProvider; - - // now stubs can be safely published - for (PsiFileImpl eachPsiRoot : bindings.keySet()) { - FileTrees trees = eachPsiRoot.myTrees; - StubTree stub = bindings.get(eachPsiRoot); - FileElement ast = trees.derefTreeElement(); - eachPsiRoot.updateTrees(ast == null ? trees.withExclusiveStub(stub, bindings.keySet()) : trees.withGreenStub(stub, eachPsiRoot)); + StubTree result = null; + for (int i = 0; i < roots.size(); i++) { + PsiFileImpl eachPsiRoot = (PsiFileImpl)roots.get(i).second; + if (eachPsiRoot.derefStub() == null) { + StubTree stubTree = eachPsiRoot.setStubTree(stubRoots[i]); + if (eachPsiRoot == this) { + result = stubTree; + } + } } + + assert result != null : "Current file not in root list: " + roots + ", vp=" + viewProvider; return result; } } - private boolean mayLoadExclusiveStub() { - return getTreeElement() == null && !hasUnbindableCachedPsi(); - } - - private static Map prepareAllStubTrees(List> roots, PsiFileStub[] rootStubs) { - Map bindings = ContainerUtil.newIdentityHashMap(); - for (int i = 0; i < roots.size(); i++) { - PsiFileImpl eachPsiRoot = (PsiFileImpl)roots.get(i).second; - //noinspection unchecked - ((StubBase)rootStubs[i]).setPsi(eachPsiRoot); - StubTree stubTree = new StubTree(rootStubs[i]); - FileElement fileElement = eachPsiRoot.getTreeElement(); - stubTree.setDebugInfo("created in getStubTree(), with AST = " + (fileElement != null)); - if (fileElement != null) { - // Set references from these stubs to AST, because: - // Stub index might call getStubTree on main PSI file, but then use getPlainListFromAllRoots and return stubs from another file. - // Even if that file already has AST, stub.getPsi() should be the same as in AST - TreeUtil.bindStubsToTree(stubTree, fileElement); - eachPsiRoot.myRefToPsi.clearStubIndexCache(); - bindings.put(eachPsiRoot, stubTree); - } else if (eachPsiRoot.derefStub() == null && eachPsiRoot.mayLoadExclusiveStub()) { - eachPsiRoot.bindStubsToCachedPsi(stubTree); - bindings.put(eachPsiRoot, stubTree); - } - } - return bindings; - } - - private boolean mayReloadStub() { - if (getTreeElement() != null || useStrongRefs()) { - return false; - } - StubTreeLoader loader = StubTreeLoader.getInstance(); - if (loader != null && loader.isStubReloadingProhibited()) { - return false; - } - return !hasUnbindableCachedPsi(); - } - - private boolean hasUnbindableCachedPsi() { - synchronized (myPsiLock) { - return ContainerUtil.exists(myViewProvider.getAllFiles(), - file -> file instanceof PsiFileImpl && ((PsiFileImpl)file).myRefToPsi.hasUnbindableCachedPsi()); - } + @NotNull + private StubTree setStubTree(PsiFileStub root) { + //noinspection unchecked + ((StubBase)root).setPsi(this); + StubTree stubTree = new StubTree(root); + FileElement fileElement = getTreeElement(); + stubTree.setDebugInfo("created in getStubTree(), with AST = " + (fileElement != null)); + updateTrees(myTrees.withStub(stubTree, fileElement)); + return stubTree; } @Nullable @@ -738,23 +628,8 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF } } - FileTrees getFileTrees() { - return myTrees; - } - - private void bindStubsToCachedPsi(StubTree stubTree) { - myRefToPsi.getAllCachedPsi().forEach(psi -> { - int index = psi.getStubIndex(); - if (index >= 0) { - //noinspection unchecked - ((StubBase)stubTree.getPlainList().get(index)).setPsi(psi); - } - }); - } - protected PsiFileImpl cloneImpl(FileElement treeElementClone) { PsiFileImpl clone = (PsiFileImpl)super.clone(); - clone.myRefToPsi = new AstPathPsiMap(getProject()); clone.setTreeElementPointer(treeElementClone); // should not use setTreeElement here because cloned file still have VirtualFile (SCR17963) treeElementClone.setPsi(clone); return clone; @@ -791,14 +666,8 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF @NotNull public final FileElement calcTreeElement() { - // Attempt to find (loaded) tree element without taking lock first. FileElement treeElement = getTreeElement(); - if (treeElement != null) return treeElement; - - treeElement = myFileElementBeingLoaded.get(); - if (treeElement != null) return treeElement; - - return loadTreeElement(); + return treeElement != null ? treeElement : loadTreeElement(); } @Override @@ -1026,7 +895,6 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF if (tree != null) { return tree; } - assert myFileElementBeingLoaded.get() == null : "non-empty thread-local"; FileElement fileElement = calcTreeElement(); synchronized (myPsiLock) { tree = derefStub(); @@ -1052,23 +920,14 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF tree = new StubTree((PsiFileStub)currentStubTree); tree.setDebugInfo("created in calcStubTree"); - try { - TreeUtil.bindStubsToTree(tree, fileElement); - myRefToPsi.clearStubIndexCache(); - } - catch (TreeUtil.StubBindingException e) { - rebuildStub(); - throw new RuntimeException("Stub and PSI element type mismatch in " + getName(), e); - } - - updateTrees(myTrees.withGreenStub(tree, this)); + updateTrees(myTrees.withStub(tree, fileElement)); } return tree; } } - private void rebuildStub() { + final void rebuildStub() { ApplicationManager.getApplication().invokeLater(() -> { if (!myManager.isDisposed()) { myManager.dropPsiCaches(); @@ -1104,13 +963,10 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF public final void beforeAstChange() { checkWritable(); - if (!useStrongRefs()) { - synchronized (myPsiLock) { - for (PsiFile root : myViewProvider.getAllFiles()) { - if (root instanceof PsiFileImpl) { - ((PsiFileImpl)root).switchToStrongRefs(); - } - } + synchronized (myPsiLock) { + FileTrees updated = myTrees.switchToStrongRefs(); + if (updated != myTrees) { + updateTrees(updated); } } } @@ -1123,42 +979,4 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF CheckUtil.checkWritable(this); } } - - private void switchToStrongRefs() { - FileElement node = calcTreeElement(); - updateTrees(myTrees.switchToStrongRefs()); - myRefToPsi.switchToStrongRefs(); - AstPath.invalidatePaths(node); - } - - @Nullable - public StubBasedPsiElementBase obtainPsi(@NotNull AstPath path, @NotNull Factory> creator) { - if (useStrongRefs()) { - return null; - } - - StubBasedPsiElementBase psi = myRefToPsi.getCachedPsi(path); - if (psi != null) return psi; - - synchronized (myPsiLock) { - if (useStrongRefs()) { - return null; - } - - psi = myRefToPsi.getCachedPsi(path); - return psi != null ? psi : myRefToPsi.cachePsi(path, creator.create()); - } - } - - final AstPathPsiMap getRefToPsi() { - return myRefToPsi; - } - - public final boolean useStrongRefs() { - return myTrees.useStrongRefs; - } - - public boolean mayCacheAst() { - return myFileElementBeingLoaded.get() == null; - } } diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/SpineRef.java b/platform/core-impl/src/com/intellij/psi/impl/source/SpineRef.java new file mode 100644 index 000000000000..f16d6b4f859f --- /dev/null +++ b/platform/core-impl/src/com/intellij/psi/impl/source/SpineRef.java @@ -0,0 +1,53 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.psi.impl.source; + +import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiFile; +import com.intellij.psi.stubs.Stub; +import com.intellij.psi.stubs.StubTree; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author peter + */ +class SpineRef extends SubstrateRef { + private final PsiFileImpl myFile; + private final int myIndex; + + SpineRef(@NotNull PsiFileImpl file, int index) { + myFile = file; + myIndex = index; + } + + @NotNull + @Override + public ASTNode getNode() { + return myFile.calcTreeElement().getStubbedSpine().getSpineNodes().get(myIndex); + } + + @Nullable + @Override + public Stub getStub() { + StubTree tree = myFile.getStubTree(); + return tree == null ? null : tree.getPlainList().get(myIndex); + } + + @Nullable + @Override + public Stub getGreenStub() { + StubTree tree = myFile.getGreenStubTree(); + return tree == null ? null : tree.getPlainList().get(myIndex); + } + + @Override + public boolean isValid() { + return myFile.isValid(); + } + + @NotNull + @Override + public PsiFile getContainingFile() { + return myFile; + } +} diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/SubstrateRef.java b/platform/core-impl/src/com/intellij/psi/impl/source/SubstrateRef.java index 45491ba69508..f862caa7d683 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/SubstrateRef.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/SubstrateRef.java @@ -42,13 +42,13 @@ public abstract class SubstrateRef { public abstract ASTNode getNode(); @Nullable - public Stub getStub(int stubIndex) { + public Stub getStub() { return null; } @Nullable - public Stub getGreenStub(int index) { - return getStub(index); + public Stub getGreenStub() { + return getStub(); } public abstract boolean isValid(); @@ -119,7 +119,7 @@ public abstract class SubstrateRef { @NotNull @Override - public Stub getStub(int stubIndex) { + public Stub getStub() { return myStub; } diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/tree/AstPath.java b/platform/core-impl/src/com/intellij/psi/impl/source/tree/AstPath.java deleted file mode 100644 index b91a3f305117..000000000000 --- a/platform/core-impl/src/com/intellij/psi/impl/source/tree/AstPath.java +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Copyright 2000-2016 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.psi.impl.source.tree; - -import com.intellij.extapi.psi.StubBasedPsiElementBase; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.vfs.VirtualFileWithId; -import com.intellij.psi.PsiElement; -import com.intellij.psi.impl.source.PsiFileImpl; -import com.intellij.psi.impl.source.SubstrateRef; -import com.intellij.psi.stubs.IStubElementType; -import com.intellij.psi.stubs.Stub; -import com.intellij.psi.stubs.StubTree; -import com.intellij.reference.SoftReference; -import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.ref.WeakReference; -import java.util.List; - -/** - * A lightweight object representing a chain of node indices (among all lazy-parseable and stub-based elements) - * allowing to restore a specific node after it's been garbage-collected and recreated. - * - * @author peter - */ -public abstract class AstPath extends SubstrateRef { - @SuppressWarnings("ConstantForZeroLengthArrayAllocation") // need a different instance to distinguish removed children for diagnostics - private static final CompositeElement[] REMOVED_PATH_CHILDREN = new CompositeElement[0]; - private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.AstPath"); - private static final Key PATH_CHILDREN = Key.create("PATH_CHILDREN"); - private static final Key NODE_PATH = Key.create("NODE_PATH"); - - @NotNull - public abstract PsiFileImpl getContainingFile(); - - @NotNull - public abstract CompositeElement getNode(); - - @Override - public boolean isValid() { - return getContainingFile().isValid(); - } - - protected abstract int getDepth(); - - @Nullable - @Override - public Stub getStub(int stubIndex) { - if (stubIndex < 0) return null; - - StubTree stubTree = getFileStubTree(); - return stubTree == null ? null : stubTree.getPlainList().get(stubIndex); - } - - @Nullable - protected abstract StubTree getFileStubTree(); - - @Nullable - @Override - public Stub getGreenStub(int stubIndex) { - if (stubIndex < 0) return null; - - StubTree stubTree = getContainingFile().getGreenStubTree(); - return stubTree == null ? null : stubTree.getPlainList().get(stubIndex); - } - - @Nullable - public static AstPath getNodePath(@NotNull CompositeElement node) { - if (node instanceof FileElement) { - PsiElement psi = node.getCachedPsi(); - if (!(psi instanceof PsiFileImpl)) return null; - - PsiFileImpl file = (PsiFileImpl)psi; - if (!(file.getVirtualFile() instanceof VirtualFileWithId) || file.getElementTypeForStubBuilder() == null) { - return null; - } - return new RootPath(file); - } - - return node.getUserData(NODE_PATH); - } - - static void cacheNodePaths(@NotNull LazyParseableElement parent, @Nullable TreeElement child, @Nullable AstPath parentPath) { - if (parentPath == null) { - return; - } - - final int depth = parentPath.getDepth() + 1; - - final List children = ContainerUtil.newArrayList(); - while (child != null) { - child.acceptTree(new RecursiveTreeElementWalkingVisitor(false) { - @Override - public void visitComposite(CompositeElement composite) { - boolean lazy = composite instanceof LazyParseableElement; - if (lazy || composite.getElementType() instanceof IStubElementType) { - int index = children.size(); - composite.putUserData(NODE_PATH, depth % 4 == 0 ? new MilestoneChildPath(parentPath, index, depth) : new ChildPath(parentPath, index)); - children.add(composite); - } - - if (!lazy) { - super.visitComposite(composite); - } - } - }); - child = child.getTreeNext(); - } - - parent.putUserData(PATH_CHILDREN, children.isEmpty() ? CompositeElement.EMPTY_ARRAY : children.toArray(CompositeElement.EMPTY_ARRAY)); - - for (CompositeElement each : children) { - if (each instanceof LazyParseableElement && ((LazyParseableElement)each).isParsed()) { - cacheNodePaths((LazyParseableElement)each, each.getFirstChildNode(), getNodePath(each)); - } - } - } - - public static void invalidatePaths(@NotNull LazyParseableElement scope) { - CompositeElement[] children = scope.getUserData(PATH_CHILDREN); - if (children == null) return; - - scope.putUserData(PATH_CHILDREN, REMOVED_PATH_CHILDREN); - for (CompositeElement child : children) { - child.putUserData(NODE_PATH, null); - assertConsistency(child.getCachedPsi()); - if (child instanceof LazyParseableElement) { - invalidatePaths((LazyParseableElement)child); - } - } - } - - private static void assertConsistency(PsiElement cachedPsi) { - if (cachedPsi instanceof StubBasedPsiElementBase && - ((StubBasedPsiElementBase)cachedPsi).getSubstrateRef() instanceof AstPath) { - LOG.error("Expected strong reference at " + cachedPsi + - " of " + cachedPsi.getClass() + - " and " + ((StubBasedPsiElementBase)cachedPsi).getElementType()); - } - } - - private static class ChildPath extends AstPath { - private final AstPath myParent; - private final int myIndex; - - ChildPath(@NotNull AstPath parent, int index) { - myParent = parent; - myIndex = index; - } - - @NotNull - @Override - public PsiFileImpl getContainingFile() { - return myParent.getContainingFile(); - } - - @NotNull - @Override - public CompositeElement getNode() { - CompositeElement parentNode = myParent.getNode(); - //noinspection ResultOfMethodCallIgnored - parentNode.getFirstChildNode(); // expand chameleons, populate PATH_CHILDREN array - CompositeElement[] children = parentNode.getUserData(PATH_CHILDREN); - boolean removed = children == REMOVED_PATH_CHILDREN; - if (children == null || removed) { - throw reportMissingChildren(parentNode, removed); - } - if (myIndex >= children.length) { - throw new AssertionError(myIndex + " >= " + children.length + "; " + parentNode + " of " + parentNode.getClass()); - } - return children[myIndex]; - } - - private AssertionError reportMissingChildren(CompositeElement parentNode, boolean removed) { - String message = "No path children in " + parentNode + " of " + parentNode.getClass() + "; removed=" + removed; - - PsiFileImpl file = getContainingFile(); - message += "\n file: " + file + " of " + file.getClass() + "; physical=" + file.isPhysical() + "; useStrongRefs=" + file.useStrongRefs(); - - FileElement fileElement = file.getTreeElement(); - message += "\n ast=" + fileElement; - if (fileElement != null) { - CompositeElement[] rootChildren = fileElement.getUserData(PATH_CHILDREN); - message += "; root.children=" + (rootChildren == REMOVED_PATH_CHILDREN ? "removed" : rootChildren == null ? "null" : "nonNull"); - } - return new AssertionError(message); - } - - @Override - protected int getDepth() { - return 1 + myParent.getDepth(); - } - - @Override - protected StubTree getFileStubTree() { - return myParent.getFileStubTree(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof ChildPath)) return false; - - ChildPath path = (ChildPath)o; - return myIndex == path.myIndex && myParent.equals(path.myParent); - } - - @Override - public int hashCode() { - return 31 * myParent.hashCode() + myIndex; - } - } - - private static class MilestoneChildPath extends ChildPath { - private final int myDepth; - private final PsiFileImpl myFile; - private volatile WeakReference myNode; - - MilestoneChildPath(@NotNull AstPath parent, int index, int depth) { - super(parent, index); - myDepth = depth; - myFile = parent.getContainingFile(); - } - - @NotNull - @Override - public CompositeElement getNode() { - CompositeElement node = SoftReference.dereference(myNode); - if (node == null) { - node = super.getNode(); - if (myFile.mayCacheAst()) { - myNode = new WeakReference<>(node); - } - } - return node; - } - - @Override - protected StubTree getFileStubTree() { - return SoftReference.dereference(myNode) == null ? myFile.getStubTree() : null; - } - - @NotNull - @Override - public PsiFileImpl getContainingFile() { - return myFile; - } - - @Override - protected int getDepth() { - return myDepth; - } - } - - private static class RootPath extends AstPath { - private final PsiFileImpl myFile; - - RootPath(@NotNull PsiFileImpl file) { - myFile = file; - } - - @Override - public boolean equals(Object o) { - return this == o || o instanceof RootPath && myFile.equals(((RootPath)o).myFile); - } - - @Override - public int hashCode() { - return myFile.hashCode(); - } - - @NotNull - @Override - public PsiFileImpl getContainingFile() { - return myFile; - } - - @NotNull - @Override - public CompositeElement getNode() { - return myFile.calcTreeElement(); - } - - @Override - protected int getDepth() { - return 0; - } - - @Override - protected StubTree getFileStubTree() { - return myFile.getStubTree(); - } - } - -} diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/tree/AstSpine.java b/platform/core-impl/src/com/intellij/psi/impl/source/tree/AstSpine.java index c93d08e4e92f..f6e1107abbbc 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/tree/AstSpine.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/tree/AstSpine.java @@ -57,7 +57,8 @@ public class AstSpine implements StubbedSpine { return index >= myNodes.size() ? null : myNodes.get(index).getElementType(); } - List getNodes() { + @NotNull + public List getSpineNodes() { return myNodes; } } diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/tree/CompositeElement.java b/platform/core-impl/src/com/intellij/psi/impl/source/tree/CompositeElement.java index 46158a8fa2de..b205c1c3aa34 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/tree/CompositeElement.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/tree/CompositeElement.java @@ -18,7 +18,6 @@ package com.intellij.psi.impl.source.tree; import com.intellij.diagnostic.ThreadDumper; import com.intellij.extapi.psi.ASTDelegatePsiElement; -import com.intellij.extapi.psi.StubBasedPsiElementBase; import com.intellij.lang.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; @@ -29,9 +28,10 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.impl.DebugUtil; import com.intellij.psi.impl.FreeThreadedFileViewProvider; -import com.intellij.psi.impl.source.*; +import com.intellij.psi.impl.source.DummyHolder; +import com.intellij.psi.impl.source.DummyHolderFactory; +import com.intellij.psi.impl.source.SourceTreeToPsiMap; import com.intellij.psi.impl.source.codeStyle.CodeEditUtil; -import com.intellij.psi.stubs.IStubElementType; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import com.intellij.psi.util.PsiUtilCore; @@ -723,21 +723,10 @@ public class CompositeElement extends TreeElement { PsiElement wrapper = myWrapper; if (wrapper != null) return wrapper; - wrapper = obtainStubBasedPsi(); - if (wrapper == null) wrapper = createPsiNoLock(); + wrapper = createPsiNoLock(); return ourPsiUpdater.compareAndSet(this, null, wrapper) ? wrapper : ObjectUtils.assertNotNull(myWrapper); } - /** - * If AST has been gced and recreated, but someone still holds a reference to a PSI, then {@link #getPsi()} should return the very same PSI object. - * So we try to find that PSI in file's {@link AstPathPsiMap}. - */ - @Nullable - private PsiElement obtainStubBasedPsi() { - AstPath path = getElementType() instanceof IStubElementType ? AstPath.getNodePath(this) : null; - return path == null ? null : path.getContainingFile().obtainPsi(path, () -> (StubBasedPsiElementBase)createPsiNoLock()); - } - @Override public T getPsi(@NotNull Class clazz) { return LeafElement.getPsi(clazz, getPsi(), LOG); @@ -755,6 +744,10 @@ public class CompositeElement extends TreeElement { } public void setPsi(@NotNull PsiElement psi) { + PsiElement prev = myWrapper; + if (prev != null && prev != psi) { + DebugUtil.onInvalidated(prev); + } myWrapper = psi; } @@ -769,13 +762,6 @@ public class CompositeElement extends TreeElement { } public void rawAddChildrenWithoutNotifications(@NotNull TreeElement first) { - if (DebugUtil.DO_EXPENSIVE_CHECKS && !(this instanceof LazyParseableElement)) { - PsiFileImpl file = getCachedFile(this); - if (file != null && !file.useStrongRefs()) { - throw new AssertionError("Attempt to modify PSI in a file with weakly-referenced AST. Possible cause: missing PomTransaction."); - } - } - final TreeElement last = getLastChildNode(); if (last == null){ TreeElement chainLast = rawSetParents(first, this); diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/tree/FileElement.java b/platform/core-impl/src/com/intellij/psi/impl/source/tree/FileElement.java index 60988897c194..311da5c0179f 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/tree/FileElement.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/tree/FileElement.java @@ -18,6 +18,7 @@ package com.intellij.psi.impl.source.tree; import com.intellij.lang.*; import com.intellij.openapi.util.Getter; +import com.intellij.openapi.util.RecursionManager; import com.intellij.psi.PsiElement; import com.intellij.psi.StubBuilder; import com.intellij.psi.impl.PsiManagerEx; @@ -27,12 +28,9 @@ import com.intellij.psi.stubs.IStubElementType; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.ILightStubFileElementType; import com.intellij.psi.tree.IStubFileElementType; -import com.intellij.reference.SoftReference; import com.intellij.util.CharTable; import org.jetbrains.annotations.NotNull; -import java.lang.ref.Reference; -import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; @@ -40,7 +38,7 @@ public class FileElement extends LazyParseableElement implements FileASTNode, Ge public static final FileElement[] EMPTY_ARRAY = new FileElement[0]; private volatile CharTable myCharTable = new CharTableImpl(); private volatile boolean myDetached; - private volatile Reference myStubbedSpine; + private volatile AstSpine myStubbedSpine; @Override protected PsiElement createPsiNoLock() { @@ -108,13 +106,17 @@ public class FileElement extends LazyParseableElement implements FileASTNode, Ge @NotNull public final AstSpine getStubbedSpine() { - AstSpine result = SoftReference.dereference(myStubbedSpine); + AstSpine result = myStubbedSpine; if (result == null) { - IStubFileElementType type = ((PsiFileImpl)getPsi()).getElementTypeForStubBuilder(); + PsiFileImpl file = (PsiFileImpl)getPsi(); + IStubFileElementType type = file.getElementTypeForStubBuilder(); if (type == null) return AstSpine.EMPTY_SPINE; - result = new AstSpine(calcStubbedDescendants(type.getBuilder())); - myStubbedSpine = getManager().isBatchFilesProcessingMode() ? new WeakReference<>(result) : new SoftReference<>(result); + result = RecursionManager.doPreventingRecursion(file, false, () -> new AstSpine(calcStubbedDescendants(type.getBuilder()))); + if (result == null) { + throw new StackOverflowError("Endless recursion prevented"); + } + myStubbedSpine = result; } return result; } diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/tree/LazyParseableElement.java b/platform/core-impl/src/com/intellij/psi/impl/source/tree/LazyParseableElement.java index af2fa02c2df1..bf0501167bdc 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/tree/LazyParseableElement.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/tree/LazyParseableElement.java @@ -19,7 +19,6 @@ */ package com.intellij.psi.impl.source.tree; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Attachment; import com.intellij.openapi.diagnostic.LogUtil; import com.intellij.openapi.diagnostic.Logger; @@ -200,7 +199,7 @@ public class LazyParseableElement extends CompositeElement { } if (parsedNode != null) { - setChildren(parsedNode, AstPath.getNodePath(this)); + setChildren(parsedNode); } myParsed = true; @@ -220,10 +219,9 @@ public class LazyParseableElement extends CompositeElement { } } - private void setChildren(@NotNull TreeElement parsedNode, @Nullable AstPath thisPath) { + private void setChildren(@NotNull TreeElement parsedNode) { ProgressManager.getInstance().executeNonCancelableSection(() -> { try { - AstPath.cacheNodePaths(this, parsedNode, thisPath); TreeElement last = rawSetParents(parsedNode, this); super.setFirstChildNode(parsedNode); super.setLastChildNode(last); diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/tree/TreeUtil.java b/platform/core-impl/src/com/intellij/psi/impl/source/tree/TreeUtil.java index f25af12cd404..c9acf84d4484 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/tree/TreeUtil.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/tree/TreeUtil.java @@ -16,21 +16,14 @@ package com.intellij.psi.impl.source.tree; -import com.intellij.extapi.psi.StubBasedPsiElementBase; import com.intellij.lang.ASTNode; import com.intellij.lexer.Lexer; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.util.Couple; import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiComment; -import com.intellij.psi.PsiFile; import com.intellij.psi.PsiWhiteSpace; import com.intellij.psi.impl.DebugUtil; -import com.intellij.psi.stubs.StubBase; -import com.intellij.psi.stubs.StubElement; -import com.intellij.psi.stubs.StubTree; import com.intellij.psi.templateLanguages.OuterLanguageElement; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.IStrongWhitespaceHolderElementType; @@ -38,7 +31,9 @@ import com.intellij.psi.tree.TokenSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; public class TreeUtil { @@ -441,45 +436,6 @@ public class TreeUtil { boolean isStrongElementOnRisingSlope = true; } - public static class StubBindingException extends RuntimeException { - StubBindingException(String message) { - super(message); - } - } - - public static void bindStubsToTree(@NotNull StubTree stubTree, @NotNull FileElement tree) throws StubBindingException { - List> bindings = calcStubAstBindings(stubTree, tree); - - for (int i = 0; i < bindings.size(); i++) { - Pair pair = bindings.get(i); - StubBasedPsiElementBase psi = (StubBasedPsiElementBase)pair.second.getPsi(); - //noinspection unchecked - pair.first.setPsi(psi); - psi.setStubIndex(i + 1); - } - } - - @NotNull - public static List> calcStubAstBindings(@NotNull StubTree stubTree, @NotNull FileElement tree) throws StubBindingException { - PsiFile file = (PsiFile)tree.getPsi(); - List nodes = tree.getStubbedSpine().getNodes(); - List> stubs = stubTree.getPlainList(); - if (stubs.size() != nodes.size()) { - throw new StubBindingException("Stub list in " + file.getName() + " length differs from PSI"); - } - - List> bindings = new ArrayList<>(); - for (int i = 1; i < stubs.size(); i++) { // start from 1 to skip file root stub - StubBase stub = (StubBase)stubs.get(i); - CompositeElement node = nodes.get(i); - if (stub.getStubType() != node.getElementType()) { - throw new StubBindingException("stub:" + stub + ", AST:" + node.getElementType()); - } - bindings.add(Pair.create(stub, node)); - } - return bindings; - } - @Nullable public static ASTNode skipWhitespaceAndComments(final ASTNode node, boolean forward) { return skipWhitespaceCommentsAndTokens(node, TokenSet.EMPTY, forward); diff --git a/platform/core-impl/src/com/intellij/psi/stubs/StubTreeLoader.java b/platform/core-impl/src/com/intellij/psi/stubs/StubTreeLoader.java index 81c764262246..8196d8352c28 100644 --- a/platform/core-impl/src/com/intellij/psi/stubs/StubTreeLoader.java +++ b/platform/core-impl/src/com/intellij/psi/stubs/StubTreeLoader.java @@ -51,10 +51,6 @@ public abstract class StubTreeLoader { @Nullable public abstract ObjectStubTree readFromVFile(Project project, final VirtualFile vFile); - public boolean isStubReloadingProhibited() { - return false; - } - public abstract void rebuildStubTree(VirtualFile virtualFile); public abstract boolean canHaveStub(VirtualFile file); diff --git a/platform/lang-impl/src/com/intellij/psi/stubs/StubTreeLoaderImpl.java b/platform/lang-impl/src/com/intellij/psi/stubs/StubTreeLoaderImpl.java index c09422b05da4..8871cba816d1 100644 --- a/platform/lang-impl/src/com/intellij/psi/stubs/StubTreeLoaderImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/stubs/StubTreeLoaderImpl.java @@ -189,11 +189,6 @@ public class StubTreeLoaderImpl extends StubTreeLoader { } } - @Override - public boolean isStubReloadingProhibited() { - return ourStubReloadingProhibited; - } - private static int getCurrentTextContentLength(Project project, VirtualFile vFile, Document document, PsiFile psiFile) { if (vFile.getFileType().isBinary()) { return -1; From 55ae6e6ac64bb2b8157b4b77ceeb5df5063d1079 Mon Sep 17 00:00:00 2001 From: Maxim Kropotov Date: Tue, 10 Apr 2018 18:01:58 +0300 Subject: [PATCH 48/53] JSON schema: IDEA-CR-31605 followup. inline confusing method --- .../jsonSchema/impl/JsonSchemaObject.java | 50 +++++++------------ 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java index 2feb02fe389d..643b2252651c 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java @@ -644,44 +644,32 @@ public class JsonSchemaObject { current = current.getProperties().get(parts.get(++i)); continue; } - Pair arrayResult = ObjectUtils.coalesce( - handleArrayItem(ITEMS, parts, i, current.getItemsSchema(), current.getItemsSchemaList()), - handleArrayItem(ADDITIONAL_ITEMS, parts, i, current.getAdditionalItemsSchema(), ContainerUtil.emptyList())); - if (arrayResult != null) { - //noinspection AssignmentToForLoopParameter - i = arrayResult.second; - current = arrayResult.first; + if (ITEMS.equals(part)) { + if (i == (parts.size() - 1)) { + current = current.getItemsSchema(); + } + else { + //noinspection AssignmentToForLoopParameter + Integer next = tryParseInt(parts.get(++i)); + List itemsSchemaList = current.getItemsSchemaList(); + if (itemsSchemaList != null && next != null && next < itemsSchemaList.size()) { + current = itemsSchemaList.get(next); + } + } continue; } + if (ADDITIONAL_ITEMS.equals(part)) { + if (i == (parts.size() - 1)) { + current = current.getAdditionalItemsSchema(); + } + continue; + } + current = current.getDefinitionsMap() == null ? null : current.getDefinitionsMap().get(part); } return current; } - @Nullable - private static Pair handleArrayItem(@NotNull String name, - List parts, - int i, - @Nullable JsonSchemaObject arraySchema, - @Nullable List arraySchemaList) { - final String part = parts.get(i); - if (!name.equals(part)) { - return null; - } - if (i == (parts.size() - 1)) { - if (arraySchema != null) { - return Pair.create(arraySchema, i); - } - } - else { - Integer next = tryParseInt(parts.get(++i)); - if (arraySchemaList != null && next != null && next >= 0 && next < arraySchemaList.size()) { - return Pair.create(arraySchemaList.get(next), i); - } - } - return null; - } - @Nullable private static Integer tryParseInt(String s) { try { From 15500d6412b3999bea390fd7df875a464c6fea03 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Tue, 10 Apr 2018 17:08:47 +0200 Subject: [PATCH 49/53] =?UTF-8?q?ExecutionTargetTest=20=E2=80=94=20use=20D?= =?UTF-8?q?isposableRule=20to=20ensure=20that=20always=20disposed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/com/intellij/testFramework/FixtureRule.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/platform/testFramework/src/com/intellij/testFramework/FixtureRule.kt b/platform/testFramework/src/com/intellij/testFramework/FixtureRule.kt index f877329280ef..26ea93d276b5 100644 --- a/platform/testFramework/src/com/intellij/testFramework/FixtureRule.kt +++ b/platform/testFramework/src/com/intellij/testFramework/FixtureRule.kt @@ -3,6 +3,7 @@ package com.intellij.testFramework import com.intellij.ide.highlighter.ProjectFileType import com.intellij.idea.IdeaTestApplication +import com.intellij.openapi.Disposable import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.runUndoTransparentWriteAction import com.intellij.openapi.components.ComponentManager @@ -16,6 +17,7 @@ import com.intellij.openapi.project.ProjectManager import com.intellij.openapi.project.ex.ProjectEx import com.intellij.openapi.project.ex.ProjectManagerEx import com.intellij.openapi.project.impl.ProjectManagerImpl +import com.intellij.openapi.util.Disposer import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS import com.intellij.openapi.vfs.newvfs.persistent.PersistentFSImpl @@ -305,4 +307,17 @@ fun createOrLoadProject(tempDirManager: TemporaryDirectory, projectCreator: ((Vi fun ComponentManager.saveStore() { stateStore.save(SmartList(), true) +} + +class DisposableRule : ExternalResource() { + private var _disposable = lazy { Disposer.newDisposable() } + + val disposable: Disposable + get() = _disposable.value + + override fun after() { + if (_disposable.isInitialized()) { + Disposer.dispose(_disposable.value) + } + } } \ No newline at end of file From 25bdd045420cefd1fe0bd6113921ff0871221556 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Wed, 4 Apr 2018 18:28:23 +0300 Subject: [PATCH 50/53] IDEA-186651 vcs: add shortcut to move range into another changelist --- .../vcs/ex/LineStatusMarkerPopupRenderer.java | 7 ++- .../src/keymaps/$default.xml | 6 ++ .../src/keymaps/Eclipse.xml | 2 + .../platform-resources/src/keymaps/Emacs.xml | 2 + .../src/keymaps/Mac OS X 10.5+.xml | 6 ++ .../src/keymaps/Mac OS X.xml | 8 +++ .../src/keymaps/NetBeans 6.5.xml | 2 + .../com/intellij/keymap/KeymapsTestCase.java | 2 + .../vcs/actions/VcsActionPromoter.java | 57 ++++++++++++------- .../vcs/ex/PartialLocalLineStatusTracker.kt | 15 ++++- 10 files changed, 83 insertions(+), 24 deletions(-) diff --git a/platform/diff-impl/src/com/intellij/openapi/vcs/ex/LineStatusMarkerPopupRenderer.java b/platform/diff-impl/src/com/intellij/openapi/vcs/ex/LineStatusMarkerPopupRenderer.java index 48e0b0eb5a56..024f5fe111ba 100644 --- a/platform/diff-impl/src/com/intellij/openapi/vcs/ex/LineStatusMarkerPopupRenderer.java +++ b/platform/diff-impl/src/com/intellij/openapi/vcs/ex/LineStatusMarkerPopupRenderer.java @@ -97,7 +97,10 @@ public abstract class LineStatusMarkerPopupRenderer extends LineStatusMarkerRend } @Nullable - protected JComponent createAdditionalInfoPanel(@NotNull Editor editor, @NotNull Range range, @Nullable Point mousePosition) { + protected JComponent createAdditionalInfoPanel(@NotNull Editor editor, + @NotNull Range range, + @Nullable Point mousePosition, + @NotNull Disposable disposable) { return null; } @@ -142,7 +145,7 @@ public abstract class LineStatusMarkerPopupRenderer extends LineStatusMarkerRend toolbar.updateActionsImmediately(); // we need valid ActionToolbar.getPreferredSize() to calc size of popup toolbar.setReservePlaceAutoPopupIcon(false); - JComponent additionalInfoPanel = createAdditionalInfoPanel(editor, range, mousePosition); + JComponent additionalInfoPanel = createAdditionalInfoPanel(editor, range, mousePosition, disposable); PopupPanel popupPanel = new PopupPanel(editor, toolbar, editorComponent, additionalInfoPanel); diff --git a/platform/platform-resources/src/keymaps/$default.xml b/platform/platform-resources/src/keymaps/$default.xml index edafc4f5c7d3..399489979755 100644 --- a/platform/platform-resources/src/keymaps/$default.xml +++ b/platform/platform-resources/src/keymaps/$default.xml @@ -593,6 +593,12 @@ + + + + + + diff --git a/platform/platform-resources/src/keymaps/Eclipse.xml b/platform/platform-resources/src/keymaps/Eclipse.xml index 8be0f588d7cd..dedb1a82cd4e 100644 --- a/platform/platform-resources/src/keymaps/Eclipse.xml +++ b/platform/platform-resources/src/keymaps/Eclipse.xml @@ -360,4 +360,6 @@ + + diff --git a/platform/platform-resources/src/keymaps/Emacs.xml b/platform/platform-resources/src/keymaps/Emacs.xml index 49b90da7f04f..e0f82f51fb72 100644 --- a/platform/platform-resources/src/keymaps/Emacs.xml +++ b/platform/platform-resources/src/keymaps/Emacs.xml @@ -310,4 +310,6 @@ + + diff --git a/platform/platform-resources/src/keymaps/Mac OS X 10.5+.xml b/platform/platform-resources/src/keymaps/Mac OS X 10.5+.xml index 115afd7ad07e..6293abe69fb9 100644 --- a/platform/platform-resources/src/keymaps/Mac OS X 10.5+.xml +++ b/platform/platform-resources/src/keymaps/Mac OS X 10.5+.xml @@ -266,6 +266,12 @@ + + + + + + diff --git a/platform/platform-resources/src/keymaps/Mac OS X.xml b/platform/platform-resources/src/keymaps/Mac OS X.xml index 1163d8b36e7b..c0ece8c598c2 100644 --- a/platform/platform-resources/src/keymaps/Mac OS X.xml +++ b/platform/platform-resources/src/keymaps/Mac OS X.xml @@ -289,6 +289,12 @@ + + + + + + @@ -401,4 +407,6 @@ + + diff --git a/platform/platform-resources/src/keymaps/NetBeans 6.5.xml b/platform/platform-resources/src/keymaps/NetBeans 6.5.xml index a374aed9d0bd..cd5393470e5e 100644 --- a/platform/platform-resources/src/keymaps/NetBeans 6.5.xml +++ b/platform/platform-resources/src/keymaps/NetBeans 6.5.xml @@ -384,4 +384,6 @@ + + diff --git a/platform/testFramework/extensions/src/com/intellij/keymap/KeymapsTestCase.java b/platform/testFramework/extensions/src/com/intellij/keymap/KeymapsTestCase.java index 8c0759e1d5ee..1b142fb3e296 100644 --- a/platform/testFramework/extensions/src/com/intellij/keymap/KeymapsTestCase.java +++ b/platform/testFramework/extensions/src/com/intellij/keymap/KeymapsTestCase.java @@ -66,6 +66,7 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase { { "shift F7", "PreviousDiff", "SmartStepInto"}, { "shift TAB", "PreviousTemplateVariable", "PrevParameter", "EditorUnindentSelection", "PrevTemplateParameter"}, { "shift alt L", "org.jetbrains.plugins.ruby.console.LoadInIrbConsoleAction", "context.load"}, + { "shift alt M", "ChangesView.Move", "Vcs.MoveChangedLinesToChangelist"}, { "shift control D", "TagDocumentationNavigation", "Diff.ShowSettingsPopup", "Uml.ShowDiff", "Console.TableResult.CompareCells"}, { "shift control DOWN", "ResizeToolWindowDown", "MoveStatementDown"}, { "shift control ENTER", "EditorCompleteStatement", "Console.Jpa.GenerateSql"}, @@ -105,6 +106,7 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase { { "meta N", "FileChooser.NewFolder", "Generate", "NewElement"}, { "meta O", "GotoClass", "GotoChangedFile"}, { "shift meta G", "ClassTemplateNavigation", "GoToClass", "FindPrevious"}, + { "shift meta M", "ChangesView.Move", "Vcs.MoveChangedLinesToChangelist"}, { "shift meta LEFT", "EditorLineStartWithSelection", "ResizeToolWindowLeft", }, { "shift meta RIGHT", "EditorLineEndWithSelection", "ResizeToolWindowRight", }, { "alt R", "Django.RunManageTaskAction", "org.jetbrains.plugins.ruby.tasks.rake.actions.RakeTasksPopupAction"}, diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/VcsActionPromoter.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/VcsActionPromoter.java index cbec6ef9e859..95952c336491 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/VcsActionPromoter.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/VcsActionPromoter.java @@ -15,39 +15,54 @@ */ package com.intellij.openapi.vcs.actions; +import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.ActionPromoter; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.diff.actions.DiffWalkerAction; -import com.intellij.openapi.vcs.ex.MoveChangesLineStatusAction; -import com.intellij.openapi.vcs.ex.RollbackLineStatusAction; +import com.intellij.util.containers.ContainerUtil; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; -/** - * @author Konstantin Bulenkov - */ public class VcsActionPromoter implements ActionPromoter { @Override public List promote(List actions, DataContext context) { - List list = new ArrayList<>(0); + ActionManager am = ActionManager.getInstance(); + List reorderedActions = new ArrayList<>(actions); + List reorderedIds = ContainerUtil.map(reorderedActions, it -> am.getId(it)); - for (AnAction action : actions) { - if (action instanceof RollbackLineStatusAction) { - list.add(action); - } - if (action instanceof MoveChangesLineStatusAction) { - list.add(action); - } - if (action instanceof ShowMessageHistoryAction) { - list.add(action); - } - if (action instanceof DiffWalkerAction) { - list.add(action); - } - } + reorderActionPair(reorderedActions, reorderedIds, "Vcs.MoveChangedLinesToChangelist", "ChangesView.Move"); + reorderActionPair(reorderedActions, reorderedIds, "Vcs.RollbackChangedLines", "ChangesView.Revert"); - return list; + Set promoted = new HashSet<>(ContainerUtil.filter(actions, action -> { + return action instanceof ShowMessageHistoryAction || + action instanceof DiffWalkerAction; + })); + + reorderedActions.removeAll(promoted); + reorderedActions.addAll(0, promoted); + + return reorderedActions; + } + + /** + * Ensures that one global action has priority over another global action. + * But is not pushing it ahead of other actions (ex: of some local action with same shortcut). + */ + private static void reorderActionPair(List reorderedActions, List reorderedIds, + String highPriority, String lowPriority) { + int highPriorityIndex = reorderedIds.indexOf(highPriority); + int lowPriorityIndex = reorderedIds.indexOf(lowPriority); + if (highPriorityIndex == -1 || lowPriorityIndex == -1) return; + if (highPriorityIndex < lowPriorityIndex) return; + + String id = reorderedIds.remove(highPriorityIndex); + AnAction action = reorderedActions.remove(highPriorityIndex); + + reorderedIds.add(lowPriorityIndex, id); + reorderedActions.add(lowPriorityIndex, action); } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/PartialLocalLineStatusTracker.kt b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/PartialLocalLineStatusTracker.kt index 8669c5b512ba..28eee68e0a97 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/PartialLocalLineStatusTracker.kt +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/PartialLocalLineStatusTracker.kt @@ -17,6 +17,8 @@ package com.intellij.openapi.vcs.ex import com.intellij.diff.util.Side import com.intellij.openapi.Disposable +import com.intellij.openapi.actionSystem.ActionManager +import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.DefaultActionGroup import com.intellij.openapi.actionSystem.Separator import com.intellij.openapi.application.ModalityState @@ -31,6 +33,7 @@ import com.intellij.openapi.editor.Document import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.event.DocumentEvent import com.intellij.openapi.editor.event.DocumentListener +import com.intellij.openapi.project.DumbAwareAction import com.intellij.openapi.project.Project import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.registry.Registry @@ -517,7 +520,10 @@ class PartialLocalLineStatusTracker(project: Project, } } - override fun createAdditionalInfoPanel(editor: Editor, range: Range, mousePosition: Point?): JComponent? { + override fun createAdditionalInfoPanel(editor: Editor, + range: Range, + mousePosition: Point?, + disposable: Disposable): JComponent? { if (range !is LocalRange) return null val changeLists = ChangeListManager.getInstance(tracker.project).changeLists @@ -536,6 +542,13 @@ class PartialLocalLineStatusTracker(project: Project, val link = ActionGroupLink(rangeList.name, null, group) + val moveChangesShortcut = ActionManager.getInstance().getAction("Vcs.MoveChangedLinesToChangelist").shortcutSet + object : DumbAwareAction() { + override fun actionPerformed(e: AnActionEvent?) { + link.linkLabel.doClick() + } + }.registerCustomShortcutSet(moveChangesShortcut, editor.component, disposable) + val panel = JPanel(BorderLayout()) panel.add(link, BorderLayout.CENTER) panel.border = JBUI.Borders.emptyLeft(7) From 74fae70f41cde130ff39448ec2b5e2787a490c40 Mon Sep 17 00:00:00 2001 From: Sergey Simonchik Date: Tue, 10 Apr 2018 19:01:53 +0300 Subject: [PATCH 51/53] add test for auto 'testFinished' event for 'testIgnored' (IDEA-CR-31598) --- .../sm/runner/events/TestIgnoredEvent.java | 12 +++-- ...IdBasedToSMTRunnerEventsConvertorTest.java | 54 +++++++++++++++++-- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/events/TestIgnoredEvent.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/events/TestIgnoredEvent.java index 500cecb109de..ea090421a46c 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/events/TestIgnoredEvent.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/events/TestIgnoredEvent.java @@ -26,14 +26,16 @@ public class TestIgnoredEvent extends TreeNodeEvent { private final String myStacktrace; public TestIgnoredEvent(@NotNull String testName, @NotNull String ignoreComment, @Nullable String stacktrace) { - super(testName, null); - myIgnoreComment = ignoreComment; - myStacktrace = stacktrace; + this(testName, null, ignoreComment, stacktrace); } public TestIgnoredEvent(@NotNull TestIgnored testIgnored, @Nullable String stacktrace) { - super(testIgnored.getTestName(), TreeNodeEvent.getNodeId(testIgnored)); - myIgnoreComment = testIgnored.getIgnoreComment(); + this(testIgnored.getTestName(), TreeNodeEvent.getNodeId(testIgnored), testIgnored.getIgnoreComment(), stacktrace); + } + + public TestIgnoredEvent(@Nullable String name, @Nullable String id, @Nullable String ignoreComment, @Nullable String stacktrace) { + super(name, id); + myIgnoreComment = ignoreComment; myStacktrace = stacktrace; } diff --git a/platform/smRunner/testSrc/com/intellij/execution/testframework/sm/runner/GeneralIdBasedToSMTRunnerEventsConvertorTest.java b/platform/smRunner/testSrc/com/intellij/execution/testframework/sm/runner/GeneralIdBasedToSMTRunnerEventsConvertorTest.java index eaca233b754b..cb09a89ff4dc 100644 --- a/platform/smRunner/testSrc/com/intellij/execution/testframework/sm/runner/GeneralIdBasedToSMTRunnerEventsConvertorTest.java +++ b/platform/smRunner/testSrc/com/intellij/execution/testframework/sm/runner/GeneralIdBasedToSMTRunnerEventsConvertorTest.java @@ -15,23 +15,32 @@ */ package com.intellij.execution.testframework.sm.runner; -import com.intellij.execution.testframework.sm.runner.events.TestFailedEvent; -import com.intellij.execution.testframework.sm.runner.events.TestStartedEvent; -import com.intellij.execution.testframework.sm.runner.events.TestSuiteStartedEvent; -import com.intellij.execution.testframework.sm.runner.events.TreeNodeEvent; +import com.intellij.execution.testframework.TestConsoleProperties; +import com.intellij.execution.testframework.sm.runner.events.*; +import com.intellij.execution.testframework.sm.runner.ui.SMTRunnerConsoleView; +import com.intellij.execution.testframework.sm.runner.ui.SMTestRunnerResultsForm; import com.intellij.openapi.util.Disposer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class GeneralIdBasedToSMTRunnerEventsConvertorTest extends BaseSMTRunnerTestCase { + private SMTRunnerConsoleView myConsole; private GeneralIdBasedToSMTRunnerEventsConvertor myEventsProcessor; private SMTestProxy.SMRootTestProxy myRootProxy; + private SMTestRunnerResultsForm myResultsViewer; @Override protected void setUp() throws Exception { super.setUp(); + + TestConsoleProperties consoleProperties = createConsoleProperties(); + myConsole = new SMTRunnerConsoleView(consoleProperties); + myConsole.initUI(); + myResultsViewer = myConsole.getResultsViewer(); + myRootProxy = new SMTestProxy.SMRootTestProxy(); myEventsProcessor = new GeneralIdBasedToSMTRunnerEventsConvertor(getProject(), myRootProxy, "test"); + myEventsProcessor.addEventsListener(myResultsViewer); myEventsProcessor.onStartTesting(); } @@ -39,6 +48,7 @@ public class GeneralIdBasedToSMTRunnerEventsConvertorTest extends BaseSMTRunnerT protected void tearDown() throws Exception { try { Disposer.dispose(myEventsProcessor); + Disposer.dispose(myConsole); } finally { super.tearDown(); @@ -84,6 +94,27 @@ public class GeneralIdBasedToSMTRunnerEventsConvertorTest extends BaseSMTRunnerT assertTrue(suiteProxy.isInProgress()); } + public void testIgnoredEvent() { + onSuiteStarted("Suite", null, "1", TreeNodeEvent.ROOT_NODE_ID); + SMTestProxy suite = validateSuite("1", "Suite", null, myRootProxy); + onTestStarted("testA", null, "A", "1", true); + SMTestProxy testA = validateTest("A", "testA", null, true, suite); + onTestIgnored("A"); + validateTestIgnored("A", testA); + assertFalse(testA.isInProgress()); + assertTrue(suite.isInProgress()); + assertEquals(1, myResultsViewer.getFinishedTestCount()); + onTestFinished("A", null); + assertEquals(1, myResultsViewer.getFinishedTestCount()); + + onTestStarted("testB", null, "B", "1", true); + SMTestProxy testB = validateTest("B", "testB", null, true, suite); + assertEquals(1, myResultsViewer.getFinishedTestCount()); + onTestIgnored("B"); + assertEquals(2, myResultsViewer.getFinishedTestCount()); + validateTestIgnored("B", testB); + } + @NotNull private SMTestProxy validateSuite(@NotNull String id, @NotNull String expectedName, @@ -129,6 +160,14 @@ public class GeneralIdBasedToSMTRunnerEventsConvertorTest extends BaseSMTRunnerT return test; } + private void validateTestIgnored(@NotNull String id, @NotNull SMTestProxy expectedTestProxy) { + SMTestProxy test = myEventsProcessor.findProxyById(id); + assertEquals(expectedTestProxy, test); + assertFalse(test.isSuite()); + assertTrue(test.isFinal()); + assertTrue(test.isIgnored()); + } + private void onSuiteStarted(@NotNull String suiteName, @Nullable String metainfo, @NotNull String id, @NotNull String parentId) { myEventsProcessor.onSuiteStarted(new TestSuiteStartedEvent(suiteName, id, parentId, null, metainfo, null, null, false)); } @@ -141,9 +180,16 @@ public class GeneralIdBasedToSMTRunnerEventsConvertorTest extends BaseSMTRunnerT myEventsProcessor.onTestStarted(new TestStartedEvent(testName, id, parentId, null, metainfo, null, null, running)); } + private void onTestFinished(@NotNull String id, @Nullable Long duration) { + myEventsProcessor.onTestFinished(new TestFinishedEvent(null, id, duration)); + } + private void onTestFailed(@NotNull String id, @NotNull String errorMessage, int durationMillis) { myEventsProcessor.onTestFailure(new TestFailedEvent(null, id, errorMessage, null, false, null, null, null, null, false, false, durationMillis)); } + private void onTestIgnored(@NotNull String id) { + myEventsProcessor.onTestIgnored(new TestIgnoredEvent(null, id, null, null)); + } } From 97bdca249437b829f98364ac722900e7e25273ee Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Tue, 10 Apr 2018 19:25:07 +0300 Subject: [PATCH 52/53] lst: add tooltip hint about "Move Lines to Changelist" shortcut --- .../openapi/vcs/ex/PartialLocalLineStatusTracker.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/PartialLocalLineStatusTracker.kt b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/PartialLocalLineStatusTracker.kt index 28eee68e0a97..9f089fc815dc 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/PartialLocalLineStatusTracker.kt +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/PartialLocalLineStatusTracker.kt @@ -33,6 +33,7 @@ import com.intellij.openapi.editor.Document import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.event.DocumentEvent import com.intellij.openapi.editor.event.DocumentListener +import com.intellij.openapi.keymap.KeymapUtil import com.intellij.openapi.project.DumbAwareAction import com.intellij.openapi.project.Project import com.intellij.openapi.util.Disposer @@ -542,12 +543,17 @@ class PartialLocalLineStatusTracker(project: Project, val link = ActionGroupLink(rangeList.name, null, group) - val moveChangesShortcut = ActionManager.getInstance().getAction("Vcs.MoveChangedLinesToChangelist").shortcutSet + val moveChangesShortcutSet = ActionManager.getInstance().getAction("Vcs.MoveChangedLinesToChangelist").shortcutSet object : DumbAwareAction() { override fun actionPerformed(e: AnActionEvent?) { link.linkLabel.doClick() } - }.registerCustomShortcutSet(moveChangesShortcut, editor.component, disposable) + }.registerCustomShortcutSet(moveChangesShortcutSet, editor.component, disposable) + + val shortcuts = moveChangesShortcutSet.shortcuts + if (shortcuts.isNotEmpty()) { + link.linkLabel.toolTipText = "Move lines to another changelist (${KeymapUtil.getShortcutText(shortcuts.first())})" + } val panel = JPanel(BorderLayout()) panel.add(link, BorderLayout.CENTER) From dd7840ac29e5d156960e5bec4167e6a1414f9a75 Mon Sep 17 00:00:00 2001 From: Anton Lobov Date: Tue, 10 Apr 2018 18:50:14 +0200 Subject: [PATCH 53/53] WEB-20353 JSON Schema: provide Edit action for created mapping --- .../jsonSchema/JsonSchemaMappingsView.java | 116 ++++++++++++++---- 1 file changed, 95 insertions(+), 21 deletions(-) diff --git a/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsView.java b/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsView.java index b6d1d39f6f47..fd166c06b72c 100644 --- a/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsView.java +++ b/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsView.java @@ -38,8 +38,11 @@ import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.io.File; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; @@ -50,6 +53,7 @@ import static com.jetbrains.jsonSchema.JsonSchemaConfigurable.isHttpPath; */ public class JsonSchemaMappingsView implements Disposable { private static final String ADD_SCHEMA_MAPPING = "settings.json.schema.add.mapping"; + private static final String EDIT_SCHEMA_MAPPING = "settings.json.schema.edit.mapping"; private final Runnable myTreeUpdater; private TableView myTableView; private JComponent myComponent; @@ -70,23 +74,20 @@ public class JsonSchemaMappingsView implements Disposable { myTableView = new TableView<>(); myTableView.getTableHeader().setVisible(false); final ToolbarDecorator decorator = ToolbarDecorator.createDecorator(myTableView); - decorator.setRemoveAction(new AnActionButtonRunnable() { + final MyEditActionButtonRunnableImpl editAction = new MyEditActionButtonRunnableImpl(project); + decorator.setRemoveAction(new MyRemoveActionButtonRunnable()) + .setAddAction(new MyAddActionButtonRunnable(project)) + .setEditAction(editAction) + .disableUpDownActions(); + + myTableView.addMouseListener(new MouseAdapter() { @Override - public void run(AnActionButton button) { - final int[] rows = myTableView.getSelectedRows(); - if (rows != null && rows.length > 0) { - int cnt = 0; - for (int row : rows) { - myTableView.getListTableModel().removeRow(row - cnt); - ++cnt; - } - myTableView.getListTableModel().fireTableDataChanged(); - myTreeUpdater.run(); + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == 2) { + editAction.execute(); } } - }) - .setAddAction(new MyAddActionButtonRunnable(project)) - .disableUpDownActions(); + }); mySchemaField = new TextFieldWithBrowseButton(); SwingHelper.installFileCompletionAndBrowseDialog(myProject, mySchemaField, JsonBundle.message("json.schema.add.schema.chooser.title"), @@ -180,21 +181,30 @@ public class JsonSchemaMappingsView implements Disposable { return myComponent; } - private class MyAddActionButtonRunnable implements AnActionButtonRunnable { + private abstract class MyAddOrEditActionButtonRunnableBase implements AnActionButtonRunnable { private final Project myProject; - public MyAddActionButtonRunnable(Project project) { + public MyAddOrEditActionButtonRunnableBase(Project project) { myProject = project; } @Override - public void run(AnActionButton button) { + public abstract void run(AnActionButton button); + + protected void doRun(@Nullable UserDefinedJsonSchemaConfiguration.Item currentItem, int selectedRow) { + assert currentItem == null || selectedRow != -1; final JBPanel panel = new JBPanel(new GridBagLayout()); final GridBag bag = new GridBag(); final JBTextField patternField = new JBTextField(); final TextFieldWithBrowseButton directoryField = new TextFieldWithBrowseButton(); + if (currentItem != null && currentItem.directory) { + directoryField.setText(currentItem.path); + } final TextFieldWithBrowseButton fileField = new TextFieldWithBrowseButton(); + if (currentItem != null && !currentItem.directory && !currentItem.pattern) { + fileField.setText(currentItem.path); + } bag.setDefaultAnchor(GridBagConstraints.NORTHWEST); final JBRadioButton radioPattern = new JBRadioButton("Filename pattern:"); @@ -218,17 +228,28 @@ public class JsonSchemaMappingsView implements Disposable { group.add(radioDirectory); group.add(radioFile); - radioDirectory.setSelected(true); + if (currentItem == null) { + radioDirectory.setSelected(true); + } + else { + if (currentItem.pattern) radioPattern.setSelected(true); + else if (currentItem.directory) radioDirectory.setSelected(true); + else radioFile.setSelected(true); + } patternField.setMinimumSize(new Dimension(JBUI.scale(200), UIUtil.getInformationIcon().getIconHeight())); patternField.getEmptyText().setText("Example: *.config.json"); + if (currentItem != null && currentItem.pattern) { + patternField.setText(currentItem.path); + } final DialogBuilder builder = new DialogBuilder(); - builder.setTitle("Add JSON Schema Mapping"); + String addOrEdit = currentItem == null ? "Add" : "Edit"; + builder.setTitle(addOrEdit + " JSON Schema Mapping"); builder.setNorthPanel(panel); builder.setPreferredFocusComponent(directoryField); builder.setDimensionServiceKey("com.jetbrains.jsonSchema.JsonSchemaMappingsView#add"); - builder.setHelpId(ADD_SCHEMA_MAPPING); + builder.setHelpId(currentItem == null ? ADD_SCHEMA_MAPPING : EDIT_SCHEMA_MAPPING); final Getter textGetter = () -> { if (radioPattern.isSelected()) { @@ -279,13 +300,66 @@ public class JsonSchemaMappingsView implements Disposable { final String pattern = textGetter.get(); final UserDefinedJsonSchemaConfiguration.Item item = new UserDefinedJsonSchemaConfiguration.Item(pattern, radioPattern.isSelected(), radioDirectory.isSelected()); - myTableView.getListTableModel().addRow(item); + if (currentItem != null) { + myTableView.getListTableModel().removeRow(selectedRow); + myTableView.getListTableModel().insertRow(selectedRow, item); + myTableView.setSelection(Collections.singleton(item)); + } + else { + myTableView.getListTableModel().addRow(item); + } myTreeUpdater.run(); } Disposer.dispose(alarm); } } + private class MyAddActionButtonRunnable extends MyAddOrEditActionButtonRunnableBase { + public MyAddActionButtonRunnable(Project project) { + super(project); + } + + @Override + public void run(AnActionButton button) { + doRun(null, -1); + } + } + + private class MyEditActionButtonRunnableImpl extends MyAddOrEditActionButtonRunnableBase { + public MyEditActionButtonRunnableImpl(Project project) { + super(project); + } + + @Override + public void run(AnActionButton button) { + execute(); + } + + public void execute() { + int selectedRow = myTableView.getSelectedRow(); + if (selectedRow == -1) return; + UserDefinedJsonSchemaConfiguration.Item item = myTableView.getListTableModel().getItem(selectedRow); + if (item == null) return; + doRun(item, selectedRow); + } + } + + private class MyRemoveActionButtonRunnable implements AnActionButtonRunnable { + @Override + public void run(AnActionButton button) { + final int[] rows = myTableView.getSelectedRows(); + if (rows != null && rows.length > 0) { + int cnt = 0; + for (int row : rows) { + myTableView.getListTableModel().removeRow(row - cnt); + ++cnt; + } + myTableView.getListTableModel().fireTableDataChanged(); + myTreeUpdater.run(); + } + } + } + private static String getRelativePath(@NotNull Project project, @NotNull String text) { text = text.trim(); if (project.isDefault() || project.getBasePath() == null) return text;