From eb22fcec0cfe9cca3da27e67fa161c861cada89d Mon Sep 17 00:00:00 2001 From: Stefan Aleksic Date: Fri, 9 Dec 2016 16:48:28 -0500 Subject: [PATCH 01/37] Update SvnBundle description to have more natural sentence --- .../svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties index d00dfa556813..d8eb50cba88f 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnBundle.properties @@ -1,7 +1,7 @@ subversion.name=Subversion subversion.executable.notification.title=Can't use Subversion command line client -subversion.executable.notification.description=Probably the path to Subversion executable is wrong. +subversion.executable.notification.description=The path to the Subversion executable is probably wrong. subversion.executable.too.old=Subversion command line client version is too old ({0}). subversion.roots.detection.errors.found.description=Errors found while detecting svn working copies. Fix it. @@ -684,4 +684,4 @@ dialog.create.branch.or.tag.from.working.copy.warning=Use this variant to

Each file that have revision different than root will be copied separately.\
It's recommended to update working copy before branch creation therefore. -exportable.SvnDiffSettings.presentable.name=Svn Diff \ No newline at end of file +exportable.SvnDiffSettings.presentable.name=Svn Diff From 8052d5198c9412f22d323224b38bd35424f0e0ff Mon Sep 17 00:00:00 2001 From: Stefan Aleksic Date: Fri, 9 Dec 2016 16:49:48 -0500 Subject: [PATCH 02/37] Update GitBundle error description to have more natural sentence --- plugins/git4idea/src/git4idea/i18n/GitBundle.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/git4idea/src/git4idea/i18n/GitBundle.properties b/plugins/git4idea/src/git4idea/i18n/GitBundle.properties index d5524281af8b..2c132323a59e 100644 --- a/plugins/git4idea/src/git4idea/i18n/GitBundle.properties +++ b/plugins/git4idea/src/git4idea/i18n/GitBundle.properties @@ -460,13 +460,13 @@ vfs.listener.delete.title=Delete Files from Git general.error = Git error executable.error.title = Git executable problem -executable.error.description = Git couldn't be started. Probably the path to Git executable is not valid. Fix it. +executable.error.description = Git couldn't be started. The path to the Git executable is probably not valid. Fix it. git.commit.message.empty=Please specify commit message git.commit.message.empty.title=Commit Message Is Empty git.executable.notification.title=Can't start Git -git.executable.notification.description=Probably the path to Git executable is not valid. +git.executable.notification.description=The path to Git executable is probably not valid. git.executable.dialog.title=Git executable git.executable.dialog.description=Specify the full path to Git executable git.executable.dialog.error=It doesn't appear to be a valid Git executable @@ -485,4 +485,4 @@ vcs.popup.git.github.section=GitHub vcs.history.action.gitlog=Select in Git Log vcs.history.action.gitlog.error=Git Log is not Ready -exportable.Git.Application.Settings.presentable.name=Git \ No newline at end of file +exportable.Git.Application.Settings.presentable.name=Git From 37df70453843e1b523cb6f31c872a0b5019f99aa Mon Sep 17 00:00:00 2001 From: Stefan Aleksic Date: Fri, 9 Dec 2016 16:52:00 -0500 Subject: [PATCH 03/37] Update Hg4idea error message description to have a more natural sentence --- .../resources/org/zmlx/hg4idea/HgVcsMessages.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties b/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties index caafa202b9e9..e40a5746050e 100644 --- a/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties +++ b/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties @@ -65,7 +65,7 @@ hg4idea.commit.partial.merge.title=Partial Merge Commit hg4idea.commit.error.messageEmpty=Please provide a commit message hg4idea.executable.notification.title=Can't start Mercurial -hg4idea.executable.notification.description=Probably the path to hg executable is not valid. +hg4idea.executable.notification.description=The path to the hg executable is probably not valid. hg4idea.init.dialog.title=Create Mercurial repository hg4idea.init.dialog.incorrect.path=The specified path is incorrect @@ -139,4 +139,4 @@ hg4idea.annotation.tool.tip=commit {0}\nAuthor: {1}\nDate: {2}\n\n{3} hg4idea.push.asNewBranch=push as &new remote branch hg4idea.push.bookmark=Book&mark -exportable.HgGlobalSettings.presentable.name=Mercurial \ No newline at end of file +exportable.HgGlobalSettings.presentable.name=Mercurial From d32b42b98c4be9332982f7cc64b2152e4f389ca2 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Wed, 5 Jul 2017 17:02:14 +0200 Subject: [PATCH 04/37] description for property jdk.regex.soe.workaround --- platform/util/resources/misc/registry.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 1d76fc1354d2..a04d4d041cb9 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -1166,3 +1166,4 @@ goto.action.skip.tophits.and.options.description=Skip top hits and options in go dsm.show.cycle.count.in.tooltip=false dsm.show.cycle.count.in.tooltip.description=Shows number of cycles found in the tooltip of the Toggle Cycles toolbar button after some calculation time jdk.regex.soe.workaround=true +jdk.regex.soe.workaround.description=In regular expression pattern replace choice \\n|. with . (and DOT_ALL option) to prevent stack overflow during matching From 27b1a0f50c912ce1343fed8c8a1d7e5f3987654a Mon Sep 17 00:00:00 2001 From: Kirill Kirichenko Date: Wed, 5 Jul 2017 17:52:21 +0300 Subject: [PATCH 05/37] IDEA-175445 UI Inspector uses user object instead of node for trees --- .../src/com/intellij/internal/inspector/UiInspectorAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/internal/inspector/UiInspectorAction.java b/platform/platform-impl/src/com/intellij/internal/inspector/UiInspectorAction.java index 1174a2e37d53..8a8f833f3fd1 100644 --- a/platform/platform-impl/src/com/intellij/internal/inspector/UiInspectorAction.java +++ b/platform/platform-impl/src/com/intellij/internal/inspector/UiInspectorAction.java @@ -1327,7 +1327,7 @@ public class UiInspectorAction extends ToggleAction implements DumbAware { DefaultMutableTreeNode node = (DefaultMutableTreeNode)value; Component rendererComponent = tree.getCellRenderer() - .getTreeCellRendererComponent(tree, node.getUserObject(), tree.getSelectionModel().isPathSelected(path), + .getTreeCellRendererComponent(tree, node, tree.getSelectionModel().isPathSelected(path), tree.isExpanded(path), node.isLeaf(), tree.getRowForPath(path), tree.hasFocus()); clickInfo.add(new PropertyBean(RENDERER_BOUNDS, tree.getPathBounds(path))); clickInfo.addAll(new InspectorTableModel(rendererComponent).myProperties); From 74bf6a7207a67fac35b21ffe8a0728b703f8dd23 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Wed, 5 Aug 2015 17:06:33 +0300 Subject: [PATCH 06/37] diff: add O(N) memory Myers algorithm implementation from Rivus --- .../src/com/intellij/util/diff/MyersLCS.java | 221 ++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 platform/util/src/com/intellij/util/diff/MyersLCS.java diff --git a/platform/util/src/com/intellij/util/diff/MyersLCS.java b/platform/util/src/com/intellij/util/diff/MyersLCS.java new file mode 100644 index 000000000000..27bdf11c5cbc --- /dev/null +++ b/platform/util/src/com/intellij/util/diff/MyersLCS.java @@ -0,0 +1,221 @@ +/* + * 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.util.diff; + +import java.util.BitSet; + +/** + * Algorithm for finding the longest common subsequence of two strings + * Based on E.W. Myers / An O(ND) Difference Algorithm and Its Variations / 1986 + * O(ND) runtime, O(N) memory + *

+ * Created by Anton Bannykh + */ +class MyersLCS { + private final int[] myFirst; + private final int[] mySecond; + + private final int myStart1; + private final int myStart2; + private final int myCount1; + private final int myCount2; + + private final BitSet myChanges1; + private final BitSet myChanges2; + + private final int[] VForward; + private final int[] VBackward; + + public MyersLCS(int[] first, int[] second) { + this(first, second, 0, first.length, 0, second.length, new BitSet(first.length), new BitSet(second.length)); + } + + public MyersLCS(int[] first, int[] second, int start1, int count1, int start2, int count2, BitSet changes1, BitSet changes2) { + myFirst = first; + mySecond = second; + myStart1 = start1; + myStart2 = start2; + myCount1 = count1; + myCount2 = count2; + + myChanges1 = changes1; + myChanges2 = changes2; + + myChanges1.set(myStart1, myStart1 + myCount1); + myChanges2.set(myStart2, myStart2 + myCount2); + + final int totalSequenceLength = myCount1 + myCount2; + VForward = new int[totalSequenceLength + 1]; + VBackward = new int[totalSequenceLength + 1]; + } + + /** + * Runs O(ND) Myers algorithm where D is bound by A + B * sqrt(N) + *

+ * Under certains assumptions about the distribution of the elements of the sequences the expected + * running time of the myers algorithm is O(N + D^2). Thus under given constraints it reduces to O(N). + */ + public void executeLinear() { + try { + execute(20000 + 10 * (int)Math.sqrt(myCount1 + myCount2), false); + } + catch (FilesTooBigForDiffException e) { + throw new IllegalStateException(e); // should not happen + } + } + + public void execute() { + try { + execute(myCount1 + myCount2, false); + } + catch (FilesTooBigForDiffException e) { + throw new IllegalStateException(e); // should not happen + } + } + + public void executeWithThreshold() throws FilesTooBigForDiffException { + execute(20000 + 10 * (int)Math.sqrt(myCount1 + myCount2), true); + } + + private void execute(int threshold, boolean throwException) throws FilesTooBigForDiffException { + if (myCount1 == 0 || myCount2 == 0) return; + execute(0, myCount1, 0, myCount2, threshold, throwException); + } + + //LCS( old[oldStart, oldEnd), new[newStart, newEnd) ) + private void execute(int oldStart, int oldEnd, int newStart, int newEnd, int differenceEstimate, + boolean throwException) throws FilesTooBigForDiffException { + assert oldStart <= oldEnd && newStart <= newEnd; + if (oldStart < oldEnd && newStart < newEnd) { + final int oldLength = oldEnd - oldStart; + final int newLength = newEnd - newStart; + VForward[newLength + 1] = 0; + VBackward[newLength + 1] = 0; + final int halfD = (differenceEstimate + 1) / 2; + int xx, kk, td; + xx = kk = td = -1; + + loop: + for (int d = 0; d <= halfD; ++d) { + final int L = newLength + Math.max(-d, -newLength + ((d ^ newLength) & 1)); + final int R = newLength + Math.min(d, oldLength - ((d ^ oldLength) & 1)); + for (int k = L; k <= R; k += 2) { + int x = k == L || k != R && VForward[k - 1] < VForward[k + 1] ? VForward[k + 1] : VForward[k - 1] + 1; + int y = x - k + newLength; + x += commonSubsequenceLengthForward(oldStart + x, newStart + y, + Math.min(oldEnd - oldStart - x, newEnd - newStart - y)); + VForward[k] = x; + } + + if ((oldLength - newLength) % 2 != 0) { + for (int k = L; k <= R; k += 2) { + if (oldLength - (d - 1) <= k && k <= oldLength + (d - 1)) { + if (VForward[k] + VBackward[newLength + oldLength - k] >= oldLength) { + xx = VForward[k]; + kk = k; + td = 2 * d - 1; + break loop; + } + } + } + } + + for (int k = L; k <= R; k += 2) { + int x = k == L || k != R && VBackward[k - 1] < VBackward[k + 1] ? VBackward[k + 1] : VBackward[k - 1] + 1; + int y = x - k + newLength; + x += commonSubsequenceLengthBackward(oldEnd - 1 - x, newEnd - 1 - y, + Math.min(oldEnd - oldStart - x, newEnd - newStart - y)); + VBackward[k] = x; + } + + if ((oldLength - newLength) % 2 == 0) { + for (int k = L; k <= R; k += 2) { + if (oldLength - d <= k && k <= oldLength + d) { + if (VForward[oldLength + newLength - k] + VBackward[k] >= oldLength) { + xx = oldLength - VBackward[k]; + kk = oldLength + newLength - k; + td = 2 * d; + break loop; + } + } + } + } + } + + if (td > 1) { + final int yy = xx - kk + newLength; + final int oldDiff = (td + 1) / 2; + if (0 < xx && 0 < yy) execute(oldStart, oldStart + xx, newStart, newStart + yy, oldDiff, throwException); + if (oldStart + xx < oldEnd && newStart + yy < newEnd) execute(oldStart + xx, oldEnd, newStart + yy, newEnd, td - oldDiff, throwException); + } + else if (td >= 0) { + int x = oldStart; + int y = newStart; + while (x < oldEnd && y < newEnd) { + final int commonLength = commonSubsequenceLengthForward(x, y, Math.min(oldEnd - x, newEnd - y)); + if (commonLength > 0) { + addUnchanged(x, y, commonLength); + x += commonLength; + y += commonLength; + } + else if (oldEnd - oldStart > newEnd - newStart) { + ++x; + } + else { + ++y; + } + } + } + else { + //The difference is more than the given estimate + if (throwException) throw new FilesTooBigForDiffException(0); + } + } + } + + private void addUnchanged(int start1, int start2, int count) { + myChanges1.set(myStart1 + start1, myStart1 + start1 + count, false); + myChanges2.set(myStart2 + start2, myStart2 + start2 + count, false); + } + + private int commonSubsequenceLengthForward(int oldIndex, int newIndex, int maxLength) { + int x = oldIndex; + int y = newIndex; + + maxLength = Math.min(maxLength, Math.min(myCount1 - oldIndex, myCount2 - newIndex)); + while (x - oldIndex < maxLength && myFirst[myStart1 + x] == mySecond[myStart2 + y]) { + ++x; + ++y; + } + return x - oldIndex; + } + + private int commonSubsequenceLengthBackward(int oldIndex, int newIndex, int maxLength) { + int x = oldIndex; + int y = newIndex; + + maxLength = Math.min(maxLength, Math.min(oldIndex, newIndex) + 1); + while (oldIndex - x < maxLength && myFirst[myStart1 + x] == mySecond[myStart2 + y]) { + --x; + --y; + } + return oldIndex - x; + } + + public BitSet[] getChanges() { + return new BitSet[]{myChanges1, myChanges2}; + } +} From 4fae3323b8984a899b372a1da3be8d7457ec9ede Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Thu, 6 Aug 2015 15:19:37 +0300 Subject: [PATCH 07/37] diff: add auto test for IntLCS --- .../intellij/util/diff/IntLCSAutoTest.java | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 platform/util/testSrc/com/intellij/util/diff/IntLCSAutoTest.java diff --git a/platform/util/testSrc/com/intellij/util/diff/IntLCSAutoTest.java b/platform/util/testSrc/com/intellij/util/diff/IntLCSAutoTest.java new file mode 100644 index 000000000000..2d254bebc492 --- /dev/null +++ b/platform/util/testSrc/com/intellij/util/diff/IntLCSAutoTest.java @@ -0,0 +1,140 @@ +/* + * 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.util.diff; + +import junit.framework.TestCase; +import org.jetbrains.annotations.NotNull; + +import java.util.BitSet; +import java.util.Random; + +public class IntLCSAutoTest extends TestCase { + private final Random RNG = new Random(); + + private static final int ITERATIONS = 1000; + private static final int MAX_LENGTH = 300; + private static final int CHAR_COUNT = 20; + + private enum Type {IntLCS, MyersLCS, PatienceLCS} + + public void testIntLCS() throws Exception { + for (int i = 0; i < ITERATIONS; i++) { + doTestLCS(MAX_LENGTH, CHAR_COUNT, Type.IntLCS); + } + } + + public void testMyersLCS() throws Exception { + for (int i = 0; i < ITERATIONS; i++) { + doTestLCS(MAX_LENGTH, CHAR_COUNT, Type.MyersLCS); + } + } + + public void testPatienceLCS() throws Exception { + for (int i = 0; i < ITERATIONS; i++) { + doTestLCS(MAX_LENGTH, CHAR_COUNT, Type.PatienceLCS); + } + } + + public void testMyersImplementations() throws Exception { + for (int i = 0; i < ITERATIONS; i++) { + doTestMyersImplementations(MAX_LENGTH, CHAR_COUNT); + } + } + + private void doTestLCS(int maxLength, int charCount, @NotNull Type type) throws FilesTooBigForDiffException { + int[] sequence1 = generateSequence(maxLength, charCount); + int[] sequence2 = generateSequence(maxLength, charCount); + + int start1 = RNG.nextInt(sequence1.length); + int start2 = RNG.nextInt(sequence2.length); + int count1 = RNG.nextInt(sequence1.length - start1); + int count2 = RNG.nextInt(sequence2.length - start2); + + BitSet changes1 = new BitSet(sequence1.length); + BitSet changes2 = new BitSet(sequence2.length); + + switch (type) { + case IntLCS: + IntLCS intLCS = new IntLCS(sequence1, sequence2, start1, count1, start2, count2, changes1, changes2); + intLCS.execute(); + break; + case MyersLCS: + MyersLCS myersLCS = new MyersLCS(sequence1, sequence2, start1, count1, start2, count2, changes1, changes2); + myersLCS.execute(); + break; + case PatienceLCS: + PatienceIntLCS patienceLCS = new PatienceIntLCS(sequence1, sequence2, start1, count1, start2, count2, changes1, changes2); + patienceLCS.execute(); + break; + } + + verifyLCS(sequence1, sequence2, start1, count1, start2, count2, changes1, changes2); + } + + private void doTestMyersImplementations(int maxLength, int charCount) throws FilesTooBigForDiffException { + generateSequence(maxLength, charCount); + + int[] sequence1 = generateSequence(maxLength, charCount); + int[] sequence2 = generateSequence(maxLength, charCount); + + int start1 = RNG.nextInt(sequence1.length); + int start2 = RNG.nextInt(sequence2.length); + int count1 = RNG.nextInt(sequence1.length - start1); + int count2 = RNG.nextInt(sequence2.length - start2); + + BitSet intLCSChanges1 = new BitSet(sequence1.length); + BitSet intLCSChanges2 = new BitSet(sequence2.length); + + IntLCS intLCS = new IntLCS(sequence1, sequence2, start1, count1, start2, count2, intLCSChanges1, intLCSChanges2); + intLCS.execute(); + + BitSet myersLCSChanges1 = new BitSet(sequence1.length); + BitSet myersLCSChanges2 = new BitSet(sequence2.length); + + MyersLCS myersLCS = new MyersLCS(sequence1, sequence2, start1, count1, start2, count2, myersLCSChanges1, myersLCSChanges2); + myersLCS.execute(); + + assertEquals(intLCSChanges1.cardinality(), myersLCSChanges1.cardinality()); + assertEquals(intLCSChanges2.cardinality(), myersLCSChanges2.cardinality()); + } + + private static void verifyLCS(@NotNull int[] sequence1, @NotNull int[] sequence2, + int start1, int count1, int start2, int count2, + @NotNull BitSet changes1, @NotNull BitSet changes2) { + int index1 = changes1.nextClearBit(start1); + int index2 = changes2.nextClearBit(start2); + + while (index1 < start1 + count1 || index2 < start2 + count2) { + assertTrue(index1 < start1 + count1); + assertTrue(index2 < start2 + count2); + + assertEquals(sequence1[index1], sequence2[index2]); + + index1 = changes1.nextClearBit(index1 + 1); + index2 = changes2.nextClearBit(index2 + 1); + } + assertTrue(index1 >= start1 + count1 && index2 >= start2 + count2); + } + + @NotNull + private int[] generateSequence(int maxLength, int charCount) { + int[] result = new int[RNG.nextInt(maxLength / 2) + maxLength / 2]; + for (int i = 0; i < result.length; i++) { + result[i] = RNG.nextInt(charCount); + } + return result; + } +} From 507c61b7c133a62f4509b5805603ccfdf466f760 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Mon, 27 Mar 2017 14:56:51 +0300 Subject: [PATCH 08/37] diff: use MyersLCS as default LCS algorithm --- .../diff/comparison/WordComparisonUtilTest.kt | 6 ++--- .../com/intellij/diff/merge/MergeTest.kt | 24 ++++++++++++------- .../util/resources/misc/registry.properties | 3 +++ .../util/src/com/intellij/util/diff/Diff.java | 4 ++-- .../diff/FilesTooBigForDiffException.java | 1 + .../src/com/intellij/util/diff/MyersLCS.java | 9 ++++--- .../intellij/util/diff/PatienceIntLCS.java | 4 ++-- 7 files changed, 33 insertions(+), 18 deletions(-) diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/WordComparisonUtilTest.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/WordComparisonUtilTest.kt index 0d0cd1215662..5056d96884f3 100644 --- a/platform/diff-impl/tests/com/intellij/diff/comparison/WordComparisonUtilTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/WordComparisonUtilTest.kt @@ -378,9 +378,9 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() { lines_inner { ("A B_C D" - "A_B C_D") - (" -- - " - " -- - ").default() - (" -- - " - " -- ").trim() - (" - " - " - ").ignore() + (" - -- " - " - -- ").default() + (" - -- " - " -- ").trim() + (" - " - " - ").ignore() testAll() } diff --git a/platform/diff-impl/tests/com/intellij/diff/merge/MergeTest.kt b/platform/diff-impl/tests/com/intellij/diff/merge/MergeTest.kt index 88c9fd49b069..ef765b7d5306 100644 --- a/platform/diff-impl/tests/com/intellij/diff/merge/MergeTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/merge/MergeTest.kt @@ -593,7 +593,8 @@ class MergeTest : MergeTestBase() { 5 ====== modify 6 ====== - 7 ======""".trimIndent() + 7 ====== + 8 ======""".trimIndent() val text2 = """ 1 ====== 2 ====== @@ -604,8 +605,9 @@ class MergeTest : MergeTestBase() { 5 ====== modify 6 ====== + 7 ====== delete modify - 7 ======""".trimIndent() + 8 ======""".trimIndent() val text3 = """ 1 ====== 2 ====== @@ -616,8 +618,9 @@ class MergeTest : MergeTestBase() { 5 ====== modify right 6 ====== + 7 ====== modify - 7 ======""".trimIndent() + 8 ======""".trimIndent() testN(text1, text2, text3) { checkUndo(1) { @@ -636,8 +639,9 @@ class MergeTest : MergeTestBase() { 5 ====== modify right 6 ====== + 7 ====== delete modify - 7 ======""".trimIndent()) + 8 ======""".trimIndent()) } testN(text1, text2, text3) { @@ -658,8 +662,9 @@ class MergeTest : MergeTestBase() { 5 ====== modify 6 ====== + 7 ====== delete modify - 7 ======""".trimIndent()) + 8 ======""".trimIndent()) } testN(text1, text2, text3) { @@ -678,8 +683,9 @@ class MergeTest : MergeTestBase() { 5 ====== modify right 6 ====== + 7 ====== delete modify - 7 ======""".trimIndent()) + 8 ======""".trimIndent()) } testN(text1, text2, text3) { @@ -701,8 +707,9 @@ class MergeTest : MergeTestBase() { 5 ====== modify right 6 ====== + 7 ====== delete modify - 7 ======""".trimIndent()) + 8 ======""".trimIndent()) } testN(text1, text2, text3) { @@ -723,8 +730,9 @@ class MergeTest : MergeTestBase() { 5 ====== USER modify 6 ====== + 7 ====== delete modify - 7 ======""".trimIndent()) + 8 ======""".trimIndent()) } } } diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index a04d4d041cb9..0d958d548f5c 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -560,6 +560,9 @@ diff.unimportant.line.char.count.description=Threshold number of non-space chara diff.unimportant.line.char.count.restartRequired=true diff.maximum.changes.array.size=10000000 diff.maximum.changes.array.size.restartRequired=true +diff.delta.threshold.size=20000 +diff.delta.threshold.size.description=Do not try to compare files if difference is bigger than given threshold +diff.delta.threshold.size.restartRequired=true diff.divider.repainting.disable.blitting=true diff.divider.repainting.disable.blitting.description=Fix painting glitch on scrolling in diff - disable BLIT_SCROLL_MODE to force repainting with RepaintManager diff.merge.resolve.conflict.action.visible=true diff --git a/platform/util/src/com/intellij/util/diff/Diff.java b/platform/util/src/com/intellij/util/diff/Diff.java index ddac3294c680..92051f57dad9 100644 --- a/platform/util/src/com/intellij/util/diff/Diff.java +++ b/platform/util/src/com/intellij/util/diff/Diff.java @@ -111,8 +111,8 @@ public class Diff { } else { try { - IntLCS intLCS = new IntLCS(discarded[0], discarded[1]); - intLCS.execute(); + MyersLCS intLCS = new MyersLCS(discarded[0], discarded[1]); + intLCS.executeWithThreshold(); changes = intLCS.getChanges(); } catch (FilesTooBigForDiffException e) { diff --git a/platform/util/src/com/intellij/util/diff/FilesTooBigForDiffException.java b/platform/util/src/com/intellij/util/diff/FilesTooBigForDiffException.java index 01e3712c4086..4519766f2bf3 100644 --- a/platform/util/src/com/intellij/util/diff/FilesTooBigForDiffException.java +++ b/platform/util/src/com/intellij/util/diff/FilesTooBigForDiffException.java @@ -8,6 +8,7 @@ import com.intellij.openapi.util.registry.Registry; * Time: 10:30 PM */ public class FilesTooBigForDiffException extends Exception { + public static final int DELTA_THRESHOLD_SIZE = Registry.intValue("diff.delta.threshold.size"); // Limit for memory consumption in IntLCS algorithm. ~ 2000 changed lines, 50Mb memory public static final int MAX_BUFFER_LEN = Registry.intValue("diff.maximum.changes.array.size"); // Do not try to compare two lines by-word after this much fails. diff --git a/platform/util/src/com/intellij/util/diff/MyersLCS.java b/platform/util/src/com/intellij/util/diff/MyersLCS.java index 27bdf11c5cbc..11534b8e84ce 100644 --- a/platform/util/src/com/intellij/util/diff/MyersLCS.java +++ b/platform/util/src/com/intellij/util/diff/MyersLCS.java @@ -70,7 +70,8 @@ class MyersLCS { */ public void executeLinear() { try { - execute(20000 + 10 * (int)Math.sqrt(myCount1 + myCount2), false); + int threshold = 20000 + 10 * (int)Math.sqrt(myCount1 + myCount2); + execute(threshold, false); } catch (FilesTooBigForDiffException e) { throw new IllegalStateException(e); // should not happen @@ -87,12 +88,14 @@ class MyersLCS { } public void executeWithThreshold() throws FilesTooBigForDiffException { - execute(20000 + 10 * (int)Math.sqrt(myCount1 + myCount2), true); + int threshold = Math.max(20000 + 10 * (int)Math.sqrt(myCount1 + myCount2), + FilesTooBigForDiffException.DELTA_THRESHOLD_SIZE); + execute(threshold, true); } private void execute(int threshold, boolean throwException) throws FilesTooBigForDiffException { if (myCount1 == 0 || myCount2 == 0) return; - execute(0, myCount1, 0, myCount2, threshold, throwException); + execute(0, myCount1, 0, myCount2, Math.min(threshold, myCount1 + myCount2), throwException); } //LCS( old[oldStart, oldEnd), new[newStart, newEnd) ) diff --git a/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java b/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java index 0f885eca41cf..9a26a304d017 100644 --- a/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java +++ b/platform/util/src/com/intellij/util/diff/PatienceIntLCS.java @@ -86,8 +86,8 @@ class PatienceIntLCS { if (matching == null) { if (thresholdCheckCounter >= 0) checkReduction(count1, count2); - IntLCS intLCS = new IntLCS(myFirst, mySecond, start1, count1, start2, count2, myChanges1, myChanges2); - intLCS.execute(); + MyersLCS intLCS = new MyersLCS(myFirst, mySecond, start1, count1, start2, count2, myChanges1, myChanges2); + intLCS.executeLinear(); } else { int s1, s2, c1, c2; From fc95623a18c590f4f6e646ebe7263d2230209879 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Mon, 27 Mar 2017 15:11:38 +0300 Subject: [PATCH 09/37] diff: remove IntLCS --- .../util/resources/misc/registry.properties | 2 - .../diff/FilesTooBigForDiffException.java | 2 - .../src/com/intellij/util/diff/IntLCS.java | 151 ---------------- .../intellij/util/diff/LinkedDiffPaths.java | 166 ------------------ .../intellij/util/diff/IntLCSAutoTest.java | 46 +---- .../com/intellij/util/diff/IntLCSNewTest.java | 6 +- .../com/intellij/util/diff/IntLCSTest.java | 22 ++- .../util/diff/LinkedDiffPathsTest.java | 80 --------- .../intellij/util/diff/ReindexerNewTest.java | 20 +-- 9 files changed, 27 insertions(+), 468 deletions(-) delete mode 100644 platform/util/src/com/intellij/util/diff/IntLCS.java delete mode 100644 platform/util/src/com/intellij/util/diff/LinkedDiffPaths.java delete mode 100644 platform/util/testSrc/com/intellij/util/diff/LinkedDiffPathsTest.java diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 0d958d548f5c..24254d529f27 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -558,8 +558,6 @@ diff.verify.iterable.restartRequired=true diff.unimportant.line.char.count=3 diff.unimportant.line.char.count.description=Threshold number of non-space characters to mark line as unimportant diff.unimportant.line.char.count.restartRequired=true -diff.maximum.changes.array.size=10000000 -diff.maximum.changes.array.size.restartRequired=true diff.delta.threshold.size=20000 diff.delta.threshold.size.description=Do not try to compare files if difference is bigger than given threshold diff.delta.threshold.size.restartRequired=true diff --git a/platform/util/src/com/intellij/util/diff/FilesTooBigForDiffException.java b/platform/util/src/com/intellij/util/diff/FilesTooBigForDiffException.java index 4519766f2bf3..e535652fca11 100644 --- a/platform/util/src/com/intellij/util/diff/FilesTooBigForDiffException.java +++ b/platform/util/src/com/intellij/util/diff/FilesTooBigForDiffException.java @@ -9,8 +9,6 @@ import com.intellij.openapi.util.registry.Registry; */ public class FilesTooBigForDiffException extends Exception { public static final int DELTA_THRESHOLD_SIZE = Registry.intValue("diff.delta.threshold.size"); - // Limit for memory consumption in IntLCS algorithm. ~ 2000 changed lines, 50Mb memory - public static final int MAX_BUFFER_LEN = Registry.intValue("diff.maximum.changes.array.size"); // Do not try to compare two lines by-word after this much fails. public static final int MAX_BAD_LINES = 3; diff --git a/platform/util/src/com/intellij/util/diff/IntLCS.java b/platform/util/src/com/intellij/util/diff/IntLCS.java deleted file mode 100644 index 7fc7162f27ed..000000000000 --- a/platform/util/src/com/intellij/util/diff/IntLCS.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2000-2009 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.util.diff; - -import java.util.Arrays; -import java.util.BitSet; - -/** - * @author dyoma - */ -class IntLCS { - private final int[] myFirst; - private final int[] mySecond; - - private final int myStart1; - private final int myStart2; - - private final LinkedDiffPaths myPathsMatrix; - private final int[] myPrevPathKey; - private int[] myPrevEnds; - private int[] myCurrentEnds; - private final int myMaxX; - private final int myMaxY; - - private final BitSet myChanges1; - private final BitSet myChanges2; - - public IntLCS(int[] first, int[] second) { - this(first, second, 0, first.length, 0, second.length, new BitSet(first.length), new BitSet(second.length)); - } - - public IntLCS(int[] first, int[] second, int start1, int count1, int start2, int count2, BitSet changes1, BitSet changes2) { - myFirst = first; - mySecond = second; - myStart1 = start1; - myStart2 = start2; - myMaxX = count1; - myMaxY = count2; - - myChanges1 = changes1; - myChanges2 = changes2; - - myPathsMatrix = new LinkedDiffPaths(myMaxX, myMaxY); - myPrevPathKey = new int[myMaxX + myMaxY + 1]; - Arrays.fill(myPrevPathKey, -1); - myPrevEnds = new int[myMaxX + myMaxY + 1]; - myCurrentEnds = new int[myMaxX + myMaxY + 1]; - } - - public int execute() throws FilesTooBigForDiffException { - for (int d =0; d <= myMaxX + myMaxY; d++) { - int minDiag = -calcBound(myMaxY, d); - int maxDiag = calcBound(myMaxX, d); - if (d != 0) - System.arraycopy(myPrevEnds, minDiag + myMaxY, myCurrentEnds, minDiag + myMaxY, maxDiag - minDiag); - else { - int end = skipEquals(0, 0); - if (end > 0) { - int xy = (end) - 1; - myPrevPathKey[myMaxY] = myPathsMatrix.encodeStep(xy, xy, end, false, -1); - } - if (myMaxX == myMaxY && end == myMaxX) return 0; - myPrevEnds[myMaxY] = end; - continue; - } - for (int k = minDiag; k <= maxDiag; k += 2) { - int end; - if (k == -d) { - int prevEndV = myPrevEnds[k + 1 + myMaxY]; - int vertical = findDiagonalEnd(k + 1, prevEndV, true); - end = encodeStep(prevEndV, vertical, k, true); - } else if (k == d) { - int prevEndH = myPrevEnds[k - 1 + myMaxY]; - int horisontal = findDiagonalEnd(k - 1, prevEndH, false); - end = encodeStep(prevEndH, horisontal, k, false); - } else { - int prevEndH = myPrevEnds[k - 1 + myMaxY]; - int prevEndV = myPrevEnds[k + 1 + myMaxY]; - if (prevEndH+1 > prevEndV) { - int horisontal = findDiagonalEnd(k - 1, prevEndH, false); - end = encodeStep(prevEndH, horisontal, k, false); - } else { - int vertical = findDiagonalEnd(k + 1, prevEndV, true); - end = encodeStep(prevEndV, vertical, k, true); - } - } - myCurrentEnds[k + myMaxY] = end; - if (k == myMaxX - myMaxY && end == myMaxX) { - myPathsMatrix.applyChanges(myStart1, myStart2, myChanges1, myChanges2); - return d; - } - } - int[] temps = myCurrentEnds; - myCurrentEnds = myPrevEnds; - myPrevEnds = temps; - } - throw new RuntimeException(); - } - - public BitSet[] getChanges() { - return new BitSet[]{myChanges1, myChanges2}; - } - - private int findDiagonalEnd(int prevDiagonal, int prevEnd, boolean isVertical) { - int x = prevEnd; - int y = x - prevDiagonal; - if (isVertical) y++; - else x++; - return skipEquals(x, y); - } - - private int encodeStep(int prevEnd, int diagLength, int tDiagonal, boolean afterVertical) throws FilesTooBigForDiffException { - int end = prevEnd + diagLength; - int prevDiagonal = tDiagonal + myMaxY; - if (!afterVertical) end++; - if (afterVertical) prevDiagonal++; - else prevDiagonal--; - int x = end - 1; - int y = x - tDiagonal; - if (x == -1 || y == -1 || x >= myMaxX || y >= myMaxY) return end; - myPrevPathKey[tDiagonal + myMaxY] = myPathsMatrix.encodeStep(x, y, diagLength, afterVertical, myPrevPathKey[prevDiagonal]); - return end; - } - - private int calcBound(int bound, int d) { - return (d <= bound) ? d : 2 * bound - d; - } - - private int skipEquals(int x, int y) { - int skipped = 0; - while (x < myMaxX && y < myMaxY && myFirst[myStart1 + x] == mySecond[myStart2 + y]) { - skipped += 1; - x++; - y++; - } - return skipped; - } -} diff --git a/platform/util/src/com/intellij/util/diff/LinkedDiffPaths.java b/platform/util/src/com/intellij/util/diff/LinkedDiffPaths.java deleted file mode 100644 index bc7582077940..000000000000 --- a/platform/util/src/com/intellij/util/diff/LinkedDiffPaths.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2000-2009 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.util.diff; - -import java.util.BitSet; - -/** - * @author dyoma - */ -final class LinkedDiffPaths { - - private int[] mySteps = new int[10]; - private int[] myPrevSteps = new int[10]; - private int myPosition = 0; - private final int myMaxX; - private final int myMaxY; - private int myCornerIndex = -1; - private static final int VERTICAL_DIRECTION_FLAG = 1 << 31; - private static final int DISTANCE_MASK = ~VERTICAL_DIRECTION_FLAG; - - public LinkedDiffPaths(int maxX, int maxY) { - myMaxX = maxX; - myMaxY = maxY; - } - - public void applyChanges(final int start1, final int start2, final BitSet changes1, final BitSet changes2) { - decodePath(new LCSBuilder() { - int x = myMaxX; - int y = myMaxY; - - @Override - public void addEqual(int length) { - x -= length; - y -= length; - } - - @Override - public void addChange(int first, int second) { - if (first > 0) { - changes1.set(start1 + x - first, start1 + x); - x -= first; - } - if (second > 0) { - changes2.set(start2 + y - second, start2 + y); - y -= second; - } - } - }); - } - - /** - * Path is decoded in reverse order (from the last change to the first) - */ - public Builder decodePath(Builder builder) { - Decoder decoder = new Decoder(getXSize(), getYSize(), builder); - int index = myCornerIndex; - while (index != -1) { - int encodedStep = mySteps[index]; - decoder.decode(encodedStep); - index = myPrevSteps[index]; - } - decoder.beforeFinish(); - return builder; - } - - public int getXSize() { - return myMaxX; - } - - public int getYSize() { - return myMaxY; - } - - public int encodeStep(int x, int y, int diagLength, boolean afterVertical, int prevIndex) throws FilesTooBigForDiffException { - int encodedPath = diagLength; - if (afterVertical) encodedPath |= VERTICAL_DIRECTION_FLAG; - int position = incPosition(); - - myPrevSteps[position] = prevIndex; - mySteps[position] = encodedPath; - if (x == myMaxX - 1 && y == myMaxY - 1) myCornerIndex = position; - return position; - } - - private int incPosition() throws FilesTooBigForDiffException { - int length = myPrevSteps.length; - if (myPosition == length - 1) { - myPrevSteps = copy(length, myPrevSteps); - mySteps = copy(length, mySteps); - } - myPosition++; - return myPosition; - } - - private int[] copy(int length, int[] prevArray) throws FilesTooBigForDiffException { - if (length * 2 >= FilesTooBigForDiffException.MAX_BUFFER_LEN) { - throw new FilesTooBigForDiffException(FilesTooBigForDiffException.MAX_BUFFER_LEN); - } - int[] array = new int[length * 2]; - System.arraycopy(prevArray, 0, array, 0, length); - return array; - } - - class Decoder { - private final LCSBuilder builder; - private int x; - private int y; - private int dx = 0; - private int dy = 0; - - public Decoder(int x, int y, LCSBuilder builder) { - this.x = x; - this.y = y; - this.builder = builder; - } - - public int getX() { - return x; - } - - public int getY() { - return y; - } - - public void decode(int encodedStep) { - int diagDist = encodedStep & DISTANCE_MASK; - if (diagDist != 0) { - if (dx != 0 || dy != 0) { - builder.addChange(dx, dy); - dx = 0; - dy = 0; - } - builder.addEqual(diagDist); - } - x -= diagDist; - y -= diagDist; - boolean verticalStep = (encodedStep & VERTICAL_DIRECTION_FLAG) != 0; - if (verticalStep) { - y--; - dy++; - } else { - x--; - dx++; - } - } - - public void beforeFinish() { - dx += x; - dy += y; - if (dx != 0 || dy != 0) builder.addChange(dx, dy); - } - } -} diff --git a/platform/util/testSrc/com/intellij/util/diff/IntLCSAutoTest.java b/platform/util/testSrc/com/intellij/util/diff/IntLCSAutoTest.java index 2d254bebc492..f19d756c7b6a 100644 --- a/platform/util/testSrc/com/intellij/util/diff/IntLCSAutoTest.java +++ b/platform/util/testSrc/com/intellij/util/diff/IntLCSAutoTest.java @@ -28,13 +28,7 @@ public class IntLCSAutoTest extends TestCase { private static final int MAX_LENGTH = 300; private static final int CHAR_COUNT = 20; - private enum Type {IntLCS, MyersLCS, PatienceLCS} - - public void testIntLCS() throws Exception { - for (int i = 0; i < ITERATIONS; i++) { - doTestLCS(MAX_LENGTH, CHAR_COUNT, Type.IntLCS); - } - } + private enum Type {MyersLCS, PatienceLCS} public void testMyersLCS() throws Exception { for (int i = 0; i < ITERATIONS; i++) { @@ -48,12 +42,6 @@ public class IntLCSAutoTest extends TestCase { } } - public void testMyersImplementations() throws Exception { - for (int i = 0; i < ITERATIONS; i++) { - doTestMyersImplementations(MAX_LENGTH, CHAR_COUNT); - } - } - private void doTestLCS(int maxLength, int charCount, @NotNull Type type) throws FilesTooBigForDiffException { int[] sequence1 = generateSequence(maxLength, charCount); int[] sequence2 = generateSequence(maxLength, charCount); @@ -67,10 +55,6 @@ public class IntLCSAutoTest extends TestCase { BitSet changes2 = new BitSet(sequence2.length); switch (type) { - case IntLCS: - IntLCS intLCS = new IntLCS(sequence1, sequence2, start1, count1, start2, count2, changes1, changes2); - intLCS.execute(); - break; case MyersLCS: MyersLCS myersLCS = new MyersLCS(sequence1, sequence2, start1, count1, start2, count2, changes1, changes2); myersLCS.execute(); @@ -84,31 +68,9 @@ public class IntLCSAutoTest extends TestCase { verifyLCS(sequence1, sequence2, start1, count1, start2, count2, changes1, changes2); } - private void doTestMyersImplementations(int maxLength, int charCount) throws FilesTooBigForDiffException { - generateSequence(maxLength, charCount); - - int[] sequence1 = generateSequence(maxLength, charCount); - int[] sequence2 = generateSequence(maxLength, charCount); - - int start1 = RNG.nextInt(sequence1.length); - int start2 = RNG.nextInt(sequence2.length); - int count1 = RNG.nextInt(sequence1.length - start1); - int count2 = RNG.nextInt(sequence2.length - start2); - - BitSet intLCSChanges1 = new BitSet(sequence1.length); - BitSet intLCSChanges2 = new BitSet(sequence2.length); - - IntLCS intLCS = new IntLCS(sequence1, sequence2, start1, count1, start2, count2, intLCSChanges1, intLCSChanges2); - intLCS.execute(); - - BitSet myersLCSChanges1 = new BitSet(sequence1.length); - BitSet myersLCSChanges2 = new BitSet(sequence2.length); - - MyersLCS myersLCS = new MyersLCS(sequence1, sequence2, start1, count1, start2, count2, myersLCSChanges1, myersLCSChanges2); - myersLCS.execute(); - - assertEquals(intLCSChanges1.cardinality(), myersLCSChanges1.cardinality()); - assertEquals(intLCSChanges2.cardinality(), myersLCSChanges2.cardinality()); + public static void verifyLCS(@NotNull int[] sequence1, @NotNull int[] sequence2, + @NotNull BitSet changes1, @NotNull BitSet changes2) { + verifyLCS(sequence1, sequence2, 0, sequence1.length, 0, sequence2.length, changes1, changes2); } private static void verifyLCS(@NotNull int[] sequence1, @NotNull int[] sequence2, diff --git a/platform/util/testSrc/com/intellij/util/diff/IntLCSNewTest.java b/platform/util/testSrc/com/intellij/util/diff/IntLCSNewTest.java index 31fd1ad0cbcd..647243c5602e 100644 --- a/platform/util/testSrc/com/intellij/util/diff/IntLCSNewTest.java +++ b/platform/util/testSrc/com/intellij/util/diff/IntLCSNewTest.java @@ -71,9 +71,9 @@ public class IntLCSNewTest extends TestCase { } private static BitSet[] buildChange(int[] first, int[] second) throws FilesTooBigForDiffException { - IntLCS intLCS = new IntLCS(first, second); - intLCS.execute(); - return intLCS.getChanges(); + MyersLCS myersLCS = new MyersLCS(first, second); + myersLCS.execute(); + return myersLCS.getChanges(); } private static void checkChange(BitSet[] change, int[] expected1, int[] expected2) { diff --git a/platform/util/testSrc/com/intellij/util/diff/IntLCSTest.java b/platform/util/testSrc/com/intellij/util/diff/IntLCSTest.java index 87d1fad58435..aa8ee8d2058e 100644 --- a/platform/util/testSrc/com/intellij/util/diff/IntLCSTest.java +++ b/platform/util/testSrc/com/intellij/util/diff/IntLCSTest.java @@ -18,6 +18,8 @@ package com.intellij.util.diff; import junit.framework.TestCase; +import java.util.BitSet; + /** * @author dyoma */ @@ -48,7 +50,7 @@ public class IntLCSTest extends TestCase { } public void testOneTail() throws FilesTooBigForDiffException { - assertEquals(1, new IntLCS(new int[]{1, 2}, new int[]{1, 2, 3}).execute()); + assertEquals(1, countChanges(new int[]{1, 2}, new int[]{1, 2, 3})); } public void testSingleMiddle() throws FilesTooBigForDiffException { @@ -58,13 +60,14 @@ public class IntLCSTest extends TestCase { } public void testAbsolutelyDifferent() throws FilesTooBigForDiffException { - assertEquals(4, new IntLCS(new int[]{1, 2}, new int[]{3, 4}).execute()); - assertEquals(6, new IntLCS(new int[]{1, 2, 3}, new int[]{4, 5, 6}).execute()); + assertEquals(4, countChanges(new int[]{1, 2}, new int[]{3, 4})); + assertEquals(6, countChanges(new int[]{1, 2, 3}, new int[]{4, 5, 6})); } - private Diff.Change buildChange(int[] first, int[] second, int expectedNonDiags) throws FilesTooBigForDiffException { - IntLCS intLCS = new IntLCS(first, second); - assertEquals(expectedNonDiags, intLCS.execute()); + private static Diff.Change buildChange(int[] first, int[] second, int expectedNonDiags) throws FilesTooBigForDiffException { + assertEquals(expectedNonDiags, countChanges(first, second)); + MyersLCS intLCS = new MyersLCS(first, second); + intLCS.execute(); Reindexer reindexer = new Reindexer(); reindexer.idInit(first.length, second.length); Diff.ChangeBuilder builder = new Diff.ChangeBuilder(0); @@ -85,4 +88,11 @@ public class IntLCSTest extends TestCase { checkChange(change, line0, line1, inserted, deleted); assertNull("Expected last change", change.link); } + + private static int countChanges(int[] first, int[] second) throws FilesTooBigForDiffException { + MyersLCS lcs = new MyersLCS(first, second); + lcs.execute(); + BitSet[] changes = lcs.getChanges(); + return changes[0].cardinality() + changes[1].cardinality(); + } } diff --git a/platform/util/testSrc/com/intellij/util/diff/LinkedDiffPathsTest.java b/platform/util/testSrc/com/intellij/util/diff/LinkedDiffPathsTest.java deleted file mode 100644 index 0f9300ab3d8c..000000000000 --- a/platform/util/testSrc/com/intellij/util/diff/LinkedDiffPathsTest.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2000-2009 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.util.diff; - -import junit.framework.TestCase; - -import java.util.BitSet; - -/** - * @author dyoma - */ -public class LinkedDiffPathsTest extends TestCase { - protected LinkedDiffPaths createPaths(int maxX, int maxY) { - return new LinkedDiffPaths(maxX, maxY); - } - - public void testOneDeleteAtferEnd() throws FilesTooBigForDiffException { - LinkedDiffPaths paths = createPaths(2, 3); - int key = paths.encodeStep(1, 1, 2, false, -1); - paths.encodeStep(1, 2, 0, true, key); - Diff.Change change = decode(paths); - IntLCSTest.checkLastChange(change, 2, 2, 1, 0); - } - - public void testOneInsertedAtBegging() throws FilesTooBigForDiffException { - LinkedDiffPaths paths = createPaths(3, 2); - paths.encodeStep(2, 1, 2, false, -1); - Diff.Change change = decode(paths); - IntLCSTest.checkLastChange(change, 0, 0, 0, 1); - } - - public void testSingleMiddleChange() throws FilesTooBigForDiffException { - LinkedDiffPaths paths = createPaths(3, 3); - int key = paths.encodeStep(0, 0, 1, true, -1); - key = paths.encodeStep(1, 0, 0, false, key); - paths.encodeStep(2, 2, 1, true, key); - IntLCSTest.checkLastChange(decode(paths), 1, 1, 1, 1); - } - - public void testSingleChangeAtEnd() throws FilesTooBigForDiffException { - LinkedDiffPaths paths = createPaths(2, 2); - int key = paths.encodeStep(0, 0, 1, false, -1); - key = paths.encodeStep(0, 1, 0, true, key); - paths.encodeStep(1, 1, 0, false, key); - IntLCSTest.checkLastChange(decode(paths), 1, 1, 1, 1); - } - - public void testNotSquareChangeAtEnd() throws FilesTooBigForDiffException { - LinkedDiffPaths paths = createPaths(2, 3); - int key = paths.encodeStep(0, 0, 1, false, -1); - key = paths.encodeStep(0, 1, 0, true, key); - key = paths.encodeStep(0, 2, 0, true, key); - paths.encodeStep(1, 2, 0, false, key); - IntLCSTest.checkLastChange(decode(paths), 1, 1, 2, 1); - } - - private Diff.Change decode(LinkedDiffPaths paths) { - BitSet[] changes = new BitSet[]{new BitSet(), new BitSet()}; - paths.applyChanges(0, 0, changes[0], changes[1]); - Reindexer reindexer = new Reindexer(); - reindexer.idInit(paths.getXSize(), paths.getYSize()); - Diff.ChangeBuilder builder = new Diff.ChangeBuilder(0); - reindexer.reindex(changes, builder); - return builder.getFirstChange(); - } -} diff --git a/platform/util/testSrc/com/intellij/util/diff/ReindexerNewTest.java b/platform/util/testSrc/com/intellij/util/diff/ReindexerNewTest.java index c9fe6fbf9463..764515e43c38 100644 --- a/platform/util/testSrc/com/intellij/util/diff/ReindexerNewTest.java +++ b/platform/util/testSrc/com/intellij/util/diff/ReindexerNewTest.java @@ -77,7 +77,7 @@ public class ReindexerNewTest extends TestCase { } public static void checkCase(int[] ints1, int[] ints2) throws FilesTooBigForDiffException { - checkCase(ints1, ints2, null); + checkCase(ints1, ints2, ""); } public static void checkCase(int[] ints1, int[] ints2, String message) throws FilesTooBigForDiffException { @@ -102,24 +102,12 @@ public class ReindexerNewTest extends TestCase { } }; - IntLCS lcs = new IntLCS(ints1, ints2); - lcs.execute(); - BitSet[] expectedChanges = lcs.getChanges(); - Reindexer reindexer = new Reindexer(); int[][] discarded = reindexer.discardUnique(ints1, ints2); - lcs = new IntLCS(discarded[0], discarded[1]); + MyersLCS lcs = new MyersLCS(discarded[0], discarded[1]); lcs.execute(); - BitSet[] changes = lcs.getChanges(); - reindexer.reindex(changes, builder); + reindexer.reindex(lcs.getChanges(), builder); - if (message != null) { - assertEquals(message, expectedChanges[0], reindexChanges[0]); - assertEquals(message, expectedChanges[1], reindexChanges[1]); - } - else { - assertEquals(expectedChanges[0], reindexChanges[0]); - assertEquals(expectedChanges[1], reindexChanges[1]); - } + IntLCSAutoTest.verifyLCS(ints1, ints2, reindexChanges[0], reindexChanges[1]); } } From e9143a0c9a898b2f1eab5568e72445af46be539d Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Wed, 5 Jul 2017 10:13:01 +0300 Subject: [PATCH 10/37] diff: update performance tests --- .../intellij/diff/util/DiffPerformanceTest.kt | 82 ++++++++++++++++--- 1 file changed, 69 insertions(+), 13 deletions(-) diff --git a/platform/diff-impl/tests/com/intellij/diff/util/DiffPerformanceTest.kt b/platform/diff-impl/tests/com/intellij/diff/util/DiffPerformanceTest.kt index 36e7b9d4e285..e94117e23be0 100644 --- a/platform/diff-impl/tests/com/intellij/diff/util/DiffPerformanceTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/util/DiffPerformanceTest.kt @@ -17,7 +17,9 @@ package com.intellij.diff.util import com.intellij.testFramework.PlatformTestUtil +import com.intellij.util.containers.StringInterner import com.intellij.util.diff.Diff +import com.intellij.util.diff.FilesTooBigForDiffException import junit.framework.TestCase import java.util.* @@ -26,7 +28,13 @@ class DiffPerformanceTest : TestCase() { private var needWarmUp = true } - private val arr_20000 = generateData(20000).toTypedArray() + private val interner: StringInterner = StringInterner(); + + val data = generateData(2000000) + private val arr_200000 = data.take(200000).toTypedArray() + private val arr_50000 = data.take(50000).toTypedArray() + private val arr_20000 = data.take(20000).toTypedArray() + private val arr_5000 = data.take(5000).toTypedArray() private val arr_2000 = arr_20000.take(2000).toTypedArray() private val arr_1000 = arr_20000.take(1000).toTypedArray() private val arr_100 = arr_20000.take(100).toTypedArray() @@ -35,16 +43,22 @@ class DiffPerformanceTest : TestCase() { private val shuffled_1000 = shuffled_2000.take(1000).toTypedArray() private val shuffled_100 = shuffled_2000.take(100).toTypedArray() + private val altered_200000 = alter(arr_200000) + private val altered_50000 = alter(arr_50000) private val altered_20000 = alter(arr_20000) private val altered_2000 = alter(arr_2000) private val altered_1000 = alter(arr_1000) private val altered_100 = alter(arr_100) + private val heavy_altered_200000 = heavy_alter(arr_200000) + private val heavy_altered_50000 = heavy_alter(arr_50000) private val heavy_altered_20000 = heavy_alter(arr_20000) private val heavy_altered_2000 = heavy_alter(arr_2000) private val heavy_altered_1000 = heavy_alter(arr_1000) private val heavy_altered_100 = heavy_alter(arr_100) + private val reversed_50000 = arr_50000.reversedArray() + private val reversed_5000 = arr_5000.reversedArray() private val reversed_2000 = arr_2000.reversedArray() private val reversed_1000 = arr_1000.reversedArray() private val reversed_100 = arr_100.reversedArray() @@ -58,19 +72,61 @@ class DiffPerformanceTest : TestCase() { } private fun warmUp() { - for (i in 0..20) { - Diff.buildChanges(arr_2000, shuffled_2000) + for (i in 0..40) { + Diff.buildChanges(arr_20000, heavy_altered_20000) + } + } + + fun `test altered 200000`() { + testCpu(3, 550) { + Diff.buildChanges(arr_200000, altered_200000) + } + } + + fun `test heavy altered 200000`() { + testCpu(1, 2900) { + Diff.buildChanges(arr_200000, heavy_altered_200000) + } + } + + fun `test reversed 50000 failure`() { + testCpu(1, 8000) { + try { + Diff.buildChanges(arr_50000, reversed_50000) + } + catch (e: FilesTooBigForDiffException) { + return@testCpu + } + fail("FilesTooBigForDiffException expected") + } + } + + fun `test reversed 5000`() { + testCpu(1, 2000) { + Diff.buildChanges(arr_5000, reversed_5000) + } + } + + fun `test altered 50000`() { + testCpu(20, 650) { + Diff.buildChanges(arr_50000, altered_50000) + } + } + + fun `test heavy altered 50000`() { + testCpu(3, 600) { + Diff.buildChanges(arr_50000, heavy_altered_50000) } } fun `test altered 20000`() { - testCpu(20, 400) { + testCpu(20, 350) { Diff.buildChanges(arr_20000, altered_20000) } } fun `test heavy altered 20000`() { - testCpu(10, 550) { + testCpu(15, 600) { Diff.buildChanges(arr_20000, heavy_altered_20000) } } @@ -88,13 +144,13 @@ class DiffPerformanceTest : TestCase() { } fun `test shuffled 2000`() { - testCpu(1, 700) { + testCpu(1, 500) { Diff.buildChanges(arr_2000, shuffled_2000) } } fun `test reversed 2000`() { - testCpu(1, 700) { + testCpu(1, 500) { Diff.buildChanges(arr_2000, reversed_2000) } } @@ -112,19 +168,19 @@ class DiffPerformanceTest : TestCase() { } fun `test shuffled 1000`() { - testCpu(5, 500) { + testCpu(10, 750) { Diff.buildChanges(arr_1000, shuffled_1000) } } fun `test reversed 1000`() { - testCpu(5, 550) { + testCpu(10, 750) { Diff.buildChanges(arr_1000, reversed_1000) } } fun `test altered 100`() { - testCpu(10000, 600) { + testCpu(10000, 550) { Diff.buildChanges(arr_100, altered_100) } } @@ -142,14 +198,14 @@ class DiffPerformanceTest : TestCase() { } fun `test reversed 100`() { - testCpu(500, 450) { + testCpu(1000, 500) { Diff.buildChanges(arr_100, reversed_100) } } private fun generateData(size: Int): List { - return (1..size).map { "${it % 200}" } + return (1..size).map { interner.intern("${it % 200}") } } private fun alter(arr: Array): Array { @@ -163,7 +219,7 @@ class DiffPerformanceTest : TestCase() { private fun heavy_alter(arr: Array): Array { val altered = arr.copyOf() for (i in 1..altered.lastIndex step 20) { - altered[i] = "${i % 200}" + altered[i] = interner.intern("${i % 200}") } altered[0] = "===" // avoid "common prefix/suffix" optimisation altered[altered.lastIndex] = "===" From d295313a04479c0f14788368ec413deb2d573de7 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Tue, 4 Jul 2017 18:58:30 +0300 Subject: [PATCH 11/37] IDEA-153574 IDEA-174872 vcs: "Compare before with local" action --- .../src/META-INF/VcsExtensionPoints.xml | 4 +- .../src/META-INF/VcsExtensions.xml | 2 +- .../src/idea/VcsActions.xml | 8 ++- .../actions/ShowDiffWithLocalAction.java | 52 ++++++++------- .../committed/RepositoryChangesBrowser.java | 4 +- .../vcs/history/FileHistoryPanelImpl.java | 4 +- ...java => ShowDiffAfterWithLocalAction.java} | 6 +- .../ShowDiffBeforeWithLocalAction.java | 41 ++++++++++++ .../impl/VcsSelectionHistoryDialog.java | 6 +- .../vcs-log/impl/src/META-INF/vcs-log.xml | 6 +- .../vcs/log/history/FileHistoryUi.java | 17 ++++- ...areRevisionsFromHistoryActionProvider.java | 4 +- ...erWithLocalFromHistoryActionProvider.java} | 11 +--- ...oreWithLocalFromHistoryActionProvider.java | 66 +++++++++++++++++++ 14 files changed, 182 insertions(+), 49 deletions(-) rename platform/vcs-impl/src/com/intellij/openapi/vcs/history/actions/{ShowDiffWithLocalAction.java => ShowDiffAfterWithLocalAction.java} (94%) create mode 100644 platform/vcs-impl/src/com/intellij/openapi/vcs/history/actions/ShowDiffBeforeWithLocalAction.java rename platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/{ShowDiffWithLocalFromHistoryActionProvider.java => ShowDiffAfterWithLocalFromHistoryActionProvider.java} (84%) create mode 100644 platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/ShowDiffBeforeWithLocalFromHistoryActionProvider.java diff --git a/platform/platform-resources/src/META-INF/VcsExtensionPoints.xml b/platform/platform-resources/src/META-INF/VcsExtensionPoints.xml index ef03e419240c..48a34a79ae3f 100644 --- a/platform/platform-resources/src/META-INF/VcsExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/VcsExtensionPoints.xml @@ -78,7 +78,9 @@ - + diff --git a/platform/platform-resources/src/META-INF/VcsExtensions.xml b/platform/platform-resources/src/META-INF/VcsExtensions.xml index 7b5a8f01920c..769a4b8e56d6 100644 --- a/platform/platform-resources/src/META-INF/VcsExtensions.xml +++ b/platform/platform-resources/src/META-INF/VcsExtensions.xml @@ -40,7 +40,7 @@ - + diff --git a/platform/platform-resources/src/idea/VcsActions.xml b/platform/platform-resources/src/idea/VcsActions.xml index ecf6df0f61e5..bbc474171838 100644 --- a/platform/platform-resources/src/idea/VcsActions.xml +++ b/platform/platform-resources/src/idea/VcsActions.xml @@ -24,9 +24,10 @@ text="Com_pare with Specified Revision..." popup="true"/> - + + @@ -375,6 +376,7 @@ + diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffWithLocalAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffWithLocalAction.java index ae56ad00304e..1faa3958b2ee 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffWithLocalAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffWithLocalAction.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.vcs.changes.actions; +import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; @@ -24,6 +25,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.VcsDataKeys; import com.intellij.openapi.vcs.changes.*; import com.intellij.openapi.vcs.changes.committed.CommittedChangesBrowserUseCase; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -32,12 +34,17 @@ import java.util.List; import static com.intellij.openapi.vcs.changes.actions.diff.ShowDiffAction.showDiffForChange; -/** - * @author yole - */ public class ShowDiffWithLocalAction extends AnAction implements DumbAware { + private final boolean myBeforeWithLocal; + public ShowDiffWithLocalAction() { - ActionUtil.copyFrom(this, "Vcs.ShowDiffWithLocal"); + this(false); + getTemplatePresentation().setIcon(AllIcons.Actions.DiffWithCurrent); + } + + public ShowDiffWithLocalAction(boolean beforeWithLocal) { + myBeforeWithLocal = beforeWithLocal; + ActionUtil.copyFrom(this, beforeWithLocal ? "Vcs.ShowDiffWithLocal.Before" : "Vcs.ShowDiffWithLocal"); } public void actionPerformed(AnActionEvent e) { @@ -49,9 +56,9 @@ public class ShowDiffWithLocalAction extends AnAction implements DumbAware { List changesToLocal = new ArrayList<>(); for (int i = 0; i < selection.getChanges().size(); i++) { if (i == selection.getIndex()) index = changesToLocal.size(); - ContentRevision afterRevision = selection.getChanges().get(i).getAfterRevision(); - if (afterRevision != null && isValidAfterRevision(afterRevision)) { - changesToLocal.add(new Change(afterRevision, getCurrentRevision(afterRevision, e))); + Change change = getChangeWithLocal(selection.getChanges().get(i)); + if (change != null) { + changesToLocal.add(change); } } @@ -60,29 +67,30 @@ public class ShowDiffWithLocalAction extends AnAction implements DumbAware { } } - @NotNull - protected ContentRevision getCurrentRevision(@NotNull ContentRevision afterRevision, @NotNull AnActionEvent e) { - return CurrentContentRevision.create(afterRevision.getFile()); - } - public void update(final AnActionEvent e) { Project project = e.getData(CommonDataKeys.PROJECT); ChangesSelection selection = e.getData(VcsDataKeys.CHANGES_SELECTION); boolean isInAir = CommittedChangesBrowserUseCase.IN_AIR.equals(CommittedChangesBrowserUseCase.DATA_KEY.getData(e.getDataContext())); + boolean isToolbar = "ChangesBrowser".equals(e.getPlace()); - e.getPresentation().setEnabled(project != null && selection != null && !isInAir && anyHasAfterRevision(selection.getChanges())); + e.getPresentation().setEnabled(project != null && !isToolbar && selection != null && !isInAir && canShowDiff(selection.getChanges())); + e.getPresentation().setVisible(!isToolbar); } - private static boolean isValidAfterRevision(@Nullable final ContentRevision afterRevision) { - return afterRevision != null && !afterRevision.getFile().isNonLocal() && !afterRevision.getFile().isDirectory(); + @Nullable + private Change getChangeWithLocal(@NotNull Change c) { + ContentRevision revision = myBeforeWithLocal ? c.getBeforeRevision() : c.getAfterRevision(); + if (!isValidRevision(revision)) return null; + + ContentRevision contentRevision = CurrentContentRevision.create(revision.getFile()); + return new Change(revision, contentRevision); } - private static boolean anyHasAfterRevision(@NotNull final List changes) { - for (Change c : changes) { - if (isValidAfterRevision(c.getAfterRevision())) { - return true; - } - } - return false; + private boolean canShowDiff(@NotNull List changes) { + return ContainerUtil.exists(changes, c -> getChangeWithLocal(c) != null); + } + + private static boolean isValidRevision(@Nullable ContentRevision revision) { + return revision != null && !revision.getFile().isNonLocal() && !revision.getFile().isDirectory(); } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/RepositoryChangesBrowser.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/RepositoryChangesBrowser.java index a595c8ffb516..9c66bba3d2d9 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/RepositoryChangesBrowser.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/RepositoryChangesBrowser.java @@ -71,7 +71,9 @@ public class RepositoryChangesBrowser extends ChangesBrowser implements DataProv protected void buildToolBar(final DefaultActionGroup toolBarGroup) { super.buildToolBar(toolBarGroup); - toolBarGroup.add(new ShowDiffWithLocalAction()); + toolBarGroup.add(new ShowDiffWithLocalAction(true)); + toolBarGroup.add(new ShowDiffWithLocalAction(false)); + myEditSourceAction = new MyEditSourceAction(); myEditSourceAction.registerCustomShortcutSet(CommonShortcuts.getEditSource(), this); toolBarGroup.add(myEditSourceAction); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java index 881bc0c35a44..a437986e8c60 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java @@ -408,7 +408,9 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton impleme diffAction.registerCustomShortcutSet(CommonShortcuts.getDiff(), null); result.add(diffAction); - result.add(ActionManager.getInstance().getAction("Vcs.ShowDiffWithLocal")); + if (popup) { + result.add(ActionManager.getInstance().getAction("Vcs.ShowDiffWithLocal")); + } final AnAction diffGroup = ActionManager.getInstance().getAction(VCS_HISTORY_ACTIONS_GROUP); if (diffGroup != null) result.add(diffGroup); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/actions/ShowDiffWithLocalAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/actions/ShowDiffAfterWithLocalAction.java similarity index 94% rename from platform/vcs-impl/src/com/intellij/openapi/vcs/history/actions/ShowDiffWithLocalAction.java rename to platform/vcs-impl/src/com/intellij/openapi/vcs/history/actions/ShowDiffAfterWithLocalAction.java index 29e7c31fb2af..d1307d4ec7cc 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/actions/ShowDiffWithLocalAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/actions/ShowDiffAfterWithLocalAction.java @@ -30,11 +30,11 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ObjectUtils; import org.jetbrains.annotations.NotNull; -public class ShowDiffWithLocalAction extends ExtendableAction implements DumbAware { +public class ShowDiffAfterWithLocalAction extends ExtendableAction implements DumbAware { private static final ExtensionPointName EP_NAME = - ExtensionPointName.create("com.intellij.openapi.vcs.history.actions.ShowDiffWithLocalAction.ExtensionProvider"); + ExtensionPointName.create("com.intellij.openapi.vcs.history.actions.ShowDiffAfterWithLocalAction.ExtensionProvider"); - public ShowDiffWithLocalAction() { + public ShowDiffAfterWithLocalAction() { super(EP_NAME); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/actions/ShowDiffBeforeWithLocalAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/actions/ShowDiffBeforeWithLocalAction.java new file mode 100644 index 000000000000..08f4dde1b9a6 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/actions/ShowDiffBeforeWithLocalAction.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.vcs.history.actions; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.AnActionExtensionProvider; +import com.intellij.openapi.actionSystem.ExtendableAction; +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.project.DumbAware; +import org.jetbrains.annotations.NotNull; + +public class ShowDiffBeforeWithLocalAction extends ExtendableAction implements DumbAware { + private static final ExtensionPointName EP_NAME = + ExtensionPointName.create("com.intellij.openapi.vcs.history.actions.ShowDiffBeforeWithLocalAction.ExtensionProvider"); + + public ShowDiffBeforeWithLocalAction() { + super(EP_NAME); + } + + @Override + public void defaultActionPerformed(@NotNull AnActionEvent e) { + } + + @Override + public void defaultUpdate(@NotNull AnActionEvent e) { + e.getPresentation().setEnabledAndVisible(false); + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/VcsSelectionHistoryDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/VcsSelectionHistoryDialog.java index 00b63eec5489..f1cc469fcb26 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/VcsSelectionHistoryDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/VcsSelectionHistoryDialog.java @@ -208,7 +208,7 @@ public class VcsSelectionHistoryDialog extends FrameWrapper implements DataProvi final DefaultActionGroup popupActions = new DefaultActionGroup(); popupActions.add(new MyDiffAction()); - popupActions.add(new MyDiffLocalAction()); + popupActions.add(new MyDiffAfterWithLocalAction()); popupActions.add(ShowAllAffectedGenericAction.getInstance()); popupActions.add(ActionManager.getInstance().getAction(VcsActions.ACTION_COPY_REVISION_NUMBER)); PopupHandler.installPopupHandler(myList, popupActions, ActionPlaces.UPDATE_POPUP, ActionManager.getInstance()); @@ -496,8 +496,8 @@ public class VcsSelectionHistoryDialog extends FrameWrapper implements DataProvi } } - private class MyDiffLocalAction extends DumbAwareAction { - public MyDiffLocalAction() { + private class MyDiffAfterWithLocalAction extends DumbAwareAction { + public MyDiffAfterWithLocalAction() { ActionUtil.copyFrom(this, "Vcs.ShowDiffWithLocal"); } diff --git a/platform/vcs-log/impl/src/META-INF/vcs-log.xml b/platform/vcs-log/impl/src/META-INF/vcs-log.xml index 3a6fcc926c5f..7d8a9ff404cc 100644 --- a/platform/vcs-log/impl/src/META-INF/vcs-log.xml +++ b/platform/vcs-log/impl/src/META-INF/vcs-log.xml @@ -37,7 +37,8 @@ - + + @@ -122,6 +123,7 @@ + @@ -132,7 +134,6 @@ - @@ -176,6 +177,7 @@ + diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryUi.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryUi.java index dade82b4dffa..a30cddb645bf 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryUi.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/history/FileHistoryUi.java @@ -129,7 +129,7 @@ public class FileHistoryUi extends AbstractVcsLogUi { } @Nullable - public FilePath getPath(@NotNull VcsFullCommitDetails details) { + public FilePath getAfterPath(@NotNull VcsFullCommitDetails details) { if (myPath.isDirectory()) return myPath; List changes = collectRelevantChanges(details); @@ -143,6 +143,21 @@ public class FileHistoryUi extends AbstractVcsLogUi { return null;// file was deleted } + @Nullable + public FilePath getBeforePath(@NotNull VcsFullCommitDetails details) { + if (myPath.isDirectory()) return myPath; + + List changes = collectRelevantChanges(details); + for (Change change : changes) { + ContentRevision revision = change.getBeforeRevision(); + if (revision != null) { + return revision.getFile(); + } + } + + return null;// file was created + } + @NotNull public List collectRelevantChanges(@NotNull VcsFullCommitDetails details) { Set fileNames = getFileNames(details); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/CompareRevisionsFromHistoryActionProvider.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/CompareRevisionsFromHistoryActionProvider.java index ea2dfc46f6e2..d698ed3ca2fa 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/CompareRevisionsFromHistoryActionProvider.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/CompareRevisionsFromHistoryActionProvider.java @@ -113,8 +113,8 @@ public class CompareRevisionsFromHistoryActionProvider implements AnActionExtens // so that it could return a single file path for each revision VcsFullCommitDetails newestDetail = details.get(0); VcsFullCommitDetails olderDetail = details.get(1); - notNull(handler).showDiff(olderDetail.getRoot(), ui.getPath(olderDetail), olderDetail.getId(), - ui.getPath(newestDetail), newestDetail.getId()); + notNull(handler).showDiff(olderDetail.getRoot(), ui.getAfterPath(olderDetail), olderDetail.getId(), + ui.getAfterPath(newestDetail), newestDetail.getId()); } else if (details.size() == 1) { VcsFullCommitDetails detail = notNull(ContainerUtil.getFirstItem(details)); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/ShowDiffWithLocalFromHistoryActionProvider.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/ShowDiffAfterWithLocalFromHistoryActionProvider.java similarity index 84% rename from platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/ShowDiffWithLocalFromHistoryActionProvider.java rename to platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/ShowDiffAfterWithLocalFromHistoryActionProvider.java index 6b0501b5b256..dcd3e2f63b70 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/ShowDiffWithLocalFromHistoryActionProvider.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/ShowDiffAfterWithLocalFromHistoryActionProvider.java @@ -28,19 +28,12 @@ import com.intellij.vcs.log.ui.VcsLogInternalDataKeys; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class ShowDiffWithLocalFromHistoryActionProvider extends FileHistorySingleCommitAction implements AnActionExtensionProvider { +public class ShowDiffAfterWithLocalFromHistoryActionProvider extends FileHistorySingleCommitAction implements AnActionExtensionProvider { @Override public boolean isActive(@NotNull AnActionEvent e) { return e.getData(VcsLogInternalDataKeys.FILE_HISTORY_UI) != null; } - @Override - public void update(@NotNull AnActionEvent e) { - super.update(e); - - e.getPresentation().setDescription("Compare selected revision with the local version of the file"); - } - @Override protected boolean isEnabled(@NotNull FileHistoryUi ui, @Nullable VcsFullCommitDetails detail, @NotNull AnActionEvent e) { FilePath filePath = e.getData(VcsDataKeys.FILE_PATH); @@ -59,6 +52,6 @@ public class ShowDiffWithLocalFromHistoryActionProvider extends FileHistorySingl FilePath path = e.getRequiredData(VcsDataKeys.FILE_PATH); VcsLogDiffHandler handler = e.getRequiredData(VcsLogInternalDataKeys.LOG_DIFF_HANDLER); - handler.showDiffWithLocal(detail.getRoot(), ui.getPath(detail), detail.getId(), path); + handler.showDiffWithLocal(detail.getRoot(), ui.getAfterPath(detail), detail.getId(), path); } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/ShowDiffBeforeWithLocalFromHistoryActionProvider.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/ShowDiffBeforeWithLocalFromHistoryActionProvider.java new file mode 100644 index 000000000000..6e061bea210b --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/history/ShowDiffBeforeWithLocalFromHistoryActionProvider.java @@ -0,0 +1,66 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.vcs.log.ui.actions.history; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.AnActionExtensionProvider; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.VcsDataKeys; +import com.intellij.openapi.vcs.changes.ChangeListManager; +import com.intellij.vcs.log.VcsFullCommitDetails; +import com.intellij.vcs.log.VcsLogDiffHandler; +import com.intellij.vcs.log.history.FileHistoryUi; +import com.intellij.vcs.log.ui.VcsLogInternalDataKeys; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ShowDiffBeforeWithLocalFromHistoryActionProvider extends FileHistorySingleCommitAction implements AnActionExtensionProvider { + @Override + public boolean isActive(@NotNull AnActionEvent e) { + return e.getData(VcsLogInternalDataKeys.FILE_HISTORY_UI) != null; + } + + @Override + protected boolean isEnabled(@NotNull FileHistoryUi ui, @Nullable VcsFullCommitDetails detail, @NotNull AnActionEvent e) { + FilePath filePath = e.getData(VcsDataKeys.FILE_PATH); + VcsLogDiffHandler handler = e.getData(VcsLogInternalDataKeys.LOG_DIFF_HANDLER); + + if (filePath == null || filePath.getVirtualFile() == null || handler == null) { + return false; + } + + if (detail != null) { + return detail.getParents().size() == 1; + } + + return true; + } + + @Override + protected void performAction(@NotNull Project project, + @NotNull FileHistoryUi ui, + @NotNull VcsFullCommitDetails detail, + @NotNull AnActionEvent e) { + if (ChangeListManager.getInstance(project).isFreezedWithNotification(null)) return; + if (detail.getParents().size() != 1) return; + + FilePath path = e.getRequiredData(VcsDataKeys.FILE_PATH); + VcsLogDiffHandler handler = e.getRequiredData(VcsLogInternalDataKeys.LOG_DIFF_HANDLER); + + handler.showDiffWithLocal(detail.getRoot(), ui.getBeforePath(detail), detail.getParents().get(0), path); + } +} From fd1ec0919b2b232f9168ed37f6f009d7396f7298 Mon Sep 17 00:00:00 2001 From: Nicolay Mitropolsky Date: Sun, 25 Jun 2017 16:20:58 +0300 Subject: [PATCH 12/37] `UrlClassLoader` deprecation updates --- platform/util/src/com/intellij/util/lang/ClassPath.java | 5 ++++- .../util/src/com/intellij/util/lang/UrlClassLoader.java | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/platform/util/src/com/intellij/util/lang/ClassPath.java b/platform/util/src/com/intellij/util/lang/ClassPath.java index 7a78abc05712..0d12eaf72158 100644 --- a/platform/util/src/com/intellij/util/lang/ClassPath.java +++ b/platform/util/src/com/intellij/util/lang/ClassPath.java @@ -76,7 +76,10 @@ public class ClassPath { push(urls); } - /** @deprecated to be removed in IDEA 15 */ + /** + * @deprecated Adding additional urls to classpath at runtime could lead to hard-to-debug errors + */ + @Deprecated void addURL(URL url) { push(Collections.singletonList(url)); } diff --git a/platform/util/src/com/intellij/util/lang/UrlClassLoader.java b/platform/util/src/com/intellij/util/lang/UrlClassLoader.java index 20215a5f6178..5f8e2a8ed15f 100644 --- a/platform/util/src/com/intellij/util/lang/UrlClassLoader.java +++ b/platform/util/src/com/intellij/util/lang/UrlClassLoader.java @@ -195,8 +195,12 @@ public class UrlClassLoader extends ClassLoader { } } - /** @deprecated to be removed in IDEA 15 */ + /** + * @deprecated Adding additional urls to classloader at runtime could lead to hard-to-debug errors + * Note: Used via reflection because of classLoaders incompatibility + */ @SuppressWarnings({"unused", "deprecation"}) + @Deprecated public void addURL(URL url) { getClassPath().addURL(url); myURLs.add(url); From b6805da8b0163dde258ef56dcbdd94a86d300b84 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Wed, 5 Jul 2017 17:41:44 +0300 Subject: [PATCH 13/37] [vcs-log] diagnostic for illegal argument exception EA-100051 --- .../src/com/intellij/vcs/log/impl/VcsLogContentProvider.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogContentProvider.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogContentProvider.java index e6c8a27ca593..e054b4e89a81 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogContentProvider.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogContentProvider.java @@ -16,6 +16,7 @@ package com.intellij.vcs.log.impl; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.openapi.vcs.ProjectLevelVcsManager; @@ -49,6 +50,7 @@ import java.util.List; * Delegates to the VcsLogManager. */ public class VcsLogContentProvider implements ChangesViewContentProvider { + private static final Logger LOG = Logger.getInstance(VcsLogContentProvider.class); public static final String TAB_NAME = "Log"; @NotNull private final Project myProject; @@ -167,6 +169,7 @@ public class VcsLogContentProvider implements ChangesViewContentProvider { for (String tabName : logManager.getTabNames()) { if (!TAB_NAME.equals(tabName)) { // main tab is closed by the ChangesViewContentManager Content content = toolWindow.getContentManager().findContent(tabName); + LOG.assertTrue(content != null, "Could not find content for tab " + tabName); ContentsUtil.closeContentTab(toolWindow.getContentManager(), content); } } From 403587e293e56089cc4ad5fe65c0cd2590994c4f Mon Sep 17 00:00:00 2001 From: Pavel Dolgov Date: Wed, 5 Jul 2017 15:57:54 +0300 Subject: [PATCH 14/37] Java: Don't fold too complex expressions when extracting method (IDEA-175221, IDEA-167255) --- .../extractMethod/ParametersFolder.java | 70 +++++++++++++++---- .../BuilderChainWith2DimArrayAccess.java | 11 +++ ...BuilderChainWith2DimArrayAccess_after.java | 16 +++++ .../BuilderChainWithArrayAccess.java | 15 ++++ .../BuilderChainWithArrayAccessExpr.java | 15 ++++ ...BuilderChainWithArrayAccessExpr_after.java | 19 +++++ .../BuilderChainWithArrayAccessIf.java | 9 +++ .../BuilderChainWithArrayAccessIf_after.java | 14 ++++ .../BuilderChainWithArrayAccess_after.java | 19 +++++ .../extractMethod/CallOn2DimArrayElement.java | 5 ++ .../CallOn2DimArrayElement_after.java | 9 +++ .../extractMethod/CallOnArrayElement.java | 5 ++ .../CallOnArrayElement_after.java | 9 +++ .../CallOnFieldArrayElement.java | 6 ++ .../CallOnFieldArrayElement_after.java | 10 +++ .../java/refactoring/ExtractMethodTest.java | 28 ++++++++ 16 files changed, 248 insertions(+), 12 deletions(-) create mode 100644 java/java-tests/testData/refactoring/extractMethod/BuilderChainWith2DimArrayAccess.java create mode 100644 java/java-tests/testData/refactoring/extractMethod/BuilderChainWith2DimArrayAccess_after.java create mode 100644 java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccess.java create mode 100644 java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessExpr.java create mode 100644 java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessExpr_after.java create mode 100644 java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessIf.java create mode 100644 java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessIf_after.java create mode 100644 java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccess_after.java create mode 100644 java/java-tests/testData/refactoring/extractMethod/CallOn2DimArrayElement.java create mode 100644 java/java-tests/testData/refactoring/extractMethod/CallOn2DimArrayElement_after.java create mode 100644 java/java-tests/testData/refactoring/extractMethod/CallOnArrayElement.java create mode 100644 java/java-tests/testData/refactoring/extractMethod/CallOnArrayElement_after.java create mode 100644 java/java-tests/testData/refactoring/extractMethod/CallOnFieldArrayElement.java create mode 100644 java/java-tests/testData/refactoring/extractMethod/CallOnFieldArrayElement_after.java diff --git a/java/java-analysis-impl/src/com/intellij/refactoring/extractMethod/ParametersFolder.java b/java/java-analysis-impl/src/com/intellij/refactoring/extractMethod/ParametersFolder.java index ac3a060c2ab2..b7593b26aa87 100644 --- a/java/java-analysis-impl/src/com/intellij/refactoring/extractMethod/ParametersFolder.java +++ b/java/java-analysis-impl/src/com/intellij/refactoring/extractMethod/ParametersFolder.java @@ -225,22 +225,27 @@ public class ParametersFolder { if (isAccessedForWriting((PsiExpression)expression)) { return null; } - for (PsiElement scopeElement : scopeElements) { - if (PsiTreeUtil.isAncestor(expression, scopeElement, true)) { - expression = null; - break; - } + if (isAncestor(expression, scopeElements)) { + break; + } + if (dependsOnLocals(expression, inputVariables)) { + break; + } + final PsiElement parent = expression.getParent(); + if (parent instanceof PsiExpressionStatement) { + break; } - if (expression == null) break; - final PsiType expressionType = ((PsiExpression)expression).getType(); - if (expressionType != null && !PsiType.VOID.equals(expressionType) && !(expression.getParent() instanceof PsiExpressionStatement)) { - if (dependsOnLocals(expression, inputVariables)) { - break; - } + if (expressionType == null || PsiType.VOID.equals(expressionType)) { + break; + } + if (isTooLongExpressionChain(expression)) { + break; + } + if (!isMethodNameExpression(expression)) { expressions.add((PsiExpression)expression); } - expression = PsiTreeUtil.getParentOfType(expression, PsiExpression.class); + expression = parent instanceof PsiExpression ? parent : null; } } else { @@ -275,6 +280,47 @@ public class ParametersFolder { return exprWithWriteAccessInside[0] != null; } + private static boolean isAncestor(PsiElement expression, PsiElement[] scopeElements) { + for (PsiElement scopeElement : scopeElements) { + if (PsiTreeUtil.isAncestor(expression, scopeElement, true)) { + return true; + } + } + return false; + } + + private static boolean isTooLongExpressionChain(PsiElement expression) { + int count = 0; + for (PsiElement element = getInnerExpression(expression); element != null; element = getInnerExpression(element)) { + count++; + if (count > 1) { // expression chains like 'var.foo().bar()' and 'var.foo[i].bar()' are too long + return true; + } + } + return false; + } + + private static PsiElement getInnerExpression(PsiElement expression) { + if (expression instanceof PsiMethodCallExpression) { + return ((PsiMethodCallExpression)expression).getMethodExpression().getQualifierExpression(); + } + if (expression instanceof PsiArrayAccessExpression) { + while (expression instanceof PsiArrayAccessExpression) { + expression = ((PsiArrayAccessExpression)expression).getArrayExpression(); + } + return expression; + } + return null; + } + + private static boolean isMethodNameExpression(@NotNull PsiElement expression) { + final PsiElement parent = expression.getParent(); + return expression instanceof PsiReferenceExpression && + parent instanceof PsiMethodCallExpression && + ((PsiReferenceExpression)expression).getReferenceNameElement() == + ((PsiMethodCallExpression)parent).getMethodExpression().getReferenceNameElement(); + } + private static boolean dependsOnLocals(final PsiElement expression, final List inputVariables) { final boolean[] localVarsUsed = new boolean[]{false}; expression.accept(new JavaRecursiveElementWalkingVisitor(){ diff --git a/java/java-tests/testData/refactoring/extractMethod/BuilderChainWith2DimArrayAccess.java b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWith2DimArrayAccess.java new file mode 100644 index 000000000000..4c8b730e589c --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWith2DimArrayAccess.java @@ -0,0 +1,11 @@ +class Foo { + boolean bar(String[][] a) { + for (int i = 0; i < a.length; i++) + for (int j = 0; i < a[i].length; j++) { + if (a[i][j].length() > 3 && i % 3 == 0) + return true; + + } + return false; + } +} diff --git a/java/java-tests/testData/refactoring/extractMethod/BuilderChainWith2DimArrayAccess_after.java b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWith2DimArrayAccess_after.java new file mode 100644 index 000000000000..77a7b3a87345 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWith2DimArrayAccess_after.java @@ -0,0 +1,16 @@ +class Foo { + boolean bar(String[][] a) { + for (int i = 0; i < a.length; i++) + for (int j = 0; i < a[i].length; j++) { + if (newMethod(a[i][j], i)) return true; + + } + return false; + } + + private boolean newMethod(String s, int i) { + if (s.length() > 3 && i % 3 == 0) + return true; + return false; + } +} diff --git a/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccess.java b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccess.java new file mode 100644 index 000000000000..3f2be2ea0d65 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccess.java @@ -0,0 +1,15 @@ +class UseBuilder { + void test(Builder builder, int[] arr) { + builder.foo("xyz").bar(arr[0]).foo("abc"); + } + + static class Builder { + Builder foo(String s) { + return this; + } + + Builder bar(int x) { + return this; + } + } +} diff --git a/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessExpr.java b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessExpr.java new file mode 100644 index 000000000000..11f0607aaf81 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessExpr.java @@ -0,0 +1,15 @@ +class UseBuilder { + void test(Builder builder, int[] arr) { + builder.foo("xyz").bar(arr[0]).foo("abc"); + } + + static class Builder { + Builder foo(String s) { + return this; + } + + Builder bar(int x) { + return this; + } + } +} diff --git a/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessExpr_after.java b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessExpr_after.java new file mode 100644 index 000000000000..0ad6a1d75ad0 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessExpr_after.java @@ -0,0 +1,19 @@ +class UseBuilder { + void test(Builder builder, int[] arr) { + newMethod(builder, arr[0]).foo("abc"); + } + + private Builder newMethod(Builder builder, int x) { + return builder.foo("xyz").bar(x); + } + + static class Builder { + Builder foo(String s) { + return this; + } + + Builder bar(int x) { + return this; + } + } +} diff --git a/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessIf.java b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessIf.java new file mode 100644 index 000000000000..d5a624418922 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessIf.java @@ -0,0 +1,9 @@ +class Foo { + boolean bar(String[] a) { + for (int i = 0; i < a.length; i++) { + if (a[i].length() > 3 && i % 3 == 0) + return true; + } + return false; + } +} diff --git a/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessIf_after.java b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessIf_after.java new file mode 100644 index 000000000000..f6f7323514f8 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccessIf_after.java @@ -0,0 +1,14 @@ +class Foo { + boolean bar(String[] a) { + for (int i = 0; i < a.length; i++) { + if (newMethod(a[i], i)) return true; + } + return false; + } + + private boolean newMethod(String s, int i) { + if (s.length() > 3 && i % 3 == 0) + return true; + return false; + } +} diff --git a/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccess_after.java b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccess_after.java new file mode 100644 index 000000000000..3e4109c1c899 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/BuilderChainWithArrayAccess_after.java @@ -0,0 +1,19 @@ +class UseBuilder { + void test(Builder builder, int[] arr) { + newMethod(builder, arr[0]); + } + + private void newMethod(Builder builder, int x) { + builder.foo("xyz").bar(x).foo("abc"); + } + + static class Builder { + Builder foo(String s) { + return this; + } + + Builder bar(int x) { + return this; + } + } +} diff --git a/java/java-tests/testData/refactoring/extractMethod/CallOn2DimArrayElement.java b/java/java-tests/testData/refactoring/extractMethod/CallOn2DimArrayElement.java new file mode 100644 index 000000000000..cf33c80c42d4 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/CallOn2DimArrayElement.java @@ -0,0 +1,5 @@ +class C { + int foo(String[][] vars, int i, int j) { + return vars[i][j].length(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethod/CallOn2DimArrayElement_after.java b/java/java-tests/testData/refactoring/extractMethod/CallOn2DimArrayElement_after.java new file mode 100644 index 000000000000..495f7d8f629e --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/CallOn2DimArrayElement_after.java @@ -0,0 +1,9 @@ +class C { + int foo(String[][] vars, int i, int j) { + return newMethod(vars[i][j]); + } + + private int newMethod(String s) { + return s.length(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethod/CallOnArrayElement.java b/java/java-tests/testData/refactoring/extractMethod/CallOnArrayElement.java new file mode 100644 index 000000000000..7794ab9692f3 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/CallOnArrayElement.java @@ -0,0 +1,5 @@ +class C { + int foo(String[] vars, int i) { + return vars[i].length(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethod/CallOnArrayElement_after.java b/java/java-tests/testData/refactoring/extractMethod/CallOnArrayElement_after.java new file mode 100644 index 000000000000..5c74700b8918 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/CallOnArrayElement_after.java @@ -0,0 +1,9 @@ +class C { + int foo(String[] vars, int i) { + return newMethod(vars[i]); + } + + private int newMethod(String var) { + return var.length(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethod/CallOnFieldArrayElement.java b/java/java-tests/testData/refactoring/extractMethod/CallOnFieldArrayElement.java new file mode 100644 index 000000000000..c5aca75e0a9d --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/CallOnFieldArrayElement.java @@ -0,0 +1,6 @@ +class C { + String[] vars; + int foo(C c, int i) { + return c.vars[i].length(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethod/CallOnFieldArrayElement_after.java b/java/java-tests/testData/refactoring/extractMethod/CallOnFieldArrayElement_after.java new file mode 100644 index 000000000000..569e9b96299c --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/CallOnFieldArrayElement_after.java @@ -0,0 +1,10 @@ +class C { + String[] vars; + int foo(C c, int i) { + return newMethod(c.vars[i]); + } + + private int newMethod(String var) { + return var.length(); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/refactoring/ExtractMethodTest.java b/java/java-tests/testSrc/com/intellij/java/refactoring/ExtractMethodTest.java index e768c0be364f..369f866f9b1b 100644 --- a/java/java-tests/testSrc/com/intellij/java/refactoring/ExtractMethodTest.java +++ b/java/java-tests/testSrc/com/intellij/java/refactoring/ExtractMethodTest.java @@ -974,6 +974,34 @@ public class ExtractMethodTest extends LightCodeInsightTestCase { doTest(); } + public void testBuilderChainWithArrayAccess() throws Exception { + doTest(); + } + + public void testBuilderChainWithArrayAccessExpr() throws Exception { + doTest(); + } + + public void testBuilderChainWithArrayAccessIf() throws Exception { + doTest(); + } + + public void testBuilderChainWith2DimArrayAccess() throws Exception { + doTest(); + } + + public void testCallOnArrayElement() throws Exception { + doTest(); + } + + public void testCallOn2DimArrayElement() throws Exception { + doTest(); + } + + public void testCallOnFieldArrayElement() throws Exception { + doTest(); + } + private void doTestDisabledParam() throws PrepareFailedException { final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject()); settings.ELSE_ON_NEW_LINE = true; From 46e1cb377dc18dee0cab242da1dbd28f4c0efda9 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Wed, 5 Jul 2017 18:56:49 +0300 Subject: [PATCH 15/37] Update patch file icons --- platform/icons/src/vcs/patch.png | Bin 136 -> 131 bytes platform/icons/src/vcs/patch@2x.png | Bin 172 -> 169 bytes platform/icons/src/vcs/patch@2x_dark.png | Bin 173 -> 169 bytes platform/icons/src/vcs/patch_applied.png | Bin 240 -> 252 bytes platform/icons/src/vcs/patch_applied@2x.png | Bin 391 -> 398 bytes .../icons/src/vcs/patch_applied@2x_dark.png | Bin 391 -> 399 bytes platform/icons/src/vcs/patch_applied_dark.png | Bin 240 -> 253 bytes platform/icons/src/vcs/patch_dark.png | Bin 136 -> 131 bytes 8 files changed, 0 insertions(+), 0 deletions(-) diff --git a/platform/icons/src/vcs/patch.png b/platform/icons/src/vcs/patch.png index 338fc4a2e16bcdf1e66ca93549aee081203a9cb5..84cda0bebf63766dd4ca4637aebd89a21d97b7de 100755 GIT binary patch delta 102 zcmeBRY-XIG67S{d;uunKD>-3-=!~s3&URDowe)pf;tBTH#At9fMY3Vd%pCCq!zn7_ zIZAB8sSVd++Ws>qGk7Rl$tA@tun;Rc6}5kH@~@o@G7JpKAMI;P&Xi^`0D-5gpUXO@ GgeCy@@Fw8^ delta 107 zcmV-x0F?iO0f+&RBxFxXL_t(|+GAkA0%k4WkPoFdVZ#jg07x@T5y=L?G?QfjvSzYf zfW=UfT(F5)Oq2mya~b{*i~%5x=y-^Ef#O)mDQo?Xjt3?kkZb_C006IifoM#djo0000BL_t(|+GAiK0nA#yAs>moi4@F0DF9iHY!F=?0JD5R9Dr;& z-5r2#Ib9uq&2qXq0GH)-aR5Hc2P_xhaOf~~0I3F#JO^maW%v)mqYfB34uDxm2oGNe yjHZE62aMJQIc2TiiNV9y0i$VPFgAn$ diff --git a/platform/icons/src/vcs/patch@2x_dark.png b/platform/icons/src/vcs/patch@2x_dark.png index b6088f513b87530955662cf3e9d58ceb0b9da48a..f5a1ba33492275399cddaea6188469bea5a7dee1 100755 GIT binary patch delta 141 zcmZ3>xRP;#N_~N+i(^Q|t>gp=)|7_G)8RAu%Ir^;(xXDJt63$QsOI tbCWfQ5|^>B|7mSl@dGHetWl7e;mlf5i7B<4?lAy?rz@ME%Q~loCIFeYIa~k$ delta 145 zcmV;C0B--O0j&X$B!6^CL_t(|+GAiK0nA#yAs>moi4@F0DF9iHY!F=?0JD5R9Dr;& z-5r2#Ib9uq&2qXq0GH)-aR5Hc2P_xhaOf~~0I3F#JO}umX7~@nqYfB34uDxm2oGNe zjHZE62aMJQ$IoB>P7EHt4j4@XBhLXN4gdh(UmVk=0qMU0015yANkvXXu0mjfdpk6r diff --git a/platform/icons/src/vcs/patch_applied.png b/platform/icons/src/vcs/patch_applied.png index 6d09223e40803eb5580b60d146ed5c5ac42493a7..0cc7bb02c9bb72a3f2171a7e5d7b846c5e050399 100755 GIT binary patch delta 224 zcmV<603ZMG0sH}wB!9z6L_t(|+GAj#6_~YrLq3$=gpP^Q3{yz50Wi&E8Gx*rY!_fL zlq45yA_fy>Ku%ffcYFqz?i8C3#16z5FcIAVphg8C{tv|WF$@4{gyHPcmd6RTg%{Bb z$%opG-2fyPeDm05Yz{lNHa_WlHdPh1R%CB0OFGlYjpgPG{FqpPf9vy1Njxn5bU-S0!V&G au^j+ap;3}?tB`R30000l zst>C8KgedBw&O_$%WZZj&W0I+({=(0X(GrAXod(dkOGk69b`ZPvK#<0x0mn?!BhkQ O0000r7hEehA4m%6oHb{0*miK*nyHLXhTZT>!ErkV$st^vhRs-zzZOo8)!&Bl7amo zF)V39mt@});Q%{O3dUk+0#J=2k|W6TJrNFw1sY-iO2z1@8YB)>=S;x&2Gl5ub#a8k zduXDxz~TG()NHB?K*1~qG_U}RBhKT<_aLl5k^=~o11Eu)ALJ`67R@Ky_oNg=K)ebR zmoi4@F0DF9iHY!F=?0JD5R9Dr;& z-5r2#Ib9uq&2qXq0GH)-aR5Hc2P_xhaOf~~0I3F#JO|{IwSEWTQ3sIg0Mngf4L}?) zAPxXpEC9ssp;&<^2f!>Ogo$(j$oK!Dct1TIFbB!^|B-M4rGHV-2E>X)M1cjG?_pSh z5(g{?Vs4NjK-w9Po;Do5pHFEb)CC$+05#YE$WOtcN0(^dli+|Fkflh5od@|68YSfW zp2(uO4Vt3wqZzs%6eU1Bk!;@+>3})VxL=RO;C^VLT#m>0{UnuyII`VNsNv_aI3fY$ za~!@0IYxjQ4rKu0ZfH7qkHrxNIDFqg?dsSWXs81g2Y@W@C)xL8wg(iU+3`OC+(4@a z39LYxkH>Q2d{04(Xd=`R=W#fofPo$WN5Z?$Ku>^HLSnD+kN^PT@GiL>=sE5H0000< KMNUMnLSTY9-w5qygI>W& S@OO&<0000moi4@F0DF9iHY!F=?0JD5R9Dr;& z-5r2#Ib9uq&2qXq0GH)-aR5Hc2P_xhaOf~~0I3F#JO>;ZIRIuMAxxwLK)(ME#rx^$fH_FM|Br+dD1VKDHXv3cA_^?fd=JA4 zlsI5H5OaeJ0n*NR^t9pd{d`Ijp)Syn0;s_TKz<4iJ-S5uo&*QffGkBa>^#Vq&?q6_ z_e2)OZO{~bAI;GHpeO<2iDdhpNC(V;#{GIM2KPe~<#If}?!142!zvDB&bf?&SAa)?efQje^05vKA@qZw`k6{2vBMhIoaOLrqb2~4h z8IljR9lHTYF8JoL&Da{K=sk!AiY*6;$8QQXgK7rh1e^w(yjpnmK2X7WsKWP^YwHYw z;webBpC=Rx%QH8mI)XIAG$8r?KSlsz3j-iN8MxQlA4wC;u>GW@gEo*~kqp6ZJ0XDN bcNE(JP#s>{OYQQ`00000NkvXXu0mjf4_#te delta 212 zcmV;_04x9f0q_BkB!9O_L_t(|+GAkA0%k4WkPoFdVZ#jg07x@T5y=L?G?QfjvSzYf zfW=UfT(F5)Oq2n~&tLvdGXqR_iU|Pmc@T|Z07xS`#$rGN5dR0_`Gj0Bv3u3bOl0xi zm2>NX8s9?=XuxN{3zu!W(?Cw`TRE=_CJq(*4>d#pUo51+Y)!J>A-@ac8l1M{3WL-G zA$?HA|3Nn6v>i`6SZ=dJaW>2loVF84NE1O`Kr=*uffRrg?;rybkmUf-F{EAQyp*5- O0000-3-=!~s3&URDowe)pf;tBTH#At9fMY3Vd%pCCq!zn7_ zIZAB8sSWQl+x{~rGk7Rl$tA@tun_B;zyFV{?B(;UPZ${LRooMry2eai~%5x=y-^Ef#O&=e*W@zbUZNWfMf&61pv!%iAz~+b|?S< N002ovPDHLkV1g!|D-r+z From faf0d45e7581d1016d118098e070140ffd52d0da Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Wed, 5 Jul 2017 18:58:36 +0300 Subject: [PATCH 16/37] Redraw pull icon to make it less similar to pull: IDEA-172874 Green is chosen, because it is different from blue, and because commit is green as well (and push is like commit: changes are submitted). --- platform/icons/src/vcs/push.png | Bin 215 -> 247 bytes platform/icons/src/vcs/push@2x.png | Bin 357 -> 434 bytes platform/icons/src/vcs/push@2x_dark.png | Bin 357 -> 437 bytes platform/icons/src/vcs/push_dark.png | Bin 215 -> 251 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/platform/icons/src/vcs/push.png b/platform/icons/src/vcs/push.png index d1df160ed26af623d0bb956eec80bf622c0d26b7..6b5a293244f0b7e206d2111c5f3f3e611b03bad3 100755 GIT binary patch delta 230 zcmVS`^s|E`*zol8gW7}15Or_{NW;ksS0^uA4Mac&T=lo#U*V4FJ6QqbLi_{L z08tG=a19GC^+HrnKi3RY4G#nC8sLEgWT3ed;iRsaOIB>&4PRFcbOzAJZ{B~zq5%Sc za%JsP;3}Rxe^ozWHa-nNuyy~jiSt)N*gyu5iy3t2!CKopvv%iK~#9!V_={gn0rkdh);lNGBsZZ;-^p= z)sUpJRs$gJ2jZDfS`A4vAJpcjNcaR;9LWHfW*F{A(!2{zGYs#78-k`8hK2EHMi_$E z1*qYG(1;Gu!eBQLr;=mx4S$T9$G$SxqBi^X6|qCcV?s8Ab-p8)Y}sJCWA z`NSt8B+W29o20~tEow;RfldoUT7>1g=_ zY60Bs$OtHi)q?5gn(>>CYzNc=Ow%EVn5+vF!89GNm^2GcKRbwNI{vgkLJ(mlo1OR8 z5KL^uMlxE+0Q1-7SEq?d&DfJ^egEvT_Q`MFe}tqNxaq*?1tvkb(YNnECcy$^aN_)x z-1^?xD>mZNc7O5u?TPbO0!cD000N+XpdDC^*nj*iPztDkv>-wQ!0;(+p8`bRzW=}~ z4hcZ=EdYY8`;TD;6p#xP1WFNEWDzLrI;JnmY3QA`Vgo$kR&-4R3KCx?60qRt*-P!y z7NRBJ)0eJO%>t6jC~_< delta 341 zcmV-b0jmD81LXpc8Gi-<0047(dh`GQ0W3*GK~#9!?Up?XgD@C|ogKVEPvAvdT-`0+ zKLI1vyC1waoBK<-=sB@z^%gw6#};*J7jml6-br^!%c3%x~U zYa0?d3cP?+P);>dt@&aoL4XdIrf9i#R)-1yvXN|7^j>OHV4E%5Fx=NJT7M<5`w(ne nHCIZQ9bPT;g#PjUXk7aMA`{el*`MH^00000NkvXXu0mjfoA8u( diff --git a/platform/icons/src/vcs/push@2x_dark.png b/platform/icons/src/vcs/push@2x_dark.png index 97afb58434f8d393cbbbd37a462ca65a302ae18d..8c4fd8787b0495287a4f3eaddd0524f0c1e5e525 100755 GIT binary patch delta 422 zcmV;X0a^a#0<{B>8Gix*008_L?V|ty0eneBK~#9!WB5-NSaEH7$=*m}5W|0R0g-l) zZvl~ZP-Foyc2H^o5FC26Z75st^z#E62GO_QUt5pn(a?egmwIWKim(QK-^mIzafAhT zA3a^TcH88ItAPl}0CMq|e&^j)tfm744Jf;RfldoUT7>1g=_ zY60Bs$OtHi)q?5gn(>>CYzNc=Ow%EVn5+vF!89GNm^2GcKRbwNI{vgkLJ(mlo1OR8 z5KL^uMlxE+0Q1-7SEq?d&DfLaf;C%r96a&u`wvK(ftwDDUSJZ08~yIXM-nVRDPdl` z+Ii^2`Kvc^X@7h9=H01_*MKCM761WIKhO@WMm%`(94G};Kw1!?=ceyJ_8vO}M8ALk z!72_3K=LgBl?M+VV+ItE3ls!O5m{srDC~}%y}V`bktOSQz!UD;?fZd(#FvQ#EVyy^ z;jHBw(30=fTlc7D0ZC;PITjFKMh#XAXdXl)RYc@iKp$#VN{$5-0-9R@0FXU-8k*$r Q@Bjb+07*qoM6N<$f+D-QVE_OC delta 341 zcmV-b0jmDB1LXpc8Gi-<0047(dh`GQ0W3*GK~#9!?Up?XgD@C|J3DxTp1_N^xVj78 zz|{kE%j_VIT?&PcIf0{7?OgC4{UTqe4KXIsL?{Lx5d3)`^Y`U+I-09iKgr0B;R8Z^ zhv$=UPVOr_m4tIrt#4c(Gyw=uA?!NQ2#*!voLChc0OcfDq<>U|M?kM3(2vh5{=8Zs zSrM+55W0O1B7}!PYY@(9$9DyRN(UjFT7+}xkA#K5n((j0sv(e1NO_p%@-v@=TM3}V z+%jx{i{@rzFEFza-GMhiy$DEzLZAl*pztn$5-AEWBJTnyaYF&RONocz^JFOUh2CPa zwF`*?1ztcVsZN@i(R{I#pg@O9Q?^_?r^AGQ*(f$Eb}w@&u+5fj86FrHt-li3eF!$K nnkyyj4zCV+!uS`^s|E`*zol8gW7}15Or_{NW;ksS0^uA4Mac&T=lo#U*V4FJ6QqbLi_{L z08tG=a19GC^+HrnKi3RY4G#nC8sLEgWT3ed;iMT$*B>}>4r8tw=nSBbzkUCKMFZ5) z-+$~qb_TBE)0eM@PMyc60SN9qczo*OH3%EX0CF*d4n0_RA3im0$vU75*Y7-lHLTG! kU=1;(^b07*qoM6N<$f%iK~#9!V_={gn0rkdh);lNGBsZZ;-^p= z)sW?zb{PP1KM>D^(rQSW`JgsGMZzb*;z$O-G{bN|lIC4#nqhbs+z>R)Ff5EmGr|zO zEV!Z From 7c24c86a64847108f1a3103fe000b01cf60b75f6 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 5 Jul 2017 17:27:39 +0200 Subject: [PATCH 17/37] adjust some performance tests to reduce flakiness --- .../codeInsight/daemon/lambda/InferencePerformanceTest.java | 4 ++-- .../com/intellij/openapi/vfs/VfsUtilPerformanceTest.java | 2 +- .../com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/InferencePerformanceTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/InferencePerformanceTest.java index 8b2a43e68f4f..e1695f605713 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/InferencePerformanceTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/InferencePerformanceTest.java @@ -25,11 +25,11 @@ public class InferencePerformanceTest extends LightDaemonAnalyzerTestCase { @NonNls static final String BASE_PATH = "/codeInsight/daemonCodeAnalyzer/lambda/performance"; public void testPolyMethodCallArgumentPassedToVarargs() throws Exception { - PlatformTestUtil.startPerformanceTest("50 poly method calls passed to Arrays.asList", 3500, this::doTest).assertTiming(); + PlatformTestUtil.startPerformanceTest("50 poly method calls passed to Arrays.asList", 4000, this::doTest).usesAllCPUCores().assertTiming(); } public void testDiamondConstructorCallPassedToVarargs() throws Exception { - PlatformTestUtil.startPerformanceTest("50 diamond constructor calls passed to Arrays.asList", 10000, this::doTest).assertTiming(); + PlatformTestUtil.startPerformanceTest("50 diamond constructor calls passed to Arrays.asList", 12000, this::doTest).usesAllCPUCores().assertTiming(); } private void doTest() { diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilPerformanceTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilPerformanceTest.java index 018518bf1e3e..16ae12206f44 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilPerformanceTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilPerformanceTest.java @@ -300,7 +300,7 @@ public class VfsUtilPerformanceTest extends BareTestFixtureTestCase { }) .assertTiming(); - PlatformTestUtil.startPerformanceTest("many files deletions", 10000, () -> { + PlatformTestUtil.startPerformanceTest("many files deletions", 11000, () -> { assertEquals(N, events.size()); processEvents(events); assertEquals(0, temp.getCachedChildren().size()); diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java index 534663d64ce8..9ebcb117aac8 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/impl/VirtualFilePointerTest.java @@ -750,7 +750,7 @@ public class VirtualFilePointerTest extends PlatformTestCase { myVirtualFilePointerManager.create(VfsUtilCore.pathToUrl("/a/b/c/d/" + i), disposable, listener); events.add(new VFileCreateEvent(this, temp, "xxx" + i, false, true)); } - PlatformTestUtil.startPerformanceTest("vfp update", 5000, () -> { + PlatformTestUtil.startPerformanceTest("vfp update", 6000, () -> { for (int i=0; i< 100; i++) { // simulate VFS refresh events since launching the actual refresh is too slow myVirtualFilePointerManager.before(events); From 002b8d4b5f2b69eb0e899185b8a7f0ad5596a5aa Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 5 Jul 2017 19:00:46 +0300 Subject: [PATCH 18/37] PY-24763 Fixed: Expected wrong return from __init__ Refactor type inference for `__init__` functions. Always return `None` as return type and always return class as call type. --- .../typing/PyTypingTypeProvider.java | 22 +++--------- .../docstrings/PyDocStringTypeProvider.java | 34 +++++++++++-------- .../src/com/jetbrains/python/psi/PyUtil.java | 27 ++++++++++++--- .../psi/impl/PyCallExpressionHelper.java | 4 +-- .../python/psi/impl/PyFunctionImpl.java | 2 +- .../jetbrains/python/pyi/PyiTypeProvider.java | 2 +- .../AnnotatedDunderInitInGenericClass.py | 6 ++++ .../PyTypeCheckerInspectionTest.java | 5 +++ 8 files changed, 62 insertions(+), 40 deletions(-) create mode 100644 python/testData/inspections/PyTypeCheckerInspection/AnnotatedDunderInitInGenericClass.py diff --git a/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java index a03e66573c26..ba897546f205 100644 --- a/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java @@ -241,24 +241,10 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { public Ref getReturnType(@NotNull PyCallable callable, @NotNull TypeEvalContext context) { if (callable instanceof PyFunction) { final PyFunction function = (PyFunction)callable; - // We model generic classes as return types of their constructors here - final boolean isInit = PyUtil.isInit(function); - if (isInit) { - final PyClass cls = function.getContainingClass(); - if (cls != null) { - final PyType genericType = getGenericType(cls, context); - if (genericType != null) { - return Ref.create(genericType); - } - } - } final PyExpression value = getReturnTypeAnnotation(function); if (value != null) { final Ref typeRef = getType(value, new Context(context)); if (typeRef != null) { - if (isInit && typeRef.get() instanceof PyNoneType) { - return null; - } if (function.isAsync() && function.isAsyncAllowed() && !function.isGenerator()) { return Ref.create(wrapInCoroutineType(typeRef.get(), callable)); } @@ -495,10 +481,6 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { if (genericType != null) { return Ref.create(genericType); } - final Ref classType = getClassType(resolved, context.getTypeContext()); - if (classType != null) { - return classType; - } final PyType stringBasedType = getStringBasedType(resolved, context); if (stringBasedType != null) { return Ref.create(stringBasedType); @@ -507,6 +489,10 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { if (anyType != null) { return anyType; } + final Ref classType = getClassType(resolved, context.getTypeContext()); + if (classType != null) { + return classType; + } return null; } finally { diff --git a/python/src/com/jetbrains/python/documentation/docstrings/PyDocStringTypeProvider.java b/python/src/com/jetbrains/python/documentation/docstrings/PyDocStringTypeProvider.java index a43bfa5936ae..737ee7b8e93c 100644 --- a/python/src/com/jetbrains/python/documentation/docstrings/PyDocStringTypeProvider.java +++ b/python/src/com/jetbrains/python/documentation/docstrings/PyDocStringTypeProvider.java @@ -58,16 +58,11 @@ public class PyDocStringTypeProvider extends PyTypeProviderBase { @Nullable @Override public Ref getReturnType(@NotNull PyCallable callable, @NotNull TypeEvalContext context) { - if (callable instanceof PyDocStringOwner) { - final StructuredDocString docString = ((PyDocStringOwner)callable).getStructuredDocString(); - if (docString != null) { - final String typeText = docString.getReturnType(); - if (StringUtil.isNotEmpty(typeText)) { - return parseType(callable, typeText, context); - } - } + if (callable instanceof PyFunction && PyUtil.isInit((PyFunction)callable)) { + return null; } - return null; + + return getReturnTypeFromDocString(callable, context); } @NotNull @@ -87,12 +82,23 @@ public class PyDocStringTypeProvider extends PyTypeProviderBase { public PyType getGenericType(@NotNull PyClass cls, @NotNull TypeEvalContext context) { final PyFunction init = cls.findInitOrNew(true, context); if (init != null) { - final PyType initType = context.getType(init); - final PyCallableType callableType = PyUtil.as(initType, PyCallableType.class); - if (callableType != null) { - return callableType.getReturnType(context); - } + return Ref.deref(getReturnTypeFromDocString(init, context)); } return null; } + + @Nullable + private static Ref getReturnTypeFromDocString(@NotNull PyCallable callable, @NotNull TypeEvalContext context) { + if (callable instanceof PyDocStringOwner) { + final StructuredDocString docString = ((PyDocStringOwner)callable).getStructuredDocString(); + if (docString != null) { + final String typeText = docString.getReturnType(); + if (StringUtil.isNotEmpty(typeText)) { + return parseType(callable, typeText, context); + } + } + } + + return null; + } } diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java index 7b048a27d616..76ec28d37e26 100644 --- a/python/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/src/com/jetbrains/python/psi/PyUtil.java @@ -72,10 +72,7 @@ import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; import com.jetbrains.python.formatter.PyCodeStyleSettings; import com.jetbrains.python.magicLiteral.PyMagicLiteralTools; -import com.jetbrains.python.psi.impl.PyBuiltinCache; -import com.jetbrains.python.psi.impl.PyPsiUtils; -import com.jetbrains.python.psi.impl.PyStringLiteralExpressionImpl; -import com.jetbrains.python.psi.impl.PythonLanguageLevelPusher; +import com.jetbrains.python.psi.impl.*; import com.jetbrains.python.psi.resolve.PyResolveContext; import com.jetbrains.python.psi.resolve.QualifiedNameFinder; import com.jetbrains.python.psi.resolve.RatedResolveResult; @@ -941,6 +938,28 @@ public class PyUtil { } } + @Nullable + public static PyType getReturnTypeToAnalyzeAsCallType(@NotNull PyFunction function, @NotNull TypeEvalContext context) { + if (isInit(function)) { + final PyClass cls = function.getContainingClass(); + if (cls != null) { + for (PyTypeProvider provider : Extensions.getExtensions(PyTypeProvider.EP_NAME)) { + final PyType providedClassType = provider.getGenericType(cls, context); + if (providedClassType != null) { + return providedClassType; + } + } + + final PyInstantiableType classType = as(context.getType(cls), PyInstantiableType.class); + if (classType != null) { + return classType.toInstance(); + } + } + } + + return context.getReturnType(function); + } + public static class KnownDecoratorProviderHolder { public static final PyKnownDecoratorProvider[] KNOWN_DECORATOR_PROVIDERS = Extensions.getExtensions(PyKnownDecoratorProvider.EP_NAME); diff --git a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java index 012a89cecfec..4703a00ec7d1 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java +++ b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java @@ -567,10 +567,10 @@ public class PyCallExpressionHelper { return Ref.create(new PyClassTypeImpl(cls, false)); } - if (t != null && !(t instanceof PyNoneType)) { + if (t != null) { return Ref.create(t); } - if (cls != null && t == null) { + if (cls != null) { final PyFunction newMethod = cls.findMethodByName(PyNames.NEW, true, null); if (newMethod != null && !PyBuiltinCache.getInstance(call).isBuiltin(newMethod)) { return Ref.create(PyUnionType.createWeakType(new PyClassTypeImpl(cls, false))); diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java index ec40da9ecaa1..102baec32273 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java @@ -265,7 +265,7 @@ public class PyFunctionImpl extends PyBaseElementImpl implements public PyType getCallType(@Nullable PyExpression receiver, @NotNull Map parameters, @NotNull TypeEvalContext context) { - return analyzeCallType(context.getReturnType(this), receiver, parameters, context); + return analyzeCallType(PyUtil.getReturnTypeToAnalyzeAsCallType(this, context), receiver, parameters, context); } @Nullable diff --git a/python/src/com/jetbrains/python/pyi/PyiTypeProvider.java b/python/src/com/jetbrains/python/pyi/PyiTypeProvider.java index f763529ecb93..f3af243aa423 100644 --- a/python/src/com/jetbrains/python/pyi/PyiTypeProvider.java +++ b/python/src/com/jetbrains/python/pyi/PyiTypeProvider.java @@ -105,7 +105,7 @@ public class PyiTypeProvider extends PyTypeProviderBase { final List matchedReturnTypes = new ArrayList<>(); for (PyFunction overload : overloads) { - final PyType returnType = context.getReturnType(overload); + final PyType returnType = PyUtil.getReturnTypeToAnalyzeAsCallType(overload, context); allReturnTypes.add(PyTypeChecker.substitute(returnType, new HashMap<>(), context)); final PyCallExpression.PyArgumentsMapping mapping = PyCallExpressionHelper.mapArguments(callSite, overload, context); diff --git a/python/testData/inspections/PyTypeCheckerInspection/AnnotatedDunderInitInGenericClass.py b/python/testData/inspections/PyTypeCheckerInspection/AnnotatedDunderInitInGenericClass.py new file mode 100644 index 000000000000..8fd3a0941734 --- /dev/null +++ b/python/testData/inspections/PyTypeCheckerInspection/AnnotatedDunderInitInGenericClass.py @@ -0,0 +1,6 @@ +from typing import TypeVar, Iterator + +T = TypeVar('T') +class MyIterator(Iterator[T]): + def __init__(self) -> None: + self.other = "other" \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java index 704fc4cf021c..90c09fc5a7b1 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java @@ -422,4 +422,9 @@ public class PyTypeCheckerInspectionTest extends PyTestCase { public void testCallOperator() { runWithLanguageLevel(LanguageLevel.PYTHON35, this::doTest); } + + // PY-24763 + public void testAnnotatedDunderInitInGenericClass() { + runWithLanguageLevel(LanguageLevel.PYTHON35, this::doTest); + } } From 6cd8d273005222782e7257ec003942cc5727fa83 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 5 Jul 2017 19:01:46 +0300 Subject: [PATCH 19/37] Remove PyFunction.getReturnTypeFromDocString as unused. --- .../com/jetbrains/python/psi/PyFunction.java | 5 +-- .../python/psi/impl/PyFunctionImpl.java | 32 ------------------- 2 files changed, 1 insertion(+), 36 deletions(-) diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyFunction.java b/python/psi-api/src/com/jetbrains/python/psi/PyFunction.java index 2727464338b4..203ed3341d61 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PyFunction.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PyFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,9 +53,6 @@ public interface PyFunction extends PsiNamedElement, StubBasedPsiElement implements } } - @Nullable - @Override - public PyType getReturnTypeFromDocString() { - final String typeName = extractReturnType(); - return typeName != null ? PyTypeParser.getTypeByName(this, typeName) : null; - } - @Nullable @Override public String getDeprecationMessage() { @@ -526,30 +518,6 @@ public class PyFunctionImpl extends PyBaseElementImpl implements return false; } - @Nullable - private String extractReturnType() { - final String ARROW = "->"; - final StructuredDocString structuredDocString = getStructuredDocString(); - if (structuredDocString != null) { - return structuredDocString.getReturnType(); - } - final String docString = getDocStringValue(); - if (docString != null && docString.contains(ARROW)) { - final List lines = StringUtil.split(docString, "\n"); - while (lines.size() > 0 && lines.get(0).trim().length() == 0) { - lines.remove(0); - } - if (lines.size() > 1 && lines.get(1).trim().length() == 0) { - String firstLine = lines.get(0); - int pos = firstLine.lastIndexOf(ARROW); - if (pos >= 0) { - return firstLine.substring(pos + 2).trim(); - } - } - } - return null; - } - private static class ReturnVisitor extends PyRecursiveElementVisitor { private final PyFunction myFunction; private final TypeEvalContext myContext; From 5b44c1723d1e368f7b3cc530aaee329bb4b4f2a0 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 5 Jul 2017 19:02:27 +0300 Subject: [PATCH 20/37] Fix inferring type for the generic class constructing with unfilled generic value --- .../python/codeInsight/typing/PyTypingTypeProvider.java | 1 + python/testSrc/com/jetbrains/python/PyTypeTest.java | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java index ba897546f205..fd11d4d8848f 100644 --- a/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java @@ -410,6 +410,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { final PyTupleExpression tupleExpr = as(e, PyTupleExpression.class); return tupleExpr != null ? StreamEx.of(tupleExpr.getElements()) : StreamEx.of(e); }) + .nonNull() .flatMap(e -> tryResolving(e, typeEvalContext).stream()) .map(e -> getGenericTypeFromTypeVar(e, context)) .select(PyType.class) diff --git a/python/testSrc/com/jetbrains/python/PyTypeTest.java b/python/testSrc/com/jetbrains/python/PyTypeTest.java index 1c2006ee2a2d..268dedb2ec96 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeTest.java @@ -1964,6 +1964,15 @@ public class PyTypeTest extends PyTestCase { "expr = my_list.count"); } + public void testConstructingGenericClassWithNotFilledGenericValue() { + doTest("MyIterator", + "from typing import Iterator\n" + + "class MyIterator(Iterator[]):\n" + + " def __init__(self) -> None:\n" + + " self.other = \"other\"\n" + + "expr = MyIterator()"); + } + private static List getTypeEvalContexts(@NotNull PyExpression element) { return ImmutableList.of(TypeEvalContext.codeAnalysis(element.getProject(), element.getContainingFile()).withTracing(), TypeEvalContext.userInitiated(element.getProject(), element.getContainingFile()).withTracing()); From a70df1131f0bcc74a247eee92f274b77a6870d7b Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 5 Jul 2017 19:02:57 +0300 Subject: [PATCH 21/37] Update PyTypeCheckerInspection to raise warning on non-None return type annotation for `__init__` --- .../python/inspections/PyTypeCheckerInspection.java | 10 +++++++--- .../DunderInitAnnotatedAsNonNone.py | 9 +++++++++ .../inspections/PyTypeCheckerInspectionTest.java | 4 ++++ 3 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 python/testData/inspections/PyTypeCheckerInspection/DunderInitAnnotatedAsNonNone.py diff --git a/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java b/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java index c128b29ff73b..ee3ce51d5030 100644 --- a/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java @@ -139,9 +139,8 @@ public class PyTypeCheckerInspection extends PyInspection { final String typeCommentAnnotation = node.getTypeCommentAnnotation(); if (annotation != null || typeCommentAnnotation != null) { if (!PyUtil.isEmptyFunction(node)) { - final PyStatementList statements = node.getStatementList(); - ReturnVisitor visitor = new ReturnVisitor(node); - statements.accept(visitor); + final ReturnVisitor visitor = new ReturnVisitor(node); + node.getStatementList().accept(visitor); if (!visitor.myHasReturns) { final PyType expected = getExpectedReturnType(node); final String expectedName = PythonDocumentationProvider.getTypeName(expected, myTypeEvalContext); @@ -151,6 +150,11 @@ public class PyTypeCheckerInspection extends PyInspection { } } } + + if (PyUtil.isInit(node) && !(getExpectedReturnType(node) instanceof PyNoneType)) { + registerProblem(annotation != null ? annotation.getValue() : node.getTypeComment(), + PyNames.INIT + " should return " + PyNames.NONE); + } } } diff --git a/python/testData/inspections/PyTypeCheckerInspection/DunderInitAnnotatedAsNonNone.py b/python/testData/inspections/PyTypeCheckerInspection/DunderInitAnnotatedAsNonNone.py new file mode 100644 index 000000000000..68e9f305ab7b --- /dev/null +++ b/python/testData/inspections/PyTypeCheckerInspection/DunderInitAnnotatedAsNonNone.py @@ -0,0 +1,9 @@ +class A: + def __init__(self) -> int: + pass + + +class B: + def __init__(self, foo): + # type: (str) -> int + pass \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java index 90c09fc5a7b1..caec7d0948be 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java @@ -427,4 +427,8 @@ public class PyTypeCheckerInspectionTest extends PyTestCase { public void testAnnotatedDunderInitInGenericClass() { runWithLanguageLevel(LanguageLevel.PYTHON35, this::doTest); } + + public void testDunderInitAnnotatedAsNonNone() { + runWithLanguageLevel(LanguageLevel.PYTHON35, this::doTest); + } } From a124e615461ef8fb81fdc3fcc458f26cd48bb21b Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Wed, 5 Jul 2017 19:14:10 +0300 Subject: [PATCH 22/37] Don't empty borders in the Git push options panel If the border is empty, the focus border is empty as well. On the other hand, insets() already take the border into account. There are a couple of extra pixels on Mac though, but seems to be a minor issue. --- .../git4idea/push/GitPushOptionsPanel.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/plugins/git4idea/src/git4idea/push/GitPushOptionsPanel.java b/plugins/git4idea/src/git4idea/push/GitPushOptionsPanel.java index fc1defc19b84..ef302d32b1a2 100644 --- a/plugins/git4idea/src/git4idea/push/GitPushOptionsPanel.java +++ b/plugins/git4idea/src/git4idea/push/GitPushOptionsPanel.java @@ -70,16 +70,20 @@ public class GitPushOptionsPanel extends VcsPushOptionsPanel { myRunHooks.setSelected(true); myRunHooks.setVisible(showSkipHookOption); - myRunHooks.setBorder(JBUI.Borders.empty()); - myPushTagsMode.setBorder(JBUI.Borders.empty()); - myPushTags.setBorder(JBUI.Borders.empty()); - setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); add(myPushTags); - add(Box.createHorizontalStrut(JBUI.scale(3))); - add(myPushTagsMode); - add(Box.createHorizontalStrut(JBUI.scale(37))); - add(myRunHooks); + if (myPushTagsMode.isVisible()) { + add(Box.createHorizontalStrut(calcStrutWidth(8, myPushTags, myPushTagsMode))); + add(myPushTagsMode); + } + if (myRunHooks.isVisible()) { + add(Box.createHorizontalStrut(calcStrutWidth(40, myPushTagsMode, myRunHooks))); + add(myRunHooks); + } + } + + private static int calcStrutWidth(int plannedWidth, @NotNull JComponent leftComponent, @NotNull JComponent rightComponent) { + return JBUI.scale(plannedWidth) - JBUI.insets(rightComponent.getInsets()).left - JBUI.insets(leftComponent.getInsets()).right; } @Nullable From 24b842b5248e182d09efddd920776fc98ddc9a83 Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Wed, 5 Jul 2017 17:14:35 +0200 Subject: [PATCH 23/37] junit 5: fix references when no explicit attr name specified --- .../references/JUnitReferenceContributor.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/junit/src/com/intellij/execution/junit/codeInsight/references/JUnitReferenceContributor.java b/plugins/junit/src/com/intellij/execution/junit/codeInsight/references/JUnitReferenceContributor.java index a6e54d5238da..4e1e085c7c85 100644 --- a/plugins/junit/src/com/intellij/execution/junit/codeInsight/references/JUnitReferenceContributor.java +++ b/plugins/junit/src/com/intellij/execution/junit/codeInsight/references/JUnitReferenceContributor.java @@ -23,7 +23,9 @@ import com.intellij.psi.filters.ElementFilter; import com.intellij.psi.filters.position.FilterPattern; import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.ObjectUtils; import com.intellij.util.ProcessingContext; +import com.siyeh.ig.junit.JUnitCommonClassNames; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -33,13 +35,13 @@ public class JUnitReferenceContributor extends PsiReferenceContributor { } public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) { - registrar.registerReferenceProvider(getElementPattern("org.junit.jupiter.params.provider.MethodSource", "value"), new PsiReferenceProvider() { + registrar.registerReferenceProvider(getElementPattern(JUnitCommonClassNames.ORG_JUNIT_JUPITER_PARAMS_PROVIDER_METHOD_SOURCE, "value"), new PsiReferenceProvider() { @NotNull public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull final ProcessingContext context) { return new MethodSourceReference[]{new MethodSourceReference((PsiLiteral)element)}; } }); - registrar.registerReferenceProvider(getElementPattern("org.junit.jupiter.params.provider.CsvFileSource", "resources"), new PsiReferenceProvider() { + registrar.registerReferenceProvider(getElementPattern(JUnitCommonClassNames.ORG_JUNIT_JUPITER_PARAMS_PROVIDER_CSV_FILE_SOURCE, "resources"), new PsiReferenceProvider() { @NotNull public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull final ProcessingContext context) { return FileReferenceSet.createSet(element, false, false, false).getAllReferences(); @@ -60,7 +62,8 @@ public class JUnitReferenceContributor extends PsiReferenceContributor { public boolean isAcceptable(Object element, PsiElement context) { PsiNameValuePair pair = PsiTreeUtil.getParentOfType(context, PsiNameValuePair.class, false, PsiMember.class, PsiStatement.class); if (pair == null) return false; - if (!myParameterName.equals(pair.getName())) return false; + String name = ObjectUtils.notNull(pair.getName(), PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME); + if (!myParameterName.equals(name)) return false; PsiAnnotation annotation = PsiTreeUtil.getParentOfType(pair, PsiAnnotation.class); if (annotation == null) return false; return myAnnotation.equals(annotation.getQualifiedName()); From 008710be3a6d7a921213b8d7fc507489f5e7f923 Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Wed, 5 Jul 2017 18:04:37 +0200 Subject: [PATCH 24/37] change signature: detect javadoc for moved&renamed parameter (IDEA-175363) --- .../JavaChangeSignatureUsageProcessor.java | 5 +++-- .../changeSignature/ParamJavadocRenamedReordered.java | 8 ++++++++ .../ParamJavadocRenamedReordered_after.java | 9 +++++++++ .../intellij/java/refactoring/ChangeSignatureTest.java | 8 ++++++++ 4 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 java/java-tests/testData/refactoring/changeSignature/ParamJavadocRenamedReordered.java create mode 100644 java/java-tests/testData/refactoring/changeSignature/ParamJavadocRenamedReordered_after.java diff --git a/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeSignatureUsageProcessor.java b/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeSignatureUsageProcessor.java index 577ec002e58c..7083aede4b4a 100644 --- a/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeSignatureUsageProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeSignatureUsageProcessor.java @@ -947,8 +947,9 @@ public class JavaChangeSignatureUsageProcessor implements ChangeSignatureUsagePr RefactoringUtil.fixJavadocsForParams(method, newParameters, pair -> { final PsiParameter parameter = pair.first; final String oldParamName = pair.second; - final int idx = ArrayUtil.find(oldParameterNames, oldParamName); - return idx >= 0 && idx == method.getParameterList().getParameterIndex(parameter) && changeInfo.getNewParameters()[idx].getOldIndex() == idx; + final int oldIdx = ArrayUtil.find(oldParameterNames, oldParamName); + int newIndex = method.getParameterList().getParameterIndex(parameter); + return oldIdx >= 0 && newIndex >= 0 && changeInfo.getNewParameters()[newIndex].getOldIndex() == oldIdx; }, paramName -> ArrayUtil.find(oldParameterNames, paramName) >= 0); } diff --git a/java/java-tests/testData/refactoring/changeSignature/ParamJavadocRenamedReordered.java b/java/java-tests/testData/refactoring/changeSignature/ParamJavadocRenamedReordered.java new file mode 100644 index 000000000000..77faca83f8fa --- /dev/null +++ b/java/java-tests/testData/refactoring/changeSignature/ParamJavadocRenamedReordered.java @@ -0,0 +1,8 @@ +class X { + + /** + * @param a aparam + * @param b bparam + */ + public void mymethod(boolean a, boolean b) { } +} diff --git a/java/java-tests/testData/refactoring/changeSignature/ParamJavadocRenamedReordered_after.java b/java/java-tests/testData/refactoring/changeSignature/ParamJavadocRenamedReordered_after.java new file mode 100644 index 000000000000..dc9349153919 --- /dev/null +++ b/java/java-tests/testData/refactoring/changeSignature/ParamJavadocRenamedReordered_after.java @@ -0,0 +1,9 @@ +class X { + + /** + * @param a aparam + * @param c + * @param b1 bparam + */ + public void mymethod(boolean a, boolean c, boolean b1) { } +} diff --git a/java/java-tests/testSrc/com/intellij/java/refactoring/ChangeSignatureTest.java b/java/java-tests/testSrc/com/intellij/java/refactoring/ChangeSignatureTest.java index 2e3d882359d1..c3613c86b4cf 100644 --- a/java/java-tests/testSrc/com/intellij/java/refactoring/ChangeSignatureTest.java +++ b/java/java-tests/testSrc/com/intellij/java/refactoring/ChangeSignatureTest.java @@ -226,6 +226,14 @@ public class ChangeSignatureTest extends ChangeSignatureBaseTest { }, false); } + public void testParamJavadocRenamedReordered() { + doTest(null, new ParameterInfoImpl[]{ + new ParameterInfoImpl(0, "a", PsiType.BOOLEAN), + new ParameterInfoImpl(-1, "c", PsiType.BOOLEAN), + new ParameterInfoImpl(1, "b1", PsiType.BOOLEAN), + }, false); + } + public void testJavadocNoNewLineInserted() { doTest(null, new ParameterInfoImpl[]{ new ParameterInfoImpl(0, "newArgs", PsiType.DOUBLE), From 8e69e8748e0db231b2ce8c832cfdd28bc9dea4ee Mon Sep 17 00:00:00 2001 From: Valentina Kiryushkina Date: Wed, 5 Jul 2017 19:17:04 +0300 Subject: [PATCH 25/37] Move action from TaskDescription toolwindow toolbar to context menu --- python/educational-core/resources/META-INF/plugin.xml | 4 +++- .../src/com/jetbrains/edu/learning/EduPluginConfigurator.java | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/python/educational-core/resources/META-INF/plugin.xml b/python/educational-core/resources/META-INF/plugin.xml index 78f53d33f08b..6620364701e7 100644 --- a/python/educational-core/resources/META-INF/plugin.xml +++ b/python/educational-core/resources/META-INF/plugin.xml @@ -84,7 +84,9 @@ text="Switch Task Description Panel"> - + + + diff --git a/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java b/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java index d6429b5bfce3..ce88e1ce27d0 100644 --- a/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java +++ b/python/educational-core/src/com/jetbrains/edu/learning/EduPluginConfigurator.java @@ -119,8 +119,7 @@ public interface EduPluginConfigurator { StudyPreviousTaskAction.ACTION_ID, StudyNextTaskAction.ACTION_ID, StudyRefreshTaskFileAction.ACTION_ID, - StudyShowHintAction.ACTION_ID, - StudyUpdateRecommendationAction.ACTION_ID + StudyShowHintAction.ACTION_ID }; ActionManager actionManager = ActionManager.getInstance(); Arrays.stream(ids) From cfeb6c005e7a5e4dd3798dc02fe3b3c2586238d6 Mon Sep 17 00:00:00 2001 From: Nicolay Mitropolsky Date: Wed, 5 Jul 2017 17:23:21 +0300 Subject: [PATCH 26/37] `JvmCommonIntentionActionsFactory` switched from UAST to `@JvmCommon`-annotated API --- .../JvmCommonIntentionActionsFactory.kt | 79 +++++++++++++------ .../inheritance/ImplicitSubclassInspection.kt | 5 +- .../impl/JavaCommonIntentionActionsFactory.kt | 60 ++++++++------ .../CreateBeanPropertyFixes.java | 13 ++- .../src/com/intellij/psi/JvmCommon.java | 41 ++++++++++ 5 files changed, 142 insertions(+), 56 deletions(-) create mode 100644 java/java-psi-api/src/com/intellij/psi/JvmCommon.java diff --git a/java/java-analysis-api/src/com/intellij/codeInsight/intention/JvmCommonIntentionActionsFactory.kt b/java/java-analysis-api/src/com/intellij/codeInsight/intention/JvmCommonIntentionActionsFactory.kt index 675cef723f0e..e8927de8a0f7 100644 --- a/java/java-analysis-api/src/com/intellij/codeInsight/intention/JvmCommonIntentionActionsFactory.kt +++ b/java/java-analysis-api/src/com/intellij/codeInsight/intention/JvmCommonIntentionActionsFactory.kt @@ -17,13 +17,13 @@ package com.intellij.codeInsight.intention import com.intellij.lang.Language import com.intellij.lang.LanguageExtension -import com.intellij.psi.PsiModifier -import com.intellij.psi.PsiType -import com.intellij.psi.PsiTypeParameter +import com.intellij.openapi.components.ServiceManager +import com.intellij.psi.* import org.jetbrains.annotations.ApiStatus import org.jetbrains.uast.UClass import org.jetbrains.uast.UDeclaration -import org.jetbrains.uast.UParameter +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UastContext /** * Extension Point provides language-abstracted code modifications for JVM-based languages. @@ -38,18 +38,22 @@ import org.jetbrains.uast.UParameter @ApiStatus.Experimental abstract class JvmCommonIntentionActionsFactory { - open fun createChangeModifierAction(declaration: UDeclaration, + open fun createChangeModifierAction(declaration: @JvmCommon PsiModifierListOwner, @PsiModifier.ModifierConstant modifier: String, - shouldPresent: Boolean): IntentionAction? = null + shouldPresent: Boolean): IntentionAction? = + //Fallback if Uast-version of method is overridden + createChangeModifierAction(declaration.asUast(), modifier, shouldPresent) open fun createAddCallableMemberActions(info: MethodInsertionInfo): List = emptyList() - open fun createAddBeanPropertyActions(uClass: UClass, + open fun createAddBeanPropertyActions(psiClass: @JvmCommon PsiClass, propertyName: String, @PsiModifier.ModifierConstant visibilityModifier: String, propertyType: PsiType, setterRequired: Boolean, - getterRequired: Boolean): List = emptyList() + getterRequired: Boolean): List = + //Fallback if Uast-version of method is overridden + createAddBeanPropertyActions(psiClass.asUast(), propertyName, visibilityModifier, propertyType, setterRequired, getterRequired) companion object : LanguageExtension( "com.intellij.codeInsight.intention.jvmCommonIntentionActionsFactory") { @@ -58,49 +62,78 @@ abstract class JvmCommonIntentionActionsFactory { override fun forLanguage(l: Language): JvmCommonIntentionActionsFactory? = super.forLanguage(l) } + //A fallback to old api + @Deprecated("use or/and override @JvmCommon-version of this method instead") + open fun createChangeModifierAction(declaration: UDeclaration, + @PsiModifier.ModifierConstant modifier: String, + shouldPresent: Boolean): IntentionAction? = null + + @Deprecated("use or/and override @JvmCommon-version of this method instead") + open fun createAddBeanPropertyActions(uClass: UClass, + propertyName: String, + @PsiModifier.ModifierConstant visibilityModifier: String, + propertyType: PsiType, + setterRequired: Boolean, + getterRequired: Boolean): List = emptyList() + + } +@ApiStatus.Experimental sealed class MethodInsertionInfo( - val containingClass: UClass, + val targetClass: @JvmCommon PsiClass, @PsiModifier.ModifierConstant val modifiers: List = emptyList(), val typeParams: List = emptyList(), - val parameters: List = emptyList() + val parameters: List<@JvmCommon PsiParameter> = emptyList() ) { + + @Deprecated("use `targetClass`", ReplaceWith("targetClass")) + val containingClass: UClass + get() = targetClass.asUast() + companion object { @JvmStatic - fun constructorInfo(containingClass: UClass, parameters: List) = - Constructor(containingClass = containingClass, parameters = parameters) + fun constructorInfo(targetClass: @JvmCommon PsiClass, parameters: List<@JvmCommon PsiParameter>) = + Constructor(targetClass = targetClass, parameters = parameters) @JvmStatic - fun simpleMethodInfo(containingClass: UClass, + fun simpleMethodInfo(containingClass: @JvmCommon PsiClass, methodName: String, @PsiModifier.ModifierConstant modifier: String, returnType: PsiType, - parameters: List) = + parameters: List<@JvmCommon PsiParameter>) = Method(name = methodName, modifiers = listOf(modifier), - containingClass = containingClass, + targetClass = containingClass, returnType = returnType, parameters = parameters) + } class Method( - containingClass: UClass, + targetClass: @JvmCommon PsiClass, val name: String, modifiers: List = emptyList(), - typeParams: List = emptyList(), + typeParams: List<@JvmCommon PsiTypeParameter> = emptyList(), val returnType: PsiType, - parameters: List = emptyList(), + parameters: List<@JvmCommon PsiParameter> = emptyList(), val isAbstract: Boolean = false - ) : MethodInsertionInfo(containingClass, modifiers, typeParams, parameters) + ) : MethodInsertionInfo(targetClass, modifiers, typeParams, parameters) class Constructor( - containingClass: UClass, + targetClass: @JvmCommon PsiClass, modifiers: List = emptyList(), - typeParams: List = emptyList(), - parameters: List = emptyList() - ) : MethodInsertionInfo(containingClass, modifiers, typeParams, parameters) + typeParams: List<@JvmCommon PsiTypeParameter> = emptyList(), + parameters: List<@JvmCommon PsiParameter> = emptyList() + ) : MethodInsertionInfo(targetClass, modifiers, typeParams, parameters) +} + +@Deprecated("remove after kotlin plugin will be ported") +private inline fun PsiElement.asUast(): T = when (this) { + is T -> this + else -> this.let { ServiceManager.getService(project, UastContext::class.java).convertElement(this, null, T::class.java) as T? } + ?: throw UnsupportedOperationException("cant convert $this to ${T::class}") } \ No newline at end of file diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/inheritance/ImplicitSubclassInspection.kt b/java/java-analysis-impl/src/com/intellij/codeInspection/inheritance/ImplicitSubclassInspection.kt index 54bfa0647d3c..7f86add69759 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/inheritance/ImplicitSubclassInspection.kt +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/inheritance/ImplicitSubclassInspection.kt @@ -17,8 +17,8 @@ package com.intellij.codeInspection.inheritance import com.intellij.CommonBundle import com.intellij.codeInsight.daemon.QuickFixBundle -import com.intellij.codeInsight.intention.JvmCommonIntentionActionsFactory import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.codeInsight.intention.JvmCommonIntentionActionsFactory import com.intellij.codeInspection.* import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.diagnostic.Logger @@ -185,7 +185,8 @@ class ImplicitSubclassInspection : AbstractBaseUastLocalInspectionTool() { shouldPresent: Boolean, actionsList: SmartList) { if (declaration.modifierList?.hasModifierProperty(name) != shouldPresent) { - (actionsFactory.createChangeModifierAction(declaration, name, shouldPresent))?.let { + (actionsFactory.createChangeModifierAction(declaration as @com.intellij.psi.JvmCommon PsiModifierListOwner, name, + shouldPresent))?.let { actionsList.add(it) } } diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/JavaCommonIntentionActionsFactory.kt b/java/java-impl/src/com/intellij/codeInsight/intention/impl/JavaCommonIntentionActionsFactory.kt index abeab071f942..e27970f05a7d 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/JavaCommonIntentionActionsFactory.kt +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/JavaCommonIntentionActionsFactory.kt @@ -22,46 +22,47 @@ import com.intellij.codeInsight.intention.AbstractIntentionAction import com.intellij.codeInsight.intention.IntentionAction import com.intellij.codeInsight.intention.JvmCommonIntentionActionsFactory import com.intellij.codeInsight.intention.MethodInsertionInfo +import com.intellij.lang.java.JavaLanguage +import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import com.intellij.psi.* +import com.intellij.psi.codeStyle.JavaCodeStyleManager import com.intellij.psi.impl.beanProperties.CreateJavaBeanPropertyFix import com.intellij.psi.util.PsiFormatUtil import com.intellij.psi.util.PsiFormatUtilBase import com.intellij.util.VisibilityUtil import org.jetbrains.annotations.NotNull -import org.jetbrains.uast.UClass -import org.jetbrains.uast.UDeclaration -import org.jetbrains.uast.UParameter - class JavaCommonIntentionActionsFactory : JvmCommonIntentionActionsFactory() { - override fun createChangeModifierAction(declaration: UDeclaration, modifier: String, shouldPresent: Boolean): IntentionAction { + override fun createChangeModifierAction(declaration: @JvmCommon PsiModifierListOwner, + modifier: String, + shouldPresent: Boolean): IntentionAction { return ModifierFix(declaration.modifierList, modifier, shouldPresent, false) } override fun createAddCallableMemberActions(info: MethodInsertionInfo): List { return when (info) { is MethodInsertionInfo.Method -> with(info) { - createAddMethodAction(containingClass, name, modifiers.joinToString(" "), returnType, parameters) + createAddMethodAction(targetClass, name, modifiers.joinToString(" "), returnType, parameters) ?.let { listOf(it) } ?: emptyList() } is MethodInsertionInfo.Constructor -> - listOf(AddConstructorFix(info.containingClass.psi, info.parameters.map { it.psi })) + listOf(AddConstructorFix(info.targetClass.javaPsi(), info.parameters.map { it.javaPsi() })) } } - private fun createAddMethodAction(uClass: UClass, + private fun createAddMethodAction(psiClass: @JvmCommon PsiClass, methodName: String, @PsiModifier.ModifierConstant @NotNull visibilityModifier: String, returnType: PsiType, - parameters: List): IntentionAction? { - val paramsString = parameters.mapIndexed { i, t -> "${t.type.presentableText} ${t.name ?: "arg$i"}" }.joinToString() + parameters: List<@JvmCommon PsiParameter>): IntentionAction? { + val paramsString = parameters.mapIndexed { i, t -> "${t.type.canonicalText} ${t.name ?: "arg$i"}" }.joinToString() val signatureString = - "${VisibilityUtil.getVisibilityString(visibilityModifier)} ${returnType.presentableText} $methodName($paramsString){}" - val targetClassPointer = SmartPointerManager.getInstance(uClass.project).createSmartPsiElementPointer(uClass.psi) + "${VisibilityUtil.getVisibilityString(visibilityModifier)} ${returnType.canonicalText} $methodName($paramsString){}" + val targetClassPointer = SmartPointerManager.getInstance(psiClass.project).createSmartPsiElementPointer(psiClass.javaPsi()) return object : AbstractIntentionAction() { private val text = targetClassPointer.element?.let { psiClass -> @@ -77,17 +78,30 @@ class JavaCommonIntentionActionsFactory : JvmCommonIntentionActionsFactory() { override fun getText(): String = text override fun invoke(project: Project, editor: Editor?, file: PsiFile) { - targetClassPointer.element?.let { targetClass -> - targetClass.add(createMethod(targetClass)) + val targetClass = targetClassPointer.element ?: return + runWriteAction { + val addedMethod = targetClass.add(createMethod(targetClass)) + JavaCodeStyleManager.getInstance(project).shortenClassReferences(addedMethod) } } - private fun createMethod(targetClass: PsiClass): PsiMethod = PsiElementFactory.SERVICE.getInstance(targetClass.project) - .createMethodFromText(signatureString, targetClass) + private fun createMethod(targetClass: PsiClass): PsiMethod { + val elementFactory = JVMElementFactories.getFactory(targetClass.language, targetClass.project) // it could be Groovy + ?: JavaPsiFacade.getElementFactory(targetClass.project) + return elementFactory.createMethodFromText(signatureString, targetClass) + } } } - override fun createAddBeanPropertyActions(uClass: UClass, + private inline fun T.javaPsi(): T { + assert(language == JavaLanguage.INSTANCE) + return when (this) { + is org.jetbrains.uast.UElement -> psi as T + else -> this + } + } + + override fun createAddBeanPropertyActions(psiClass: @JvmCommon PsiClass, propertyName: String, @PsiModifier.ModifierConstant visibilityModifier: String, propertyType: PsiType, @@ -95,20 +109,20 @@ class JavaCommonIntentionActionsFactory : JvmCommonIntentionActionsFactory() { getterRequired: Boolean): List { if (getterRequired && setterRequired) return listOf( - CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, getterRequired, setterRequired, + CreateJavaBeanPropertyFix(psiClass.javaPsi(), propertyName, propertyType, getterRequired, setterRequired, true), - CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, getterRequired, setterRequired, + CreateJavaBeanPropertyFix(psiClass.javaPsi(), propertyName, propertyType, getterRequired, setterRequired, false)) if (getterRequired || setterRequired) return listOf( - CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, getterRequired, setterRequired, + CreateJavaBeanPropertyFix(psiClass.javaPsi(), propertyName, propertyType, getterRequired, setterRequired, true), - CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, getterRequired, setterRequired, + CreateJavaBeanPropertyFix(psiClass.javaPsi(), propertyName, propertyType, getterRequired, setterRequired, false), - CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, true, true, true)) + CreateJavaBeanPropertyFix(psiClass.javaPsi(), propertyName, propertyType, true, true, true)) return listOf( - CreateJavaBeanPropertyFix(uClass.psi, propertyName, propertyType, getterRequired, setterRequired, true)) + CreateJavaBeanPropertyFix(psiClass.javaPsi(), propertyName, propertyType, getterRequired, setterRequired, true)) } } \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/psi/impl/beanProperties/CreateBeanPropertyFixes.java b/java/java-impl/src/com/intellij/psi/impl/beanProperties/CreateBeanPropertyFixes.java index b300bb34803e..1f1cd47a37ca 100644 --- a/java/java-impl/src/com/intellij/psi/impl/beanProperties/CreateBeanPropertyFixes.java +++ b/java/java-impl/src/com/intellij/psi/impl/beanProperties/CreateBeanPropertyFixes.java @@ -25,8 +25,6 @@ import com.intellij.psi.search.GlobalSearchScope; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.uast.UClass; -import org.jetbrains.uast.UastContextKt; import static com.intellij.psi.CommonClassNames.JAVA_LANG_STRING; import static com.intellij.util.ArrayUtil.toObjectArray; @@ -35,14 +33,14 @@ import static com.intellij.util.ArrayUtil.toObjectArray; public class CreateBeanPropertyFixes { public static LocalQuickFix[] createFixes(String propertyName, - @NotNull PsiClass psiClass, + @NotNull @JvmCommon PsiClass psiClass, @Nullable PsiType type, final boolean createSetter) { return IntentionWrapper.wrapToQuickFixes(createActions(propertyName, psiClass, type, createSetter), psiClass.getContainingFile()); } public static IntentionAction[] createActions(String propertyName, - @NotNull PsiClass psiClass, + @NotNull @JvmCommon PsiClass psiClass, @Nullable PsiType type, final boolean createSetter) { if (psiClass instanceof PsiCompiledElement) return IntentionAction.EMPTY_ARRAY; @@ -55,9 +53,8 @@ public class CreateBeanPropertyFixes { } JvmCommonIntentionActionsFactory factory = JvmCommonIntentionActionsFactory.forLanguage(psiClass.getLanguage()); if (factory == null) return IntentionAction.EMPTY_ARRAY; - UClass uClass = UastContextKt.toUElement(psiClass, UClass.class); - if (uClass == null) return IntentionAction.EMPTY_ARRAY; - return toObjectArray(factory.createAddBeanPropertyActions(uClass, propertyName, PsiModifier.PUBLIC, type, createSetter, !createSetter), - IntentionAction.class); + return toObjectArray( + factory.createAddBeanPropertyActions(psiClass, propertyName, PsiModifier.PUBLIC, type, createSetter, !createSetter), + IntentionAction.class); } } diff --git a/java/java-psi-api/src/com/intellij/psi/JvmCommon.java b/java/java-psi-api/src/com/intellij/psi/JvmCommon.java new file mode 100644 index 000000000000..a643491097f4 --- /dev/null +++ b/java/java-psi-api/src/com/intellij/psi/JvmCommon.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi; + +import org.jetbrains.annotations.ApiStatus; + +import java.lang.annotation.*; + +/** + * Annotation for marking {@link PsiElement} subclasses usages that they will be used in + * language-abstracted way: + *

    + *
  • No modification methods should be directly called on such instances
  • + *
  • Usages should be aware of {@link PsiElement#getLanguage()}, and not be hardcoded to dedicated language
  • + *
  • Instances could be "virtual" and don't have physical representations
  • + *
  • Complex operations should be performed via corresponding {@link com.intellij.lang.LanguageExtensionPoint} or similar API
  • + *
  • {@link org.jetbrains.uast.UClass}-like instances should be supported
  • + *
+ * Note: this annotation considered as kind-of "transitional" until generic language-independent API will be implemented + */ +@ApiStatus.Experimental +@Documented +@Retention(RetentionPolicy.CLASS) +@Target({ + ElementType.TYPE, ElementType.PARAMETER, ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER +}) +public @interface JvmCommon { +} From dc8e583b53ad673d4100d50e75556757c6a8ce36 Mon Sep 17 00:00:00 2001 From: Nicolay Mitropolsky Date: Wed, 5 Jul 2017 17:28:42 +0300 Subject: [PATCH 27/37] `UastCreateMethodFix` no more Uast --- ...tCreateMethodFix.kt => CreateMethodFix.kt} | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) rename java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/{UastCreateMethodFix.kt => CreateMethodFix.kt} (70%) diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/UastCreateMethodFix.kt b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateMethodFix.kt similarity index 70% rename from java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/UastCreateMethodFix.kt rename to java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateMethodFix.kt index fba05a6ef16e..798202471e86 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/UastCreateMethodFix.kt +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateMethodFix.kt @@ -22,15 +22,13 @@ import com.intellij.codeInspection.LocalQuickFixBase import com.intellij.codeInspection.ProblemDescriptor import com.intellij.openapi.module.ModuleUtilCore import com.intellij.openapi.project.Project -import com.intellij.psi.PsiModifier -import com.intellij.psi.PsiType -import com.intellij.psi.SmartPointerManager +import com.intellij.psi.* import com.intellij.psi.codeStyle.CodeStyleManager import com.intellij.util.PsiNavigateUtil -import org.jetbrains.uast.UClass import org.jetbrains.uast.UMethod +import org.jetbrains.uast.toUElementOfType -class UastCreateMethodFix(containingClass: UClass, private val createMethodAction: IntentionAction) +class CreateMethodFix(containingClass: @JvmCommon PsiClass, private val createMethodAction: IntentionAction) : LocalQuickFixBase(createMethodAction.text, createMethodAction.familyName) { private val containingClass = SmartPointerManager.getInstance(containingClass.project).createSmartPsiElementPointer(containingClass) @@ -44,22 +42,27 @@ class UastCreateMethodFix(containingClass: UClass, private val createMethodActio reformatAndOpenCreatedMethod(newMethod) } - private fun reformatAndOpenCreatedMethod(method: UMethod) { + private fun reformatAndOpenCreatedMethod(method: @JvmCommon PsiMethod) { CodeStyleManager.getInstance(containingClass.project).reformat(method) - PsiNavigateUtil.navigate(method.uastBody?.psi?.lastChild ?: method) + PsiNavigateUtil.navigate((method.body ?: uastBody(method))?.lastChild ?: method) + } + + private fun uastBody(method: PsiMethod): PsiElement? = when (method) { + is UMethod -> method.uastBody?.psi + else -> method.toUElementOfType()?.uastBody?.psi } companion object { @JvmStatic - fun createVoidMethodIfFixPossible(uClass: UClass, + fun createVoidMethodIfFixPossible(psiClass: @JvmCommon PsiClass, methodName: String, - @PsiModifier.ModifierConstant modifier: String): UastCreateMethodFix? { - if (!ModuleUtilCore.projectContainsFile(uClass.project, uClass.containingFile.virtualFile, false)) return null - val actionsFactory = JvmCommonIntentionActionsFactory.forLanguage(uClass.language) ?: return null + @PsiModifier.ModifierConstant modifier: String): CreateMethodFix? { + if (!ModuleUtilCore.projectContainsFile(psiClass.project, psiClass.containingFile.virtualFile, false)) return null + val actionsFactory = JvmCommonIntentionActionsFactory.forLanguage(psiClass.language) ?: return null val action = actionsFactory.createAddCallableMemberActions( - MethodInsertionInfo.simpleMethodInfo(uClass, methodName, modifier, PsiType.VOID, emptyList()) + MethodInsertionInfo.simpleMethodInfo(psiClass, methodName, modifier, PsiType.VOID, emptyList()) ).firstOrNull() ?: return null - return UastCreateMethodFix(uClass, action) + return CreateMethodFix(psiClass, action) } } From 7c464c65a1725cef4f22996bc4500520f78895f1 Mon Sep 17 00:00:00 2001 From: Nicolay Mitropolsky Date: Wed, 5 Jul 2017 17:30:19 +0300 Subject: [PATCH 28/37] Update `JavaFxEventHandlerReference` to new `JvmCommonIntentionActionsFactory` API (IDEA-172229) --- .../javaFX/fxml/JavaFXQuickfixTest.java | 18 +++--- .../refs/JavaFxEventHandlerReference.java | 57 +++++++++++++++---- .../eventHandler/QuickfixHalfRaw_after.java | 1 - .../QuickfixNoFieldNested_after.java | 1 - .../eventHandler/QuickfixNoField_after.java | 1 - .../eventHandler/QuickfixRaw_after.java | 1 - .../eventHandler/QuickfixSpecific_after.java | 1 - .../eventHandler/QuickfixSuper_after.java | 1 - .../CreateControllerMethodGeneric_after.java | 1 - .../CreateControllerMethodHalfRaw_after.java | 1 - ...reateControllerMethodInGroovy_after.groovy | 4 +- .../CreateControllerMethod_after.java | 1 - .../CreateMethodEscalateVisibility_after.java | 1 - ...ateMethodPackageLocalVisibility_after.java | 1 - .../CreateMethodPrivateVisibility_after.java | 1 - ...CreateMethodProtectedVisibility_after.java | 1 - .../CreateMethodPublicVisibility_after.java | 1 - 17 files changed, 56 insertions(+), 37 deletions(-) diff --git a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXQuickfixTest.java b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXQuickfixTest.java index 3b7239fd5702..196753d84891 100644 --- a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXQuickfixTest.java +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXQuickfixTest.java @@ -40,12 +40,12 @@ import java.util.Set; public class JavaFXQuickfixTest extends LightCodeInsightFixtureTestCase { public static final DefaultLightProjectDescriptor JAVA_FX_WITH_GROOVY_DESCRIPTOR = new DefaultLightProjectDescriptor() { @Override - public void configureModule(@NotNull Module module, @NotNull ModifiableRootModel model, @NotNull ContentEntry contentEntry) { + public void configureModule(@NotNull Module module, @NotNull ModifiableRootModel model, @NotNull ContentEntry contentEntry) { AbstractJavaFXTestCase.addJavaFxJarAsLibrary(module, model); - PsiTestUtil.addLibrary(module, model, "javafx", PluginPathManager.getPluginHomePath("javaFX") + "/testData", "groovy-1.8.0.jar"); - super.configureModule(module, model, contentEntry); - } - }; + PsiTestUtil.addLibrary(module, model, "javafx", PluginPathManager.getPluginHomePath("javaFX") + "/testData", "groovy-1.8.0.jar"); + super.configureModule(module, model, contentEntry); + } + }; @NotNull @Override @@ -106,7 +106,8 @@ public class JavaFXQuickfixTest extends LightCodeInsightFixtureTestCase { } public void testCreateMethodEscalateVisibility() throws Exception { - doTestWithDefaultVisibility("Create method 'void onAction(ActionEvent)'", "CreateMethod", VisibilityUtil.ESCALATE_VISIBILITY, ".java"); + doTestWithDefaultVisibility("Create method 'void onAction(ActionEvent)'", "CreateMethod", VisibilityUtil.ESCALATE_VISIBILITY, + ".java"); } public void testCreateFieldEmptyName() throws Exception { @@ -122,7 +123,8 @@ public class JavaFXQuickfixTest extends LightCodeInsightFixtureTestCase { assertNotNull(intention); Set languages = JavaFxInjectPageLanguageIntention.getAvailableLanguages(getProject()); assertContainsElements(languages, "groovy"); - JavaFxInjectPageLanguageIntention languageIntention = (JavaFxInjectPageLanguageIntention)((IntentionActionDelegate)intention).getDelegate(); + JavaFxInjectPageLanguageIntention languageIntention = + (JavaFxInjectPageLanguageIntention)((IntentionActionDelegate)intention).getDelegate(); languageIntention.registerPageLanguage(getProject(), (XmlFile)myFixture.getFile(), "groovy"); myFixture.checkResultByFile(getTestName(true) + ".fxml", getTestName(true) + "_after.fxml", true); } @@ -168,7 +170,7 @@ public class JavaFXQuickfixTest extends LightCodeInsightFixtureTestCase { super.setUp(); myFixture.enableInspections(new JavaFxUnresolvedFxIdReferenceInspection()); } - + @NotNull @Override protected String getTestDataPath() { diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxEventHandlerReference.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxEventHandlerReference.java index e359dd4bfc7b..b4fce902a350 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxEventHandlerReference.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxEventHandlerReference.java @@ -16,12 +16,16 @@ package org.jetbrains.plugins.javaFX.fxml.refs; import com.intellij.codeInsight.daemon.QuickFixActionRegistrar; -import com.intellij.codeInsight.daemon.impl.quickfix.CreateMethodQuickFix; +import com.intellij.codeInsight.intention.JvmCommonIntentionActionsFactory; +import com.intellij.codeInsight.intention.MethodInsertionInfo; import com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixProvider; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.codeStyle.SuggestedNameInfo; +import com.intellij.psi.codeStyle.VariableKind; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.psi.xml.XmlAttribute; @@ -34,6 +38,7 @@ import org.jetbrains.plugins.javaFX.fxml.JavaFxCommonNames; import org.jetbrains.plugins.javaFX.fxml.JavaFxPsiUtil; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; public class JavaFxEventHandlerReference extends PsiReferenceBase { @@ -59,8 +64,8 @@ public class JavaFxEventHandlerReference extends PsiReferenceBase availableHandlers = new ArrayList<>(); for (PsiMethod psiMethod : myController.getAllMethods()) { if (isHandlerMethodSignature(psiMethod, myController) && JavaFxPsiUtil.isVisibleInFxml(psiMethod)) { - availableHandlers.add(psiMethod); - } + availableHandlers.add(psiMethod); + } } return availableHandlers.isEmpty() ? EMPTY_ARRAY : ArrayUtil.toObjectArray(availableHandlers); } @@ -92,30 +97,58 @@ public class JavaFxEventHandlerReference extends PsiReferenceBase { + public static class JavaFxUnresolvedReferenceHandlerQuickfixProvider + extends UnresolvedReferenceQuickFixProvider { @Override public void registerFixes(@NotNull final JavaFxEventHandlerReference ref, @NotNull final QuickFixActionRegistrar registrar) { if (ref.myController != null && ref.myEventHandler == null) { - final CreateMethodQuickFix quickFix = CreateMethodQuickFix.createFix(ref.myController, getHandlerSignature(ref), ""); - if (quickFix != null) { - registrar.register(quickFix); - } + JvmCommonIntentionActionsFactory intentionActionsFactory = + JvmCommonIntentionActionsFactory.forLanguage(ref.myController.getLanguage()); + if (intentionActionsFactory == null) return; + + String javaSignature = getHandlerSignature(ref); + PsiMethod javaMethod = JavaPsiFacade.getElementFactory(ref.myController.getProject()) + .createMethodFromText(javaSignature, ref.myController); + + MethodInsertionInfo.Method method = + MethodInsertionInfo.simpleMethodInfo(ref.myController, + javaMethod.getName(), + javaMethod.getModifierList().getText(), + javaMethod.getReturnType(), + Arrays.asList(javaMethod.getParameterList().getParameters())); + intentionActionsFactory.createAddCallableMemberActions(method).forEach(registrar::register); } } private static String getHandlerSignature(JavaFxEventHandlerReference ref) { final XmlAttributeValue element = ref.getElement(); - String canonicalText = JavaFxCommonNames.JAVAFX_EVENT; + PsiType eventType = getEventType(element); + final String modifiers = getModifiers(element.getProject()); + return modifiers + " void " + element.getValue().substring(1) + "(" + + eventType.getCanonicalText() + " " + suggestParamName(element.getProject(), eventType) + + ")"; + } + + private static String suggestParamName(Project project, PsiType eventType) { + SuggestedNameInfo suggestedInfo = JavaCodeStyleManager.getInstance(project) + .suggestVariableName(VariableKind.PARAMETER, null, null, eventType); + if (suggestedInfo.names.length < 1) { + return "e"; + } + return suggestedInfo.names[0]; + } + + @NotNull + private static PsiType getEventType(XmlAttributeValue element) { final PsiElement parent = element.getParent(); if (parent instanceof XmlAttribute) { final PsiClassType eventType = JavaFxPsiUtil.getDeclaredEventType((XmlAttribute)parent); if (eventType != null) { - canonicalText = eventType.getCanonicalText(); + return eventType; } } - final String modifiers = getModifiers(element.getProject()); - return modifiers + " void " + element.getValue().substring(1) + "(" + canonicalText + " e)"; + return PsiType.getTypeByName(JavaFxCommonNames.JAVAFX_EVENT, element.getProject(), element.getResolveScope()); } @NotNull diff --git a/plugins/javaFX/testData/inspections/eventHandler/QuickfixHalfRaw_after.java b/plugins/javaFX/testData/inspections/eventHandler/QuickfixHalfRaw_after.java index 95e7e418114f..3ee137da3bd9 100644 --- a/plugins/javaFX/testData/inspections/eventHandler/QuickfixHalfRaw_after.java +++ b/plugins/javaFX/testData/inspections/eventHandler/QuickfixHalfRaw_after.java @@ -7,6 +7,5 @@ public class QuickfixHalfRaw { @FXML TableView table; public void onSort(SortEvent> tableViewSortEvent) { - } } diff --git a/plugins/javaFX/testData/inspections/eventHandler/QuickfixNoFieldNested_after.java b/plugins/javaFX/testData/inspections/eventHandler/QuickfixNoFieldNested_after.java index dbd3ba34beae..3c31e95de47f 100644 --- a/plugins/javaFX/testData/inspections/eventHandler/QuickfixNoFieldNested_after.java +++ b/plugins/javaFX/testData/inspections/eventHandler/QuickfixNoFieldNested_after.java @@ -2,6 +2,5 @@ import javafx.scene.control.TableColumn.CellEditEvent; public class QuickfixNoFieldNested { public void onColumnEditStart(CellEditEvent cellEditEvent) { - } } diff --git a/plugins/javaFX/testData/inspections/eventHandler/QuickfixNoField_after.java b/plugins/javaFX/testData/inspections/eventHandler/QuickfixNoField_after.java index 55046662bd9f..909821015de3 100644 --- a/plugins/javaFX/testData/inspections/eventHandler/QuickfixNoField_after.java +++ b/plugins/javaFX/testData/inspections/eventHandler/QuickfixNoField_after.java @@ -3,6 +3,5 @@ import javafx.scene.control.TableView; public class QuickfixNoField { public void onSort(SortEvent tableViewSortEvent) { - } } diff --git a/plugins/javaFX/testData/inspections/eventHandler/QuickfixRaw_after.java b/plugins/javaFX/testData/inspections/eventHandler/QuickfixRaw_after.java index 73bd806e4d9c..d837356bd973 100644 --- a/plugins/javaFX/testData/inspections/eventHandler/QuickfixRaw_after.java +++ b/plugins/javaFX/testData/inspections/eventHandler/QuickfixRaw_after.java @@ -6,6 +6,5 @@ public class QuickfixRaw { @FXML TableView table; public void onSort(SortEvent tableViewSortEvent) { - } } diff --git a/plugins/javaFX/testData/inspections/eventHandler/QuickfixSpecific_after.java b/plugins/javaFX/testData/inspections/eventHandler/QuickfixSpecific_after.java index 9ad1f4c4332f..e94b44ce825d 100644 --- a/plugins/javaFX/testData/inspections/eventHandler/QuickfixSpecific_after.java +++ b/plugins/javaFX/testData/inspections/eventHandler/QuickfixSpecific_after.java @@ -8,6 +8,5 @@ public class QuickfixSpecific { TableView> table; public void onSort(SortEvent>> tableViewSortEvent) { - } } \ No newline at end of file diff --git a/plugins/javaFX/testData/inspections/eventHandler/QuickfixSuper_after.java b/plugins/javaFX/testData/inspections/eventHandler/QuickfixSuper_after.java index 02ba66adbea2..37f1e025890b 100644 --- a/plugins/javaFX/testData/inspections/eventHandler/QuickfixSuper_after.java +++ b/plugins/javaFX/testData/inspections/eventHandler/QuickfixSuper_after.java @@ -2,6 +2,5 @@ import javafx.scene.input.MouseEvent; public class QuickfixSuper{ public void click(MouseEvent mouseEvent) { - } } \ No newline at end of file diff --git a/plugins/javaFX/testData/quickfix/CreateControllerMethodGeneric_after.java b/plugins/javaFX/testData/quickfix/CreateControllerMethodGeneric_after.java index 25662ad5bfe1..49a0540150cd 100644 --- a/plugins/javaFX/testData/quickfix/CreateControllerMethodGeneric_after.java +++ b/plugins/javaFX/testData/quickfix/CreateControllerMethodGeneric_after.java @@ -9,6 +9,5 @@ public class CreateControllerMethodGeneric { TableView> table; public void onSort(SortEvent>> tableViewSortEvent) { - } } diff --git a/plugins/javaFX/testData/quickfix/CreateControllerMethodHalfRaw_after.java b/plugins/javaFX/testData/quickfix/CreateControllerMethodHalfRaw_after.java index d064fe1f4bb2..6942764dd744 100644 --- a/plugins/javaFX/testData/quickfix/CreateControllerMethodHalfRaw_after.java +++ b/plugins/javaFX/testData/quickfix/CreateControllerMethodHalfRaw_after.java @@ -9,6 +9,5 @@ public class CreateControllerMethodHalfRaw { TableView table; public void onSort(SortEvent> tableViewSortEvent) { - } } diff --git a/plugins/javaFX/testData/quickfix/CreateControllerMethodInGroovy_after.groovy b/plugins/javaFX/testData/quickfix/CreateControllerMethodInGroovy_after.groovy index 9537d414dc21..bddfc00fe53b 100644 --- a/plugins/javaFX/testData/quickfix/CreateControllerMethodInGroovy_after.groovy +++ b/plugins/javaFX/testData/quickfix/CreateControllerMethodInGroovy_after.groovy @@ -1,7 +1,5 @@ import javafx.event.ActionEvent public class CreateControllerMethodInGroovy { - public void bar(ActionEvent actionEvent) { - - } + public void bar(ActionEvent actionEvent) {} } \ No newline at end of file diff --git a/plugins/javaFX/testData/quickfix/CreateControllerMethod_after.java b/plugins/javaFX/testData/quickfix/CreateControllerMethod_after.java index d410e79b41fe..193f7642c3ae 100644 --- a/plugins/javaFX/testData/quickfix/CreateControllerMethod_after.java +++ b/plugins/javaFX/testData/quickfix/CreateControllerMethod_after.java @@ -2,6 +2,5 @@ import javafx.event.ActionEvent; public class CreateControllerMethod { public void bar(ActionEvent actionEvent) { - } } \ No newline at end of file diff --git a/plugins/javaFX/testData/quickfix/CreateMethodEscalateVisibility_after.java b/plugins/javaFX/testData/quickfix/CreateMethodEscalateVisibility_after.java index 20913689cd15..9b9a6dfdf051 100644 --- a/plugins/javaFX/testData/quickfix/CreateMethodEscalateVisibility_after.java +++ b/plugins/javaFX/testData/quickfix/CreateMethodEscalateVisibility_after.java @@ -4,6 +4,5 @@ import javafx.fxml.FXML; public class CreateMethod { @FXML private void onAction(ActionEvent actionEvent) { - } } \ No newline at end of file diff --git a/plugins/javaFX/testData/quickfix/CreateMethodPackageLocalVisibility_after.java b/plugins/javaFX/testData/quickfix/CreateMethodPackageLocalVisibility_after.java index db22d3ef4123..94777309d719 100644 --- a/plugins/javaFX/testData/quickfix/CreateMethodPackageLocalVisibility_after.java +++ b/plugins/javaFX/testData/quickfix/CreateMethodPackageLocalVisibility_after.java @@ -4,6 +4,5 @@ import javafx.fxml.FXML; public class CreateMethod { @FXML void onAction(ActionEvent actionEvent) { - } } \ No newline at end of file diff --git a/plugins/javaFX/testData/quickfix/CreateMethodPrivateVisibility_after.java b/plugins/javaFX/testData/quickfix/CreateMethodPrivateVisibility_after.java index 20913689cd15..9b9a6dfdf051 100644 --- a/plugins/javaFX/testData/quickfix/CreateMethodPrivateVisibility_after.java +++ b/plugins/javaFX/testData/quickfix/CreateMethodPrivateVisibility_after.java @@ -4,6 +4,5 @@ import javafx.fxml.FXML; public class CreateMethod { @FXML private void onAction(ActionEvent actionEvent) { - } } \ No newline at end of file diff --git a/plugins/javaFX/testData/quickfix/CreateMethodProtectedVisibility_after.java b/plugins/javaFX/testData/quickfix/CreateMethodProtectedVisibility_after.java index 8ab76bb9ccb8..a09c8da422cf 100644 --- a/plugins/javaFX/testData/quickfix/CreateMethodProtectedVisibility_after.java +++ b/plugins/javaFX/testData/quickfix/CreateMethodProtectedVisibility_after.java @@ -4,6 +4,5 @@ import javafx.fxml.FXML; public class CreateMethod { @FXML protected void onAction(ActionEvent actionEvent) { - } } \ No newline at end of file diff --git a/plugins/javaFX/testData/quickfix/CreateMethodPublicVisibility_after.java b/plugins/javaFX/testData/quickfix/CreateMethodPublicVisibility_after.java index 36687f750c0d..8233bee8152a 100644 --- a/plugins/javaFX/testData/quickfix/CreateMethodPublicVisibility_after.java +++ b/plugins/javaFX/testData/quickfix/CreateMethodPublicVisibility_after.java @@ -2,6 +2,5 @@ import javafx.event.ActionEvent; public class CreateMethod { public void onAction(ActionEvent actionEvent) { - } } \ No newline at end of file From 7223d362d35678fb25af64e71cac2a9438a4aa88 Mon Sep 17 00:00:00 2001 From: Nicolay Mitropolsky Date: Wed, 5 Jul 2017 17:31:36 +0300 Subject: [PATCH 29/37] Groovy uses Java-implementation of `JvmCommonIntentionActionsFactory` --- plugins/groovy/src/META-INF/plugin.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index ae269d3a234a..592baa420813 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -335,6 +335,8 @@ + From 44c8262aaf45eccf2c6108e482ab6fcd8115f9cb Mon Sep 17 00:00:00 2001 From: Nadya Zabrodina Date: Wed, 5 Jul 2017 20:05:25 +0300 Subject: [PATCH 30/37] [branchPopup]: memory leak re-fix - remove componentListener on closed * instead of wrong fix in fc653d4; * ComponentListener.componentHidden method may not be called at all, so we need to store window instance and remove the listener manually at some place that would be called during complex dispose(hide) popup process; --- .../dvcs/ui/BranchActionGroupPopup.java | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java b/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java index 86fdc2cf85f9..821df50c7785 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java @@ -48,10 +48,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.awt.event.InputEvent; -import java.awt.event.MouseEvent; +import java.awt.event.*; import java.util.List; import static com.intellij.util.ui.UIUtil.DEFAULT_HGAP; @@ -133,29 +130,24 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { private void trackDimensions(@Nullable String dimensionKey) { Window popupWindow = getPopupWindow(); if (popupWindow == null) return; - popupWindow.addComponentListener(new ComponentAdapter() { + ComponentListener windowListener = new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { if (myShown) { processOnSizeChanged(); } } - + }; + popupWindow.addComponentListener(windowListener); + addPopupListener(new JBPopupAdapter() { @Override - public void componentHidden(ComponentEvent e) { - popupWindow.removeComponentListener(this); + public void onClosed(LightweightWindowEvent event) { + popupWindow.removeComponentListener(windowListener); + if (dimensionKey != null && myUserSizeChanged) { + WindowStateService.getInstance(myProject).putSizeFor(myProject, dimensionKey, myPrevSize); + } } }); - if (dimensionKey != null) { - addPopupListener(new JBPopupAdapter() { - @Override - public void onClosed(LightweightWindowEvent event) { - if (myUserSizeChanged) { - WindowStateService.getInstance(myProject).putSizeFor(myProject, dimensionKey, myPrevSize); - } - } - }); - } } private void processOnSizeChanged() { From cb598c94c5d8384e0b849ee0e3b9768edfcfd52f Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 5 Jul 2017 20:10:37 +0300 Subject: [PATCH 31/37] PY-23540 Fixed: PyCharm doesn't recognise metaclass when the class has other builtin bases While choosing most derived class among metaclasses ignore abc.ABCMeta. --- .../python/psi/types/PyClassTypeImpl.java | 36 +++++++++++-------- .../builtinListGetItem.py | 2 +- ...taclassWhenSuperclassMetaclassIsABCMeta.py | 14 ++++++++ .../PyUnresolvedReferencesInspectionTest.java | 5 +++ 4 files changed, 42 insertions(+), 15 deletions(-) create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/memberFromMetaclassWhenSuperclassMetaclassIsABCMeta.py diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index ee98c6d753bf..995c1540703f 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -412,20 +412,28 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { return null; } try { - return Collections.max(classTypes, (t1, t2) -> { - if (t1 == t2 || t1 != null && t1.equals(t2)) { - return 0; - } - else if (t2 == null || t1 != null && Sets.newHashSet(t1.getAncestorTypes(context)).contains(t2)) { - return 1; - } - else if (t1 == null || Sets.newHashSet(t2.getAncestorTypes(context)).contains(t1)) { - return -1; - } - else { - throw new NotDerivedClassTypeException(); - } - }); + final String abcMeta = "abc." + PyNames.ABC_META_CLASS; + + return classTypes + .stream() + .filter(t -> !abcMeta.equals(t.getClassQName())) + .max( + (t1, t2) -> { + if (Objects.equals(t1, t2)) { + return 0; + } + else if (t2 == null || t1 != null && Sets.newHashSet(t1.getAncestorTypes(context)).contains(t2)) { + return 1; + } + else if (t1 == null || Sets.newHashSet(t2.getAncestorTypes(context)).contains(t1)) { + return -1; + } + else { + throw new NotDerivedClassTypeException(); + } + } + ) + .orElse(null); } catch (NotDerivedClassTypeException ignored) { return null; diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/builtinListGetItem.py b/python/testData/inspections/PyUnresolvedReferencesInspection/builtinListGetItem.py index 5e9dbb73ed47..9c10c8424c45 100644 --- a/python/testData/inspections/PyUnresolvedReferencesInspection/builtinListGetItem.py +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/builtinListGetItem.py @@ -1,2 +1,2 @@ -x = list[int] +x = list[int] x = [][0] diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/memberFromMetaclassWhenSuperclassMetaclassIsABCMeta.py b/python/testData/inspections/PyUnresolvedReferencesInspection/memberFromMetaclassWhenSuperclassMetaclassIsABCMeta.py new file mode 100644 index 000000000000..82c403ba2b84 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/memberFromMetaclassWhenSuperclassMetaclassIsABCMeta.py @@ -0,0 +1,14 @@ +class MyMeta(type): + def __getitem__(self, item): + return 0 + + def foo(cls): + pass + + +class C(str, metaclass=MyMeta): + pass + + +print(C['foo']) +print(C.foo()) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java index 0777b4ea01db..10a88d2360e7 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java @@ -603,6 +603,11 @@ public class PyUnresolvedReferencesInspectionTest extends PyInspectionTestCase { doTest(); } + // PY-23540 + public void testMemberFromMetaclassWhenSuperclassMetaclassIsABCMeta() { + runWithLanguageLevel(LanguageLevel.PYTHON30, this::doTest); + } + // PY-23623 public void testCachedOperatorInRecursivelyTypeInference() { doTest(); From 934f3da2a54039eec5aeff5bd04f126df8ee3337 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Sat, 1 Jul 2017 17:35:15 +0300 Subject: [PATCH 32/37] PY-24817 Fixed: False positive: Method may be 'static' when docstring is present Update PyMethodMayBeStaticInspection to consider more methods as empty ones. --- .../python/inspections/PyMethodMayBeStaticInspection.java | 7 ++----- .../PyMethodMayBeStaticInspection/documentedEmpty.py | 4 ++++ .../inspections/PyMethodMayBeStaticInspectionTest.java | 7 ++++++- 3 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 python/testData/inspections/PyMethodMayBeStaticInspection/documentedEmpty.py diff --git a/python/src/com/jetbrains/python/inspections/PyMethodMayBeStaticInspection.java b/python/src/com/jetbrains/python/inspections/PyMethodMayBeStaticInspection.java index f3f81ad6473e..24a3c1f4cf51 100644 --- a/python/src/com/jetbrains/python/inspections/PyMethodMayBeStaticInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyMethodMayBeStaticInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -78,10 +78,7 @@ public class PyMethodMayBeStaticInspection extends PyInspection { if (!attributes.isEmpty()) return; if (isTestElement(node)) return; - final PyStatementList statementList = node.getStatementList(); - final PyStatement[] statements = statementList.getStatements(); - - if (statements.length == 1 && statements[0] instanceof PyPassStatement) return; + if (PyUtil.isEmptyFunction(node)) return; final PyParameter[] parameters = node.getParameterList().getParameters(); diff --git a/python/testData/inspections/PyMethodMayBeStaticInspection/documentedEmpty.py b/python/testData/inspections/PyMethodMayBeStaticInspection/documentedEmpty.py new file mode 100644 index 000000000000..55f88475538c --- /dev/null +++ b/python/testData/inspections/PyMethodMayBeStaticInspection/documentedEmpty.py @@ -0,0 +1,4 @@ +class A: + def foo(self): + """Do something""" + pass \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/PyMethodMayBeStaticInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyMethodMayBeStaticInspectionTest.java index 1d33844e87e2..7a84199d2127 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyMethodMayBeStaticInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyMethodMayBeStaticInspectionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -105,6 +105,11 @@ public class PyMethodMayBeStaticInspectionTest extends PyTestCase { runWithLanguageLevel(LanguageLevel.PYTHON30, () -> doTest()); } + // PY-24817 + public void testDocumentedEmpty() { + doTest(); + } + private void doTest() { myFixture.configureByFile(getTestName(true) + ".py"); myFixture.enableInspections(PyMethodMayBeStaticInspection.class); From 7127bf7cf5b72ebb2096e2b1aac1117ec5bf028d Mon Sep 17 00:00:00 2001 From: Ivan Chirkov Date: Wed, 5 Jul 2017 19:09:19 +0200 Subject: [PATCH 33/37] Statistics: collect statistics in internal Send Statistics action Collect and persist statistics in opened projects before send in internal Send Statistics action. --- .../ApplicationStatisticsPersistenceComponent.java | 5 ++++- .../internal/statistic/tmp/SendStatisticsAction.java | 9 ++++++++- platform/platform-resources/src/idea/PlatformActions.xml | 2 +- resources-en/src/search/searchableOptions.xml | 5 +++-- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/persistence/ApplicationStatisticsPersistenceComponent.java b/platform/platform-impl/src/com/intellij/internal/statistic/persistence/ApplicationStatisticsPersistenceComponent.java index d223f65628a5..9fcb031227a1 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/persistence/ApplicationStatisticsPersistenceComponent.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/persistence/ApplicationStatisticsPersistenceComponent.java @@ -186,7 +186,10 @@ public class ApplicationStatisticsPersistenceComponent extends ApplicationStatis JobScheduler.getScheduler().scheduleWithFixedDelay(ApplicationStatisticsPersistenceComponent::persistOpenedProjects, 1, 1, TimeUnit.DAYS); } - private static void persistOpenedProjects() { + /** + * Collects statistics from all opened projects and persists it + */ + public static void persistOpenedProjects() { for (Project project : ProjectManager.getInstance().getOpenProjects()) { UsagesCollector.doPersistProjectUsages(project); } diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/tmp/SendStatisticsAction.java b/platform/platform-impl/src/com/intellij/internal/statistic/tmp/SendStatisticsAction.java index 0b61d2794c4e..5dbf71754fc1 100755 --- a/platform/platform-impl/src/com/intellij/internal/statistic/tmp/SendStatisticsAction.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/tmp/SendStatisticsAction.java @@ -18,6 +18,8 @@ package com.intellij.internal.statistic.tmp; import com.intellij.internal.statistic.StatisticsUploadAssistant; import com.intellij.internal.statistic.connect.StatisticsResult; import com.intellij.internal.statistic.connect.StatisticsService; +import com.intellij.internal.statistic.persistence.ApplicationStatisticsPersistenceComponent; +import com.intellij.internal.statistic.persistence.UsageStatisticsPersistenceComponent; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.application.ApplicationManager; @@ -39,9 +41,14 @@ public class SendStatisticsAction extends AnAction { return; } - ProgressManager.getInstance().run(new Task.Backgroundable(project, "Sending Statistics", false) { + ProgressManager.getInstance().run(new Task.Backgroundable(project, "Collecting And Sending Statistics", false) { @Override public void run(@NotNull ProgressIndicator indicator) { + UsageStatisticsPersistenceComponent statisticsPersistenceComponent = UsageStatisticsPersistenceComponent.getInstance(); + boolean sendAllowed = statisticsPersistenceComponent.isAllowed(); + statisticsPersistenceComponent.setAllowed(true); + ApplicationStatisticsPersistenceComponent.persistOpenedProjects(); + statisticsPersistenceComponent.setAllowed(sendAllowed); StatisticsService service = StatisticsUploadAssistant.getStatisticsService(); final StatisticsResult result = service.send(); diff --git a/platform/platform-resources/src/idea/PlatformActions.xml b/platform/platform-resources/src/idea/PlatformActions.xml index 5f42745d5c8f..3da3e00bc5eb 100644 --- a/platform/platform-resources/src/idea/PlatformActions.xml +++ b/platform/platform-resources/src/idea/PlatformActions.xml @@ -788,7 +788,7 @@ - + diff --git a/resources-en/src/search/searchableOptions.xml b/resources-en/src/search/searchableOptions.xml index 1c513527ff5d..417b400d7b4b 100644 --- a/resources-en/src/search/searchableOptions.xml +++ b/resources-en/src/search/searchableOptions.xml @@ -9915,8 +9915,9 @@