From 9bd3de1a08b98e93c0f186e2d09e5b0915bb20be Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Tue, 14 Apr 2015 16:19:03 +0200 Subject: [PATCH 01/85] IDEA-65437 Tomcat server console: support ANSI coloring --- .../execution/configurations/JavaCommandLineTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/execution/configurations/JavaCommandLineTest.java b/java/java-tests/testSrc/com/intellij/execution/configurations/JavaCommandLineTest.java index e4d0bc039c5d..730ea6005104 100644 --- a/java/java-tests/testSrc/com/intellij/execution/configurations/JavaCommandLineTest.java +++ b/java/java-tests/testSrc/com/intellij/execution/configurations/JavaCommandLineTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -18,7 +18,7 @@ package com.intellij.execution.configurations; import com.intellij.execution.CantRunException; import com.intellij.execution.ExecutionBundle; import com.intellij.execution.ExecutionException; -import com.intellij.execution.process.DefaultJavaProcessHandler; +import com.intellij.execution.process.KillableColoredProcessHandler; import com.intellij.ide.IdeBundle; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl; @@ -101,7 +101,7 @@ public class JavaCommandLineTest extends LightIdeaTestCase { public void testCreateProcess() { try { - new DefaultJavaProcessHandler(new GeneralCommandLine()); + new KillableColoredProcessHandler(new GeneralCommandLine()); fail("ExecutionException (executable is not specified) expected"); } catch (ExecutionException e) { From d1c2b6e5ef0e65499a746150026b84797cc01df6 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Tue, 14 Apr 2015 12:53:43 +0200 Subject: [PATCH 02/85] EA-67432 - IOE: PsiJavaParserFacadeImpl.createStatementFromText --- .../completion/JavaInheritorsGetter.java | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java index e96eb36c6385..a6ee18c6e4a1 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java @@ -160,20 +160,23 @@ public class JavaInheritorsGetter extends CompletionProvider()", parameters.getOriginalFile()); - final PsiVariable declaredVar = (PsiVariable)((PsiDeclarationStatement)statement).getDeclaredElements()[0]; - final PsiNewExpression initializer = (PsiNewExpression)declaredVar.getInitializer(); - final boolean hasDefaultConstructorOrNoGenericsOne = PsiDiamondTypeImpl.hasDefaultConstructor(psiClass) || - !PsiDiamondTypeImpl.haveConstructorsGenericsParameters(psiClass); - if (hasDefaultConstructorOrNoGenericsOne) { - final PsiDiamondTypeImpl.DiamondInferenceResult inferenceResult = PsiDiamondTypeImpl.resolveInferredTypes(initializer); - if (inferenceResult.getErrorMessage() == null && - !psiClass.hasModifierProperty(PsiModifier.ABSTRACT) && - areInferredTypesApplicable(inferenceResult.getTypes(), parameters.getPosition())) { - psiType = initializer.getType(); + try { + final PsiStatement statement = elementFactory + .createStatementFromText(canonicalText + " v = new " + erasedText + "<>()", parameters.getOriginalFile()); + final PsiVariable declaredVar = (PsiVariable)((PsiDeclarationStatement)statement).getDeclaredElements()[0]; + final PsiNewExpression initializer = (PsiNewExpression)declaredVar.getInitializer(); + final boolean hasDefaultConstructorOrNoGenericsOne = PsiDiamondTypeImpl.hasDefaultConstructor(psiClass) || + !PsiDiamondTypeImpl.haveConstructorsGenericsParameters(psiClass); + if (hasDefaultConstructorOrNoGenericsOne) { + final PsiDiamondTypeImpl.DiamondInferenceResult inferenceResult = PsiDiamondTypeImpl.resolveInferredTypes(initializer); + if (inferenceResult.getErrorMessage() == null && + !psiClass.hasModifierProperty(PsiModifier.ABSTRACT) && + areInferredTypesApplicable(inferenceResult.getTypes(), parameters.getPosition())) { + psiType = initializer.getType(); + } } } + catch (IncorrectOperationException ignore) {} } } final PsiTypeLookupItem item = PsiTypeLookupItem.createLookupItem(psiType, position); From 1a26481c7de29bb626cb3ade77f77edd54342f4d Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Tue, 14 Apr 2015 13:06:00 +0200 Subject: [PATCH 03/85] logging for EA-67345 - assert: InferenceSession.argConstraints --- .../impl/source/resolve/graphInference/InferenceSession.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java index a2fb17f2015a..d2037b8bab88 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java @@ -1447,7 +1447,8 @@ public class InferenceSession { if (arg instanceof PsiMethodReferenceExpression && ((PsiMethodReferenceExpression)arg).isExact()) { final PsiParameter[] sParameters = sInterfaceMethod.getParameterList().getParameters(); final PsiParameter[] tParameters = tInterfaceMethod.getParameterList().getParameters(); - LOG.assertTrue(sParameters.length == tParameters.length); + LOG.assertTrue(sParameters.length == tParameters.length, + "s: " + sInterfaceMethod.getParameterList().getText() + "; t: " + tInterfaceMethod.getParameterList().getText()); for (int i = 0; i < tParameters.length; i++) { final PsiType tSubstituted = tSubstitutor.substitute(tParameters[i].getType()); final PsiType sSubstituted = sSubstitutor.substitute(sParameters[i].getType()); @@ -1471,7 +1472,7 @@ public class InferenceSession { if (sPrimitive ^ tPrimitive) { final PsiMember member = ((PsiMethodReferenceExpression)arg).getPotentiallyApplicableMember(); - LOG.assertTrue(member != null); + LOG.assertTrue(member != null, arg); if (member instanceof PsiMethod) { final PsiType methodReturnType = ((PsiMethod)member).getReturnType(); if (sPrimitive && methodReturnType instanceof PsiPrimitiveType && methodReturnType != PsiType.VOID || From b838f7b960bca15ea4571c88b3cad908a30963c8 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Tue, 14 Apr 2015 13:07:59 +0200 Subject: [PATCH 04/85] logging for EA-67346 - assert: InferenceSession.collectApplicabilityConstraints --- .../impl/source/resolve/graphInference/InferenceSession.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java index d2037b8bab88..ea18bac35945 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java @@ -1252,7 +1252,7 @@ public class InferenceSession { if (methodContainingClass != null) { psiSubstitutor = TypeConversionUtil.getClassSubstitutor(methodContainingClass, containingClass, psiSubstitutor); - LOG.assertTrue(psiSubstitutor != null); + LOG.assertTrue(psiSubstitutor != null, "derived: " + containingClass + "; super: " + methodContainingClass); } for (int i = 0; i < functionalMethodParameters.length; i++) { From 08a5ba094f3d195911fe7360ae058bb692d55ab2 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Tue, 14 Apr 2015 15:20:17 +0200 Subject: [PATCH 05/85] pull up: skip conflict if super method is available in target module --- .../intellij/refactoring/util/RefactoringConflictsUtil.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/java/java-impl/src/com/intellij/refactoring/util/RefactoringConflictsUtil.java b/java/java-impl/src/com/intellij/refactoring/util/RefactoringConflictsUtil.java index 19a6be869c43..9fc61e413029 100644 --- a/java/java-impl/src/com/intellij/refactoring/util/RefactoringConflictsUtil.java +++ b/java/java-impl/src/com/intellij/refactoring/util/RefactoringConflictsUtil.java @@ -260,6 +260,11 @@ public class RefactoringConflictsUtil { !CommonRefactoringUtil.isAncestor(resolved, scopes) && !PsiSearchScopeUtil.isInScope(resolveScope, resolved) && !(resolved instanceof LightElement)) { + if (resolved instanceof PsiMethod) { + for (PsiMethod superMethod : ((PsiMethod)resolved).findDeepestSuperMethods()) { + if (PsiSearchScopeUtil.isInScope (resolveScope, superMethod)) return; + } + } final String scopeDescription = RefactoringUIUtil.getDescription(ConflictsUtil.getContainer(reference), true); final String message = RefactoringBundle.message("0.referenced.in.1.will.not.be.accessible.in.module.2", RefactoringUIUtil.getDescription(resolved, true), From 9476fcf548f8624945c6fe8e9573f05247c91540 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Tue, 14 Apr 2015 16:28:30 +0200 Subject: [PATCH 06/85] sm java test locator: accept param names with dot inside --- .../JavaTestLocationProvider.java | 36 ++++++------------- .../junit/JUnitRerunFailedTestsTest.java | 10 +++--- 2 files changed, 16 insertions(+), 30 deletions(-) diff --git a/java/execution/impl/src/com/intellij/execution/testframework/JavaTestLocationProvider.java b/java/execution/impl/src/com/intellij/execution/testframework/JavaTestLocationProvider.java index 6731375a8e79..6bd1b167871c 100644 --- a/java/execution/impl/src/com/intellij/execution/testframework/JavaTestLocationProvider.java +++ b/java/execution/impl/src/com/intellij/execution/testframework/JavaTestLocationProvider.java @@ -47,25 +47,21 @@ public class JavaTestLocationProvider implements TestLocationProvider { public List getLocation(@NotNull String protocolId, @NotNull String locationData, Project project) { List results = Collections.emptyList(); + final int idx = locationData.indexOf("["); + final String paramName = idx >= 0 ? locationData.substring(idx) : null; + if (idx >= 0) { + locationData = locationData.substring(0, idx); + } + final JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(project); if (SUITE_PROTOCOL.equals(protocolId)) { + locationData = StringUtil.trimEnd(locationData, "."); PsiClass[] classes = javaPsiFacade.findClasses(locationData, myScope); if (classes.length > 0) { results = ContainerUtil.newSmartList(); for (PsiClass aClass : classes) { - results.add(new PsiLocation(project, aClass)); - } - } - else { - //parameter root for parameterized tests: ClassName.[paramName] - final String className = StringUtil.getPackageName(locationData); - classes = javaPsiFacade.findClasses(className, myScope); - if (classes.length > 0) { - final String paramName = StringUtil.getShortName(locationData); - results = ContainerUtil.newSmartList(); - for (PsiClass aClass : classes) { - results.add(PsiMemberParameterizedLocation.getParameterizedLocation(aClass, paramName)); - } + results.add(paramName != null ? PsiMemberParameterizedLocation.getParameterizedLocation(aClass, paramName) + : new PsiLocation(project, aClass)); } } } @@ -80,18 +76,8 @@ public class JavaTestLocationProvider implements TestLocationProvider { PsiMethod[] methods = aClass.findMethodsByName(methodName, true); if (methods.length > 0) { for (PsiMethod method : methods) { - results.add(MethodLocation.elementInClass(method, aClass)); - } - } - else { - //parameterized tests: ClassName.testName[paramName] - final int paramIdx = methodName.indexOf("["); - if (paramIdx > -1 && methodName.endsWith("]")) { - final String paramName = methodName.substring(paramIdx); - methods = aClass.findMethodsByName(methodName.substring(0, paramIdx), true); - for (PsiMethod method : methods) { - results.add(new PsiMemberParameterizedLocation(project, method, aClass, paramName)); - } + results.add(paramName != null ? new PsiMemberParameterizedLocation(project, method, aClass, paramName) + : MethodLocation.elementInClass(method, aClass)); } } } diff --git a/plugins/junit/test/com/intellij/execution/junit/JUnitRerunFailedTestsTest.java b/plugins/junit/test/com/intellij/execution/junit/JUnitRerunFailedTestsTest.java index d42b741ad653..82e5f466788e 100644 --- a/plugins/junit/test/com/intellij/execution/junit/JUnitRerunFailedTestsTest.java +++ b/plugins/junit/test/com/intellij/execution/junit/JUnitRerunFailedTestsTest.java @@ -87,10 +87,10 @@ public class JUnitRerunFailedTestsTest extends LightCodeInsightFixtureTestCase final SMTestProxy rootProxy = new SMTestProxy("MyTest", true, "java:suite://MyTest"); rootProxy.setLocator(locationProvider); - final SMTestProxy proxyParam = new SMTestProxy("[0]", true, "java:suite://MyTest.[0]"); + final SMTestProxy proxyParam = new SMTestProxy("[0.java]", true, "java:suite://MyTest.[0.java]"); proxyParam.setLocator(locationProvider); - final SMTestProxy parameterizedTestProxy = new SMTestProxy("testName1[0]", false, "java:test://MyTest.testName1[0]"); + final SMTestProxy parameterizedTestProxy = new SMTestProxy("testName1[0.java]", false, "java:test://MyTest.testName1[0.java]"); parameterizedTestProxy.setLocator(locationProvider); final Location rootLocation = rootProxy.getLocation(project, searchScope); @@ -100,15 +100,15 @@ public class JUnitRerunFailedTestsTest extends LightCodeInsightFixtureTestCase final Location proxyParamLocation = proxyParam.getLocation(project, searchScope); assertNotNull(proxyParamLocation); assertInstanceOf(proxyParamLocation, PsiMemberParameterizedLocation.class); - assertEquals("[0]", ((PsiMemberParameterizedLocation)proxyParamLocation).getParamSetName()); + assertEquals("[0.java]", ((PsiMemberParameterizedLocation)proxyParamLocation).getParamSetName()); assertEquals(testClass, proxyParamLocation.getPsiElement()); final Location parameterizedTestProxyLocation = parameterizedTestProxy.getLocation(project, searchScope); assertNotNull(parameterizedTestProxyLocation); assertInstanceOf(parameterizedTestProxyLocation, PsiMemberParameterizedLocation.class); - assertEquals("[0]", ((PsiMemberParameterizedLocation)parameterizedTestProxyLocation).getParamSetName()); + assertEquals("[0.java]", ((PsiMemberParameterizedLocation)parameterizedTestProxyLocation).getParamSetName()); assertEquals(testClass.getMethods()[0], parameterizedTestProxyLocation.getPsiElement()); assertEquals(testClass, ((PsiMemberParameterizedLocation)parameterizedTestProxyLocation).getContainingClass()); - assertEquals("MyTest,testName1[0]", TestMethods.getTestPresentation(parameterizedTestProxy, project, searchScope)); + assertEquals("MyTest,testName1[0.java]", TestMethods.getTestPresentation(parameterizedTestProxy, project, searchScope)); } } From b4badef6fe83600c75fcaae08fce499db36d70be Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Mon, 13 Apr 2015 19:30:16 +0300 Subject: [PATCH 07/85] diff: iterating between differences should make both sides visible * work around SyncScroll issue with big insertion/deletion at the end of file * move carets in all editors to change --- .../diff/tools/simple/SimpleDiffViewer.java | 15 +- .../simple/SimpleThreesideDiffViewer.java | 1 + .../diff/tools/util/SyncScrollSupport.java | 194 ++++++++++++++---- .../util/twoside/TwosideTextDiffViewer.java | 18 +- .../src/com/intellij/diff/util/DiffUtil.java | 14 +- 5 files changed, 190 insertions(+), 52 deletions(-) diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java index 761a7ce8467f..f81a25c9b238 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java @@ -411,12 +411,19 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { return true; } - private void doScrollToChange(@NotNull SimpleDiffChange change, boolean animated) { + private void doScrollToChange(@NotNull SimpleDiffChange change, final boolean animated) { if (myEditor1 == null || myEditor2 == null) return; + assert mySyncScrollSupport != null; - EditorEx editor = getCurrentEditor(); - int line = change.getStartLine(getCurrentSide()); - DiffUtil.scrollEditor(editor, line, animated); + final int line1 = change.getStartLine(Side.LEFT); + final int line2 = change.getStartLine(Side.RIGHT); + final int endLine1 = change.getEndLine(Side.LEFT); + final int endLine2 = change.getEndLine(Side.RIGHT); + + DiffUtil.moveCaret(myEditor1, line1); + DiffUtil.moveCaret(myEditor2, line2); + + mySyncScrollSupport.makeVisible(getCurrentSide(), line1, endLine1, line2, endLine2, animated); } @Override diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java index 64e54c98258b..1471ad397d33 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java @@ -346,6 +346,7 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer { } private void doScrollToChange(@NotNull SimpleThreesideDiffChange change, boolean animated) { + // TODO: use anchors to fix scrolling issue at the start/end of file EditorEx editor = getCurrentEditor(); int line = change.getStartLine(getCurrentSide()); DiffUtil.scrollEditor(editor, line, animated); diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/SyncScrollSupport.java b/platform/diff-impl/src/com/intellij/diff/tools/util/SyncScrollSupport.java index 68788e3b9030..3ae8591b103e 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/SyncScrollSupport.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/SyncScrollSupport.java @@ -15,15 +15,18 @@ */ package com.intellij.diff.tools.util; +import com.intellij.diff.util.IntPair; import com.intellij.diff.util.Side; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.LogicalPosition; import com.intellij.openapi.editor.ScrollingModel; import com.intellij.openapi.editor.event.VisibleAreaEvent; import com.intellij.openapi.editor.event.VisibleAreaListener; +import com.intellij.openapi.editor.ex.EditorEx; import gnu.trove.TIntFunction; import org.jetbrains.annotations.CalledInAwt; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; @@ -82,6 +85,51 @@ public class SyncScrollSupport { public boolean isDuringSyncScroll() { return myDuringSyncScroll; } + + public void makeVisible(@NotNull Side masterSide, + int startLine1, int endLine1, int startLine2, int endLine2, + final boolean animate) { + Side slaveSide = masterSide.other(); + + final IntPair offsets = getTargetOffsets(myEditor1, myEditor2, startLine1, endLine1, startLine2, endLine2); + + final Editor masterEditor = masterSide.select(myEditor1, myEditor2); + final Editor slaveEditor = slaveSide.select(myEditor1, myEditor2); + + final int masterOffset = masterSide.select(offsets.val1, offsets.val2); + final int slaveOffset = slaveSide.select(offsets.val1, offsets.val2); + + int startOffset1 = myEditor1.getScrollingModel().getVisibleArea().y; + int startOffset2 = myEditor2.getScrollingModel().getVisibleArea().y; + final int masterStartOffset = masterSide.select(startOffset1, startOffset2); + + myHelper1.setAnchor(startOffset1, offsets.val1, startOffset2, offsets.val2); + myHelper2.setAnchor(startOffset2, offsets.val2, startOffset1, offsets.val1); + + doScrollHorizontally(masterEditor, 0, false); // animation will be canceled by "scroll vertically" anyway + doScrollVertically(masterEditor, masterOffset, animate); + + masterEditor.getScrollingModel().runActionOnScrollingFinished(new Runnable() { + @Override + public void run() { + myHelper1.removeAnchor(); + myHelper2.removeAnchor(); + + if (masterOffset == masterStartOffset) { // master editor didn't scrolled + myDuringSyncScroll = true; + + doScrollVertically(slaveEditor, slaveOffset, animate); + + slaveEditor.getScrollingModel().runActionOnScrollingFinished(new Runnable() { + @Override + public void run() { + myDuringSyncScroll = false; + } + }); + } + } + }); + } } public static class ThreesideSyncScrollSupport { @@ -157,78 +205,150 @@ public class SyncScrollSupport { private static class MyScrollHelper implements VisibleAreaListener { @NotNull private final Editor myMaster; @NotNull private final Editor mySlave; - @NotNull private final TIntFunction myConvertor; + @Nullable private Anchor myAnchor; + public MyScrollHelper(@NotNull Editor master, @NotNull Editor slave, @NotNull TIntFunction convertor) { myMaster = master; mySlave = slave; myConvertor = convertor; } + public void setAnchor(int masterStartOffset, int masterEndOffset, int slaveStartOffset, int slaveEndOffset) { + myAnchor = new Anchor(masterStartOffset, masterEndOffset, slaveStartOffset, slaveEndOffset); + } + + public void removeAnchor() { + myAnchor = null; + } + @Override public void visibleAreaChanged(VisibleAreaEvent e) { Rectangle newRectangle = e.getNewRectangle(); Rectangle oldRectangle = e.getOldRectangle(); if (oldRectangle == null) return; - syncVerticalScroll(newRectangle, oldRectangle); - syncHorizontalScroll(newRectangle, oldRectangle); + if (newRectangle.x != oldRectangle.x) syncHorizontalScroll(false); + if (newRectangle.y != oldRectangle.y) syncVerticalScroll(false); } - private void syncVerticalScroll(@NotNull Rectangle newRectangle, @NotNull Rectangle oldRectangle) { - if (newRectangle.y == oldRectangle.y) return; - + private void syncVerticalScroll(boolean animated) { if (myMaster.getDocument().getTextLength() == 0) return; - int masterVerticalScrollOffset = myMaster.getScrollingModel().getVerticalScrollOffset(); - Rectangle viewRect = myMaster.getScrollingModel().getVisibleArea(); int middleY = viewRect.height / 3; - LogicalPosition masterPos = myMaster.xyToLogicalPosition(new Point(viewRect.x, masterVerticalScrollOffset + middleY)); - int masterCenterLine = masterPos.line; - int scrollToLine = myConvertor.execute(masterCenterLine); + int offset; + if (myAnchor == null) { + LogicalPosition masterPos = myMaster.xyToLogicalPosition(new Point(viewRect.x, viewRect.y + middleY)); + int masterCenterLine = masterPos.line; + int convertedCenterLine = myConvertor.execute(masterCenterLine); - int correction = (masterVerticalScrollOffset + middleY) % myMaster.getLineHeight(); - Point point = mySlave.logicalPositionToXY(new LogicalPosition(scrollToLine, masterPos.column)); - int offset = point.y - middleY + correction; + Point point = mySlave.logicalPositionToXY(new LogicalPosition(convertedCenterLine, masterPos.column)); + int correction = (viewRect.y + middleY) % myMaster.getLineHeight(); + offset = point.y - middleY + correction; + } + else { + double progress = myAnchor.masterStartOffset == myAnchor.masterEndOffset || viewRect.y == myAnchor.masterEndOffset ? 1 : + ((double)(viewRect.y - myAnchor.masterStartOffset)) / (myAnchor.masterEndOffset - myAnchor.masterStartOffset); + + offset = myAnchor.slaveStartOffset + (int)((myAnchor.slaveEndOffset - myAnchor.slaveStartOffset) * progress); + } int deltaHeaderOffset = getHeaderOffset(mySlave) - getHeaderOffset(myMaster); - doScrollVertically(mySlave.getScrollingModel(), offset + deltaHeaderOffset); + doScrollVertically(mySlave, offset + deltaHeaderOffset, animated); } - private void syncHorizontalScroll(@NotNull Rectangle newRectangle, @NotNull Rectangle oldRectangle) { - if (newRectangle.x == oldRectangle.x) return; - - int offset = newRectangle.x; - - doScrollHorizontally(mySlave.getScrollingModel(), offset); + private void syncHorizontalScroll(boolean animated) { + int offset = myMaster.getScrollingModel().getVisibleArea().x; + doScrollHorizontally(mySlave, offset, animated); } } - private static void doScrollVertically(@NotNull ScrollingModel model, int offset) { - model.disableAnimation(); - try { - model.scrollVertically(offset); - } - finally { - model.enableAnimation(); - } + private static void doScrollVertically(@NotNull Editor editor, int offset, boolean animated) { + ScrollingModel model = editor.getScrollingModel(); + if (!animated) model.disableAnimation(); + model.scrollVertically(offset); + if (!animated) model.enableAnimation(); } - private static void doScrollHorizontally(@NotNull ScrollingModel model, int offset) { - model.disableAnimation(); - try { - model.scrollHorizontally(offset); - } - finally { - model.enableAnimation(); - } + private static void doScrollHorizontally(@NotNull Editor editor, int offset, boolean animated) { + ScrollingModel model = editor.getScrollingModel(); + if (!animated) model.disableAnimation(); + model.scrollHorizontally(offset); + if (!animated) model.enableAnimation(); } private static int getHeaderOffset(@NotNull final Editor editor) { final JComponent header = editor.getHeaderComponent(); return header == null ? 0 : header.getHeight(); } + + @NotNull + private static IntPair getTargetOffsets(@NotNull Editor editor1, @NotNull Editor editor2, + int startLine1, int endLine1, int startLine2, int endLine2) { + int topOffset1 = editor1.logicalPositionToXY(new LogicalPosition(startLine1, 0)).y; + int bottomOffset1 = editor1.logicalPositionToXY(new LogicalPosition(endLine1 + 1, 0)).y; + int topOffset2 = editor2.logicalPositionToXY(new LogicalPosition(startLine2, 0)).y; + int bottomOffset2 = editor2.logicalPositionToXY(new LogicalPosition(endLine2 + 1, 0)).y; + + int rangeHeight1 = bottomOffset1 - topOffset1; + int rangeHeight2 = bottomOffset2 - topOffset2; + + int gapLines1 = 2 * editor1.getLineHeight(); + int gapLines2 = 2 * editor2.getLineHeight(); + + int editorHeight1 = editor1.getScrollingModel().getVisibleArea().height; + int editorHeight2 = editor2.getScrollingModel().getVisibleArea().height; + + int maximumOffset1 = ((EditorEx)editor1).getScrollPane().getVerticalScrollBar().getMaximum() - editorHeight1; + int maximumOffset2 = ((EditorEx)editor1).getScrollPane().getVerticalScrollBar().getMaximum() - editorHeight2; + + // 'shift' here - distance between editor's top and first line of range + + // make whole range visible. If possible, locate it at 'center' (1/3 of height) + // If can't show whole range - show as much as we can + boolean canShow1 = 2 * gapLines1 + rangeHeight1 <= editorHeight1; + boolean canShow2 = 2 * gapLines2 + rangeHeight2 <= editorHeight2; + + int topShift1 = canShow1 ? Math.min(editorHeight1 - gapLines1 - rangeHeight1, editorHeight1 / 3) : gapLines1; + int topShift2 = canShow2 ? Math.min(editorHeight2 - gapLines2 - rangeHeight2, editorHeight2 / 3) : gapLines2; + + int topShift = Math.min(topShift1, topShift2); + + // check if we're at the top of file + topShift = Math.min(topShift, Math.min(topOffset1, topOffset2)); + + int offset1 = topOffset1 - topShift; + int offset2 = topOffset2 - topShift; + if (maximumOffset1 > offset1 && maximumOffset2 > offset2) return new IntPair(offset1, offset2); + + // One of the ranges is at end of file - we can't scroll where we want to. + topShift = Math.min(topOffset1 - maximumOffset1, topOffset2 - maximumOffset2); + + // Try to show as much of range as we can (even if it breaks alignment) + offset1 = topOffset1 - topShift + Math.max(topShift + rangeHeight1 + gapLines1 - editorHeight1, 0); + offset2 = topOffset2 - topShift + Math.max(topShift + rangeHeight2 + gapLines2 - editorHeight2, 0); + + // always show top of the range + offset1 = Math.min(offset1, topOffset1 - gapLines1); + offset2 = Math.min(offset2, topOffset2 - gapLines2); + + return new IntPair(offset1, offset2); + } + + private static class Anchor { + public final int masterStartOffset; + public final int masterEndOffset; + public final int slaveStartOffset; + public final int slaveEndOffset; + + public Anchor(int masterStartOffset, int masterEndOffset, int slaveStartOffset, int slaveEndOffset) { + this.masterStartOffset = masterStartOffset; + this.masterEndOffset = masterEndOffset; + this.slaveStartOffset = slaveStartOffset; + this.slaveEndOffset = slaveEndOffset; + } + } } diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/twoside/TwosideTextDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/util/twoside/TwosideTextDiffViewer.java index cc81f9d39742..011b4459bc02 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/twoside/TwosideTextDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/twoside/TwosideTextDiffViewer.java @@ -82,7 +82,7 @@ public abstract class TwosideTextDiffViewer extends TextDiffViewerBase { @NotNull private final MyScrollToLineHelper myScrollToLineHelper = new MyScrollToLineHelper(); - @Nullable private TwosideSyncScrollSupport mySyncScrollListener; + @Nullable protected TwosideSyncScrollSupport mySyncScrollSupport; @NotNull private Side myCurrentSide; @@ -230,7 +230,7 @@ public abstract class TwosideTextDiffViewer extends TextDiffViewerBase { if (myEditor1 != null && myEditor2 != null) { SyncScrollSupport.SyncScrollable scrollable = getSyncScrollable(); if (scrollable != null) { - mySyncScrollListener = new TwosideSyncScrollSupport(myEditor1, myEditor2, scrollable); + mySyncScrollSupport = new TwosideSyncScrollSupport(myEditor1, myEditor2, scrollable); } } } @@ -248,15 +248,15 @@ public abstract class TwosideTextDiffViewer extends TextDiffViewerBase { myEditor2.getScrollingModel().removeVisibleAreaListener(myVisibleAreaListener); } if (myEditor1 != null && myEditor2 != null) { - if (mySyncScrollListener != null) { - mySyncScrollListener = null; + if (mySyncScrollSupport != null) { + mySyncScrollSupport = null; } } } protected void disableSyncScrollSupport(boolean disable) { - if (mySyncScrollListener != null) { - mySyncScrollListener.myDuringSyncScroll = disable; + if (mySyncScrollSupport != null) { + mySyncScrollSupport.myDuringSyncScroll = disable; } } @@ -325,8 +325,8 @@ public abstract class TwosideTextDiffViewer extends TextDiffViewerBase { @CalledInAwt @NotNull protected LogicalPosition transferPosition(@NotNull Side baseSide, @NotNull LogicalPosition position) { - if (mySyncScrollListener == null) return position; - int line = mySyncScrollListener.getScrollable().transfer(baseSide, position.line); + if (mySyncScrollSupport == null) return position; + int line = mySyncScrollSupport.getScrollable().transfer(baseSide, position.line); return new LogicalPosition(line, position.column); } @@ -479,7 +479,7 @@ public abstract class TwosideTextDiffViewer extends TextDiffViewerBase { private class MyVisibleAreaListener implements VisibleAreaListener { @Override public void visibleAreaChanged(VisibleAreaEvent e) { - if (mySyncScrollListener != null) mySyncScrollListener.visibleAreaChanged(e); + if (mySyncScrollSupport != null) mySyncScrollSupport.visibleAreaChanged(e); if (Registry.is("diff.divider.repainting.fix")) { myContentPanel.repaint(); } diff --git a/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java b/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java index b2af70e18c92..d1df97e088b3 100644 --- a/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java @@ -191,6 +191,12 @@ public class DiffUtil { // Scrolling // + public static void moveCaret(@Nullable final Editor editor, int line) { + if (editor == null) return; + editor.getCaretModel().removeSecondaryCarets(); + editor.getCaretModel().moveToLogicalPosition(new LogicalPosition(line, 0)); + } + public static void scrollEditor(@Nullable final Editor editor, int line, boolean animated) { scrollEditor(editor, line, 0, animated); } @@ -203,11 +209,15 @@ public class DiffUtil { } public static void scrollToPoint(@Nullable Editor editor, @NotNull Point point) { + scrollToPoint(editor, point, false); + } + + public static void scrollToPoint(@Nullable Editor editor, @NotNull Point point, boolean animated) { if (editor == null) return; - editor.getScrollingModel().disableAnimation(); + if (!animated) editor.getScrollingModel().disableAnimation(); editor.getScrollingModel().scrollHorizontally(point.x); editor.getScrollingModel().scrollVertically(point.y); - editor.getScrollingModel().enableAnimation(); + if (!animated) editor.getScrollingModel().enableAnimation(); } public static void scrollToCaret(@Nullable Editor editor, boolean animated) { From a782fa8b072e8a790aad64e05e350370c8bb9b07 Mon Sep 17 00:00:00 2001 From: Sergey Simonchik Date: Tue, 14 Apr 2015 18:03:29 +0300 Subject: [PATCH 08/85] undeprecate StringUtil.stripQuotesAroundValue in order to allow valid odd usages, update its javadoc and add SSR --- .../src/com/intellij/openapi/util/text/StringUtil.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java index efff439b12c2..ebfc3caf7161 100644 --- a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java +++ b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java @@ -1503,14 +1503,13 @@ public class StringUtil extends StringUtilRt { } /** - * Strips quotes around the value. - * Quotes are removed even if leading and trailing quotes are different or if there is only one quote (leading or trailing). - * @deprecated use {@link com.intellij.openapi.util.text.StringUtil#unquoteString(String)} instead - * To be removed in IDEA 17 + * Consider using {@link StringUtil#unquoteString(String)} instead. + * Note: this method has an odd behavior: + * Quotes are removed even if leading and trailing quotes are different or + * if there is only one quote (leading or trailing). */ @NotNull @Contract(pure = true) - @Deprecated public static String stripQuotesAroundValue(@NotNull String text) { final int len = text.length(); if (len > 0) { From ac725fbfeca54edce41d1f6d4108cb6723910662 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Tue, 14 Apr 2015 18:12:11 +0300 Subject: [PATCH 09/85] [git] IDEA-139020 apostrophe should be escaped in MessageFormat --- plugins/git4idea/src/git4idea/i18n/GitBundle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/git4idea/src/git4idea/i18n/GitBundle.properties b/plugins/git4idea/src/git4idea/i18n/GitBundle.properties index a141fbe4cb86..7b1ad1821161 100644 --- a/plugins/git4idea/src/git4idea/i18n/GitBundle.properties +++ b/plugins/git4idea/src/git4idea/i18n/GitBundle.properties @@ -256,7 +256,7 @@ rebase.editor.button=Start Rebasing rebase.editor.comment.column=Comment rebase.editor.commit.column=Commit rebase.editor.invalid.entryset=No commits found to rebase -rebase.editor.invalid.squash=The first non-skip commit can't be marked as {0} since it merges commit with the previous commit. +rebase.editor.invalid.squash=The first non-skip commit can''t be marked as {0} since it merges commit with the previous commit. rebase.editor.message=Reorder and edit &rebased commits rebase.editor.move.down.tooltip=Move commit down (commit will be applied later) rebase.editor.move.down=Move &Down From 1f9949af28ffe0006ce7e90768ec44a672cfc1fe Mon Sep 17 00:00:00 2001 From: Sergey Ignatov Date: Tue, 14 Apr 2015 18:19:47 +0300 Subject: [PATCH 10/85] ui util.isRetina: process IllegalArgumentException as well --- platform/util/src/com/intellij/util/ui/UIUtil.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index b1d27b3c27fb..5bc953b1c891 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -314,13 +314,16 @@ public class UIUtil { } try { - isRetina = (getScaleFactorMethod == null) || ((Integer)getScaleFactorMethod.invoke(device) != 1); + isRetina = getScaleFactorMethod == null || (Integer)getScaleFactorMethod.invoke(device) != 1; } catch (IllegalAccessException e) { LOG.debug("CGraphicsDevice.getScaleFactor(): Access issue"); isRetina = false; } catch (InvocationTargetException e) { LOG.debug("CGraphicsDevice.getScaleFactor(): Invocation issue"); isRetina = false; + } catch (IllegalArgumentException e) { + LOG.debug("object is not an instance of declaring class: " + device.getClass().getName()); + isRetina = false; } devicesToRetinaSupportCacheMap.put(device, isRetina); From 8d7378ccc3c3e55b22ee9caafaa6e8b0c59c912a Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Tue, 14 Apr 2015 17:40:10 +0200 Subject: [PATCH 11/85] Fix jumping of plugins with long names --- .../com/intellij/ide/plugins/PluginManagerMain.form | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerMain.form b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerMain.form index 1f9cea154af4..bba955d00422 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerMain.form +++ b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManagerMain.form @@ -54,7 +54,10 @@ - + + + + @@ -91,7 +94,10 @@ - + + + + From 6ca140ef3962955173a86d573fbf658a482c8c10 Mon Sep 17 00:00:00 2001 From: Sergey Simonchik Date: Tue, 14 Apr 2015 18:43:36 +0300 Subject: [PATCH 12/85] SSR: StringUtil.stripQuotesAroundValue -> StringUtil.unquoteString --- .idea/inspectionProfiles/idea_default.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.idea/inspectionProfiles/idea_default.xml b/.idea/inspectionProfiles/idea_default.xml index e3a1f1109e8f..3366d229b7d4 100644 --- a/.idea/inspectionProfiles/idea_default.xml +++ b/.idea/inspectionProfiles/idea_default.xml @@ -513,6 +513,9 @@ + + + From ad899a88ace08081283f2b63d5e4bbf26a021412 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Fri, 10 Apr 2015 16:59:05 +0300 Subject: [PATCH 13/85] Add method isEmpty() in PySequenceExpression, simplify several usages of getElements() --- .../python/psi/PySequenceExpression.java | 8 +++++ .../jetbrains/python/formatter/PyBlock.java | 30 +++++++++---------- .../PyDictDuplicateKeysInspection.java | 5 ++-- .../PyReplaceTupleWithListQuickFix.java | 2 +- .../python/psi/impl/PyBaseElementImpl.java | 7 +++++ .../psi/impl/PySequenceExpressionImpl.java | 8 ++++- .../validation/AssignTargetAnnotator.java | 4 +-- 7 files changed, 42 insertions(+), 22 deletions(-) diff --git a/python/psi-api/src/com/jetbrains/python/psi/PySequenceExpression.java b/python/psi-api/src/com/jetbrains/python/psi/PySequenceExpression.java index 030c5cf560ff..0a070be196ab 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PySequenceExpression.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PySequenceExpression.java @@ -23,4 +23,12 @@ import org.jetbrains.annotations.NotNull; public interface PySequenceExpression extends PyExpression{ @NotNull PyExpression[] getElements(); + + /** + * Calling {@link #getElements()} may take too much time in case of large literals with thousands of elements. If you only need to + * know whether collection is empty, use this method instead. + * + * @return true if sequence expression contains no elements + */ + boolean isEmpty(); } diff --git a/python/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java index ae2fad7ba858..2bb0291fafb0 100644 --- a/python/src/com/jetbrains/python/formatter/PyBlock.java +++ b/python/src/com/jetbrains/python/formatter/PyBlock.java @@ -25,6 +25,7 @@ import com.intellij.psi.impl.source.tree.TreeUtil; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.ArrayUtil; import com.jetbrains.python.PyElementTypes; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.PythonDialectsTokenSetProvider; @@ -82,7 +83,7 @@ public class PyBlock implements ASTBlock { private final PyBlockContext myContext; private List mySubBlocks = null; private Alignment myChildAlignment; - private final boolean myEmptyList; + private final boolean myEmptySequence; public PyBlock(final PyBlock parent, final ASTNode node, @@ -96,7 +97,7 @@ public class PyBlock implements ASTBlock { myNode = node; myWrap = wrap; myContext = context; - myEmptyList = node.getPsi() instanceof PySequenceExpression && ((PySequenceExpression)node.getPsi()).getElements().length == 0; + myEmptySequence = isEmptySequence(node); } @NotNull @@ -133,7 +134,7 @@ public class PyBlock implements ASTBlock { final IElementType childType = child.getElementType(); - if (child.getTextRange().getLength() == 0) continue; + if (child.getTextRange().isEmpty()) continue; if (childType == TokenType.WHITE_SPACE) { continue; @@ -160,7 +161,7 @@ public class PyBlock implements ASTBlock { while (p != null) { final ASTNode pNode = p.getNode(); if (ourListElementTypes.contains(pNode.getElementType())) { - if (needListAlignment(child) && !myEmptyList) { + if (needListAlignment(child) && !myEmptySequence) { childAlignment = p.getChildAlignment(); break; @@ -198,7 +199,7 @@ public class PyBlock implements ASTBlock { !isSliceOperand(child) /*&& !isSubscriptionOperand(child)*/) { wrap = Wrap.createWrap(WrapType.NORMAL, true); } - if (needListAlignment(child) && !myEmptyList) { + if (needListAlignment(child) && !myEmptySequence) { childAlignment = getAlignmentForChildren(); } if (childType == PyTokenTypes.END_OF_LINE_COMMENT) { @@ -349,6 +350,10 @@ public class PyBlock implements ASTBlock { return new PyBlock(this, child, childAlignment, childIndent, wrap, myContext); } + private static boolean isEmptySequence(@NotNull ASTNode node) { + return node.getPsi() instanceof PySequenceExpression && ((PySequenceExpression)node.getPsi()).isEmpty(); + } + private boolean argumentMayHaveSameIndentAsFollowingStatementList() { // This check is supposed to prevent PEP8's error: Continuation line with the same indent as next logical line final PsiElement header = getControlStatementHeader(myNode); @@ -499,11 +504,8 @@ public class PyBlock implements ASTBlock { if (PyTokenTypes.OPEN_BRACES.contains(childType)) { return false; } - if (PyTokenTypes.OPEN_BRACES.contains(firstGrandchildType)) { - final PsiElement psi = child.getPsi(); - if (psi instanceof PySequenceExpression && ((PySequenceExpression)psi).getElements().length == 0) { - return false; - } + if (PyTokenTypes.OPEN_BRACES.contains(firstGrandchildType) && isEmptySequence(child)) { + return false; } if (PyTokenTypes.CLOSE_BRACES.contains(childType)) { final ASTNode prevNonSpace = findPrevNonSpaceNode(child); @@ -757,12 +759,8 @@ public class PyBlock implements ASTBlock { return null; } if (myNode.getPsi() instanceof PyDictLiteralExpression) { - final PyKeyValueExpression[] elements = ((PyDictLiteralExpression)myNode.getPsi()).getElements(); - if (elements.length == 0) { - return null; - } - final PyKeyValueExpression last = elements[elements.length - 1]; - if (last.getValue() == null) { // incomplete + final PyKeyValueExpression lastElement = ArrayUtil.getLastElement(((PyDictLiteralExpression)myNode.getPsi()).getElements()); + if (lastElement == null || lastElement.getValue() == null /* incomplete */) { return null; } } diff --git a/python/src/com/jetbrains/python/inspections/PyDictDuplicateKeysInspection.java b/python/src/com/jetbrains/python/inspections/PyDictDuplicateKeysInspection.java index 571e4f6d5fcf..bb913ad16b56 100644 --- a/python/src/com/jetbrains/python/inspections/PyDictDuplicateKeysInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyDictDuplicateKeysInspection.java @@ -58,9 +58,10 @@ public class PyDictDuplicateKeysInspection extends PyInspection { @Override public void visitPyDictLiteralExpression(PyDictLiteralExpression node) { - if (node.getElements().length != 0){ + final PyKeyValueExpression[] elements = node.getElements(); + if (elements.length != 0){ final Map map = new HashMap(); - for (PyExpression exp : node.getElements()) { + for (PyExpression exp : elements) { final PyExpression key = ((PyKeyValueExpression)exp).getKey(); if (key instanceof PyNumericLiteralExpression || key instanceof PyStringLiteralExpression || key instanceof PyReferenceExpression) { diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyReplaceTupleWithListQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyReplaceTupleWithListQuickFix.java index e786dcec1a31..d480d339bb36 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/PyReplaceTupleWithListQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyReplaceTupleWithListQuickFix.java @@ -59,7 +59,7 @@ public class PyReplaceTupleWithListQuickFix implements LocalQuickFix { } private static void replaceWithListLiteral(PsiElement element, PyTupleExpression expression) { - final String expressionText = expression.getElements().length == 0 ? "" :expression.getText(); + final String expressionText = expression.isEmpty() ? "" :expression.getText(); final PyExpression literal = PyElementGenerator.getInstance(element.getProject()). createExpressionFromText(LanguageLevel.forElement(element), "[" + expressionText + "]"); diff --git a/python/src/com/jetbrains/python/psi/impl/PyBaseElementImpl.java b/python/src/com/jetbrains/python/psi/impl/PyBaseElementImpl.java index 72a2a64528b3..cb155cebd8f1 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyBaseElementImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyBaseElementImpl.java @@ -110,6 +110,13 @@ public class PyBaseElementImpl extends StubBasedPsiElemen return (T)node.getPsi(); } + @Nullable + protected T childToPsi(@NotNull TokenSet elTypes) { + final ASTNode node = getNode().findChildByType(elTypes); + //noinspection unchecked + return node != null ? (T)node.getPsi() : null; + } + @NotNull protected T childToPsiNotNull(TokenSet filterSet, int index) { final PyElement child = childToPsi(filterSet, index); diff --git a/python/src/com/jetbrains/python/psi/impl/PySequenceExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PySequenceExpressionImpl.java index 1eb6ea0f4cb6..b76c73e24474 100644 --- a/python/src/com/jetbrains/python/psi/impl/PySequenceExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PySequenceExpressionImpl.java @@ -4,12 +4,13 @@ import com.intellij.lang.ASTNode; import com.intellij.util.ArrayUtil; import com.jetbrains.python.PythonDialectsTokenSetProvider; import com.jetbrains.python.psi.PyExpression; +import com.jetbrains.python.psi.PySequenceExpression; import org.jetbrains.annotations.NotNull; /** * @author Mikhail Golubev */ -public class PySequenceExpressionImpl extends PyElementImpl { +public abstract class PySequenceExpressionImpl extends PyElementImpl implements PySequenceExpression { public PySequenceExpressionImpl(ASTNode astNode) { super(astNode); } @@ -26,4 +27,9 @@ public class PySequenceExpressionImpl extends PyElementImpl { public PyExpression[] getElements() { return childrenToPsi(PythonDialectsTokenSetProvider.INSTANCE.getExpressionTokens(), PyExpression.EMPTY_ARRAY); } + + @Override + public boolean isEmpty() { + return childToPsi(PythonDialectsTokenSetProvider.INSTANCE.getExpressionTokens()) == null; + } } diff --git a/python/src/com/jetbrains/python/validation/AssignTargetAnnotator.java b/python/src/com/jetbrains/python/validation/AssignTargetAnnotator.java index a648f37b8ec1..a55011e37693 100644 --- a/python/src/com/jetbrains/python/validation/AssignTargetAnnotator.java +++ b/python/src/com/jetbrains/python/validation/AssignTargetAnnotator.java @@ -130,7 +130,7 @@ public class AssignTargetAnnotator extends PyAnnotator { @Override public void visitPyTupleExpression(final PyTupleExpression node) { - if (node.getElements().length == 0) { + if (node.isEmpty()) { getHolder().createErrorAnnotation(node, message("ANN.cant.assign.to.parens")); } else if (myOp == Operation.AugAssign) { @@ -153,7 +153,7 @@ public class AssignTargetAnnotator extends PyAnnotator { @Override public void visitPyListLiteralExpression(final PyListLiteralExpression node) { - if (node.getElements().length == 0) { + if (node.isEmpty()) { getHolder().createErrorAnnotation(node, message("ANN.cant.assign.to.brackets")); } else if (myOp == Operation.AugAssign) { From 0cc893db95de1082b64c91ac3b6b0c9f74790a50 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Mon, 13 Apr 2015 19:24:16 +0300 Subject: [PATCH 14/85] IDEA-138792 Use wrapFirstElement=false property for wrapping in JSON formatter For some reason the opposite value of this property leads to excess calls of CoreFormatterUtil#getStartColumn(block) (via isSuitableInCurrentPosition(wrap)) which is very heavy in case of single long line of minified JSON. Also add "none" wrap before commas just in case. --- .../src/com/intellij/json/formatter/JsonBlock.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/json/src/com/intellij/json/formatter/JsonBlock.java b/json/src/com/intellij/json/formatter/JsonBlock.java index c503c0df2290..b57a366de1cf 100644 --- a/json/src/com/intellij/json/formatter/JsonBlock.java +++ b/json/src/com/intellij/json/formatter/JsonBlock.java @@ -12,6 +12,7 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.TokenType; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import com.intellij.psi.formatter.FormatterUtil; import com.intellij.psi.tree.TokenSet; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; @@ -66,10 +67,10 @@ public class JsonBlock implements ASTBlock { mySpacingBuilder = JsonFormattingBuilderModel.createSpacingBuilder(settings); if (myPsiElement instanceof JsonObject) { - myChildWrap = Wrap.createWrap(getCustomSettings().OBJECT_WRAPPING, false); + myChildWrap = Wrap.createWrap(getCustomSettings().OBJECT_WRAPPING, true); } else if (myPsiElement instanceof JsonArray) { - myChildWrap = Wrap.createWrap(getCustomSettings().ARRAY_WRAPPING, false); + myChildWrap = Wrap.createWrap(getCustomSettings().ARRAY_WRAPPING, true); } else { myChildWrap = null; @@ -113,9 +114,14 @@ public class JsonBlock implements ASTBlock { final JsonCodeStyleSettings customSettings = getCustomSettings(); if (hasElementType(myNode, JSON_CONTAINERS)) { - if (!hasElementType(childNode, COMMA) && !hasElementType(childNode, JSON_ALL_BRACES)) { + if (hasElementType(childNode, COMMA)) { + wrap = Wrap.createWrap(WrapType.NONE, true); + } + else if (!hasElementType(childNode, JSON_ALL_BRACES)) { assert myChildWrap != null; - wrap = myChildWrap; + if (!FormatterUtil.isPrecededBy(childNode, JSON_OPEN_BRACES)) { + wrap = myChildWrap; + } indent = Indent.getNormalIndent(); } else if (hasElementType(childNode, JSON_OPEN_BRACES)) { From 53594b20049a39d34c0e304d4d118fd545d49b70 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Tue, 14 Apr 2015 15:48:19 +0300 Subject: [PATCH 15/85] IDEA-138902 Do not omit wrapping for the first item in JSON arrays in objects --- .../codeinsight/JsonStandardComplianceInspection.java | 5 ++++- json/src/com/intellij/json/formatter/JsonBlock.java | 5 +---- .../test/com/intellij/json/JsonFormattingTest.java | 10 ++++++++++ .../testData/formatting/ObjectsWithSingleProperty.json | 1 + .../formatting/ObjectsWithSingleProperty_after.json | 7 +++++++ json/tests/testData/formatting/Wrapping_after.json | 4 +++- json/tests/testData/surround/SingleValue_after.json | 4 +++- 7 files changed, 29 insertions(+), 7 deletions(-) create mode 100644 json/tests/testData/formatting/ObjectsWithSingleProperty.json create mode 100644 json/tests/testData/formatting/ObjectsWithSingleProperty_after.json diff --git a/json/src/com/intellij/json/codeinsight/JsonStandardComplianceInspection.java b/json/src/com/intellij/json/codeinsight/JsonStandardComplianceInspection.java index 58dab5945dfc..ab31e67431fa 100644 --- a/json/src/com/intellij/json/codeinsight/JsonStandardComplianceInspection.java +++ b/json/src/com/intellij/json/codeinsight/JsonStandardComplianceInspection.java @@ -6,6 +6,7 @@ import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel; import com.intellij.json.JsonBundle; import com.intellij.json.JsonElementTypes; import com.intellij.json.psi.*; +import com.intellij.lang.ASTNode; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; @@ -141,7 +142,9 @@ public class JsonStandardComplianceInspection extends LocalInspectionTool { final PsiElement element = descriptor.getPsiElement(); if (element instanceof JsonLiteral || element instanceof JsonReferenceExpression) { final String content = StringUtil.stripQuotesAroundValue(element.getText()); - element.replace(new JsonElementGenerator(project).createStringLiteral(content)); + // TODO: find out better way to replace element and skip reformatting step afterwards + final ASTNode replacement = new JsonElementGenerator(project).createStringLiteral(content).getNode(); + element.getParent().getNode().replaceChild(element.getNode(), replacement); } else if (element != null) { LOG.error("Quick fix was applied to unexpected element", element.getText(), element.getParent().getText()); diff --git a/json/src/com/intellij/json/formatter/JsonBlock.java b/json/src/com/intellij/json/formatter/JsonBlock.java index b57a366de1cf..174e1607f010 100644 --- a/json/src/com/intellij/json/formatter/JsonBlock.java +++ b/json/src/com/intellij/json/formatter/JsonBlock.java @@ -12,7 +12,6 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.TokenType; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; -import com.intellij.psi.formatter.FormatterUtil; import com.intellij.psi.tree.TokenSet; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; @@ -119,9 +118,7 @@ public class JsonBlock implements ASTBlock { } else if (!hasElementType(childNode, JSON_ALL_BRACES)) { assert myChildWrap != null; - if (!FormatterUtil.isPrecededBy(childNode, JSON_OPEN_BRACES)) { - wrap = myChildWrap; - } + wrap = myChildWrap; indent = Indent.getNormalIndent(); } else if (hasElementType(childNode, JSON_OPEN_BRACES)) { diff --git a/json/tests/test/com/intellij/json/JsonFormattingTest.java b/json/tests/test/com/intellij/json/JsonFormattingTest.java index ff2b8a915eaf..f8d8d55f0691 100644 --- a/json/tests/test/com/intellij/json/JsonFormattingTest.java +++ b/json/tests/test/com/intellij/json/JsonFormattingTest.java @@ -101,6 +101,16 @@ public class JsonFormattingTest extends FormatterTestCase { }); } + // IDEA-138902 + public void testObjectsWithSingleProperty() throws Exception { + withPreservedSettings(new ThrowableRunnable() { + @Override + public void run() throws Exception { + doTest(); + } + }); + } + // Moved from JavaScript public void testWeb3830() throws Exception { diff --git a/json/tests/testData/formatting/ObjectsWithSingleProperty.json b/json/tests/testData/formatting/ObjectsWithSingleProperty.json new file mode 100644 index 000000000000..a75a8d3af061 --- /dev/null +++ b/json/tests/testData/formatting/ObjectsWithSingleProperty.json @@ -0,0 +1 @@ +{"level0": {"level1": {"level2": "someValue"}}} \ No newline at end of file diff --git a/json/tests/testData/formatting/ObjectsWithSingleProperty_after.json b/json/tests/testData/formatting/ObjectsWithSingleProperty_after.json new file mode 100644 index 000000000000..fe42890beca0 --- /dev/null +++ b/json/tests/testData/formatting/ObjectsWithSingleProperty_after.json @@ -0,0 +1,7 @@ +{ + "level0": { + "level1": { + "level2": "someValue" + } + } +} \ No newline at end of file diff --git a/json/tests/testData/formatting/Wrapping_after.json b/json/tests/testData/formatting/Wrapping_after.json index f6f41b61f43b..e53138a182de 100644 --- a/json/tests/testData/formatting/Wrapping_after.json +++ b/json/tests/testData/formatting/Wrapping_after.json @@ -8,5 +8,7 @@ 5, 6, 7, - ["this line is long too"] + [ + "this line is long too" + ] ] \ No newline at end of file diff --git a/json/tests/testData/surround/SingleValue_after.json b/json/tests/testData/surround/SingleValue_after.json index 440c9ce72ee7..876ab8e46e66 100644 --- a/json/tests/testData/surround/SingleValue_after.json +++ b/json/tests/testData/surround/SingleValue_after.json @@ -1,3 +1,5 @@ { - "property": [null] + "property": [ + null + ] } \ No newline at end of file From 60995318020d9b8764138ba8abff59355c71847a Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Tue, 14 Apr 2015 16:47:53 +0300 Subject: [PATCH 16/85] Get rid of deprecated StringUtil#stripQuotesAroundValue in favor of more robust JsonPsiUtil#stripQuotes --- .../codeinsight/JsonLiteralAnnotator.java | 10 +---- .../JsonStandardComplianceInspection.java | 5 +-- .../com/intellij/json/psi/JsonPsiUtil.java | 42 +++++++++++++++++++ .../json/psi/impl/JsonPsiImplUtils.java | 4 +- .../com/intellij/json/JsonQuickFixTest.java | 1 + 5 files changed, 48 insertions(+), 14 deletions(-) diff --git a/json/src/com/intellij/json/codeinsight/JsonLiteralAnnotator.java b/json/src/com/intellij/json/codeinsight/JsonLiteralAnnotator.java index 2854c8a7132e..c468c0767697 100644 --- a/json/src/com/intellij/json/codeinsight/JsonLiteralAnnotator.java +++ b/json/src/com/intellij/json/codeinsight/JsonLiteralAnnotator.java @@ -54,7 +54,7 @@ public class JsonLiteralAnnotator implements Annotator { final int length = text.length(); // Check that string literal is closed properly - if (length <= 1 || text.charAt(0) != text.charAt(length - 1) || quoteEscaped(text, length - 1)) { + if (length <= 1 || text.charAt(0) != text.charAt(length - 1) || JsonPsiUtil.isEscapedChar(text, length - 1)) { holder.createErrorAnnotation(element, JsonBundle.message("msg.missing.closing.quote")); } @@ -79,12 +79,4 @@ public class JsonLiteralAnnotator implements Annotator { } } } - - private static boolean quoteEscaped(String text, int quotePos) { - int count = 0; - for (int i = quotePos - 1; i >= 0 && text.charAt(i) == '\\'; i--) { - count++; - } - return count % 2 != 0; - } } diff --git a/json/src/com/intellij/json/codeinsight/JsonStandardComplianceInspection.java b/json/src/com/intellij/json/codeinsight/JsonStandardComplianceInspection.java index ab31e67431fa..20fa1e7b0610 100644 --- a/json/src/com/intellij/json/codeinsight/JsonStandardComplianceInspection.java +++ b/json/src/com/intellij/json/codeinsight/JsonStandardComplianceInspection.java @@ -9,7 +9,6 @@ import com.intellij.json.psi.*; import com.intellij.lang.ASTNode; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiComment; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; @@ -141,9 +140,9 @@ public class JsonStandardComplianceInspection extends LocalInspectionTool { public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { final PsiElement element = descriptor.getPsiElement(); if (element instanceof JsonLiteral || element instanceof JsonReferenceExpression) { - final String content = StringUtil.stripQuotesAroundValue(element.getText()); + final String content = JsonPsiUtil.stripQuotes(element.getText()); // TODO: find out better way to replace element and skip reformatting step afterwards - final ASTNode replacement = new JsonElementGenerator(project).createStringLiteral(content).getNode(); + final ASTNode replacement = new JsonElementGenerator(project).createValue("\"" + content + "\"").getNode(); element.getParent().getNode().replaceChild(element.getNode(), replacement); } else if (element != null) { diff --git a/json/src/com/intellij/json/psi/JsonPsiUtil.java b/json/src/com/intellij/json/psi/JsonPsiUtil.java index 66f785c0e76b..80105014aa39 100644 --- a/json/src/com/intellij/json/psi/JsonPsiUtil.java +++ b/json/src/com/intellij/json/psi/JsonPsiUtil.java @@ -137,4 +137,46 @@ public class JsonPsiUtil { return element.getText(); } } + + /** + * Returns content of the string literal (without escaping) striving to preserve as much of user data as possible. + *
    + *
  • If literal length is greater than one and it starts and ends with the same quote and the last quote is not escaped, returns + * text without first and last characters.
  • + *
  • Otherwise if literal still begins with a quote, returns text without first character only.
  • + *
  • Returns unmodified text in all other cases.
  • + *
+ * + * @param text presumably result of {@link JsonStringLiteral#getText()} + * @return + */ + @NotNull + public static String stripQuotes(@NotNull String text) { + if (text.length() > 0) { + final char firstChar = text.charAt(0); + final char lastChar = text.charAt(text.length() - 1); + if (firstChar == '\'' || firstChar == '"') { + if (text.length() > 1 && firstChar == lastChar && !isEscapedChar(text, text.length() - 1)) { + return text.substring(1, text.length() - 1); + } + return text.substring(1); + } + } + return text; + } + + /** + * Checks that character in given position is escaped with backslashes. + * + * @param text text character belongs to + * @param position position of the character + * @return whether character at given position is escaped, i.e. preceded by odd number of backslashes + */ + public static boolean isEscapedChar(@NotNull String text, int position) { + int count = 0; + for (int i = position - 1; i >= 0 && text.charAt(i) == '\\'; i--) { + count++; + } + return count % 2 != 0; + } } diff --git a/json/src/com/intellij/json/psi/impl/JsonPsiImplUtils.java b/json/src/com/intellij/json/psi/impl/JsonPsiImplUtils.java index 59a112103398..d3814dc6c498 100644 --- a/json/src/com/intellij/json/psi/impl/JsonPsiImplUtils.java +++ b/json/src/com/intellij/json/psi/impl/JsonPsiImplUtils.java @@ -28,7 +28,7 @@ public class JsonPsiImplUtils { @NotNull public static String getName(@NotNull JsonProperty property) { - return StringUtil.stripQuotesAroundValue(property.getNameElement().getText()); + return StringUtil.unescapeStringCharacters(JsonPsiUtil.stripQuotes(property.getNameElement().getText())); } /** @@ -210,7 +210,7 @@ public class JsonPsiImplUtils { @NotNull public static String getValue(@NotNull JsonStringLiteral literal) { - return StringUtil.unescapeStringCharacters(StringUtil.unquoteString(literal.getText())); + return StringUtil.unescapeStringCharacters(JsonPsiUtil.stripQuotes(literal.getText())); } public static boolean getValue(@NotNull JsonBooleanLiteral literal) { diff --git a/json/tests/test/com/intellij/json/JsonQuickFixTest.java b/json/tests/test/com/intellij/json/JsonQuickFixTest.java index 8a80967b4da7..f66d189aba84 100644 --- a/json/tests/test/com/intellij/json/JsonQuickFixTest.java +++ b/json/tests/test/com/intellij/json/JsonQuickFixTest.java @@ -27,6 +27,7 @@ public class JsonQuickFixTest extends JsonTestCase { checkWrapInDoubleQuotes("{42: false}", "{\"42\": false}"); checkWrapInDoubleQuotes("{foo: false}", "{\"foo\": false}"); checkWrapInDoubleQuotes("{'foo': false}", "{\"foo\": false}"); + checkWrapInDoubleQuotes("'foo\\\"", "\"foo\\\"\""); checkWrapInDoubleQuotes("{\"foo\": bar}", "{\"foo\": \"bar\"}"); checkWrapInDoubleQuotes("{\"foo\": 'bar'}", "{\"foo\": \"bar\"}"); } From 1c046432d450f1b3a68a9655a8bd6983d7cce2ff Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Tue, 14 Apr 2015 18:22:19 +0300 Subject: [PATCH 17/85] Quickfix "Wrap with double quotes" properly handles single and double quotes inside JSON string --- .../JsonStandardComplianceInspection.java | 31 +++++++++++++++++-- .../com/intellij/json/JsonQuickFixTest.java | 1 + 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/json/src/com/intellij/json/codeinsight/JsonStandardComplianceInspection.java b/json/src/com/intellij/json/codeinsight/JsonStandardComplianceInspection.java index 20fa1e7b0610..b4f7ffc2374d 100644 --- a/json/src/com/intellij/json/codeinsight/JsonStandardComplianceInspection.java +++ b/json/src/com/intellij/json/codeinsight/JsonStandardComplianceInspection.java @@ -139,15 +139,42 @@ public class JsonStandardComplianceInspection extends LocalInspectionTool { @Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { final PsiElement element = descriptor.getPsiElement(); + final String rawText = element.getText(); if (element instanceof JsonLiteral || element instanceof JsonReferenceExpression) { - final String content = JsonPsiUtil.stripQuotes(element.getText()); + String content = JsonPsiUtil.stripQuotes(rawText); + if (element instanceof JsonStringLiteral && rawText.startsWith("'")) { + content = escapeSingleQuotedStringContent(content); + } // TODO: find out better way to replace element and skip reformatting step afterwards final ASTNode replacement = new JsonElementGenerator(project).createValue("\"" + content + "\"").getNode(); element.getParent().getNode().replaceChild(element.getNode(), replacement); } else if (element != null) { - LOG.error("Quick fix was applied to unexpected element", element.getText(), element.getParent().getText()); + LOG.error("Quick fix was applied to unexpected element", rawText, element.getParent().getText()); } } + + @NotNull + private static String escapeSingleQuotedStringContent(@NotNull String content) { + final StringBuilder result = new StringBuilder(); + boolean nextCharEscaped = false; + for (int i = 0; i < content.length(); i++) { + final char c = content.charAt(i); + if ((nextCharEscaped && c != '\'') || (!nextCharEscaped && c == '"')) { + result.append('\\'); + } + if (c != '\\' || nextCharEscaped) { + result.append(c); + nextCharEscaped = false; + } + else { + nextCharEscaped = true; + } + } + if (nextCharEscaped) { + result.append('\\'); + } + return result.toString(); + } } } diff --git a/json/tests/test/com/intellij/json/JsonQuickFixTest.java b/json/tests/test/com/intellij/json/JsonQuickFixTest.java index f66d189aba84..92d4cc4bb175 100644 --- a/json/tests/test/com/intellij/json/JsonQuickFixTest.java +++ b/json/tests/test/com/intellij/json/JsonQuickFixTest.java @@ -30,6 +30,7 @@ public class JsonQuickFixTest extends JsonTestCase { checkWrapInDoubleQuotes("'foo\\\"", "\"foo\\\"\""); checkWrapInDoubleQuotes("{\"foo\": bar}", "{\"foo\": \"bar\"}"); checkWrapInDoubleQuotes("{\"foo\": 'bar'}", "{\"foo\": \"bar\"}"); + checkWrapInDoubleQuotes("'foo\\n\\'\"\\\\\\\"bar", "\"foo\\n'\\\"\\\\\\\"bar\""); } private void checkWrapInDoubleQuotes(@NotNull String before, @NotNull String after) { From 6583698e7aa765fabcc4a7bdb6e9b09a66bb5d46 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Tue, 14 Apr 2015 16:28:09 +0200 Subject: [PATCH 18/85] =?UTF-8?q?simplify=20=E2=80=94=20get=20rid=20of=20"?= =?UTF-8?q?Show=20functions"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/jetbrains/debugger/MemberFilter.java | 17 +++++++++++++- .../jetbrains/debugger/MemberFilterBase.java | 17 +++++++++++++- .../org/jetbrains/debugger/VariableView.java | 2 +- .../src/org/jetbrains/debugger/Variables.java | 23 +++++++++++++++---- 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/MemberFilter.java b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/MemberFilter.java index 7d39fb8ca451..b25d555d465c 100644 --- a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/MemberFilter.java +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/MemberFilter.java @@ -1,3 +1,18 @@ +/* + * Copyright 2000-2015 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 org.jetbrains.debugger; import org.jetbrains.annotations.NotNull; @@ -6,7 +21,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Collection; public interface MemberFilter { - boolean isMemberVisible(@NotNull Variable variable, boolean filterFunctions); + boolean isMemberVisible(@NotNull Variable variable); @NotNull Collection getAdditionalVariables(); diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/MemberFilterBase.java b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/MemberFilterBase.java index 414b169b4679..27ca585710ab 100644 --- a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/MemberFilterBase.java +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/MemberFilterBase.java @@ -1,3 +1,18 @@ +/* + * Copyright 2000-2015 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 org.jetbrains.debugger; import org.jetbrains.annotations.NotNull; @@ -8,7 +23,7 @@ import java.util.Collections; public abstract class MemberFilterBase implements MemberFilter { @Override - public boolean isMemberVisible(@NotNull Variable variable, boolean filterFunctions) { + public boolean isMemberVisible(@NotNull Variable variable) { return variable.isReadable(); } diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/VariableView.java b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/VariableView.java index 8e3bf4b8c93c..e6462e61cce5 100644 --- a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/VariableView.java +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/VariableView.java @@ -431,7 +431,7 @@ public final class VariableView extends XNamedValue implements VariableContext { } private void computeArrayRanges(@NotNull List properties, @NotNull XCompositeNode node) { - final List variables = Variables.filterAndSort(properties, memberFilter, false); + final List variables = Variables.filterAndSort(properties, memberFilter); int count = variables.size(); int bucketSize = XCompositeNode.MAX_CHILDREN_TO_SHOW; if (count <= bucketSize) { diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/Variables.java b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/Variables.java index 9d3fbc5724ac..ad38b972d3b6 100644 --- a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/Variables.java +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/Variables.java @@ -1,3 +1,18 @@ +/* + * Copyright 2000-2015 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 org.jetbrains.debugger; import com.intellij.openapi.util.text.StringUtil; @@ -65,7 +80,7 @@ public final class Variables { List properties = new ArrayList(variables.size() + additionalVariables.size()); List functions = new SmartList(); for (Variable variable : variables) { - if (memberFilter.isMemberVisible(variable, false)) { + if (memberFilter.isMemberVisible(variable)) { Value value = variable.getValue(); if (value != null && value.getType() == ValueType.FUNCTION && @@ -112,7 +127,7 @@ public final class Variables { @NotNull MemberFilter memberFilter, int maxChildrenToAdd, boolean defaultIsLast) { - List list = filterAndSort(variables, memberFilter, true); + List list = filterAndSort(variables, memberFilter); if (list.isEmpty()) { if (defaultIsLast) { node.addChildren(XValueChildrenList.EMPTY, true); @@ -133,7 +148,7 @@ public final class Variables { } @NotNull - public static List filterAndSort(@NotNull List variables, @NotNull MemberFilter memberFilter, boolean filterFunctions) { + public static List filterAndSort(@NotNull List variables, @NotNull MemberFilter memberFilter) { if (variables.isEmpty()) { return Collections.emptyList(); } @@ -141,7 +156,7 @@ public final class Variables { Collection additionalVariables = memberFilter.getAdditionalVariables(); List result = new ArrayList(variables.size() + additionalVariables.size()); for (Variable variable : variables) { - if (memberFilter.isMemberVisible(variable, filterFunctions)) { + if (memberFilter.isMemberVisible(variable)) { result.add(variable); } } From f8c4b4188366613ef817ecc306cfad0350676346 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Tue, 14 Apr 2015 17:42:36 +0200 Subject: [PATCH 19/85] WEB-15467 copy / paste debugger watch values as JSON string --- .../tree/actions/XFetchValueActionBase.java | 77 +++++++++++-------- 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/actions/XFetchValueActionBase.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/actions/XFetchValueActionBase.java index f629904d14cc..b663348762a1 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/actions/XFetchValueActionBase.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/actions/XFetchValueActionBase.java @@ -48,14 +48,7 @@ public abstract class XFetchValueActionBase extends AnAction { if (paths != null) { for (TreePath path : paths) { Object node = path.getLastPathComponent(); - if (node instanceof XValueNodeImpl) { - if (((XValueNodeImpl)node).isComputed()) { - e.getPresentation().setEnabled(true); - return; - } - } - else if (node instanceof WatchMessageNode) { - e.getPresentation().setEnabled(true); + if (isEnabled(e, node)) { return; } } @@ -63,8 +56,22 @@ public abstract class XFetchValueActionBase extends AnAction { e.getPresentation().setEnabled(false); } + protected boolean isEnabled(@NotNull AnActionEvent event, @NotNull Object node) { + if (node instanceof XValueNodeImpl) { + if (((XValueNodeImpl)node).isComputed()) { + event.getPresentation().setEnabled(true); + return true; + } + } + else if (node instanceof WatchMessageNode) { + event.getPresentation().setEnabled(true); + return true; + } + return false; + } + @Override - public void actionPerformed(@NotNull final AnActionEvent e) { + public void actionPerformed(@NotNull AnActionEvent e) { TreePath[] paths = getSelectedNodes(e.getDataContext()); if (paths == null) { return; @@ -72,28 +79,31 @@ public abstract class XFetchValueActionBase extends AnAction { ValueCollector valueCollector = createCollector(e); for (TreePath path : paths) { - Object node = path.getLastPathComponent(); - if (node instanceof XValueNodeImpl) { - XValueNodeImpl valueNode = (XValueNodeImpl)node; - XFullValueEvaluator fullValueEvaluator = valueNode.getFullValueEvaluator(); - if (paths.length > 1) { // multiselection - copy the whole node text, see IDEA-136722 - valueCollector.add(valueNode.getText().toString(), valueNode.getPath().getPathCount()); - } - else { - if (fullValueEvaluator == null || !fullValueEvaluator.isShowValuePopup()) { - valueCollector.add(StringUtil.notNullize(DebuggerUIUtil.getNodeRawValue(valueNode))); - } - else { - new CopyValueEvaluationCallback(valueNode, valueCollector).startFetchingValue(fullValueEvaluator); - } - } - } - else if (node instanceof WatchMessageNode) { - valueCollector.add(((WatchMessageNode)node).getExpression().getExpression()); - } + addToCollector(paths, path.getLastPathComponent(), valueCollector); } valueCollector.processed = true; - valueCollector.finish(e.getProject()); + valueCollector.finish(); + } + + protected void addToCollector(@NotNull TreePath[] paths, @NotNull Object node, @NotNull ValueCollector valueCollector) { + if (node instanceof XValueNodeImpl) { + XValueNodeImpl valueNode = (XValueNodeImpl)node; + XFullValueEvaluator fullValueEvaluator = valueNode.getFullValueEvaluator(); + if (paths.length > 1) { // multiselection - copy the whole node text, see IDEA-136722 + valueCollector.add(valueNode.getText().toString(), valueNode.getPath().getPathCount()); + } + else { + if (fullValueEvaluator == null || !fullValueEvaluator.isShowValuePopup()) { + valueCollector.add(StringUtil.notNullize(DebuggerUIUtil.getNodeRawValue(valueNode))); + } + else { + new CopyValueEvaluationCallback(valueNode, valueCollector).startFetchingValue(fullValueEvaluator); + } + } + } + else if (node instanceof WatchMessageNode) { + valueCollector.add(((WatchMessageNode)node).getExpression().getExpression()); + } } @NotNull @@ -120,7 +130,8 @@ public abstract class XFetchValueActionBase extends AnAction { indents.put(values.size() - 1, indent); } - public void finish(Project project) { + public void finish() { + Project project = myTree.getProject(); if (processed && !values.contains(null) && !project.isDisposed()) { int minIndent = Integer.MAX_VALUE; for (int indent : indents.getValues()) { @@ -151,12 +162,12 @@ public abstract class XFetchValueActionBase extends AnAction { return index; } - public void evaluationComplete(final int index, @NotNull final String value, final Project project) { + public void evaluationComplete(final int index, @NotNull final String value) { AppUIUtil.invokeOnEdt(new Runnable() { @Override public void run() { values.set(index, value); - finish(project); + finish(); } }); } @@ -177,7 +188,7 @@ public abstract class XFetchValueActionBase extends AnAction { @Override protected void evaluationComplete(@NotNull String value, @NotNull Project project) { - myValueCollector.evaluationComplete(myValueIndex, value, project); + myValueCollector.evaluationComplete(myValueIndex, value); } } } From f756f4456dfae1706641c30520d277f3685b1e03 Mon Sep 17 00:00:00 2001 From: nik Date: Tue, 14 Apr 2015 18:57:02 +0300 Subject: [PATCH 20/85] include real produce code to 'api-version' to ensure that correct code is passed by Android Studio to plugin repository and exception reporter --- .../intellij/openapi/application/impl/ApplicationInfoImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/core-impl/src/com/intellij/openapi/application/impl/ApplicationInfoImpl.java b/platform/core-impl/src/com/intellij/openapi/application/impl/ApplicationInfoImpl.java index 90c243c6a947..b9a0330c90e3 100644 --- a/platform/core-impl/src/com/intellij/openapi/application/impl/ApplicationInfoImpl.java +++ b/platform/core-impl/src/com/intellij/openapi/application/impl/ApplicationInfoImpl.java @@ -210,7 +210,7 @@ public class ApplicationInfoImpl extends ApplicationInfoEx implements JDOMExtern @Override public String getApiVersion() { if (myApiVersion != null) { - return BuildNumber.fromString(myApiVersion, getProductPrefix()).asString(); + return BuildNumber.fromString(myApiVersion, getBuild().getProductCode()).asString(); } return getBuild().asString(); } From 6c780e752f3241a5158d6f4fe5489993e1dfc600 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Tue, 14 Apr 2015 17:39:01 +0200 Subject: [PATCH 21/85] testng sm runner: include param names in tree view presentation --- .../src/org/testng/IDEARemoteTestNG.java | 4 ---- .../org/testng/IDEATestNGRemoteListener.java | 19 ++++++++++++++----- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/plugins/testng_rt/src/org/testng/IDEARemoteTestNG.java b/plugins/testng_rt/src/org/testng/IDEARemoteTestNG.java index baa732ad087b..f0f8fd96c975 100644 --- a/plugins/testng_rt/src/org/testng/IDEARemoteTestNG.java +++ b/plugins/testng_rt/src/org/testng/IDEARemoteTestNG.java @@ -19,7 +19,6 @@ package org.testng; import jetbrains.buildServer.messages.serviceMessages.ServiceMessage; import org.testng.collections.Lists; import org.testng.xml.XmlClass; -import org.testng.xml.XmlInclude; import org.testng.xml.XmlSuite; import org.testng.xml.XmlTest; @@ -51,9 +50,6 @@ public class IDEARemoteTestNG extends TestNG { for (XmlTest test : tests) { for (XmlClass aClass : test.getXmlClasses()) { System.out.println("##teamcity[suiteTreeStarted name=\'" + aClass.getName() + "\' locationHint=\'java:suite://" + aClass.getName() + "\']"); - for (XmlInclude include : aClass.getIncludedMethods()) { - System.out.println("##teamcity[suiteTreeNode name=\'" + include.getName() + "\']"); - } System.out.println("##teamcity[suiteTreeEnded name=\'" + aClass.getName() + "\']"); } testCount += test.getClasses().size(); diff --git a/plugins/testng_rt/src/org/testng/IDEATestNGRemoteListener.java b/plugins/testng_rt/src/org/testng/IDEATestNGRemoteListener.java index 75bd8a157d88..f8413d1c7943 100644 --- a/plugins/testng_rt/src/org/testng/IDEATestNGRemoteListener.java +++ b/plugins/testng_rt/src/org/testng/IDEATestNGRemoteListener.java @@ -47,13 +47,22 @@ public class IDEATestNGRemoteListener implements ISuiteListener, IResultListener System.out.println("##teamcity[testSuiteStarted name =\'" + className + "\']"); myCurrentClassName = className; } - final String methodName = result.getMethod().getMethodName(); + String methodName = getMethodName(result); System.out.println("##teamcity[testStarted name=\'" + methodName + "\' locationHint=\'java:test://" + className + "." + methodName + "\']"); } + private static String getMethodName(ITestResult result) { + String methodName = result.getMethod().getMethodName(); + final Object[] parameters = result.getParameters(); + if (parameters.length > 0) { + methodName += "[" + parameters[0].toString() + "]"; + } + return methodName; + } + public void onTestSuccess(ITestResult result) { - System.out.println("##teamcity[testFinished name=\'" + result.getMethod().getMethodName() + "\']"); + System.out.println("##teamcity[testFinished name=\'" + getMethodName(result) + "\']"); } public String getTrace(Throwable tr) { @@ -68,17 +77,17 @@ public class IDEATestNGRemoteListener implements ISuiteListener, IResultListener final Throwable ex = result.getThrowable(); final String trace = getTrace(ex); final Map attrs = new HashMap(); - attrs.put("name", result.getMethod().getMethodName()); + attrs.put("name", getMethodName(result)); final String failureMessage = ex.getMessage(); attrs.put("message", failureMessage != null ? failureMessage : ""); attrs.put("details", trace); attrs.put("error", "true"); System.out.println(ServiceMessage.asString(ServiceMessageTypes.TEST_FAILED, attrs)); - System.out.println("##teamcity[testFinished name=\'" + result.getMethod().getMethodName() + "\']"); + System.out.println("##teamcity[testFinished name=\'" + getMethodName(result) + "\']"); } public void onTestSkipped(ITestResult result) { - System.out.println("##teamcity[testFinished name=\'" + result.getMethod().getMethodName() + "\']"); + System.out.println("##teamcity[testFinished name=\'" + getMethodName(result) + "\']"); } public void onTestFailedButWithinSuccessPercentage(ITestResult result) { From 4a47a6bfb733203dba371a6c537b97822700cfbf Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Tue, 14 Apr 2015 17:44:13 +0200 Subject: [PATCH 22/85] NPE --- .../testng/configuration/TestNGRunnableState.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGRunnableState.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGRunnableState.java index 39774fca9065..14591ab39a4b 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGRunnableState.java +++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGRunnableState.java @@ -244,7 +244,9 @@ public class TestNGRunnableState extends JavaTestFrameworkRunnableState Date: Tue, 14 Apr 2015 18:36:40 +0200 Subject: [PATCH 23/85] do not deduplicate tests if test tree was not built --- .../runner/GeneralToSMTRunnerEventsConvertor.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralToSMTRunnerEventsConvertor.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralToSMTRunnerEventsConvertor.java index e3e1fc3543b6..d6232de605ee 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralToSMTRunnerEventsConvertor.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralToSMTRunnerEventsConvertor.java @@ -52,6 +52,7 @@ public class GeneralToSMTRunnerEventsConvertor extends GeneralTestEventsProcesso private final String myTestFrameworkName; private boolean myIsTestingFinished; private TestLocationProvider myLocator = null; + private boolean myTreeBuildBeforeStart = false; public GeneralToSMTRunnerEventsConvertor(@NotNull final SMTestProxy.SMRootTestProxy testsRootNode, @NotNull final String testFrameworkName) { @@ -117,6 +118,7 @@ public class GeneralToSMTRunnerEventsConvertor extends GeneralTestEventsProcesso @Override public void onSuiteTreeNodeAdded(final String testName, final String locationHint) { + myTreeBuildBeforeStart = true; addToInvokeLater(new Runnable() { @Override public void run() { @@ -132,6 +134,7 @@ public class GeneralToSMTRunnerEventsConvertor extends GeneralTestEventsProcesso @Override public void onSuiteTreeStarted(final String suiteName, final String locationHint) { + myTreeBuildBeforeStart = true; addToInvokeLater(new Runnable() { @Override public void run() { @@ -229,10 +232,12 @@ public class GeneralToSMTRunnerEventsConvertor extends GeneralTestEventsProcesso }); } - private static SMTestProxy findChildByName(SMTestProxy parentSuite, String fullName) { - for (SMTestProxy proxy : parentSuite.getChildren()) { - if (fullName.equals(proxy.getName())) { - return proxy; + private SMTestProxy findChildByName(SMTestProxy parentSuite, String fullName) { + if (myTreeBuildBeforeStart) { + for (SMTestProxy proxy : parentSuite.getChildren()) { + if (fullName.equals(proxy.getName())) { + return proxy; + } } } return null; From 671d4988aa6846e2a7b9fe004610f88a8dcff1c1 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Tue, 14 Apr 2015 19:15:39 +0300 Subject: [PATCH 24/85] diff: fix initial scroll position for "Go To Change" action if we select change in constructor, while ChangeTreeList has zero size, scroll position will be wrong --- .../diff/ChangeGoToChangePopupAction.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/ChangeGoToChangePopupAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/ChangeGoToChangePopupAction.java index 2e5b6dfd2c30..012591b0baa5 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/ChangeGoToChangePopupAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/ChangeGoToChangePopupAction.java @@ -1,5 +1,7 @@ package com.intellij.openapi.vcs.changes.actions.diff; +import com.intellij.diff.actions.impl.GoToChangePopupBuilder; +import com.intellij.diff.chains.DiffRequestChain; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.project.Project; @@ -7,12 +9,11 @@ import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.util.Ref; -import com.intellij.diff.actions.impl.GoToChangePopupBuilder; -import com.intellij.diff.chains.DiffRequestChain; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ui.ChangesBrowser; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.util.Consumer; +import com.intellij.util.ui.update.UiNotifyConnector; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -68,16 +69,22 @@ public abstract class ChangeGoToChangePopupAction myPopup; + @NotNull private final Ref myPopup; public MyChangesBrowser(@NotNull Project project, @NotNull List changes, - @Nullable Change currentChange, + @Nullable final Change currentChange, @NotNull Ref popup) { super(project, null, changes, null, false, false, null, MyUseCase.LOCAL_CHANGES, null); setSelectionMode(ListSelectionModel.SINGLE_SELECTION); setChangesToDisplay(changes); - if (currentChange != null) select(Collections.singletonList(currentChange)); + + UiNotifyConnector.doWhenFirstShown(this, new Runnable() { + @Override + public void run() { + if (currentChange != null) select(Collections.singletonList(currentChange)); + } + }); myPopup = popup; } From 2f91a3b6a4f963a7b8362948ebc805644f9984bb Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Tue, 14 Apr 2015 19:17:56 +0200 Subject: [PATCH 25/85] Cleanup (no PSI leak in quick fix; deprecated calls dropped) --- .../highlighting/MissingFinalNewlineInspection.java | 12 +++++------- .../manifest/MissingFinalNewlineInspectionTest.java | 7 +++---- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/java/manifest/src/org/jetbrains/lang/manifest/highlighting/MissingFinalNewlineInspection.java b/java/manifest/src/org/jetbrains/lang/manifest/highlighting/MissingFinalNewlineInspection.java index 7404b27de30f..60bfce5fd13c 100644 --- a/java/manifest/src/org/jetbrains/lang/manifest/highlighting/MissingFinalNewlineInspection.java +++ b/java/manifest/src/org/jetbrains/lang/manifest/highlighting/MissingFinalNewlineInspection.java @@ -61,16 +61,14 @@ public class MissingFinalNewlineInspection extends LocalInspectionTool { return null; } - private static class AddNewlineQuickFix implements LocalQuickFix { - private final Section mySection; - + private static class AddNewlineQuickFix extends LocalQuickFixOnPsiElement { private AddNewlineQuickFix(Section section) { - mySection = section; + super(section); } @NotNull @Override - public String getName() { + public String getText() { return ManifestBundle.message("inspection.newline.fix"); } @@ -81,8 +79,8 @@ public class MissingFinalNewlineInspection extends LocalInspectionTool { } @Override - public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - PsiElement lastChild = mySection.getLastChild(); + public void invoke(@NotNull Project project, @NotNull PsiFile file, @NotNull PsiElement startElement, @NotNull PsiElement endElement) { + PsiElement lastChild = startElement.getLastChild(); if (lastChild instanceof Header) { lastChild.getNode().addLeaf(ManifestTokenType.NEWLINE, "\n", null); } diff --git a/java/manifest/test/org/jetbrains/lang/manifest/MissingFinalNewlineInspectionTest.java b/java/manifest/test/org/jetbrains/lang/manifest/MissingFinalNewlineInspectionTest.java index a55b938145cb..d0b56a2fd6df 100644 --- a/java/manifest/test/org/jetbrains/lang/manifest/MissingFinalNewlineInspectionTest.java +++ b/java/manifest/test/org/jetbrains/lang/manifest/MissingFinalNewlineInspectionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -17,7 +17,6 @@ package org.jetbrains.lang.manifest; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; -import junit.framework.Assert; import org.jetbrains.lang.manifest.highlighting.MissingFinalNewlineInspection; public class MissingFinalNewlineInspectionTest extends LightCodeInsightFixtureTestCase { @@ -29,12 +28,12 @@ public class MissingFinalNewlineInspectionTest extends LightCodeInsightFixtureTe public void testEmptyFile() { myFixture.configureByText(ManifestFileTypeFactory.MANIFEST, ""); - Assert.assertEquals(0, myFixture.getAvailableIntentions().size()); + assertEquals(0, myFixture.getAvailableIntentions().size()); } public void testNoProblem() { myFixture.configureByText(ManifestFileTypeFactory.MANIFEST, "Manifest-Version: 1.0\n"); - Assert.assertEquals(0, myFixture.getAvailableIntentions().size()); + assertEquals(0, myFixture.getAvailableIntentions().size()); } public void testFix() { From 1551d12209f5251eff163a8d5b7e4f4abe42083b Mon Sep 17 00:00:00 2001 From: Sergey Savenko Date: Mon, 13 Apr 2015 19:17:39 +0300 Subject: [PATCH 26/85] EditorTextFieldCellRenderer: make the renderer component public, simplify API --- .../JavaChangeSignatureDialog.java | 10 +- .../ui/EditorTextFieldCellRenderer.java | 93 +++++++++---------- .../EditorTextFieldJBTableRowRenderer.java | 27 +----- 3 files changed, 47 insertions(+), 83 deletions(-) diff --git a/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeSignatureDialog.java b/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeSignatureDialog.java index ce84d8a84dbc..11df0db74a8a 100644 --- a/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeSignatureDialog.java +++ b/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeSignatureDialog.java @@ -26,7 +26,6 @@ import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.colors.EditorFontType; import com.intellij.openapi.editor.event.DocumentAdapter; import com.intellij.openapi.editor.event.DocumentEvent; -import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.LanguageFileType; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.project.Project; @@ -226,7 +225,8 @@ public class JavaChangeSignatureDialog extends ChangeSignatureDialogBase item = getRowItem(row); @@ -249,12 +249,6 @@ public class JavaChangeSignatureDialog extends ChangeSignatureDialogBase MY_PANEL_PROPERTY = Key.create("EditorTextFieldCellRenderer.MyEditorPanel"); + private static final Key MY_PANEL_PROPERTY = Key.create("EditorTextFieldCellRenderer.MyEditorPanel"); private final Project myProject; + private final FileType myFileType; private final boolean myInheritFontFromLaF; - protected EditorTextFieldCellRenderer(@Nullable Project project, @NotNull Disposable parent) { - this(project, true, parent); + protected EditorTextFieldCellRenderer(@Nullable Project project, @Nullable FileType fileType, @NotNull Disposable parent) { + this(project, fileType, true, parent); } - protected EditorTextFieldCellRenderer(@Nullable Project project, boolean inheritFontFromLaF, @NotNull Disposable parent) { + protected EditorTextFieldCellRenderer(@Nullable Project project, @Nullable FileType fileType, + boolean inheritFontFromLaF, @NotNull Disposable parent) { myProject = project; + myFileType = fileType; myInheritFontFromLaF = inheritFontFromLaF; Disposer.register(parent, this); } protected abstract String getText(JTable table, Object value, int row, int column); - @Nullable - protected TextAttributes getTextAttributes(JTable table, Object value, boolean selected, boolean focused, int row, int col) { - return null; - } - - protected Color getCellBackground(JTable table, Object value, boolean selected, boolean focused, int row, int column) { - return UIUtil.getTableBackground(selected); - } - - @Nullable - protected FileType getFileType() { - return null; - } - - @NotNull - protected EditorColorsScheme getColorScheme() { - return EditorColorsManager.getInstance().getGlobalScheme(); - } - @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focused, int row, int column) { - MyPanel panel = getEditorPanel(table); - EditorEx editor = panel.myEditor; + RendererComponent panel = getEditorPanel(table); + EditorEx editor = panel.getEditor(); editor.getColorsScheme().setEditorFontSize(table.getFont().getSize()); String text = getText(table, value, row, column); - TextAttributes textAttributes = getTextAttributes(table, value, selected, focused, row, column); - panel.setText(text, textAttributes, selected); + panel.setText(text, null, selected); editor.getColorsScheme().setColor(EditorColors.SELECTION_BACKGROUND_COLOR, table.getSelectionBackground()); editor.getColorsScheme().setColor(EditorColors.SELECTION_FOREGROUND_COLOR, table.getSelectionForeground()); - editor.setBackgroundColor(getCellBackground(table, value, selected, focused, row, column)); + editor.setBackgroundColor(selected ? table.getSelectionBackground() : table.getBackground()); panel.setOpaque(!Comparing.equal(editor.getBackgroundColor(), table.getBackground())); panel.setBorder(null); // prevents double border painting when ExtendedItemRendererComponentWrapper is used @@ -114,29 +96,15 @@ public abstract class EditorTextFieldCellRenderer implements TableCellRenderer, } @NotNull - private MyPanel getEditorPanel(final JTable table) { - MyPanel panel = UIUtil.getClientProperty(table, MY_PANEL_PROPERTY); + private RendererComponent getEditorPanel(final JTable table) { + RendererComponent panel = UIUtil.getClientProperty(table, MY_PANEL_PROPERTY); if (panel != null) { DelegateColorScheme scheme = (DelegateColorScheme)panel.myEditor.getColorsScheme(); - scheme.setDelegate(getColorScheme()); + scheme.setDelegate(EditorColorsManager.getInstance().getGlobalScheme()); return panel; } - FileType fileType = ObjectUtils.notNull(getFileType(), FileTypes.PLAIN_TEXT); - EditorTextField field = new EditorTextField(new MyDocument(), myProject, fileType, false, false); - field.setSupplementary(true); - field.setFontInheritedFromLAF(myInheritFontFromLaF); - field.addNotify(); // creates editor - - EditorEx editor = (EditorEx)ObjectUtils.assertNotNull(field.getEditor()); - editor.setRendererMode(true); - - editor.setColorsScheme(editor.createBoundColorSchemeDelegate(null)); - editor.getSettings().setCaretRowShown(false); - - editor.getScrollPane().setBorder(null); - - panel = new MyPanel(editor); + panel = new RendererComponent(myProject, myFileType, myInheritFontFromLaF); Disposer.register(this, panel); Disposer.register(this, new Disposable() { @Override @@ -153,7 +121,7 @@ public abstract class EditorTextFieldCellRenderer implements TableCellRenderer, public void dispose() { } - private static class MyPanel extends CellRendererPanel implements Disposable { + public static class RendererComponent extends CellRendererPanel implements Disposable { private static final char ABBREVIATION_SUFFIX = '\u2026'; // 2026 '...' private static final char RETURN_SYMBOL = '\u23ce'; @@ -165,9 +133,13 @@ public abstract class EditorTextFieldCellRenderer implements TableCellRenderer, private TextAttributes myTextAttributes; private boolean mySelected; - public MyPanel(EditorEx editor) { - add(editor.getContentComponent()); - this.myEditor = editor; + public RendererComponent(Project project, @Nullable FileType fileType, boolean inheritFontFromLaF) { + myEditor = createEditor(project, fileType, inheritFontFromLaF); + add(myEditor.getContentComponent()); + } + + public EditorEx getEditor() { + return myEditor; } @Override @@ -325,6 +297,25 @@ public abstract class EditorTextFieldCellRenderer implements TableCellRenderer, return abbrLength; } + + @NotNull + private static EditorEx createEditor(Project project, @Nullable FileType fileType, boolean inheritFontFromLaF) { + fileType = ObjectUtils.notNull(fileType, FileTypes.PLAIN_TEXT); + EditorTextField field = new EditorTextField(new MyDocument(), project, fileType, false, false); + field.setSupplementary(true); + field.setFontInheritedFromLAF(inheritFontFromLaF); + field.addNotify(); // creates editor + + EditorEx editor = (EditorEx)ObjectUtils.assertNotNull(field.getEditor()); + editor.setRendererMode(true); + + editor.setColorsScheme(editor.createBoundColorSchemeDelegate(null)); + editor.getSettings().setCaretRowShown(false); + + editor.getScrollPane().setBorder(null); + + return editor; + } } private static class MyDocument extends UserDataHolderBase implements DocumentEx { diff --git a/platform/platform-impl/src/com/intellij/util/ui/table/EditorTextFieldJBTableRowRenderer.java b/platform/platform-impl/src/com/intellij/util/ui/table/EditorTextFieldJBTableRowRenderer.java index 5c64391669ec..c6553dcfec70 100644 --- a/platform/platform-impl/src/com/intellij/util/ui/table/EditorTextFieldJBTableRowRenderer.java +++ b/platform/platform-impl/src/com/intellij/util/ui/table/EditorTextFieldJBTableRowRenderer.java @@ -16,18 +16,17 @@ package com.intellij.util.ui.table; import com.intellij.openapi.Disposable; -import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.Project; import com.intellij.ui.EditorTextFieldCellRenderer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.awt.*; public abstract class EditorTextFieldJBTableRowRenderer extends EditorTextFieldCellRenderer implements JBTableRowRenderer { - protected EditorTextFieldJBTableRowRenderer(@Nullable Project project, @NotNull Disposable parent) { - super(project, parent); + protected EditorTextFieldJBTableRowRenderer(@Nullable Project project, @Nullable FileType fileType, @NotNull Disposable parent) { + super(project, fileType, parent); } @Override @@ -40,25 +39,5 @@ public abstract class EditorTextFieldJBTableRowRenderer extends EditorTextFieldC return getText(table, row); } - @Override - protected final Color getCellBackground(JTable table, Object value, boolean selected, boolean focused, int row, int column) { - return getRowBackground(table, selected, focused, row); - } - - @Nullable - @Override - protected final TextAttributes getTextAttributes(JTable table, Object value, boolean selected, boolean focused, int row, int col) { - return getTextAttributes(table, row); - } - protected abstract String getText(JTable table, int row); - - @Nullable - protected TextAttributes getTextAttributes(JTable table, int row) { - return null; - } - - protected Color getRowBackground(JTable table, boolean selected, boolean focused, int row) { - return super.getCellBackground(table, null, selected, focused, row, 0); - } } From 0909619303aae71f84b9e95777ddaed7d7299162 Mon Sep 17 00:00:00 2001 From: Sergey Savenko Date: Tue, 14 Apr 2015 16:40:36 +0300 Subject: [PATCH 27/85] EditorTextFieldCellRenderer: get rid of TRP-specific border painting hacks --- .../src/com/intellij/ui/EditorTextFieldCellRenderer.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ui/EditorTextFieldCellRenderer.java b/platform/platform-impl/src/com/intellij/ui/EditorTextFieldCellRenderer.java index 01c4a23b4f7a..293ae39c8db1 100644 --- a/platform/platform-impl/src/com/intellij/ui/EditorTextFieldCellRenderer.java +++ b/platform/platform-impl/src/com/intellij/ui/EditorTextFieldCellRenderer.java @@ -172,14 +172,6 @@ public abstract class EditorTextFieldCellRenderer implements TableCellRenderer, @Override protected void paintComponent(Graphics g) { - if (getBorder() == null || !myEditor.getContentComponent().isOpaque()) return; - - Color oldColor = g.getColor(); - g.setColor(myEditor.getBackgroundColor()); - Insets insets = getInsets(); - g.fillRect(0, 0, insets.left, getHeight()); - g.fillRect(getWidth() - insets.left - insets.right, 0, getWidth(), getHeight()); - g.setColor(oldColor); } @Override From 9777f340723e609b79aa1e938d56f3fdb0cfd663 Mon Sep 17 00:00:00 2001 From: Sergey Savenko Date: Tue, 14 Apr 2015 17:11:22 +0300 Subject: [PATCH 28/85] UIUtil.createImageForGraphics: do not create HiDPI images for non-HiDPI graphics --- platform/util/src/com/intellij/util/ui/UIUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index 5bc953b1c891..8620f356f11d 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -1786,7 +1786,7 @@ public class UIUtil { } public static BufferedImage createImageForGraphics(Graphics2D g, int width, int height, int type) { - if (DetectRetinaKit.isMacRetina(g)) { + if (isRetina(g)) { return RetinaImage.create(width, height, type); } //noinspection UndesirableClassUsage From 47fbe0f2f994b3f3064a3a36f040255ccb1ab092 Mon Sep 17 00:00:00 2001 From: "Ilya.Kazakevich" Date: Tue, 14 Apr 2015 22:46:30 +0300 Subject: [PATCH 29/85] Error messages in BDD are always converted to UTF-8 regardless python version for PY-15258 --- python/helpers/pycharm/_bdd_utils.py | 48 ++++++++++++++++++++++-- python/helpers/pycharm/behave_runner.py | 10 +++-- python/helpers/pycharm/lettuce_runner.py | 2 +- 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/python/helpers/pycharm/_bdd_utils.py b/python/helpers/pycharm/_bdd_utils.py index a71cc37e346d..4d4116197ff4 100644 --- a/python/helpers/pycharm/_bdd_utils.py +++ b/python/helpers/pycharm/_bdd_utils.py @@ -8,7 +8,7 @@ You may also need "get_what_to_run_by_env" that gets folder (current or passed a import os import time import abc - +import sys import tcmessages @@ -139,11 +139,11 @@ class BddRunner(object): :param name: test name :type name str :param message: failure message - :type message str + :type message basestring :param details: failure details (probably stacktrace) :type details str """ - self.tc_messages.testFailed(name, message=message, details=details) + self.tc_messages.testFailed(name, message=VersionAgnosticUtils().to_unicode(message), details=details) self.__last_test_name = None def _test_passed(self, name, duration=None): @@ -229,3 +229,45 @@ class BddRunner(object): pass +class VersionAgnosticUtils(object): + """ + "six" emulator: this class fabrics appropriate tool to use regardless python version. + Use it to write code that works both on py2 and py3 + """ + + @staticmethod + def __new__(cls, *more): + """ + Fabrics Py2 or Py3 instance based on py version + """ + real_class = _Py3KUtils if sys.version_info >= (3, 0) else _Py2Utils + return super(cls, real_class).__new__(real_class, *more) + + def to_unicode(self, obj): + """ + + :param obj: string to convert to unicode + :return: unicode string + """ + + raise NotImplementedError() + + + +class _Py2Utils(VersionAgnosticUtils): + """ + Util for Py2 + """ + def to_unicode(self, obj): + if isinstance(obj, unicode): + return obj + return unicode(obj.decode("utf-8")) + + + +class _Py3KUtils(VersionAgnosticUtils): + """ + Util for Py3 + """ + def to_unicode(self, obj): + return str(obj) \ No newline at end of file diff --git a/python/helpers/pycharm/behave_runner.py b/python/helpers/pycharm/behave_runner.py index f35c7e242021..7072e47e615b 100644 --- a/python/helpers/pycharm/behave_runner.py +++ b/python/helpers/pycharm/behave_runner.py @@ -136,9 +136,10 @@ class _BehaveRunner(_bdd_utils.BddRunner): :param element feature/suite/step """ element.location.file = element.location.filename # To preserve _bdd_utils contract + utils = _bdd_utils.VersionAgnosticUtils() if isinstance(element, Step): # Process step - step_name = "{0} {1}".format(element.keyword, element.name) + step_name = u"{0} {1}".format(utils.to_unicode(element.keyword), utils.to_unicode(element.name)) if is_started: self._test_started(step_name, element.location) elif element.status == 'passed': @@ -148,9 +149,10 @@ class _BehaveRunner(_bdd_utils.BddRunner): trace = traceback.format_exc() except Exception: trace = "".join(traceback.format_tb(element.exc_traceback)) - if trace in str(element.error_message): - trace = None # No reason to duplicate output (see PY-13647) - self._test_failed(step_name, element.error_message, trace) + error_message = utils.to_unicode(element.error_message) + if "Traceback " in error_message: + error_message = "" # No reason to duplicate output (see PY-13647) + self._test_failed(step_name, error_message, trace) elif element.status == 'undefined': self._test_undefined(step_name, element.location) else: diff --git a/python/helpers/pycharm/lettuce_runner.py b/python/helpers/pycharm/lettuce_runner.py index b61f7af9a332..2c97672b69cb 100644 --- a/python/helpers/pycharm/lettuce_runner.py +++ b/python/helpers/pycharm/lettuce_runner.py @@ -103,7 +103,7 @@ class _LettuceRunner(_bdd_utils.BddRunner): elif step.failed: reason = step.why assert isinstance(reason, ReasonToFail), reason - self._test_failed(test_name, message=reason.exception, details=reason.traceback) + self._test_failed(test_name, message=reason.exception.message, details=reason.traceback) elif step.has_definition: self._test_skipped(test_name, "In lettuce, we do know the reason", step.described_at) else: From 1b7a05e26fbcdd471cfebb1f8e52ee7e847dfcbb Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 14 Apr 2015 17:14:03 +0300 Subject: [PATCH 30/85] IDEA-138553 Inspection for usages of Optinoal.ofNullable() for values known to be null or non-null --- .../dataFlow/DataFlowInspectionBase.java | 84 +++-------- .../dataFlow/DfaOptionalSupport.java | 139 ++++++++++++++++++ .../dataFlow/NullabilityProblem.java | 2 + .../dataFlow/StandardInstructionVisitor.java | 21 ++- .../afterGuavaNotNullLiteral.java | 6 + .../afterGuavaNullLiteral.java | 6 + .../afterNotNullLiteral.java | 6 + .../afterNotNullVariable.java | 8 + .../afterNullLiteral.java | 7 + .../afterNullVariable.java | 7 + .../beforeGuavaNotNullLiteral.java | 6 + .../beforeGuavaNullLiteral.java | 6 + .../beforeNotNullLiteral.java | 6 + .../beforeNotNullVariable.java | 8 + .../beforeNullLiteral.java | 7 + .../beforeNullVariable.java | 7 + .../replaceFromOfNullable/beforeUnknown.java | 6 + .../ReplaceFromOfNullableFixTest.groovy | 86 +++++++++++ .../DataFlowInspectionTestSuite.java | 12 ++ 19 files changed, 364 insertions(+), 66 deletions(-) create mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaOptionalSupport.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterGuavaNotNullLiteral.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterGuavaNullLiteral.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNotNullLiteral.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNotNullVariable.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNullLiteral.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNullVariable.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeGuavaNotNullLiteral.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeGuavaNullLiteral.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNotNullLiteral.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNotNullVariable.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNullLiteral.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNullVariable.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeUnknown.java create mode 100644 java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ReplaceFromOfNullableFixTest.groovy diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java index 68a74f887f3f..8c3804d8a500 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java @@ -53,7 +53,6 @@ import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import org.jdom.Element; -import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -211,7 +210,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool { } } - ContainerUtil.addIfNotNull(fixes, ReplaceOptionalOfWithOfNullableFix.registerReplaceOptionalOfWithOfNullableFix(qualifier)); + ContainerUtil.addIfNotNull(fixes, DfaOptionalSupport.registerReplaceOptionalOfWithOfNullableFix(qualifier)); return fixes.isEmpty() ? null : fixes.toArray(new LocalQuickFix[fixes.size()]); } catch (IncorrectOperationException e) { @@ -272,11 +271,29 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool { reportNullableArgumentsPassedToNonAnnotated(visitor, holder, reportedAnchors); } + reportOptionalOfNullableImprovements(holder, visitor, reportedAnchors); + + if (REPORT_CONSTANT_REFERENCE_VALUES) { reportConstantReferenceValues(holder, visitor, reportedAnchors); } } + private static void reportOptionalOfNullableImprovements(ProblemsHolder holder, + DataFlowInstructionVisitor visitor, + HashSet reportedAnchors) { + for (PsiElement expr : visitor.getProblems(NullabilityProblem.passingNullToOptional)) { + if (!reportedAnchors.add(expr)) continue; + holder.registerProblem(expr, "Passing null argument to Optional", + DfaOptionalSupport.createReplaceOptionalOfNullableWithEmptyFix(expr)); + } + for (PsiElement expr : visitor.getProblems(NullabilityProblem.passingNotNullToOptional)) { + if (!reportedAnchors.add(expr)) continue; + holder.registerProblem(expr, "Passing a non-null argument to Optional", + DfaOptionalSupport.createReplaceOptionalOfNullableWithOfFix()); + } + } + private static void reportConstantReferenceValues(ProblemsHolder holder, StandardInstructionVisitor visitor, Set reportedAnchors) { for (Pair pair : visitor.getConstantReferenceValues()) { PsiReferenceExpression ref = pair.first; @@ -764,67 +781,4 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool { boolean normalOk; } } - - private static class ReplaceOptionalOfWithOfNullableFix implements LocalQuickFix { - - private static final String GUAVA_OPTIONAL = "com.google.common.base.Optional"; - private final String myTargetMethodName; - - public ReplaceOptionalOfWithOfNullableFix(final String targetMethodName) { - myTargetMethodName = targetMethodName; - } - - private static LocalQuickFix registerReplaceOptionalOfWithOfNullableFix(PsiExpression qualifier) { - final PsiElement argList = PsiUtil.skipParenthesizedExprUp(qualifier).getParent(); - if (argList instanceof PsiExpressionList) { - final PsiElement parent = argList.getParent(); - if (parent instanceof PsiMethodCallExpression) { - final PsiMethod method = ((PsiMethodCallExpression)parent).resolveMethod(); - if (method != null) { - final PsiClass containingClass = method.getContainingClass(); - if ("of".equals(method.getName()) && containingClass != null) { - final String qualifiedName = containingClass.getQualifiedName(); - if (CommonClassNames.JAVA_UTIL_OPTIONAL.equals(qualifiedName)) { - return new ReplaceOptionalOfWithOfNullableFix("ofNullable"); - } - else if (GUAVA_OPTIONAL.equals(qualifiedName)) { - return new ReplaceOptionalOfWithOfNullableFix("fromNullable"); - } - } - } - } - } - return null; - } - - @Nls - @NotNull - @Override - public String getName() { - return getFamilyName(); - } - - @NotNull - @Override - public String getFamilyName() { - return "Replace with '." + myTargetMethodName + "()'"; - } - - @Override - public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - final PsiMethodCallExpression - methodCallExpression = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiMethodCallExpression.class); - if (methodCallExpression != null) { - final PsiElement ofNullableExprName = - ((PsiMethodCallExpression)JavaPsiFacade.getElementFactory(project) - .createExpressionFromText("Optional.ofNullable(null)", null)).getMethodExpression(); - final PsiElement referenceNameElement = methodCallExpression.getMethodExpression().getReferenceNameElement(); - if (referenceNameElement != null) { - final PsiElement ofNullableNameElement = ((PsiReferenceExpression)ofNullableExprName).getReferenceNameElement(); - LOG.assertTrue(ofNullableNameElement != null); - referenceNameElement.replace(ofNullableNameElement); - } - } - } - } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaOptionalSupport.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaOptionalSupport.java new file mode 100644 index 000000000000..12c0d3ca6f3f --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaOptionalSupport.java @@ -0,0 +1,139 @@ +/* + * Copyright 2000-2015 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.codeInspection.dataFlow; + +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author anet, peter + */ +class DfaOptionalSupport { + private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.dataFlow.DfaOptionalSupport"); + private static final String GUAVA_OPTIONAL = "com.google.common.base.Optional"; + + @Nullable + static LocalQuickFix registerReplaceOptionalOfWithOfNullableFix(@NotNull PsiExpression qualifier) { + final PsiElement call = findCallExpression(qualifier); + final PsiMethod method = call == null ? null : ((PsiMethodCallExpression)call).resolveMethod(); + final PsiClass containingClass = method == null ? null : method.getContainingClass(); + if (containingClass != null && "of".equals(method.getName())) { + final String qualifiedName = containingClass.getQualifiedName(); + if (CommonClassNames.JAVA_UTIL_OPTIONAL.equals(qualifiedName)) { + return new ReplaceOptionalCallFix("ofNullable", false); + } + if (GUAVA_OPTIONAL.equals(qualifiedName)) { + return new ReplaceOptionalCallFix("fromNullable", false); + } + } + return null; + } + + private static PsiMethodCallExpression findCallExpression(@NotNull PsiElement anchor) { + final PsiElement argList = PsiUtil.skipParenthesizedExprUp(anchor).getParent(); + if (argList instanceof PsiExpressionList) { + final PsiElement parent = argList.getParent(); + if (parent instanceof PsiMethodCallExpression) { + return (PsiMethodCallExpression)parent; + } + } + return null; + } + private static boolean isJdkOptional(@NotNull PsiElement anchor) { + final PsiElement parent = findCallExpression(anchor); + PsiMethod method = parent == null ? null : resolveOfNullable(findCallExpression(anchor)); + return method != null && "ofNullable".equals(method.getName()); + } + + @NotNull + static LocalQuickFix createReplaceOptionalOfNullableWithEmptyFix(@NotNull PsiElement anchor) { + return new ReplaceOptionalCallFix(isJdkOptional(anchor) ? "empty" : "absent", true); + } + + @NotNull + static LocalQuickFix createReplaceOptionalOfNullableWithOfFix() { + return new ReplaceOptionalCallFix("of", false); + } + + @Nullable + static PsiMethod resolveOfNullable(PsiCallExpression expression) { + String name = ((PsiMethodCallExpression)expression).getMethodExpression().getReferenceName(); + if ("ofNullable".equals(name) || "fromNullable".equals(name)) { + PsiMethod method = expression.resolveMethod(); + PsiClass psiClass = method == null ? null : method.getContainingClass(); + String qname = psiClass == null ? null : psiClass.getQualifiedName(); + if (CommonClassNames.JAVA_UTIL_OPTIONAL.equals(qname) || GUAVA_OPTIONAL.equals(qname)) { + return method; + } + } + return null; + } + + private static class ReplaceOptionalCallFix implements LocalQuickFix { + private final String myTargetMethodName; + private final boolean myClearArguments; + + public ReplaceOptionalCallFix(final String targetMethodName, boolean clearArguments) { + myTargetMethodName = targetMethodName; + myClearArguments = clearArguments; + } + + @Nls + @NotNull + @Override + public String getName() { + return getFamilyName(); + } + + @NotNull + @Override + public String getFamilyName() { + return "Replace with '." + myTargetMethodName + "()'"; + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + final PsiMethodCallExpression + methodCallExpression = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiMethodCallExpression.class); + if (methodCallExpression != null) { + final PsiElement ofNullableExprName = + ((PsiMethodCallExpression)JavaPsiFacade.getElementFactory(project) + .createExpressionFromText("Optional." + myTargetMethodName + "(null)", null)).getMethodExpression(); + final PsiElement referenceNameElement = methodCallExpression.getMethodExpression().getReferenceNameElement(); + if (referenceNameElement != null) { + final PsiElement ofNullableNameElement = ((PsiReferenceExpression)ofNullableExprName).getReferenceNameElement(); + LOG.assertTrue(ofNullableNameElement != null); + referenceNameElement.replace(ofNullableNameElement); + } + if (myClearArguments) { + PsiExpressionList argList = methodCallExpression.getArgumentList(); + PsiExpression[] args = argList.getExpressions(); + if (args.length > 0) { + argList.deleteChildRange(args[0], args[args.length - 1]); + } + } + } + } + } +} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullabilityProblem.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullabilityProblem.java index 8155149ed1ec..2810ffdb2e34 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullabilityProblem.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullabilityProblem.java @@ -11,4 +11,6 @@ public enum NullabilityProblem { nullableReturn, passingNullableToNotNullParameter, passingNullableArgumentToNonAnnotatedParameter, + passingNullToOptional, + passingNotNullToOptional } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java index 898c744286bc..66893c0c0c1b 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java @@ -57,6 +57,15 @@ public class StandardInstructionVisitor extends InstructionVisitor { return callExpression != null ? DfaPsiUtil.getElementNullability(key.getResultType(), callExpression.resolveMethod()) : null; } }; + @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") + private final FactoryMap myOptionOfNullable = new FactoryMap() { + @Nullable + @Override + protected Boolean create(MethodCallInstruction key) { + PsiCallExpression expression = key.getCallExpression(); + return expression instanceof PsiMethodCallExpression && DfaOptionalSupport.resolveOfNullable(expression) != null; + } + }; @Override public DfaInstructionState[] visitAssign(AssignInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { @@ -231,6 +240,10 @@ public class StandardInstructionVisitor extends InstructionVisitor { forceNotNull(runner, memState, arg); } } + else if (myOptionOfNullable.get(instruction)) { + checkNotNullable(memState, arg, NullabilityProblem.passingNotNullToOptional, expr); + checkNotNullable(memState, arg, NullabilityProblem.passingNullToOptional, expr); + } else if (requiredNullability == Nullness.UNKNOWN) { checkNotNullable(memState, arg, NullabilityProblem.passingNullableArgumentToNonAnnotatedParameter, expr); } @@ -371,8 +384,14 @@ public class StandardInstructionVisitor extends InstructionVisitor { protected boolean checkNotNullable(DfaMemoryState state, DfaValue value, NullabilityProblem problem, PsiElement anchor) { + if (problem == NullabilityProblem.passingNotNullToOptional) { + return !state.isNotNull(value); + } + boolean notNullable = state.checkNotNullable(value); - if (notNullable && problem != NullabilityProblem.passingNullableArgumentToNonAnnotatedParameter) { + if (notNullable && + problem != NullabilityProblem.passingNullableArgumentToNonAnnotatedParameter && + problem != NullabilityProblem.passingNullToOptional) { DfaValueFactory factory = ((DfaMemoryStateImpl)state).getFactory(); state.applyCondition(factory.getRelationFactory().createRelation(value, factory.getConstFactory().getNull(), NE, false)); } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterGuavaNotNullLiteral.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterGuavaNotNullLiteral.java new file mode 100644 index 000000000000..966136953922 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterGuavaNotNullLiteral.java @@ -0,0 +1,6 @@ +// "Replace with '.of()'" "true" +class A{ + void test(){ + com.google.common.base.Optional.of(11); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterGuavaNullLiteral.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterGuavaNullLiteral.java new file mode 100644 index 000000000000..e5599f2a86b6 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterGuavaNullLiteral.java @@ -0,0 +1,6 @@ +// "Replace with '.absent()'" "true" +class A{ + void test(){ + com.google.common.base.Optional.absent(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNotNullLiteral.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNotNullLiteral.java new file mode 100644 index 000000000000..bfe725293c8b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNotNullLiteral.java @@ -0,0 +1,6 @@ +// "Replace with '.of()'" "true" +class A{ + void test(){ + java.util.Optional.of(11); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNotNullVariable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNotNullVariable.java new file mode 100644 index 000000000000..c1e85703be4b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNotNullVariable.java @@ -0,0 +1,8 @@ +// "Replace with '.of()'" "true" + +class A{ + void test(String s){ + assert s != null; + java.util.Optional.of(s); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNullLiteral.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNullLiteral.java new file mode 100644 index 000000000000..850a7c3df1c8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNullLiteral.java @@ -0,0 +1,7 @@ +// "Replace with '.empty()'" "true" +class A { + void test() { + String s = null; + java.util.Optional.empty(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNullVariable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNullVariable.java new file mode 100644 index 000000000000..850a7c3df1c8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/afterNullVariable.java @@ -0,0 +1,7 @@ +// "Replace with '.empty()'" "true" +class A { + void test() { + String s = null; + java.util.Optional.empty(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeGuavaNotNullLiteral.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeGuavaNotNullLiteral.java new file mode 100644 index 000000000000..8c7c9be02f35 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeGuavaNotNullLiteral.java @@ -0,0 +1,6 @@ +// "Replace with '.of()'" "true" +class A{ + void test(){ + com.google.common.base.Optional.fromNullable(11); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeGuavaNullLiteral.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeGuavaNullLiteral.java new file mode 100644 index 000000000000..fa25c555e2d0 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeGuavaNullLiteral.java @@ -0,0 +1,6 @@ +// "Replace with '.absent()'" "true" +class A{ + void test(){ + com.google.common.base.Optional.fromNullable(null); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNotNullLiteral.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNotNullLiteral.java new file mode 100644 index 000000000000..5ef9972f2ca8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNotNullLiteral.java @@ -0,0 +1,6 @@ +// "Replace with '.of()'" "true" +class A{ + void test(){ + java.util.Optional.ofNullable(11); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNotNullVariable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNotNullVariable.java new file mode 100644 index 000000000000..53c9a43841f6 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNotNullVariable.java @@ -0,0 +1,8 @@ +// "Replace with '.of()'" "true" + +class A{ + void test(String s){ + assert s != null; + java.util.Optional.ofNullable(s); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNullLiteral.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNullLiteral.java new file mode 100644 index 000000000000..b57dda49a134 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNullLiteral.java @@ -0,0 +1,7 @@ +// "Replace with '.empty()'" "true" +class A { + void test() { + String s = null; + java.util.Optional.ofNullable(null); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNullVariable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNullVariable.java new file mode 100644 index 000000000000..5f6d350318ae --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeNullVariable.java @@ -0,0 +1,7 @@ +// "Replace with '.empty()'" "true" +class A { + void test() { + String s = null; + java.util.Optional.ofNullable(s); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeUnknown.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeUnknown.java new file mode 100644 index 000000000000..6d506e8e8529 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable/beforeUnknown.java @@ -0,0 +1,6 @@ +// "Replace with '.of()'" "false" +class A{ + void test(String s){ + java.util.Optional.ofNullable(s); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ReplaceFromOfNullableFixTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ReplaceFromOfNullableFixTest.groovy new file mode 100644 index 000000000000..d1670967ab55 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ReplaceFromOfNullableFixTest.groovy @@ -0,0 +1,86 @@ +/* + * Copyright 2000-2015 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. + */ + +/* + * User: anna + * Date: 21-Mar-2008 + */ +package com.intellij.codeInsight.daemon.quickFix +import com.intellij.codeInspection.LocalInspectionTool +import com.intellij.codeInspection.dataFlow.DataFlowInspection +import com.intellij.openapi.command.WriteCommandAction +import com.intellij.openapi.projectRoots.Sdk +import com.intellij.openapi.vfs.VfsUtil +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.testFramework.IdeaTestUtil +import org.jetbrains.annotations.NotNull + +public class ReplaceFromOfNullableFixTest extends LightQuickFixParameterizedTestCase { + @NotNull + @Override + protected LocalInspectionTool[] configureLocalInspectionTools() { + return [new DataFlowInspection()] as LocalInspectionTool[] + } + + public void test() throws Exception { + doAllTests(); + } + + @Override + protected String getBasePath() { + return "/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable"; + } + + static void addGuavaOptional() { + if (JavaPsiFacade.getInstance(project).findClass("com.google.common.base.Optional", GlobalSearchScope.allScope(project))) { + return + } + + WriteCommandAction.runWriteCommandAction(project) { + VirtualFile optional = getSourceRoot() + .createChildDirectory(this, "com") + .createChildDirectory(this, "google") + .createChildDirectory(this, "common") + .createChildDirectory(this, "base") + .createChildData(this, "Optional.java"); + VfsUtil.saveText(optional, """ +package com.google.common.base; +public abstract class Optional { + public static Optional absent() { } + + public static Optional of(T reference) { } + + public static Optional fromNullable(@Nullable T nullableReference) { } +} +""") + } + } + + @Override + protected void beforeActionStarted(String testName, String contents) { + if (testName.contains("Guava")) { + addGuavaOptional() + } + super.beforeActionStarted(testName, contents) + } + + @Override + protected Sdk getProjectJDK() { + return IdeaTestUtil.getMockJdk18(); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTestSuite.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTestSuite.java index 674a0f8cbb50..c7664bd15bc8 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTestSuite.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTestSuite.java @@ -17,6 +17,9 @@ package com.intellij.codeInspection; import com.intellij.codeInsight.completion.NormalCompletionDfaTest; import com.intellij.codeInsight.completion.SmartTypeCompletionDfaTest; +import com.intellij.codeInsight.daemon.quickFix.AddAssertStatementFixTest; +import com.intellij.codeInsight.daemon.quickFix.ReplaceFromOfNullableFixTest; +import com.intellij.codeInsight.daemon.quickFix.ReplaceWithOfNullableFixTest; import com.intellij.slicer.SliceBackwardTest; import com.intellij.slicer.SliceTreeTest; import junit.framework.Test; @@ -25,20 +28,29 @@ import junit.framework.TestSuite; public class DataFlowInspectionTestSuite { public static Test suite() { TestSuite suite = new TestSuite(); + suite.addTestSuite(DataFlowInspectionTest.class); suite.addTestSuite(DataFlowInspection8Test.class); suite.addTestSuite(DataFlowInspectionAncientTest.class); suite.addTestSuite(ContractCheckTest.class); + suite.addTestSuite(ContractInferenceFromSourceTest.class); suite.addTestSuite(NullityInferenceFromSourceTestCase.DfaInferenceTest.class); suite.addTestSuite(NullityInferenceFromSourceTestCase.LightInferenceTest.class); suite.addTestSuite(PurityInferenceFromSourceTest.class); + suite.addTestSuite(SliceTreeTest.class); suite.addTestSuite(SliceBackwardTest.class); + suite.addTestSuite(SmartTypeCompletionDfaTest.class); suite.addTestSuite(NormalCompletionDfaTest.class); + suite.addTestSuite(NullableStuffInspectionTest.class); suite.addTestSuite(NullableStuffInspection14Test.class); + + suite.addTestSuite(AddAssertStatementFixTest.class); + suite.addTestSuite(ReplaceWithOfNullableFixTest.class); + suite.addTestSuite(ReplaceFromOfNullableFixTest.class); return suite; } } From 6c8750291f6b17b3b3915022e9361599fed8626c Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 14 Apr 2015 17:38:42 +0300 Subject: [PATCH 31/85] IDEA-124359 Add Inspection for detecting nullable values passed into Optional.of() test that it correctly works with Guava --- .../afterGuavaNullable.java | 6 ++++ .../beforeGuavaNullable.java | 6 ++++ .../ReplaceFromOfNullableFixTest.groovy | 31 ++++++++++--------- .../ReplaceWithOfNullableFixTest.java | 14 +++++++++ 4 files changed, 43 insertions(+), 14 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithOfNullable/afterGuavaNullable.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithOfNullable/beforeGuavaNullable.java diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithOfNullable/afterGuavaNullable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithOfNullable/afterGuavaNullable.java new file mode 100644 index 000000000000..007fbb6d4e2a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithOfNullable/afterGuavaNullable.java @@ -0,0 +1,6 @@ +// "Replace with '.fromNullable()'" "true" +class A{ + void test(){ + com.google.common.base.Optional.fromNullable(null); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithOfNullable/beforeGuavaNullable.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithOfNullable/beforeGuavaNullable.java new file mode 100644 index 000000000000..a438e6a82110 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithOfNullable/beforeGuavaNullable.java @@ -0,0 +1,6 @@ +// "Replace with '.fromNullable()'" "true" +class A{ + void test(){ + com.google.common.base.Optional.of(null); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ReplaceFromOfNullableFixTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ReplaceFromOfNullableFixTest.groovy index d1670967ab55..15ef5ff77f14 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ReplaceFromOfNullableFixTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ReplaceFromOfNullableFixTest.groovy @@ -14,19 +14,14 @@ * limitations under the License. */ -/* - * User: anna - * Date: 21-Mar-2008 - */ package com.intellij.codeInsight.daemon.quickFix import com.intellij.codeInspection.LocalInspectionTool import com.intellij.codeInspection.dataFlow.DataFlowInspection +import com.intellij.openapi.Disposable import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.vfs.VfsUtil import com.intellij.openapi.vfs.VirtualFile -import com.intellij.psi.JavaPsiFacade -import com.intellij.psi.search.GlobalSearchScope import com.intellij.testFramework.IdeaTestUtil import org.jetbrains.annotations.NotNull @@ -46,11 +41,7 @@ public class ReplaceFromOfNullableFixTest extends LightQuickFixParameterizedTest return "/codeInsight/daemonCodeAnalyzer/quickFix/replaceFromOfNullable"; } - static void addGuavaOptional() { - if (JavaPsiFacade.getInstance(project).findClass("com.google.common.base.Optional", GlobalSearchScope.allScope(project))) { - return - } - + static void addGuavaOptional(Disposable parent) { WriteCommandAction.runWriteCommandAction(project) { VirtualFile optional = getSourceRoot() .createChildDirectory(this, "com") @@ -63,22 +54,34 @@ package com.google.common.base; public abstract class Optional { public static Optional absent() { } - public static Optional of(T reference) { } + public static Optional of(@org.jetbrains.annotations.NotNull T reference) { } - public static Optional fromNullable(@Nullable T nullableReference) { } + public static Optional fromNullable(T nullableReference) { } } """) } } + static void cleanupGuava() { + WriteCommandAction.runWriteCommandAction(project) { + getSourceRoot().findChild("com")?.delete(this) + } + } + @Override protected void beforeActionStarted(String testName, String contents) { if (testName.contains("Guava")) { - addGuavaOptional() + addGuavaOptional(testRootDisposable) } super.beforeActionStarted(testName, contents) } + @Override + protected void afterActionCompleted(String testName, String contents) { + cleanupGuava() + super.afterActionCompleted(testName, contents) + } + @Override protected Sdk getProjectJDK() { return IdeaTestUtil.getMockJdk18(); diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ReplaceWithOfNullableFixTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ReplaceWithOfNullableFixTest.java index 6eef97352f65..9c5c07013b7e 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ReplaceWithOfNullableFixTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/ReplaceWithOfNullableFixTest.java @@ -42,6 +42,20 @@ public class ReplaceWithOfNullableFixTest extends LightQuickFixParameterizedTest return "/codeInsight/daemonCodeAnalyzer/quickFix/replaceWithOfNullable"; } + @Override + protected void beforeActionStarted(String testName, String contents) { + if (testName.contains("Guava")) { + ReplaceFromOfNullableFixTest.addGuavaOptional(myTestRootDisposable); + } + super.beforeActionStarted(testName, contents); + } + + @Override + protected void afterActionCompleted(String testName, String contents) { + ReplaceFromOfNullableFixTest.cleanupGuava(); + super.afterActionCompleted(testName, contents); + } + @Override protected Sdk getProjectJDK() { return IdeaTestUtil.getMockJdk18(); From c9b80f223ca09f38a82431a64ea2da72c17be907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yann=20C=C3=A9bron?= Date: Wed, 15 Apr 2015 07:53:00 +0200 Subject: [PATCH 32/85] DomExtenderEP: instantiate DomExtender lazily --- .../util/xml/reflect/DomExtenderEP.java | 49 +++++++++++++------ 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtenderEP.java b/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtenderEP.java index 1f12500aef34..16ba3ab828ec 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtenderEP.java +++ b/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtenderEP.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -29,9 +29,11 @@ import org.jetbrains.annotations.Nullable; * @author peter */ public class DomExtenderEP extends AbstractExtensionPointBean { - private static final Logger LOG = Logger.getInstance("#com.intellij.util.xml.reflect.DomExtenderEP"); + public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.dom.extender"); + private static final Logger LOG = Logger.getInstance("#com.intellij.util.xml.reflect.DomExtenderEP"); + @Attribute("domClass") public String domClassName; @Attribute("extenderClass") @@ -40,28 +42,45 @@ public class DomExtenderEP extends AbstractExtensionPointBean { private Class myDomClass; private DomExtender myExtender; - @Nullable - public DomExtensionsRegistrarImpl extend(@NotNull final Project project, @NotNull final DomInvocationHandler handler, @Nullable DomExtensionsRegistrarImpl registrar) { - if (myExtender == null) { + public DomExtensionsRegistrarImpl extend(@NotNull final Project project, + @NotNull final DomInvocationHandler handler, + @Nullable DomExtensionsRegistrarImpl registrar) { + if (myDomClass == null) { try { myDomClass = findClass(domClassName); - myExtender = instantiate(extenderClassName, project.getPicoContainer()); } - catch(Exception e) { + catch (Exception e) { LOG.error(e); return null; } } - if (myDomClass.isAssignableFrom(handler.getRawType())) { - if (!myExtender.supportsStubs() && XmlUtil.isStubBuilding()) return registrar; - if (registrar == null) { - registrar = new DomExtensionsRegistrarImpl(); - } - //noinspection unchecked - myExtender.registerExtensions(handler.getProxy(), registrar); + + if (!myDomClass.isAssignableFrom(handler.getRawType())) { + return registrar; } + + + if (myExtender == null) { + try { + myExtender = instantiate(extenderClassName, project.getPicoContainer()); + } + catch (Exception e) { + LOG.error(e); + return null; + } + } + + if (!myExtender.supportsStubs() && XmlUtil.isStubBuilding()) { + return registrar; + } + + if (registrar == null) { + registrar = new DomExtensionsRegistrarImpl(); + } + //noinspection unchecked + myExtender.registerExtensions(handler.getProxy(), registrar); + return registrar; } - } From 380fcc6aaf76ab5495f0d450edd2f495213851ed Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 15 Apr 2015 10:17:31 +0300 Subject: [PATCH 33/85] DomExtenderEP: thread safety --- .../src/com/intellij/util/xml/reflect/DomExtenderEP.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtenderEP.java b/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtenderEP.java index 16ba3ab828ec..3a30de2d81b3 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtenderEP.java +++ b/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtenderEP.java @@ -39,8 +39,8 @@ public class DomExtenderEP extends AbstractExtensionPointBean { @Attribute("extenderClass") public String extenderClassName; - private Class myDomClass; - private DomExtender myExtender; + private volatile Class myDomClass; + private volatile DomExtender myExtender; @Nullable public DomExtensionsRegistrarImpl extend(@NotNull final Project project, From 217b46615abee70eb0f99ffec4a7437bdb73592d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yann=20C=C3=A9bron?= Date: Wed, 15 Apr 2015 10:52:01 +0200 Subject: [PATCH 34/85] ActionsTreeTest#testPresentation: collect all errors instead of failing on first one --- .../intellij/openapi/keymap/impl/ui/ActionsTreeTest.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/keymap/impl/ui/ActionsTreeTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/keymap/impl/ui/ActionsTreeTest.java index 465b570afe81..6e9b494183c1 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/keymap/impl/ui/ActionsTreeTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/keymap/impl/ui/ActionsTreeTest.java @@ -28,6 +28,7 @@ import com.intellij.openapi.keymap.impl.ShortcutRestrictions; import com.intellij.openapi.util.text.StringUtil; import com.intellij.testFramework.LightPlatformCodeInsightTestCase; import com.intellij.testFramework.PlatformTestCase; +import com.intellij.util.SmartList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.picocontainer.MutablePicoContainer; @@ -208,6 +209,8 @@ public class ActionsTreeTest extends LightPlatformCodeInsightTestCase { public void testPresentation() { ActionManager manager = ActionManager.getInstance(); + + List failures = new SmartList(); for (String id : manager.getActionIds("")) { if (!ACTION_WITHOUT_TEXT_AND_DESCRIPTION.equals(id)) { try { @@ -224,8 +227,8 @@ public class ActionsTreeTest extends LightPlatformCodeInsightTestCase { if (action instanceof ActionGroup) { System.out.println("ignored action group: " + message); } - else { - assertFalse("no text: " + message, StringUtil.isEmpty(action.getTemplatePresentation().getText())); + else if (StringUtil.isEmpty(action.getTemplatePresentation().getText())) { + failures.add("no text: " + message); } } catch (PluginException exception) { @@ -233,6 +236,8 @@ public class ActionsTreeTest extends LightPlatformCodeInsightTestCase { } } } + + assertEmpty(failures); } private static void checkPresentationProperty(String name, String message, Object expected, Object actual) { From 45b14d86fa214f8b84625bee37b4d95ba88fd49c Mon Sep 17 00:00:00 2001 From: nik Date: Wed, 15 Apr 2015 12:06:29 +0300 Subject: [PATCH 35/85] IDEA-139162: "Show Excluded Files" cannot be disabled while "Indexing" --- .../src/com/intellij/ide/projectView/impl/ProjectViewPane.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewPane.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewPane.java index 788a6d48366f..9f15bffa9a88 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewPane.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewPane.java @@ -35,6 +35,7 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.JDOMExternalizerUtil; @@ -191,7 +192,7 @@ public class ProjectViewPane extends AbstractProjectViewPSIPane { } } - private final class ShowExcludedFilesAction extends ToggleAction { + private final class ShowExcludedFilesAction extends ToggleAction implements DumbAware { private ShowExcludedFilesAction() { super(IdeBundle.message("action.show.excluded.files"), IdeBundle.message("action.show.hide.excluded.files"), null); } From 6ed7c34995acc18bc6ec8fe1cd6ff41cac8e7330 Mon Sep 17 00:00:00 2001 From: nik Date: Wed, 15 Apr 2015 12:12:51 +0300 Subject: [PATCH 36/85] JDK editor: select correct JDK home directory in file chooser when '+' button is pressed after changing JDK home path --- .../src/com/intellij/openapi/projectRoots/ui/SdkEditor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/java/idea-ui/src/com/intellij/openapi/projectRoots/ui/SdkEditor.java b/java/idea-ui/src/com/intellij/openapi/projectRoots/ui/SdkEditor.java index 0c41048c0db8..ae544ea419ed 100644 --- a/java/idea-ui/src/com/intellij/openapi/projectRoots/ui/SdkEditor.java +++ b/java/idea-ui/src/com/intellij/openapi/projectRoots/ui/SdkEditor.java @@ -331,7 +331,9 @@ public class SdkEditor implements Configurable, Place.Navigator { } sdkModificator = dummySdk.getSdkModificator(); for (OrderRootType type : myPathEditors.keySet()) { - myPathEditors.get(type).addPaths(sdkModificator.getRoots(type)); + SdkPathEditor pathEditor = myPathEditors.get(type); + pathEditor.setAddBaseDir(dummySdk.getHomeDirectory()); + pathEditor.addPaths(sdkModificator.getRoots(type)); } mySdkModel.getMulticaster().sdkHomeSelected(dummySdk, homePath); } From 4b640da91815aa6c1179f8f0ee14c6c08109aaef Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 15 Apr 2015 11:40:16 +0200 Subject: [PATCH 37/85] IDEA-139164 (check tools.jar class w/o init; catch linkage errors) --- .../platform-impl/src/com/intellij/idea/StartupUtil.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/idea/StartupUtil.java b/platform/platform-impl/src/com/intellij/idea/StartupUtil.java index e4805a1efff8..6e0348d8e0d3 100644 --- a/platform/platform-impl/src/com/intellij/idea/StartupUtil.java +++ b/platform/platform-impl/src/com/intellij/idea/StartupUtil.java @@ -124,7 +124,7 @@ public class StartupUtil { if (!"true".equals(System.getProperty("idea.no.jre.check"))) { try { // try to find a class from tools.jar - Class.forName("com.sun.jdi.Field"); + Class.forName("com.sun.jdi.Field", false, StartupUtil.class.getClassLoader()); } catch (ClassNotFoundException e) { String message = "'tools.jar' seems to be not in " + ApplicationNamesInfo.getInstance().getProductName() + " classpath.\n" + @@ -132,6 +132,12 @@ public class StartupUtil { Main.showMessage("JDK Required", message, true); return false; } + catch (LinkageError e) { + String message = "Cannot load a class from 'tools.jar': " + e.getMessage() + "\n" + + "Please ensure JAVA_HOME points to JDK rather than JRE."; + Main.showMessage("JDK Required", message, true); + return false; + } if (StringUtil.containsIgnoreCase(System.getProperty("java.vm.name", ""), "OpenJDK") && !SystemInfo.isJavaVersionAtLeast("1.7")) { String message = "OpenJDK 6 is not supported. Please use Oracle Java or newer OpenJDK."; From 510dfb990fd1c14548521fbdc2e664e5379385da Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 15 Apr 2015 12:39:31 +0300 Subject: [PATCH 38/85] DomExtenderEP: in case of errors, don't ignore already registered extensions --- .../src/com/intellij/util/xml/reflect/DomExtenderEP.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtenderEP.java b/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtenderEP.java index 3a30de2d81b3..f051c2d1d879 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtenderEP.java +++ b/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtenderEP.java @@ -52,7 +52,7 @@ public class DomExtenderEP extends AbstractExtensionPointBean { } catch (Exception e) { LOG.error(e); - return null; + return registrar; } } @@ -67,7 +67,7 @@ public class DomExtenderEP extends AbstractExtensionPointBean { } catch (Exception e) { LOG.error(e); - return null; + return registrar; } } From 730e9efc47c5e9166250baa0a9502e8f66264352 Mon Sep 17 00:00:00 2001 From: Andrey Vokin Date: Fri, 10 Apr 2015 19:17:47 +0300 Subject: [PATCH 39/85] RUBY-16568 Spell check does not work outside of HTML tags --- .../spellchecker/tokenizer/SpellcheckingStrategy.java | 5 +++++ spellchecker/testData/inspection/xmlWithMistakes/test.html | 3 +++ .../inspection/XmlWithMistakesInspectionTest.java | 4 ++++ 3 files changed, 12 insertions(+) create mode 100644 spellchecker/testData/inspection/xmlWithMistakes/test.html diff --git a/spellchecker/src/com/intellij/spellchecker/tokenizer/SpellcheckingStrategy.java b/spellchecker/src/com/intellij/spellchecker/tokenizer/SpellcheckingStrategy.java index ccdc1199f534..fa88295a75cd 100644 --- a/spellchecker/src/com/intellij/spellchecker/tokenizer/SpellcheckingStrategy.java +++ b/spellchecker/src/com/intellij/spellchecker/tokenizer/SpellcheckingStrategy.java @@ -25,6 +25,8 @@ import com.intellij.psi.*; import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.psi.xml.XmlText; +import com.intellij.psi.xml.XmlToken; +import com.intellij.psi.xml.XmlTokenType; import com.intellij.spellchecker.inspections.PlainTextSplitter; import com.intellij.spellchecker.inspections.TextSplitter; import com.intellij.spellchecker.quickfixes.AcceptWordAsCorrect; @@ -71,6 +73,9 @@ public class SpellcheckingStrategy { } return TEXT_TOKENIZER; } + if (element instanceof XmlToken && ((XmlToken)element).getTokenType() == XmlTokenType.XML_DATA_CHARACTERS) { + return TEXT_TOKENIZER; + } return EMPTY_TOKENIZER; } diff --git a/spellchecker/testData/inspection/xmlWithMistakes/test.html b/spellchecker/testData/inspection/xmlWithMistakes/test.html new file mode 100644 index 000000000000..4670964129ee --- /dev/null +++ b/spellchecker/testData/inspection/xmlWithMistakes/test.html @@ -0,0 +1,3 @@ +test +ttest +
test
\ No newline at end of file diff --git a/spellchecker/testSrc/com/intellij/spellchecker/inspection/XmlWithMistakesInspectionTest.java b/spellchecker/testSrc/com/intellij/spellchecker/inspection/XmlWithMistakesInspectionTest.java index a212cda9aad2..18f039b21533 100644 --- a/spellchecker/testSrc/com/intellij/spellchecker/inspection/XmlWithMistakesInspectionTest.java +++ b/spellchecker/testSrc/com/intellij/spellchecker/inspection/XmlWithMistakesInspectionTest.java @@ -27,4 +27,8 @@ public class XmlWithMistakesInspectionTest extends SpellcheckerInspectionTestCas public void testXml() { doTest("test.xml"); } + + public void testCharacterData() { + doTest("test.html"); + } } From a78ef337d8e6c871b5dd42549102a75b4f159474 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Wed, 15 Apr 2015 13:15:08 +0300 Subject: [PATCH 40/85] IDEA-138954 in debug window when click 'stop' the 're-run' buttons disappears for a short while causing all other buttons shift up and then down again --- .../src/com/intellij/execution/runners/FakeRerunAction.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/execution/runners/FakeRerunAction.java b/platform/lang-impl/src/com/intellij/execution/runners/FakeRerunAction.java index 34f96b0b51c6..be7a2da4ef41 100644 --- a/platform/lang-impl/src/com/intellij/execution/runners/FakeRerunAction.java +++ b/platform/lang-impl/src/com/intellij/execution/runners/FakeRerunAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -37,11 +37,11 @@ class FakeRerunAction extends AnAction implements DumbAware { if (environment != null) { presentation.setText(ExecutionBundle.message("rerun.configuration.action.name", environment.getRunProfile().getName())); presentation.setIcon(ExecutionManagerImpl.isProcessRunning(getDescriptor(event)) ? AllIcons.Actions.Restart : environment.getExecutor().getIcon()); - presentation.setEnabledAndVisible(isEnabled(event)); + presentation.setEnabled(isEnabled(event)); return; } - presentation.setEnabledAndVisible(false); + presentation.setEnabled(false); } @Override From 0d45ed8f659ade45f01970296699fb2d1fa5303f Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 15 Apr 2015 12:59:19 +0200 Subject: [PATCH 41/85] EA-67812 (NPE fixed) --- .../Java15APIUsageInspectionBase.java | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java index 63f50480bcfc..37a41a198182 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -21,6 +21,7 @@ import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInsight.daemon.GroupNames; import com.intellij.codeInsight.intention.QuickFixFactory; import com.intellij.codeInspection.*; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.ExtensionPoint; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.extensions.Extensions; @@ -31,6 +32,7 @@ import com.intellij.openapi.projectRoots.JavaSdkVersion; import com.intellij.openapi.projectRoots.JavaVersionService; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; @@ -43,7 +45,6 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.hash.HashSet; import gnu.trove.THashSet; import org.jdom.Element; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -52,6 +53,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.lang.ref.Reference; +import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -107,16 +109,17 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo return result; } - private static void loadForbiddenApi(@NonNls String fileName, Set set) { + private static void loadForbiddenApi(String fileName, Set set) { + URL resource = Java15APIUsageInspectionBase.class.getResource(fileName); + if (resource == null) { + Logger.getInstance(Java15APIUsageInspectionBase.class).warn("not found: " + fileName); + return; + } + try { - Class aClass = Java15APIUsageInspectionBase.class; - BufferedReader reader = new BufferedReader(new InputStreamReader(aClass.getResourceAsStream(fileName), CharsetToolkit.UTF8_CHARSET)); + BufferedReader reader = new BufferedReader(new InputStreamReader(resource.openStream(), CharsetToolkit.UTF8_CHARSET)); try { - do { - String line = reader.readLine(); - if (line == null) break; - set.add(line); - } while(true); + set.addAll(FileUtil.loadLines(reader)); } finally { reader.close(); @@ -330,6 +333,7 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo private void registerError(PsiJavaCodeReferenceElement reference, LanguageLevel api) { if (reference != null && isInProject(reference)) { + //noinspection DialogTitleCapitalization myHolder.registerProblem(reference, InspectionsBundle.message("inspection.1.5.problem.descriptor", getShortName(api))); } } @@ -382,7 +386,7 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo } /** - * please leave public for {@link #com.intellij.codeInspection.JavaAPIUsagesInspectionTest#testCollectSinceApiUsages} + * please leave public for JavaAPIUsagesInspectionTest#testCollectSinceApiUsages */ @Nullable public static String getSignature(@Nullable PsiMember member) { From 501c2f740e1522be97e6dc5ca2ff6ef886c2f5a3 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Wed, 15 Apr 2015 12:57:10 +0200 Subject: [PATCH 42/85] introduce disposeChildren (List) for accessing FlyweightCapableTreeStructure.disposeChildren(LighterASTNode[] nodesArray, length) --- .../intellij/lang/FCTSBackedLighterAST.java | 33 +++++++++++++++++++ .../src/com/intellij/lang/LighterAST.java | 2 ++ .../intellij/lang/TreeBackedLighterAST.java | 8 +++-- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/platform/core-api/src/com/intellij/lang/FCTSBackedLighterAST.java b/platform/core-api/src/com/intellij/lang/FCTSBackedLighterAST.java index dc241b519872..3eac3fb46a36 100644 --- a/platform/core-api/src/com/intellij/lang/FCTSBackedLighterAST.java +++ b/platform/core-api/src/com/intellij/lang/FCTSBackedLighterAST.java @@ -21,6 +21,7 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.diff.FlyweightCapableTreeStructure; import org.jetbrains.annotations.NotNull; +import java.util.AbstractList; import java.util.List; public class FCTSBackedLighterAST extends LighterAST { @@ -55,4 +56,36 @@ public class FCTSBackedLighterAST extends LighterAST { assert elements != null : myTreeStructure +" ("+parent+")"; return ContainerUtil.newArrayList(elements, 0, numKids); } + + public void disposeChildren(@NotNull List children) { + if (children instanceof LighterASTNodeList) { + LighterASTNodeList nodes = (LighterASTNodeList)children; + + myTreeStructure.disposeChildren(nodes.myElements, nodes.mySize); + } else { + LighterASTNode[] astNodes = new LighterASTNode[children.size()]; + myTreeStructure.disposeChildren(children.toArray(astNodes), astNodes.length); + } + } + + private static class LighterASTNodeList extends AbstractList { + private final int mySize; + private final LighterASTNode[] myElements; + + public LighterASTNodeList(int size, LighterASTNode[] elements) { + mySize = size; + myElements = elements; + } + + @Override + public LighterASTNode get(final int index) { + if (index < 0 || index >= mySize) throw new IndexOutOfBoundsException("index:" + index + " size:" + mySize); + return myElements[index]; + } + + @Override + public int size() { + return mySize; + } + } } diff --git a/platform/core-api/src/com/intellij/lang/LighterAST.java b/platform/core-api/src/com/intellij/lang/LighterAST.java index 73bedde677e9..7d60edca0591 100644 --- a/platform/core-api/src/com/intellij/lang/LighterAST.java +++ b/platform/core-api/src/com/intellij/lang/LighterAST.java @@ -44,4 +44,6 @@ public abstract class LighterAST { @NotNull public abstract List getChildren(@NotNull final LighterASTNode parent); + + public abstract void disposeChildren(@NotNull List children); } \ No newline at end of file diff --git a/platform/core-api/src/com/intellij/lang/TreeBackedLighterAST.java b/platform/core-api/src/com/intellij/lang/TreeBackedLighterAST.java index 8bfbd3c5da36..277e98650305 100644 --- a/platform/core-api/src/com/intellij/lang/TreeBackedLighterAST.java +++ b/platform/core-api/src/com/intellij/lang/TreeBackedLighterAST.java @@ -45,9 +45,8 @@ public class TreeBackedLighterAST extends LighterAST { @Override public List getChildren(@NotNull final LighterASTNode parent) { final ASTNode[] children = ((NodeWrapper)parent).myNode.getChildren(null); - if (children == null || children.length == 0) { - return ContainerUtil.emptyList(); - } + if (children.length == 0) return ContainerUtil.emptyList(); + List result = new ArrayList(children.length); for (final ASTNode child : children) { result.add(wrap(child)); @@ -55,6 +54,9 @@ public class TreeBackedLighterAST extends LighterAST { return result; } + public void disposeChildren(@NotNull List children) { + } + @NotNull private static LighterASTNode wrap(@NotNull ASTNode node) { return node.getFirstChildNode() == null && node.getTextLength() > 0 ? new TokenNodeWrapper(node) : new NodeWrapper(node); From 09dbb8b5fcbda5096618275daf4015e5e7c3a68f Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Wed, 15 Apr 2015 11:51:02 +0200 Subject: [PATCH 43/85] simplify testdata --- .../intellij/execution/junit/JUnitClasspathTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/junit/test/com/intellij/execution/junit/JUnitClasspathTest.java b/plugins/junit/test/com/intellij/execution/junit/JUnitClasspathTest.java index 27757569ab49..8e3b6eb16bcc 100644 --- a/plugins/junit/test/com/intellij/execution/junit/JUnitClasspathTest.java +++ b/plugins/junit/test/com/intellij/execution/junit/JUnitClasspathTest.java @@ -83,11 +83,11 @@ public class JUnitClasspathTest extends JavaCodeInsightFixtureTestCase { String file = preparePathsForComparison(FileUtil.loadFile(workingDirsFile), mod1, mod2); assertEquals("p\n" + "MODULE_1\n" + - "IDEA_HOME/lib/junit-4.12.jar;IDEA_HOME/java/mockJDK-1.7/jre/lib/annotations.jar;IDEA_HOME/java/mockJDK-1.7/jre/lib/rt.jar\n" + + "IDEA_HOME/lib/junit-4.12.jar;IDEA_HOME/java/mockJDK-1.8/jre/lib/rt.jar\n" + "1\n" + "p.T1\n" + "MODULE_2\n" + - "IDEA_HOME/lib/junit-4.12.jar;IDEA_HOME/java/mockJDK-1.7/jre/lib/annotations.jar;IDEA_HOME/java/mockJDK-1.7/jre/lib/rt.jar\n" + + "IDEA_HOME/lib/junit-4.12.jar;IDEA_HOME/java/mockJDK-1.8/jre/lib/rt.jar\n" + "1\n" + "p.T2\n", file); @@ -99,11 +99,11 @@ public class JUnitClasspathTest extends JavaCodeInsightFixtureTestCase { file = preparePathsForComparison(FileUtil.loadFile(workingDirsFile), mod1, mod2); assertEquals("p\n" + "MODULE_1\n" + - "IDEA_HOME/lib/serviceMessages.jar;IDEA_HOME/lib/junit-4.12.jar;IDEA_HOME/java/mockJDK-1.7/jre/lib/annotations.jar;IDEA_HOME/java/mockJDK-1.7/jre/lib/rt.jar\n" + + "IDEA_HOME/lib/serviceMessages.jar;IDEA_HOME/lib/junit-4.12.jar;IDEA_HOME/java/mockJDK-1.8/jre/lib/rt.jar\n" + "1\n" + "p.T1\n" + "MODULE_2\n" + - "IDEA_HOME/lib/serviceMessages.jar;IDEA_HOME/lib/junit-4.12.jar;IDEA_HOME/java/mockJDK-1.7/jre/lib/annotations.jar;IDEA_HOME/java/mockJDK-1.7/jre/lib/rt.jar\n" + + "IDEA_HOME/lib/serviceMessages.jar;IDEA_HOME/lib/junit-4.12.jar;IDEA_HOME/java/mockJDK-1.8/jre/lib/rt.jar\n" + "1\n" + "p.T2\n", file); } @@ -133,7 +133,7 @@ public class JUnitClasspathTest extends JavaCodeInsightFixtureTestCase { private Module setupModule(String moduleName, final String className) throws IOException { final VirtualFile root1 = myFixture.getTempDirFixture().findOrCreateDir(moduleName); final Module module = PsiTestUtil.addModule(getProject(), JavaModuleType.getModuleType(), moduleName, root1); - PsiTestUtil.removeAllRoots(module, IdeaTestUtil.getMockJdk17()); + PsiTestUtil.removeAllRoots(module, IdeaTestUtil.getMockJdk18()); PsiTestUtil.addSourceRoot(module, root1, true); myFixture.addFileToProject(moduleName + "/p/" + className + ".java", "package p;\n" + From 4dab496aaa838f4c3e4214a7e04a1baa73722742 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Wed, 15 Apr 2015 12:53:42 +0200 Subject: [PATCH 44/85] sm testng: run parameterized test with concrete parameter (IDEA-57906) --- .../JavaTestFrameworkRunnableState.java | 6 ++++ .../intellij/execution/junit/TestObject.java | 5 --- .../TestNGConfigurationProducer.java | 32 +++++++++++++----- .../TestNGInClassConfigurationProducer.java | 10 ++++++ .../src/org/testng/IDEARemoteTestNG.java | 17 ++++++++-- .../org/testng/IDEATestNGRemoteListener.java | 33 ++++++++++++++++--- .../src/org/testng/RemoteTestNGStarter.java | 8 +++-- 7 files changed, 88 insertions(+), 23 deletions(-) diff --git a/java/execution/impl/src/com/intellij/execution/JavaTestFrameworkRunnableState.java b/java/execution/impl/src/com/intellij/execution/JavaTestFrameworkRunnableState.java index bc75c4f1359d..f98778128f5d 100644 --- a/java/execution/impl/src/com/intellij/execution/JavaTestFrameworkRunnableState.java +++ b/java/execution/impl/src/com/intellij/execution/JavaTestFrameworkRunnableState.java @@ -45,6 +45,7 @@ import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Getter; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.registry.Registry; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.util.text.StringUtilRt; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.psi.JavaPsiFacade; @@ -195,6 +196,11 @@ public abstract class JavaTestFrameworkRunnableState 0) { diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationProducer.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationProducer.java index ce777ae383bb..950138652b22 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationProducer.java +++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationProducer.java @@ -20,21 +20,20 @@ */ package com.theoryinpractice.testng.configuration; -import com.intellij.execution.*; +import com.intellij.execution.JavaExecutionUtil; +import com.intellij.execution.Location; +import com.intellij.execution.RunManager; +import com.intellij.execution.RunnerAndConfigurationSettings; import com.intellij.execution.actions.ConfigurationContext; import com.intellij.execution.actions.RunConfigurationProducer; import com.intellij.execution.configurations.RunConfiguration; -import com.intellij.execution.impl.RunManagerImpl; -import com.intellij.execution.junit.JUnitUtil; import com.intellij.execution.junit.JavaRunConfigurationProducerBase; -import com.intellij.execution.junit.JavaRuntimeConfigurationProducerBase; +import com.intellij.execution.junit2.PsiMemberParameterizedLocation; import com.intellij.openapi.module.Module; import com.intellij.openapi.util.Comparing; -import com.intellij.psi.*; +import com.intellij.psi.PsiElement; import com.theoryinpractice.testng.model.TestData; -import org.jetbrains.annotations.NotNull; - -import java.util.List; +import org.testng.IDEATestNGRemoteListener; public abstract class TestNGConfigurationProducer extends JavaRunConfigurationProducerBase implements Cloneable { @@ -48,13 +47,17 @@ public abstract class TestNGConfigurationProducer extends JavaRunConfigurationPr return false; } final RunConfiguration predefinedConfiguration = context.getOriginalConfiguration(TestNGConfigurationType.getInstance()); - Location location = JavaExecutionUtil.stepIntoSingleClass(context.getLocation()); + final Location contextLocation = context.getLocation(); + Location location = JavaExecutionUtil.stepIntoSingleClass(contextLocation); final PsiElement element = location.getPsiElement(); RunnerAndConfigurationSettings template = RunManager.getInstance(location.getProject()).getConfigurationTemplate(getConfigurationFactory()); final Module predefinedModule = ((TestNGConfiguration)template.getConfiguration()).getConfigurationModule().getModule(); final String vmParameters = predefinedConfiguration instanceof TestNGConfiguration ? ((TestNGConfiguration)predefinedConfiguration).getVMParameters() : null; if (vmParameters != null && !Comparing.strEqual(vmParameters, testNGConfiguration.getVMParameters())) return false; + String paramSetName = contextLocation instanceof PsiMemberParameterizedLocation + ? getInvocationNumber(((PsiMemberParameterizedLocation)contextLocation).getParamSetName()) : null; + if (paramSetName != null && !Comparing.strEqual(paramSetName, testNGConfiguration.getProgramParameters())) return false; TestData testobject = testNGConfiguration.getPersistantData(); if (testobject != null) { if (testobject.isConfiguredByElement(element)) { @@ -65,4 +68,15 @@ public abstract class TestNGConfigurationProducer extends JavaRunConfigurationPr } return false; } + + protected static String getInvocationNumber(String str) { + final int indexOf = str.indexOf(IDEATestNGRemoteListener.INVOCATION_NUMBER); + if (indexOf > 0) { + final int lastIdx = str.indexOf(")", indexOf); + if (lastIdx > 0) { + return str.substring(indexOf + IDEATestNGRemoteListener.INVOCATION_NUMBER.length(), lastIdx); + } + } + return null; + } } \ No newline at end of file diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGInClassConfigurationProducer.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGInClassConfigurationProducer.java index fdc1387f3c57..497967e352cf 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGInClassConfigurationProducer.java +++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGInClassConfigurationProducer.java @@ -20,12 +20,14 @@ */ package com.theoryinpractice.testng.configuration; +import com.intellij.execution.Location; import com.intellij.execution.PsiLocation; import com.intellij.execution.RunnerAndConfigurationSettings; import com.intellij.execution.actions.ConfigurationContext; import com.intellij.execution.actions.ConfigurationFromContext; import com.intellij.execution.actions.RunConfigurationProducer; import com.intellij.execution.junit.InheritorChooser; +import com.intellij.execution.junit2.PsiMemberParameterizedLocation; import com.intellij.execution.junit2.info.MethodLocation; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; @@ -100,6 +102,14 @@ public class TestNGInClassConfigurationProducer extends TestNGConfigurationProdu return false; } + final Location contextLocation = context.getLocation(); + if (contextLocation instanceof PsiMemberParameterizedLocation) { + final String paramSetName = getInvocationNumber(((PsiMemberParameterizedLocation)contextLocation).getParamSetName()); + if (paramSetName != null) { + configuration.setProgramParameters(paramSetName); + } + } + PsiClass psiClass = null; PsiElement element = context.getPsiLocation(); while (element != null) { diff --git a/plugins/testng_rt/src/org/testng/IDEARemoteTestNG.java b/plugins/testng_rt/src/org/testng/IDEARemoteTestNG.java index f0f8fd96c975..cb55ecda63aa 100644 --- a/plugins/testng_rt/src/org/testng/IDEARemoteTestNG.java +++ b/plugins/testng_rt/src/org/testng/IDEARemoteTestNG.java @@ -19,14 +19,21 @@ package org.testng; import jetbrains.buildServer.messages.serviceMessages.ServiceMessage; import org.testng.collections.Lists; import org.testng.xml.XmlClass; +import org.testng.xml.XmlInclude; import org.testng.xml.XmlSuite; import org.testng.xml.XmlTest; +import java.util.Arrays; import java.util.HashMap; import java.util.List; public class IDEARemoteTestNG extends TestNG { + private final String myParam; + public IDEARemoteTestNG(String param) { + myParam = param; + } + private static void calculateAllSuites(List suites, List outSuites) { for (XmlSuite s : suites) { outSuites.add(s); @@ -50,6 +57,12 @@ public class IDEARemoteTestNG extends TestNG { for (XmlTest test : tests) { for (XmlClass aClass : test.getXmlClasses()) { System.out.println("##teamcity[suiteTreeStarted name=\'" + aClass.getName() + "\' locationHint=\'java:suite://" + aClass.getName() + "\']"); + if (myParam != null) { + for (XmlInclude include : aClass.getIncludedMethods()) { + aClass.setIncludedMethods(Arrays.asList(new XmlInclude(include.getName(), Arrays.asList(Integer.parseInt(myParam)), 0))); + } + } + System.out.println("##teamcity[suiteTreeEnded name=\'" + aClass.getName() + "\']"); } testCount += test.getClasses().size(); @@ -60,8 +73,8 @@ public class IDEARemoteTestNG extends TestNG { final HashMap map = new HashMap(); map.put("count", String.valueOf(testCount)); System.out.println(ServiceMessage.asString("testCount", map)); - addListener((ISuiteListener) new IDEATestNGRemoteListener()); - addListener((ITestListener) new IDEATestNGRemoteListener()); + addListener((ISuiteListener) new IDEATestNGRemoteListener(myParam)); + addListener((ITestListener) new IDEATestNGRemoteListener(myParam)); super.run(); } else { diff --git a/plugins/testng_rt/src/org/testng/IDEATestNGRemoteListener.java b/plugins/testng_rt/src/org/testng/IDEATestNGRemoteListener.java index f8413d1c7943..8aa1176d245a 100644 --- a/plugins/testng_rt/src/org/testng/IDEATestNGRemoteListener.java +++ b/plugins/testng_rt/src/org/testng/IDEATestNGRemoteListener.java @@ -15,7 +15,15 @@ import java.util.Map; */ public class IDEATestNGRemoteListener implements ISuiteListener, IResultListener{ + public static final String INVOCATION_NUMBER = "invocation number: "; + private final String myParam; private String myCurrentClassName; + private String myMethodName; + private int myInvocationCount = 0; + + public IDEATestNGRemoteListener(String param) { + myParam = param; + } public void onConfigurationSuccess(ITestResult itr) { //won't be called @@ -46,17 +54,31 @@ public class IDEATestNGRemoteListener implements ISuiteListener, IResultListener } System.out.println("##teamcity[testSuiteStarted name =\'" + className + "\']"); myCurrentClassName = className; + myInvocationCount = 0; } - String methodName = getMethodName(result); + String methodName = getMethodName(result, false); System.out.println("##teamcity[testStarted name=\'" + methodName + "\' locationHint=\'java:test://" + className + "." + methodName + "\']"); } - private static String getMethodName(ITestResult result) { + private String getMethodName(ITestResult result) { + return getMethodName(result, true); + } + + private String getMethodName(ITestResult result, boolean changeCount) { String methodName = result.getMethod().getMethodName(); final Object[] parameters = result.getParameters(); + if (changeCount) { + if (!methodName.equals(myMethodName)) { + myInvocationCount = 0; + myMethodName = methodName; + } + } if (parameters.length > 0) { - methodName += "[" + parameters[0].toString() + "]"; + methodName += "[" + parameters[0].toString() + (myParam == null ? (" (" + INVOCATION_NUMBER + myInvocationCount + ")") : "") + "]"; + if (changeCount) { + myInvocationCount++; + } } return methodName; } @@ -77,13 +99,14 @@ public class IDEATestNGRemoteListener implements ISuiteListener, IResultListener final Throwable ex = result.getThrowable(); final String trace = getTrace(ex); final Map attrs = new HashMap(); - attrs.put("name", getMethodName(result)); + final String methodName = getMethodName(result); + attrs.put("name", methodName); final String failureMessage = ex.getMessage(); attrs.put("message", failureMessage != null ? failureMessage : ""); attrs.put("details", trace); attrs.put("error", "true"); System.out.println(ServiceMessage.asString(ServiceMessageTypes.TEST_FAILED, attrs)); - System.out.println("##teamcity[testFinished name=\'" + getMethodName(result) + "\']"); + System.out.println("##teamcity[testFinished name=\'" + methodName + "\']"); } public void onTestSkipped(ITestResult result) { diff --git a/plugins/testng_rt/src/org/testng/RemoteTestNGStarter.java b/plugins/testng_rt/src/org/testng/RemoteTestNGStarter.java index 3aa287ec66a1..8068d099ee99 100644 --- a/plugins/testng_rt/src/org/testng/RemoteTestNGStarter.java +++ b/plugins/testng_rt/src/org/testng/RemoteTestNGStarter.java @@ -38,10 +38,14 @@ public class RemoteTestNGStarter { private static final String SOCKET = "-socket"; public static void main(String[] args) throws Exception { int i = 0; + String param = null; Vector resultArgs = new Vector(); for (; i < args.length; i++) { String arg = args[i]; - if (arg.startsWith(SOCKET)) { + if (arg.startsWith("@name")) { + param = arg.substring(5); + continue; + } else if (arg.startsWith(SOCKET)) { final int port = Integer.parseInt(arg.substring(SOCKET.length())); try { final Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), port); //start collecting tests @@ -95,7 +99,7 @@ public class RemoteTestNGStarter { } if (SM_RUNNER) { - final IDEARemoteTestNG testNG = new IDEARemoteTestNG(); + final IDEARemoteTestNG testNG = new IDEARemoteTestNG(param); CommandLineArgs cla = new CommandLineArgs(); RemoteArgs ra = new RemoteArgs(); new JCommander(Arrays.asList(cla, ra), (String[])resultArgs.toArray(new String[resultArgs.size()])); From 8dc5830a311be228580d6e0bc587a7bb21a3b619 Mon Sep 17 00:00:00 2001 From: "Vladimir.Orlov" Date: Wed, 15 Apr 2015 14:23:15 +0300 Subject: [PATCH 45/85] IDEA-139181 Move nsis_installer.gant to open-source codebase --- build/scripts/nsis_installer.gant | 104 ++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 build/scripts/nsis_installer.gant diff --git a/build/scripts/nsis_installer.gant b/build/scripts/nsis_installer.gant new file mode 100644 index 000000000000..a74a7e6f36f6 --- /dev/null +++ b/build/scripts/nsis_installer.gant @@ -0,0 +1,104 @@ +target(nsis: 'Build nsis windows installer') { + nsis_installer() +} + +String p(String key) { + return getProperty(key) +} + +def nsis_installer(pathsToInclude, stringsFile, pathsFile, outNamePrefix, includeToolsJar = true, associateIpr = true, + system_selector = getProperty("system_selector"), failOnSignError = false, outFileName = null, + boolean hasBundledJRE = false) { + // p.s: Groovy reevaluates default value of system_selector on each call, so it always uses actual property value. + + if (outFileName == null) { + outFileName = "${outNamePrefix}${buildNumber}" + } + + ant.taskdef(name: "nsis", classname: "com.intellij.internalUtilities.ant.NsiFiles", classpath: "${home}/community/build/lib/NsiFiles.jar") + + def box = paths.sandbox + ant.mkdir(dir: "$box/bin") + ant.mkdir(dir: "$box/nsiconf") + + if (!hasBundledJRE) { + ant.mkdir(dir: "$box/jre") + requireProperty("jdk_bundled_win", "1.7") + def jdk_bundled_version = p("jdk_bundled_win") == "1.8" ? "${home}/build/jdk/jdk8_redist.zip" : "${home}/build/jdk/jdk_redist.zip" + ant.unzip(dest: "$box/jre", src: "${jdk_bundled_version}") + + ant.copy(todir: "$box/bin") { + fileset(dir: "$box/jre/jre/bin") { + include(name: "msvcr71.dll") + } + } + } + + ant.copy(todir: "$box/nsiconf") { + fileset(dir: "$home/community/build/conf/nsis") { + include(name: "*") + exclude(name: "version*") + exclude(name: "strings*") + exclude(name: "paths*") + } + } + + if (isEap()) { + ant.copy(file: "$home/community/build/conf/nsis/version.eap.nsi", + tofile: "$box/nsiconf/version.nsi", overwrite: true) + } + else { + ant.copy(file: "$home/community/build/conf/nsis/version.nsi", + tofile: "$box/nsiconf/version.nsi", overwrite: true) + } + ant.copy(file: pathsFile, toFile: "$box/nsiconf/paths.nsi", overwrite: true) + + ant.nsis(instfile: "$box/nsiconf/idea_win.nsh", uninstfile: "$box/nsiconf/unidea_win.nsh") { + pathsToInclude.each { + fileset(dir: it, includes: "**/*") { + exclude(name: "**/idea.properties") + exclude(name: "**/*.vmoptions") + } + } + fileset(dir: box, includes: "bin/msvcr71.dll") + if (!hasBundledJRE) { + fileset(dir: box, includes: "jre/**/*") + if (includeToolsJar) { + fileset(dir: box) { + include(name: "jre/lib/tools.jar") + } + } + } + } + + ant.replace(file: "$stringsFile") { + replacefilter(token: "__VERSION_MAJOR__", value: p("component.version.major")) + replacefilter(token: "__VERSION_MINOR__", value: p("component.version.minor")) + } + ant.copy(file: stringsFile, toFile: "$box/nsiconf/strings.nsi", overwrite: true) + + ant.replace(file: "$box/nsiconf/version.nsi") { + replacefilter(token: "__BUILD_NUMBER__", value: buildNumber) + replacefilter(token: "__VERSION_MAJOR__", value: p("component.version.major")) + replacefilter(token: "__VERSION_MINOR__", value: p("component.version.minor")) + replacefilter(token: "__MIN_UPGRADE_BUILD__", value: p("component.install-over.minbuild")) + replacefilter(token: "__MAX_UPGRADE_BUILD__", value: p("component.install-over.maxbuild")) + replacefilter(token: "__UPGRADE_VERSION__", value: p("component.install-over.version")) + replacefilter(token: "__PRODUCT_PATHS_SELECTOR__", value: system_selector) + } + + ant.unzip(src: "$home/build/tools/NSIS.zip", dest: box) + ant.exec(command: "$box/NSIS/makensis.exe" + + " /DBASE_DIR=\"$home\"" + + " /DCOMMUNITY_DIR=\"$home/community\"" + + " /DIPR=\"${associateIpr}\"" + + " /DOUT_FILE=\"${outFileName}\"" + + " /DOUT_DIR=\"$paths.artifacts\"" + + " $box/nsiconf/idea.nsi") + + projectBuilder.stage("Signing binaries") + executeExternalAnt(["dirName": "${paths.artifacts}", "fileName": "${outFileName}.exe"], "$home/build/signBuild.xml") + projectBuilder.stage("Signing done") + + return "${paths.artifacts}/${outFileName}.exe" +} From 0da4ecc495ab92b91fa4745565aa6ca2f0415c1f Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Wed, 15 Apr 2015 14:41:29 +0300 Subject: [PATCH 46/85] IDEA-138939 Step into (F7) is not working on EJB --- resources/src/DebuggerSettings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/resources/src/DebuggerSettings.xml b/resources/src/DebuggerSettings.xml index e1371944741b..3686d0956edc 100644 --- a/resources/src/DebuggerSettings.xml +++ b/resources/src/DebuggerSettings.xml @@ -76,4 +76,16 @@