From 2d915743adad2587348699c352e1a60e530f177c Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Sat, 15 Oct 2016 13:09:29 +0200 Subject: [PATCH 01/66] minor changes based on review IDEA-CR-14479 --- .../history/integration/IdeaGateway.java | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java b/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java index 7733f00e1c2a..d22cf482d30b 100644 --- a/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java +++ b/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java @@ -32,7 +32,6 @@ import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.Clock; -import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; @@ -69,19 +68,12 @@ public class IdeaGateway { LocalHistoryImpl.getInstanceImpl().dispatchPendingEvents(); - VersionedFilterData versionedFilterData; - VfsEventDispatchContext vfsEventDispatchContext = ourCurrentEventDispatchContext.get(); - if (vfsEventDispatchContext != null) { - versionedFilterData = vfsEventDispatchContext.myFilterData; - if (versionedFilterData == null) versionedFilterData = vfsEventDispatchContext.myFilterData = new VersionedFilterData(); - } else { - versionedFilterData = new VersionedFilterData(); - } + VersionedFilterData versionedFilterData = getVersionedFilterData(); boolean isInContent = false; int numberOfOpenProjects = versionedFilterData.myOpenedProjects.size(); for (int i = 0; i < numberOfOpenProjects; ++i) { - if (Comparing.equal(versionedFilterData.myWorkspaceFiles.get(i), f)) return false; + if (f.equals(versionedFilterData.myWorkspaceFiles.get(i))) return false; ProjectFileIndex index = versionedFilterData.myProjectFileIndices.get(i); if (index.isExcluded(f)) return false; @@ -93,33 +85,49 @@ public class IdeaGateway { return numberOfOpenProjects != 0 || !FileTypeManager.getInstance().isFileIgnored(f); } + @NotNull + protected static VersionedFilterData getVersionedFilterData() { + VersionedFilterData versionedFilterData; + VfsEventDispatchContext vfsEventDispatchContext = ourCurrentEventDispatchContext.get(); + if (vfsEventDispatchContext != null) { + versionedFilterData = vfsEventDispatchContext.myFilterData; + if (versionedFilterData == null) versionedFilterData = vfsEventDispatchContext.myFilterData = new VersionedFilterData(); + } else { + versionedFilterData = new VersionedFilterData(); + } + return versionedFilterData; + } + private static final ThreadLocal ourCurrentEventDispatchContext = new ThreadLocal<>(); - private static class VfsEventDispatchContext { + private static class VfsEventDispatchContext implements AutoCloseable { final List myEvents; final boolean myBeforeEvents; final VfsEventDispatchContext myPreviousContext; VersionedFilterData myFilterData; - VfsEventDispatchContext(List events, boolean beforeEvents, VfsEventDispatchContext context) { + VfsEventDispatchContext(List events, boolean beforeEvents) { myEvents = events; myBeforeEvents = beforeEvents; - myPreviousContext = context; + myPreviousContext = ourCurrentEventDispatchContext.get(); + if (myPreviousContext != null) { + myFilterData = myPreviousContext.myFilterData; + } + ourCurrentEventDispatchContext.set(this); } public void close() { ourCurrentEventDispatchContext.set(myPreviousContext); + if (myPreviousContext != null && myPreviousContext.myFilterData == null && myFilterData != null) { + myPreviousContext.myFilterData = myFilterData; + } } } public void runWithVfsEventsDispatchContext(List events, boolean beforeEvents, Runnable action) { - VfsEventDispatchContext vfsEventDispatchContext = new VfsEventDispatchContext(events, beforeEvents, ourCurrentEventDispatchContext.get()); - ourCurrentEventDispatchContext.set(vfsEventDispatchContext); - try { + try (VfsEventDispatchContext ignored = new VfsEventDispatchContext(events, beforeEvents)) { action.run(); - } finally { - vfsEventDispatchContext.close(); } } From 6f128d7c7bc185aa8f0426d4f1b83d9854ba0e20 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Sun, 16 Oct 2016 01:02:20 +0300 Subject: [PATCH 02/66] fixed tests --- .../src/com/intellij/codeInsight/hints/PopupActions.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hints/PopupActions.kt b/platform/lang-impl/src/com/intellij/codeInsight/hints/PopupActions.kt index b6e8057b2f99..e28ed40babb7 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hints/PopupActions.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/hints/PopupActions.kt @@ -27,6 +27,7 @@ import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.CommonDataKeys import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.ex.EditorSettingsExternalizable +import com.intellij.openapi.editor.impl.InlayModelImpl import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.project.Project import com.intellij.openapi.project.ProjectManager @@ -116,7 +117,7 @@ class ToggleInlineHintsAction : AnAction() { private fun hasParameterHintAtOffset(editor: Editor): Boolean { val offset = editor.caretModel.offset - return editor.inlayModel + return editor.inlayModel is InlayModelImpl && editor.inlayModel .getInlineElementsInRange(offset, offset) .find { ParameterHintsPresentationManager.getInstance().isParameterHint(it) } != null } From 9f5786dc05734ac566f6914a570d32df07a17121 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Sun, 16 Oct 2016 03:29:48 +0300 Subject: [PATCH 03/66] [vcs-log] remove redundant text parameter that is not actually used --- .../vcs/log/ui/frame/VcsLogGraphTable.java | 22 +++++++++---------- .../ui/render/GraphCommitCellRenderer.java | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/VcsLogGraphTable.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/VcsLogGraphTable.java index eeac9e9bdd04..698ebd3ec0f0 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/VcsLogGraphTable.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/VcsLogGraphTable.java @@ -186,7 +186,7 @@ public class VcsLogGraphTable extends TableWithProgress implements DataProvider, for (int row = 0; row < maxRowsToCheck; row++) { String value = getModel().getValueAt(row, i).toString(); Font font = tableFont; - VcsLogHighlighter.TextStyle style = getStyle(row, i, value, false, false).getTextStyle(); + VcsLogHighlighter.TextStyle style = getStyle(row, i, false, false).getTextStyle(); if (BOLD.equals(style)) { font = tableFont.deriveFont(Font.BOLD); } @@ -336,13 +336,13 @@ public class VcsLogGraphTable extends TableWithProgress implements DataProvider, myHighlighters.clear(); } + @NotNull public SimpleTextAttributes applyHighlighters(@NotNull Component rendererComponent, int row, int column, - String text, boolean hasFocus, final boolean selected) { - VcsLogHighlighter.VcsCommitStyle style = getStyle(row, column, text, hasFocus, selected); + VcsLogHighlighter.VcsCommitStyle style = getStyle(row, column, hasFocus, selected); assert style.getBackground() != null && style.getForeground() != null && style.getTextStyle() != null; @@ -359,14 +359,14 @@ public class VcsLogGraphTable extends TableWithProgress implements DataProvider, return SimpleTextAttributes.REGULAR_ATTRIBUTES; } - public VcsLogHighlighter.VcsCommitStyle getBaseStyle(int row, int column, String text, boolean hasFocus, boolean selected) { - Component dummyRendererComponent = myDummyRenderer.getTableCellRendererComponent(this, text, selected, hasFocus, row, column); + public VcsLogHighlighter.VcsCommitStyle getBaseStyle(int row, int column, boolean hasFocus, boolean selected) { + Component dummyRendererComponent = myDummyRenderer.getTableCellRendererComponent(this, "", selected, hasFocus, row, column); return VcsCommitStyleFactory .createStyle(dummyRendererComponent.getForeground(), dummyRendererComponent.getBackground(), VcsLogHighlighter.TextStyle.NORMAL); } - private VcsLogHighlighter.VcsCommitStyle getStyle(int row, int column, String text, boolean hasFocus, boolean selected) { - VcsLogHighlighter.VcsCommitStyle baseStyle = getBaseStyle(row, column, text, hasFocus, selected); + private VcsLogHighlighter.VcsCommitStyle getStyle(int row, int column, boolean hasFocus, boolean selected) { + VcsLogHighlighter.VcsCommitStyle baseStyle = getBaseStyle(row, column, hasFocus, selected); VisibleGraph visibleGraph = getVisibleGraph(); if (row < 0 || row >= visibleGraph.getVisibleCommitCount()) { @@ -549,7 +549,7 @@ public class VcsLogGraphTable extends TableWithProgress implements DataProvider, protected void paintFooter(@NotNull Graphics g, int x, int y, int width, int height) { int lastRow = getRowCount() - 1; if (lastRow >= 0) { - g.setColor(getStyle(lastRow, GraphTableModel.COMMIT_COLUMN, "", hasFocus(), false).getBackground()); + g.setColor(getStyle(lastRow, GraphTableModel.COMMIT_COLUMN, hasFocus(), false).getBackground()); g.fillRect(x, y, width, height); if (myUi.isMultipleRoots()) { g.setColor(getRootBackgroundColor(getModel().getRoot(lastRow), myUi.getColorManager())); @@ -561,7 +561,7 @@ public class VcsLogGraphTable extends TableWithProgress implements DataProvider, } } else { - g.setColor(getBaseStyle(lastRow, GraphTableModel.COMMIT_COLUMN, "", hasFocus(), false).getBackground()); + g.setColor(getBaseStyle(lastRow, GraphTableModel.COMMIT_COLUMN, hasFocus(), false).getBackground()); g.fillRect(x, y, width, height); } } @@ -626,7 +626,7 @@ public class VcsLogGraphTable extends TableWithProgress implements DataProvider, } myColor = color; - Color background = ((VcsLogGraphTable)table).getStyle(row, column, text, hasFocus, isSelected).getBackground(); + Color background = ((VcsLogGraphTable)table).getStyle(row, column, hasFocus, isSelected).getBackground(); assert background != null; myBorderColor = background; setForeground(UIUtil.getTableForeground(false)); @@ -655,7 +655,7 @@ public class VcsLogGraphTable extends TableWithProgress implements DataProvider, if (value == null) { return; } - append(value.toString(), applyHighlighters(this, row, column, value.toString(), hasFocus, selected)); + append(value.toString(), applyHighlighters(this, row, column, hasFocus, selected)); } public int getHorizontalTextPadding() { diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/GraphCommitCellRenderer.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/GraphCommitCellRenderer.java index 0591dd7ac0bb..1aaf1f823507 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/GraphCommitCellRenderer.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/GraphCommitCellRenderer.java @@ -135,10 +135,10 @@ public class GraphCommitCellRenderer extends ColoredTableCellRenderer { graphPadding = 0; } - SimpleTextAttributes style = myGraphTable.applyHighlighters(this, row, column, "", hasFocus, isSelected); + SimpleTextAttributes style = myGraphTable.applyHighlighters(this, row, column, hasFocus, isSelected); Collection refs = cell.getRefsToThisCommit(); - Color foreground = ObjectUtils.assertNotNull(myGraphTable.getBaseStyle(row, column, "", hasFocus, isSelected).getForeground()); + Color foreground = ObjectUtils.assertNotNull(myGraphTable.getBaseStyle(row, column, hasFocus, isSelected).getForeground()); myExpanded = myGraphTable.getExpandableItemsHandler().getExpandedItems().contains(new TableCell(row, column)); if (myFadeOutPainter != null) { myFadeOutPainter.customize(refs, row, column, table, foreground); From be77fb1fe02e3807778306db56309fc17e91df38 Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Sun, 16 Oct 2016 08:32:31 +0300 Subject: [PATCH 04/66] [vcs-log] more precise graph width calculation Take into account position of the edges on the other row. --- .../log/ui/render/GraphCommitCellRenderer.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/GraphCommitCellRenderer.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/GraphCommitCellRenderer.java index 1aaf1f823507..bf43444298f2 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/GraphCommitCellRenderer.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/GraphCommitCellRenderer.java @@ -20,6 +20,7 @@ import com.intellij.util.ui.UIUtil; import com.intellij.vcs.log.VcsRef; import com.intellij.vcs.log.VcsRefType; import com.intellij.vcs.log.data.VcsLogData; +import com.intellij.vcs.log.graph.EdgePrintElement; import com.intellij.vcs.log.graph.PrintElement; import com.intellij.vcs.log.graph.VisibleGraph; import com.intellij.vcs.log.paint.GraphCellPainter; @@ -165,19 +166,24 @@ public class GraphCommitCellRenderer extends ColoredTableCellRenderer { private PaintInfo getGraphImage(int row) { VisibleGraph graph = myGraphTable.getVisibleGraph(); Collection printElements = graph.getRowInfo(row).getPrintElements(); - int maxIndex = 0; + double maxIndex = 0; for (PrintElement printElement : printElements) { maxIndex = Math.max(maxIndex, printElement.getPositionInCurrentRow()); + if (printElement instanceof EdgePrintElement) { + maxIndex = Math.max(maxIndex, + (printElement.getPositionInCurrentRow() + ((EdgePrintElement)printElement).getPositionInOtherRow()) / 2.0); + } } maxIndex++; + maxIndex = Math.max(maxIndex, Math.min(MAX_GRAPH_WIDTH, graph.getRecommendedWidth())); - final BufferedImage image = UIUtil - .createImage(PaintParameters.getNodeWidth(myGraphTable.getRowHeight()) * (maxIndex + 4), myGraphTable.getRowHeight(), - BufferedImage.TYPE_INT_ARGB); + BufferedImage image = UIUtil.createImage((int)(PaintParameters.getNodeWidth(myGraphTable.getRowHeight()) * (maxIndex + 2)), + myGraphTable.getRowHeight(), + BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = image.createGraphics(); myPainter.draw(g2, printElements); - int width = maxIndex * PaintParameters.getNodeWidth(myGraphTable.getRowHeight()); + int width = (int)(maxIndex * PaintParameters.getNodeWidth(myGraphTable.getRowHeight())); return new PaintInfo(image, width); } From 16b781a0194c7bf19545d49dbf315c11c349e8b0 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Sun, 16 Oct 2016 15:36:16 +0300 Subject: [PATCH 05/66] Use a utility method to avoid suppression --- .../src/com/intellij/vcs/log/data/InMemoryStorage.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/InMemoryStorage.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/InMemoryStorage.java index 26e753e76861..cb7e15bc0e83 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/InMemoryStorage.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/InMemoryStorage.java @@ -22,15 +22,14 @@ import com.intellij.vcs.log.CommitId; import com.intellij.vcs.log.Hash; import com.intellij.vcs.log.VcsLogStorage; import com.intellij.vcs.log.VcsRef; -import gnu.trove.TObjectHashingStrategy; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import static com.intellij.util.containers.ContainerUtil.canonicalStrategy; + public class InMemoryStorage implements VcsLogStorage { - @SuppressWarnings("unchecked") private final BiDirectionalEnumerator myCommitIdEnumerator = - new BiDirectionalEnumerator<>(1, TObjectHashingStrategy.CANONICAL); - @SuppressWarnings("unchecked") private final BiDirectionalEnumerator myRefsEnumerator = - new BiDirectionalEnumerator<>(1, TObjectHashingStrategy.CANONICAL); + private final BiDirectionalEnumerator myCommitIdEnumerator = new BiDirectionalEnumerator<>(1, canonicalStrategy()); + private final BiDirectionalEnumerator myRefsEnumerator = new BiDirectionalEnumerator<>(1, canonicalStrategy()); @Override public int getCommitIndex(@NotNull Hash hash, @NotNull VirtualFile root) { From eedb2ad320064a699170eb2c793711c36cbd3580 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Fri, 14 Oct 2016 15:43:41 +0200 Subject: [PATCH 06/66] assertThat(Element) --- .../src/com/intellij/testFramework/PlatformTestUtil.java | 9 +-------- platform/testFramework/testFramework.iml | 2 +- .../testSrc/com/intellij/testFramework/assertJEx.kt | 7 ++++++- .../org/jetbrains/idea/eclipse/EclipseImlTest.java | 7 +++---- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java b/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java index 7a55ea8dde42..7cfbe7b8c8c9 100644 --- a/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java +++ b/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java @@ -846,7 +846,7 @@ public class PlatformTestUtil { public static void assertElementsEqual(final Element expected, final Element actual) throws IOException { if (!JDOMUtil.areElementsEqual(expected, actual)) { - Assert.assertEquals(printElement(expected), printElement(actual)); + Assert.assertEquals(JDOMUtil.writeElement(expected), JDOMUtil.writeElement(actual)); } } @@ -859,12 +859,6 @@ public class PlatformTestUtil { } } - public static String printElement(final Element element) throws IOException { - final StringWriter writer = new StringWriter(); - JDOMUtil.writeElement(element, writer, "\n"); - return writer.getBuffer().toString(); - } - public static String getCommunityPath() { final String homePath = PathManager.getHomePath(); if (new File(homePath, "community/.idea").isDirectory()) { @@ -877,7 +871,6 @@ public class PlatformTestUtil { return getCommunityPath().replace(File.separatorChar, '/') + "/platform/platform-tests/testData/"; } - public static Comparator createComparator(final Queryable.PrintInfo printInfo) { return (o1, o2) -> { String displayText1 = o1.toTestString(printInfo); diff --git a/platform/testFramework/testFramework.iml b/platform/testFramework/testFramework.iml index a3d3621de9e7..1cd589c3830e 100644 --- a/platform/testFramework/testFramework.iml +++ b/platform/testFramework/testFramework.iml @@ -25,7 +25,7 @@ - + diff --git a/platform/testFramework/testSrc/com/intellij/testFramework/assertJEx.kt b/platform/testFramework/testSrc/com/intellij/testFramework/assertJEx.kt index 5d4ad07e94b6..6903d23efaa4 100644 --- a/platform/testFramework/testSrc/com/intellij/testFramework/assertJEx.kt +++ b/platform/testFramework/testSrc/com/intellij/testFramework/assertJEx.kt @@ -24,6 +24,7 @@ import org.assertj.core.api.AbstractAssert import org.assertj.core.api.PathAssert import org.assertj.core.internal.Objects import org.jdom.Element +import java.io.File import java.nio.file.Files import java.nio.file.LinkOption import java.nio.file.Path @@ -39,10 +40,14 @@ class JdomAssert(actual: Element?) : AbstractAssert(actual return this } + fun isEqualTo(file: File): JdomAssert { + return isEqualTo(file.readText()) + } + fun isEqualTo(expected: String): JdomAssert { isNotNull - Objects.instance().assertEqual(info, JDOMUtil.writeElement(actual!!), expected.trimIndent()) + Objects.instance().assertEqual(info, JDOMUtil.writeElement(actual!!), expected.trimIndent().removePrefix("""""").trimStart()) return this } } diff --git a/plugins/eclipse/testSources/org/jetbrains/idea/eclipse/EclipseImlTest.java b/plugins/eclipse/testSources/org/jetbrains/idea/eclipse/EclipseImlTest.java index 8a2f6ed77023..9c880689ec62 100644 --- a/plugins/eclipse/testSources/org/jetbrains/idea/eclipse/EclipseImlTest.java +++ b/plugins/eclipse/testSources/org/jetbrains/idea/eclipse/EclipseImlTest.java @@ -40,13 +40,14 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.testFramework.IdeaTestCase; -import com.intellij.testFramework.PlatformTestUtil; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.idea.eclipse.conversion.EclipseClasspathReader; import java.io.File; +import static com.intellij.testFramework.Assertions.assertThat; + public class EclipseImlTest extends IdeaTestCase { @NonNls private static final String JUNIT = "JUNIT"; @@ -100,11 +101,9 @@ public class EclipseImlTest extends IdeaTestCase { PathMacroManager.getInstance(project).collapsePaths(actualImlElement); PathMacros.getInstance().removeMacro(JUNIT); - Element expectedIml = JDOMUtil.load(new File(project.getBaseDir().getPath() + "/expected", "expected.iml")); - PlatformTestUtil.assertElementsEqual(expectedIml, actualImlElement); + assertThat(actualImlElement).isEqualTo(FileUtil.loadFile(new File(project.getBaseDir().getPath() + "/expected", "expected.iml"))); } - public void testWorkspaceOnly() throws Exception { doTest(); } From 361fdda4655e0ec2d25c8feb20867e781fe1ac98 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Fri, 14 Oct 2016 16:25:38 +0200 Subject: [PATCH 07/66] =?UTF-8?q?use=20correct=20serialization=20implement?= =?UTF-8?q?ation=20in=20the=20copyProfile=20=E2=80=93=20otherwise=20when?= =?UTF-8?q?=20modifiable=20model=20of=20scheme=20is=20created,=20lazy=20lo?= =?UTF-8?q?aded=20element=20data=20is=20not=20used=20(and=20as=20result,?= =?UTF-8?q?=20locked=20attribute=20is=20not=20correctly=20set)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit testPreserveCompatibility is removed because we not DO NOT TOUCH files unless it is really modified In production we "do not preserve compatibility" (flag) since spring 2016 --- .../ex/InspectionProfileTest.java | 38 ++++--------- .../codeInspection/ex/InspectionSchemeTest.kt | 2 +- .../src/com/intellij/profile/Profile.java | 7 --- .../daemon/InspectionProfileConvertor.java | 9 +-- .../ex/InspectionProfileImpl.java | 55 +++++++------------ .../src/com/intellij/profile/ProfileEx.java | 34 ++++++------ .../ProjectInspectionProfileManager.kt | 5 +- .../src/SchemeManagerImpl.kt | 16 +++--- .../ApplicationInspectionProfileManager.java | 21 +++---- .../header/InspectionToolsConfigurable.java | 2 +- .../configurationStore/scheme-impl.kt | 2 +- .../com/intellij/testFramework/assertJEx.kt | 9 +++ .../idea/copyright/CopyrightProfile.java | 2 +- 13 files changed, 81 insertions(+), 121 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionProfileTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionProfileTest.java index 1600bfa069f4..922d706384e2 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionProfileTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionProfileTest.java @@ -27,7 +27,6 @@ import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.WriteExternalException; import com.intellij.profile.Profile; import com.intellij.profile.codeInspection.InspectionProfileManager; -import com.intellij.profile.codeInspection.InspectionProjectProfileManager; import com.intellij.profile.codeInspection.ProjectInspectionProfileManager; import com.intellij.profile.codeInspection.ui.header.InspectionToolsConfigurable; import com.intellij.psi.PsiModifier; @@ -43,8 +42,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import static com.intellij.profile.ProfileEx.serializeProfile; -import static com.intellij.testFramework.PlatformTestUtil.assertElementsEqual; +import static com.intellij.testFramework.Assertions.assertThat; /** * @author Anna.Kozlova @@ -80,7 +78,7 @@ public class InspectionProfileTest extends LightIdeaTestCase { profile.readExternal(element); final ModifiableModel model = profile.getModifiableModel(); model.commit(); - assertElementsEqual(element, serializeProfile(profile)); + assertThat(profile.writeScheme()).isEqualTo(element); } private static InspectionProfileImpl createProfile() { @@ -150,7 +148,7 @@ public class InspectionProfileTest extends LightIdeaTestCase { ModifiableModel model = profile.getModifiableModel(); model.commit(); - assertElementsEqual(loadProfile(), serializeProfile(profile)); + assertThat(profile.writeScheme()).isEqualTo(loadProfile()); } private static Element loadProfile() throws IOException, JDOMException { @@ -206,7 +204,7 @@ public class InspectionProfileTest extends LightIdeaTestCase { profile.readExternal(element); final ModifiableModel model = profile.getModifiableModel(); model.commit(); - assertElementsEqual(element, serializeProfile(profile)); + assertThat(profile.writeScheme()).isEqualTo(element); } public void testMergeUnusedDeclarationAndUnusedSymbol() throws Exception { @@ -218,7 +216,7 @@ public class InspectionProfileTest extends LightIdeaTestCase { profile.readExternal(element); ModifiableModel model = profile.getModifiableModel(); model.commit(); - assertElementsEqual(element, serializeProfile(profile)); + assertThat(profile.writeScheme()).isEqualTo(element); //settings to merge @@ -290,7 +288,7 @@ public class InspectionProfileTest extends LightIdeaTestCase { ""; assertEquals(mergedText, serialize(profile)); - Element toImportElement = serializeProfile(profile); + Element toImportElement = profile.writeScheme(); final InspectionProfileImpl importedProfile = InspectionToolsConfigurable.importInspectionProfile(toImportElement, InspectionProfileManager.getInstance(), getProject(), null); @@ -300,9 +298,9 @@ public class InspectionProfileTest extends LightIdeaTestCase { profile.readExternal(mergedElement); model = profile.getModifiableModel(); model.commit(); - assertElementsEqual(mergedElement, serializeProfile(profile)); + assertThat(profile.writeScheme()).isEqualTo(mergedElement); - assertElementsEqual(mergedElement, serializeProfile(importedProfile)); + assertThat(importedProfile.writeScheme()).isEqualTo(mergedElement); } public void testStoredMemberVisibility() throws Exception { @@ -510,7 +508,7 @@ public class InspectionProfileTest extends LightIdeaTestCase { "", serialize(profile)); - Element element = serializeProfile(profile); + Element element = profile.writeScheme(); list.add(createTool("bar", true)); list.add(createTool("disabled", false)); @@ -537,7 +535,7 @@ public class InspectionProfileTest extends LightIdeaTestCase { } private static String serialize(InspectionProfileImpl profile) throws WriteExternalException { - return JDOMUtil.writeElement(serializeProfile(profile)); + return JDOMUtil.writeElement(profile.writeScheme()); } private static InspectionProfileImpl createProfile(@NotNull InspectionToolRegistrar registrar) { @@ -596,7 +594,7 @@ public class InspectionProfileTest extends LightIdeaTestCase { profile.enableTool(id, getProject()); } assertEquals(0, countInitializedTools(profile)); - serializeProfile(profile); + profile.writeScheme(); List initializedTools = getInitializedTools(profile); if (initializedTools.size() > 0) { for (InspectionToolWrapper initializedTool : initializedTools) { @@ -619,20 +617,6 @@ public class InspectionProfileTest extends LightIdeaTestCase { assertEquals(1, countInitializedTools(foo)); } - public void testPreserveCompatibility() throws Exception { - InspectionProfileImpl foo = new InspectionProfileImpl("foo", InspectionToolRegistrar.getInstance(), InspectionProjectProfileManager.getInstance(getProject())); - String test = "\n" + - " "; - foo.readExternal(JDOMUtil.loadDocument(test).getRootElement()); - foo.initInspectionTools(getProject()); - assertEquals(test, JDOMUtil.writeElement(serializeProfile(foo))); - } - public static int countInitializedTools(@NotNull Profile foo) { return getInitializedTools((InspectionProfileImpl)foo).size(); } diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionSchemeTest.kt b/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionSchemeTest.kt index 8709ee796ef3..974c9661cba7 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionSchemeTest.kt +++ b/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionSchemeTest.kt @@ -41,7 +41,7 @@ class InspectionSchemeTest { @Test fun loadSchemes() { val schemeFile = fsRule.fs.getPath("inspection/Bar.xml") val schemeData = """ - + """.trimIndent() diff --git a/platform/analysis-api/src/com/intellij/profile/Profile.java b/platform/analysis-api/src/com/intellij/profile/Profile.java index 08e65fc15cc3..163875cb8423 100644 --- a/platform/analysis-api/src/com/intellij/profile/Profile.java +++ b/platform/analysis-api/src/com/intellij/profile/Profile.java @@ -17,7 +17,6 @@ package com.intellij.profile; import com.intellij.openapi.options.Scheme; import com.intellij.util.xmlb.annotations.Transient; -import org.jdom.Element; import org.jetbrains.annotations.NotNull; /** @@ -25,8 +24,6 @@ import org.jetbrains.annotations.NotNull; * Date: 20-Nov-2005 */ public interface Profile extends Comparable, Scheme { - void copyFrom(@NotNull Profile profile); - @Transient boolean isProjectLevel(); @@ -42,8 +39,4 @@ public interface Profile extends Comparable, Scheme { @NotNull ProfileManager getProfileManager(); - - void readExternal(Element element); - - void writeExternal(Element element); } diff --git a/platform/analysis-impl/src/com/intellij/codeInsight/daemon/InspectionProfileConvertor.java b/platform/analysis-impl/src/com/intellij/codeInsight/daemon/InspectionProfileConvertor.java index f45ffc640444..63fadcb0afa5 100644 --- a/platform/analysis-impl/src/com/intellij/codeInsight/daemon/InspectionProfileConvertor.java +++ b/platform/analysis-impl/src/com/intellij/codeInsight/daemon/InspectionProfileConvertor.java @@ -26,13 +26,13 @@ import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.io.FileUtil; import com.intellij.profile.codeInspection.InspectionProfileManager; import com.intellij.profile.codeInspection.SeverityProvider; -import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -50,7 +50,6 @@ public class InspectionProfileConvertor { @NonNls private static final String NAME_ATT = "name"; @NonNls private static final String VERSION_ATT = "version"; - @NonNls private static final String PROFILE_NAME_ATT = "profile_name"; @NonNls private static final String OPTION_TAG = "option"; @NonNls private static final String DISPLAY_LEVEL_MAP_OPTION = "DISPLAY_LEVEL_MAP"; @NonNls protected static final String VALUE_ATT = "value"; @@ -110,11 +109,9 @@ public class InspectionProfileConvertor { return; } try { - Document doc = JDOMUtil.loadDocument(files[0]); - Element root = doc.getRootElement(); + Element root = JDOMUtil.load(files[0]); if (root.getAttributeValue(VERSION_ATT) == null){ - root.setAttribute(PROFILE_NAME_ATT, OLD_DEFAUL_PROFILE); - JDOMUtil.writeDocument(doc, new File(profileDirectory, OLD_DEFAUL_PROFILE + XML_EXTENSION), "\n"); + JDOMUtil.writeParent(root, new FileOutputStream(new File(profileDirectory, OLD_DEFAUL_PROFILE + XML_EXTENSION)), "\n"); FileUtil.delete(files[0]); } } diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java index ac9028837678..5f6ac4764dba 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java @@ -33,10 +33,7 @@ import com.intellij.openapi.util.*; import com.intellij.profile.ProfileEx; import com.intellij.profile.ProfileManager; import com.intellij.profile.codeInspection.InspectionProfileManager; -import com.intellij.profile.codeInspection.ProjectInspectionProfileManager; -import com.intellij.profile.codeInspection.ProjectInspectionProfileManagerKt; import com.intellij.profile.codeInspection.SeverityProvider; -import com.intellij.project.ProjectKt; import com.intellij.psi.PsiElement; import com.intellij.psi.search.scope.packageSet.NamedScope; import com.intellij.util.ArrayUtil; @@ -281,26 +278,15 @@ public class InspectionProfileImpl extends ProfileEx implements ModifiableModel, @NotNull public Element writeScheme() { - if (myDataHolder != null) { - return myDataHolder.read(); - } - - Element element = new Element("profile"); - Element result = isProjectLevel() ? element.setAttribute("version", "1.0") : element.setAttribute("profile_name", getName()); - serializeInto(result, false); - - if (isProjectLevel() && ProjectKt.isDirectoryBased(((ProjectInspectionProfileManager)myProfileManager).getProject())) { - return new Element("component").setAttribute("name", "InspectionProjectProfileManager").addContent(result); - } - return result; + return myDataHolder == null ? super.writeScheme() : myDataHolder.read(); } @Override - public void serializeInto(@NotNull Element element, boolean preserveCompatibility) { + public void writeExternal(@NotNull Element element) { // must be first - compatibility element.setAttribute(VERSION_TAG, VALID_VERSION); - super.serializeInto(element, preserveCompatibility); + super.writeExternal(element); synchronized (myLock) { if (!myInitialized) { @@ -857,24 +843,23 @@ public class InspectionProfileImpl extends ProfileEx implements ModifiableModel, initInspectionTools(project); for (Element scopeElement : scopes.getChildren(SCOPE)) { - final String profile = scopeElement.getAttributeValue(ProjectInspectionProfileManagerKt.PROFILE); - if (profile != null) { - final InspectionProfileImpl inspectionProfile = (InspectionProfileImpl)getProfileManager().getProfile(profile); - if (inspectionProfile != null) { - final NamedScope scope = getProfileManager().getScopesManager().getScope(scopeElement.getAttributeValue(NAME)); - if (scope != null) { - for (InspectionToolWrapper toolWrapper : inspectionProfile.getInspectionTools(null)) { - final HighlightDisplayKey key = HighlightDisplayKey.find(toolWrapper.getShortName()); - try { - InspectionToolWrapper toolWrapperCopy = copyToolSettings(toolWrapper); - HighlightDisplayLevel errorLevel = inspectionProfile.getErrorLevel(key, null, project); - getTools(toolWrapper.getShortName(), project).addTool(scope, toolWrapperCopy, inspectionProfile.isToolEnabled(key), errorLevel); - } - catch (Exception e) { - LOG.error(e); - } - } - } + final String profile = scopeElement.getAttributeValue(PROFILE); + InspectionProfileImpl inspectionProfile = profile == null ? null : (InspectionProfileImpl)getProfileManager().getProfile(profile); + NamedScope scope = inspectionProfile == null ? null : getProfileManager().getScopesManager().getScope(scopeElement.getAttributeValue(NAME)); + if (scope == null) { + continue; + } + + for (InspectionToolWrapper toolWrapper : inspectionProfile.getInspectionTools(null)) { + final HighlightDisplayKey key = HighlightDisplayKey.find(toolWrapper.getShortName()); + try { + InspectionToolWrapper toolWrapperCopy = copyToolSettings(toolWrapper); + HighlightDisplayLevel errorLevel = inspectionProfile.getErrorLevel(key, null, project); + getTools(toolWrapper.getShortName(), project) + .addTool(scope, toolWrapperCopy, inspectionProfile.isToolEnabled(key), errorLevel); + } + catch (Exception e) { + LOG.error(e); } } } diff --git a/platform/analysis-impl/src/com/intellij/profile/ProfileEx.java b/platform/analysis-impl/src/com/intellij/profile/ProfileEx.java index 9ff4b6785519..6060227b2c09 100644 --- a/platform/analysis-impl/src/com/intellij/profile/ProfileEx.java +++ b/platform/analysis-impl/src/com/intellij/profile/ProfileEx.java @@ -15,7 +15,8 @@ */ package com.intellij.profile; -import com.intellij.profile.codeInspection.ProjectInspectionProfileManagerKt; +import com.intellij.profile.codeInspection.ProjectInspectionProfileManager; +import com.intellij.project.ProjectKt; import com.intellij.util.xmlb.SmartSerializer; import com.intellij.util.xmlb.annotations.OptionTag; import com.intellij.util.xmlb.annotations.Transient; @@ -29,6 +30,7 @@ import org.jetbrains.annotations.NotNull; public abstract class ProfileEx implements Profile { public static final String SCOPE = "scope"; public static final String NAME = "name"; + public static final String PROFILE = "profile"; private final SmartSerializer mySerializer; @@ -84,18 +86,12 @@ public abstract class ProfileEx implements Profile { myProfileManager = profileManager; } - @Override public void readExternal(Element element) { mySerializer.readExternal(this, element); } - public void serializeInto(@NotNull Element element, boolean preserveCompatibility) { - mySerializer.writeExternal(this, element, preserveCompatibility); - } - - @Override - public final void writeExternal(Element element) { - serializeInto(element, true); + public void writeExternal(@NotNull Element element) { + mySerializer.writeExternal(this, element, false); } public boolean equals(Object o) { @@ -114,15 +110,21 @@ public abstract class ProfileEx implements Profile { return 0; } - @Override - public final void copyFrom(@NotNull Profile profile) { - readExternal(serializeProfile(profile)); + public final void copyFrom(@NotNull ProfileEx profile) { + readExternal(profile.writeScheme()); } @NotNull - public static Element serializeProfile(@NotNull Profile profile) { - Element result = new Element(ProjectInspectionProfileManagerKt.PROFILE); - profile.writeExternal(result); - return result; + public Element writeScheme() { + Element element = new Element(PROFILE); + if (isProjectLevel()) { + element.setAttribute("version", "1.0"); + } + writeExternal(element); + + if (isProjectLevel() && ProjectKt.isDirectoryBased(((ProjectInspectionProfileManager)myProfileManager).getProject())) { + return new Element("component").setAttribute("name", "InspectionProjectProfileManager").addContent(element); + } + return element; } } diff --git a/platform/analysis-impl/src/com/intellij/profile/codeInspection/ProjectInspectionProfileManager.kt b/platform/analysis-impl/src/com/intellij/profile/codeInspection/ProjectInspectionProfileManager.kt index d0593856fc8a..f7f31fc78e52 100644 --- a/platform/analysis-impl/src/com/intellij/profile/codeInspection/ProjectInspectionProfileManager.kt +++ b/platform/analysis-impl/src/com/intellij/profile/codeInspection/ProjectInspectionProfileManager.kt @@ -33,6 +33,7 @@ import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.text.StringUtil import com.intellij.packageDependencies.DependencyValidationManager import com.intellij.profile.Profile +import com.intellij.profile.ProfileEx import com.intellij.project.isDirectoryBased import com.intellij.psi.search.scope.packageSet.NamedScopeManager import com.intellij.psi.search.scope.packageSet.NamedScopesHolder @@ -50,8 +51,6 @@ import org.jetbrains.concurrency.runAsync import java.util.* import java.util.function.Function -const val PROFILE = "profile" - private const val VERSION = "1.0" private const val SCOPE = "scope" private const val NAME = "name" @@ -308,7 +307,7 @@ class ProjectInspectionProfileManager(val project: Project, if (currentScheme == null) { currentScheme = InspectionProfileImpl(PROJECT_DEFAULT_PROFILE_NAME, InspectionToolRegistrar.getInstance(), this, InspectionProfileImpl.getDefaultProfile(), null) - currentScheme.copyFrom(applicationProfileManager.currentProfile) + currentScheme.copyFrom(applicationProfileManager.currentProfile as ProfileEx) currentScheme.isProjectLevel = true currentScheme.name = PROJECT_DEFAULT_PROFILE_NAME schemeManager.addScheme(currentScheme) diff --git a/platform/configuration-store-impl/src/SchemeManagerImpl.kt b/platform/configuration-store-impl/src/SchemeManagerImpl.kt index eb9b341f4316..a210200778f6 100644 --- a/platform/configuration-store-impl/src/SchemeManagerImpl.kt +++ b/platform/configuration-store-impl/src/SchemeManagerImpl.kt @@ -233,11 +233,11 @@ class SchemeManagerImpl(val fileSpec: String, val bytes = URLUtil.openStream(url).readBytes() lazyPreloadScheme(bytes, isUseOldFileNameSanitize) { name, parser -> val attributeProvider = Function { parser.getAttributeValue(null, it) } - val schemeName = name ?: (processor as LazySchemeProcessor).getName(attributeProvider) - val fileName = PathUtilRt.getFileName(url.path) val extension = getFileExtension(fileName, true) val externalInfo = ExternalInfo(fileName.substring(0, fileName.length - extension.length), extension) + + val schemeName = name ?: (processor as LazySchemeProcessor).getName(attributeProvider, externalInfo.fileNameWithoutExtension) externalInfo.schemeName = schemeName val scheme = (processor as LazySchemeProcessor).createScheme(SchemeDataHolderImpl(bytes, externalInfo), schemeName, attributeProvider, true) @@ -316,7 +316,7 @@ class SchemeManagerImpl(val fileSpec: String, processPendingCurrentSchemeName(scheme) } - messageBus?.let { it.connect().subscribe(VirtualFileManager.VFS_CHANGES, SchemeFileTracker()) } + messageBus?.connect()?.subscribe(VirtualFileManager.VFS_CHANGES, SchemeFileTracker()) return schemes.subList(newSchemesOffset, schemes.size) } @@ -438,7 +438,7 @@ class SchemeManagerImpl(val fileSpec: String, val bytes = input.readBytes() lazyPreloadScheme(bytes, isUseOldFileNameSanitize) { name, parser -> val attributeProvider = Function { parser.getAttributeValue(null, it) } - val schemeName = name ?: processor.getName(attributeProvider) + val schemeName = name ?: processor.getName(attributeProvider, fileNameWithoutExtension) if (!checkExisting(schemeName)) { return null } @@ -573,7 +573,7 @@ class SchemeManagerImpl(val fileSpec: String, var externalInfo: ExternalInfo? = schemeToInfo.get(scheme) val currentFileNameWithoutExtension = externalInfo?.fileNameWithoutExtension val parent = processor.writeScheme(scheme) - val element = if (parent is Element) parent else (parent as Document).detachRootElement() + val element = parent as? Element ?: (parent as Document).detachRootElement() if (element.isEmpty()) { externalInfo?.scheduleDelete() return @@ -685,7 +685,7 @@ class SchemeManagerImpl(val fileSpec: String, val bundledScheme = readOnlyExternalizableSchemes.get(scheme.name) if (bundledScheme == null) { - if ((processor as? LazySchemeProcessor)?.let { it.isSchemeEqualToBundled(scheme) } ?: false) { + if ((processor as? LazySchemeProcessor)?.isSchemeEqualToBundled(scheme) ?: false) { externalInfo?.scheduleDelete() return true } @@ -875,9 +875,7 @@ class SchemeManagerImpl(val fileSpec: String, private fun collectExistingNames(schemes: Collection): Collection { val result = THashSet(schemes.size) - for (scheme in schemes) { - result.add(scheme.name) - } + schemes.mapTo(result) { it.name } return result } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/ApplicationInspectionProfileManager.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/ApplicationInspectionProfileManager.java index 93ec446cb6f5..91b13c8643d4 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/ApplicationInspectionProfileManager.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/ApplicationInspectionProfileManager.java @@ -22,7 +22,6 @@ import com.intellij.codeInsight.daemon.impl.HighlightInfoType; import com.intellij.codeInsight.daemon.impl.SeveritiesProvider; import com.intellij.codeInsight.daemon.impl.SeverityRegistrar; import com.intellij.codeInsight.daemon.impl.analysis.HighlightingSettingsPerFile; -import com.intellij.codeInspection.InspectionProfile; import com.intellij.codeInspection.InspectionsBundle; import com.intellij.configurationStore.BundledSchemeEP; import com.intellij.configurationStore.SchemeDataHolder; @@ -93,8 +92,8 @@ public class ApplicationInspectionProfileManager extends BaseInspectionProfileMa mySchemeManager = schemeManagerFactory.create(INSPECTION_DIR, new InspectionProfileProcessor() { @NotNull @Override - public String getName(@NotNull Function attributeProvider) { - return "unnamed"; + public String getName(@NotNull Function attributeProvider, String fileNameWithoutExtension) { + return fileNameWithoutExtension; } @NotNull @@ -102,11 +101,7 @@ public class ApplicationInspectionProfileManager extends BaseInspectionProfileMa @NotNull String name, @NotNull Function attributeProvider, boolean isBundled) { - InspectionProfileImpl profile = new InspectionProfileImpl(name, myRegistrar, ApplicationInspectionProfileManager.this, dataHolder); - if (isBundled) { - profile.lockProfile(true); - } - return profile; + return new InspectionProfileImpl(name, myRegistrar, ApplicationInspectionProfileManager.this, dataHolder); } @Override @@ -169,12 +164,10 @@ public class ApplicationInspectionProfileManager extends BaseInspectionProfileMa } public void initProfiles() { - if (!myProfilesAreInitialized.compareAndSet(false, true)) { + if (!myProfilesAreInitialized.compareAndSet(false, true) || !LOAD_PROFILES) { return; } - if (!LOAD_PROFILES) return; - loadBundledSchemes(); mySchemeManager.loadSchemes(); createDefaultProfile(); @@ -270,12 +263,12 @@ public class ApplicationInspectionProfileManager extends BaseInspectionProfileMa @NotNull @Override - public InspectionProfile getCurrentProfile() { + public InspectionProfileImpl getCurrentProfile() { initProfiles(); - Profile current = mySchemeManager.getCurrentScheme(); + InspectionProfileImpl current = mySchemeManager.getCurrentScheme(); if (current != null) { - return (InspectionProfile)current; + return current; } // use default as base, not random custom profile diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/InspectionToolsConfigurable.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/InspectionToolsConfigurable.java index fc8ff4abd3e9..9da66f7a13d8 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/InspectionToolsConfigurable.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/header/InspectionToolsConfigurable.java @@ -108,7 +108,7 @@ public abstract class InspectionToolsConfigurable extends BaseConfigurable } @NotNull - private InspectionProfileImpl copyToNewProfile(ModifiableModel selectedProfile, + private InspectionProfileImpl copyToNewProfile(@NotNull InspectionProfileImpl selectedProfile, @NotNull Project project, boolean modifyName, boolean modifyLevel) { diff --git a/platform/projectModel-impl/src/com/intellij/configurationStore/scheme-impl.kt b/platform/projectModel-impl/src/com/intellij/configurationStore/scheme-impl.kt index cc6b497a61ce..d152cce89576 100644 --- a/platform/projectModel-impl/src/com/intellij/configurationStore/scheme-impl.kt +++ b/platform/projectModel-impl/src/com/intellij/configurationStore/scheme-impl.kt @@ -54,7 +54,7 @@ interface SchemeExtensionProvider { } abstract class LazySchemeProcessor(private val nameAttribute: String = "name") : SchemeProcessor() { - open fun getName(attributeProvider: Function): String { + open fun getName(attributeProvider: Function, fileNameWithoutExtension: String): String { return attributeProvider.apply(nameAttribute) ?: throw IllegalStateException("name is missed in the scheme data") } diff --git a/platform/testFramework/testSrc/com/intellij/testFramework/assertJEx.kt b/platform/testFramework/testSrc/com/intellij/testFramework/assertJEx.kt index 6903d23efaa4..455744a91dde 100644 --- a/platform/testFramework/testSrc/com/intellij/testFramework/assertJEx.kt +++ b/platform/testFramework/testSrc/com/intellij/testFramework/assertJEx.kt @@ -44,6 +44,15 @@ class JdomAssert(actual: Element?) : AbstractAssert(actual return isEqualTo(file.readText()) } + fun isEqualTo(element: Element): JdomAssert { + isNotNull + + if (!JDOMUtil.areElementsEqual(actual, element)) { + isEqualTo(JDOMUtil.writeElement(element)) + } + return this + } + fun isEqualTo(expected: String): JdomAssert { isNotNull diff --git a/plugins/copyright/src/com/maddyhome/idea/copyright/CopyrightProfile.java b/plugins/copyright/src/com/maddyhome/idea/copyright/CopyrightProfile.java index b880fbad0ffa..642f5e79ac6b 100644 --- a/plugins/copyright/src/com/maddyhome/idea/copyright/CopyrightProfile.java +++ b/plugins/copyright/src/com/maddyhome/idea/copyright/CopyrightProfile.java @@ -34,7 +34,7 @@ public class CopyrightProfile extends ProfileEx { //read external public CopyrightProfile() { - super("", new SmartSerializer()); + this(""); } public CopyrightProfile(String profileName) { From a89e8304525717593ade52ac9a77ac686a70012e Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Fri, 14 Oct 2016 16:54:43 +0200 Subject: [PATCH 08/66] cleanup --- plugins/settings-repository/src/IcsManager.kt | 4 ++-- plugins/settings-repository/src/sync.kt | 20 +++++++------------ 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/plugins/settings-repository/src/IcsManager.kt b/plugins/settings-repository/src/IcsManager.kt index 03ddf6ec335a..492116735276 100644 --- a/plugins/settings-repository/src/IcsManager.kt +++ b/plugins/settings-repository/src/IcsManager.kt @@ -23,8 +23,8 @@ import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.PathManager import com.intellij.openapi.components.RoamingType import com.intellij.openapi.components.stateStore -import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.catchAndLog +import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.progress.runBackgroundableTask import com.intellij.openapi.project.Project import com.intellij.openapi.project.impl.ProjectLifecycleListener @@ -42,7 +42,7 @@ import kotlin.properties.Delegates internal const val PLUGIN_NAME = "Settings Repository" -internal val LOG: Logger = Logger.getInstance(IcsManager::class.java) +internal val LOG = logger() val icsManager by lazy(LazyThreadSafetyMode.NONE) { ApplicationLoadListener.EP_NAME.findExtension(IcsApplicationLoadListener::class.java).icsManager diff --git a/plugins/settings-repository/src/sync.kt b/plugins/settings-repository/src/sync.kt index decfaf44585e..59ebb1699ec9 100644 --- a/plugins/settings-repository/src/sync.kt +++ b/plugins/settings-repository/src/sync.kt @@ -17,15 +17,14 @@ package org.jetbrains.settingsRepository import com.intellij.configurationStore.* import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.application.ModalityState import com.intellij.openapi.application.impl.ApplicationImpl +import com.intellij.openapi.application.invokeAndWaitIfNeed import com.intellij.openapi.components.StateStorage import com.intellij.openapi.components.stateStore import com.intellij.openapi.options.SchemeManagerFactory import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.progress.runModalTask import com.intellij.openapi.project.Project -import com.intellij.openapi.util.Ref import com.intellij.util.SmartList import com.intellij.util.messages.MessageBus import gnu.trove.THashSet @@ -142,7 +141,6 @@ internal class SyncManager(private val icsManager: IcsManager, private val autoS } internal fun updateStoragesFromStreamProvider(store: ComponentStoreImpl, updateResult: UpdateResult, messageBus: MessageBus, reloadAllSchemes: Boolean = false): Boolean { - val changedComponentNames = LinkedHashSet() val (changed, deleted) = (store.storageManager as StateStorageManagerImpl).getCachedFileStorages(updateResult.changed, updateResult.deleted, ::toIdeaPath) val schemeManagersToReload = SmartList>() @@ -168,9 +166,8 @@ internal fun updateStoragesFromStreamProvider(store: ComponentStoreImpl, updateR return false } - val result = Ref.create(false) - ApplicationManager.getApplication().invokeAndWait(Runnable { - val notReloadableComponents: Collection + return invokeAndWaitIfNeed { + val changedComponentNames = LinkedHashSet() updateStateStorage(changedComponentNames, changed, false) updateStateStorage(changedComponentNames, deleted, true) @@ -179,20 +176,17 @@ internal fun updateStoragesFromStreamProvider(store: ComponentStoreImpl, updateR } if (changedComponentNames.isEmpty()) { - return@Runnable + return@invokeAndWaitIfNeed false } - notReloadableComponents = store.getNotReloadableComponents(changedComponentNames) - + val notReloadableComponents = store.getNotReloadableComponents(changedComponentNames) val changedStorageSet = THashSet(changed) changedStorageSet.addAll(deleted) runBatchUpdate(messageBus) { store.reinitComponents(changedComponentNames, changedStorageSet, notReloadableComponents) } - - result.set(!notReloadableComponents.isEmpty() && askToRestart(store, notReloadableComponents, null, true)) - }, ModalityState.defaultModalityState()) - return result.get() + return@invokeAndWaitIfNeed !notReloadableComponents.isEmpty() && askToRestart(store, notReloadableComponents, null, true) + } } private fun updateStateStorage(changedComponentNames: MutableSet, stateStorages: Collection, deleted: Boolean) { From 779f69c2744b93d6e4cf465b54d090c043ea2f0e Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Sun, 16 Oct 2016 22:28:07 +0300 Subject: [PATCH 09/66] [vcs-log] graph history action is visible when root is in log but not indexed yet --- .../intellij/vcs/log/ui/actions/ShowGraphHistoryAction.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/ShowGraphHistoryAction.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/ShowGraphHistoryAction.java index 967d4ff20326..fa8cdffcdc75 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/ShowGraphHistoryAction.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/actions/ShowGraphHistoryAction.java @@ -64,7 +64,8 @@ public class ShowGraphHistoryAction extends DumbAwareAction { presentation.setEnabledAndVisible(false); } else { - presentation.setEnabledAndVisible(dataManager.getIndex().isIndexed(root)); + presentation.setVisible(dataManager.getRoots().contains(root)); + presentation.setEnabled(dataManager.getIndex().isIndexed(root)); } } } From 985405e3190d1df42bc927673b5d31a30f12a3fd Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Fri, 14 Oct 2016 18:39:56 +0200 Subject: [PATCH 10/66] extended logging EA-89997 - assert: ChangeSignatureProcessorBase.filterUsages --- .../changeSignature/ChangeSignatureProcessorBase.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureProcessorBase.java b/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureProcessorBase.java index b61abc436e2c..d61fbe81425b 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureProcessorBase.java +++ b/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureProcessorBase.java @@ -93,19 +93,18 @@ public abstract class ChangeSignatureProcessorBase extends BaseRefactoringProces List infos = new ArrayList<>(); final ChangeSignatureUsageProcessor[] processors = ChangeSignatureUsageProcessor.EP_NAME.getExtensions(); for (ChangeSignatureUsageProcessor processor : processors) { - ContainerUtil.addAll(infos, processor.findUsages(changeInfo)); + ContainerUtil.addAll(infos, filterUsages(processor.findUsages(changeInfo), processor)); } - infos = filterUsages(infos); return infos.toArray(new UsageInfo[infos.size()]); } - protected static List filterUsages(List infos) { + protected static List filterUsages(UsageInfo[] infos, ChangeSignatureUsageProcessor processor) { Map moveRenameInfos = new HashMap<>(); Set usedElements = new HashSet<>(); - List result = new ArrayList<>(infos.size() / 2); + List result = new ArrayList<>(infos.length / 2); for (UsageInfo info : infos) { - LOG.assertTrue(info != null); + LOG.assertTrue(info != null, processor); PsiElement element = info.getElement(); if (info instanceof MoveRenameUsageInfo) { if (usedElements.contains(element)) continue; From b324cfdd83bd9161ae2771c49d6b91d8b3662093 Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Fri, 14 Oct 2016 19:41:04 +0200 Subject: [PATCH 11/66] testng: ensure public test class so gutters and run provider treat classes in the same way (IDEA-162561) --- ...stractTestNGInClassConfigurationProducer.java | 2 +- .../theoryinpractice/testng/util/TestNGUtil.java | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/AbstractTestNGInClassConfigurationProducer.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/AbstractTestNGInClassConfigurationProducer.java index dcdb4cf59d6f..4198e3529415 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/configuration/AbstractTestNGInClassConfigurationProducer.java +++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/AbstractTestNGInClassConfigurationProducer.java @@ -47,7 +47,7 @@ public abstract class AbstractTestNGInClassConfigurationProducer extends TestNGC } private static boolean isTestNGClass(PsiClass psiClass) { - return psiClass != null && PsiClassUtil.isRunnableClass(psiClass, true, false) && TestNGUtil.hasTest(psiClass); + return psiClass != null && TestNGUtil.hasTest(psiClass); } @Override diff --git a/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java b/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java index f18214ac5287..6a885e88d589 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java +++ b/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java @@ -192,6 +192,20 @@ public class TestNGUtil { } public static boolean hasTest(PsiModifierListOwner element, boolean checkHierarchy, boolean checkDisabled, boolean checkJavadoc) { + final PsiClass aClass; + if (element instanceof PsiClass) { + aClass = ((PsiClass)element); + } + else if (element instanceof PsiMethod) { + aClass = ((PsiMethod)element).getContainingClass(); + } + else { + aClass = null; + } + + if (aClass == null || !PsiClassUtil.isRunnableClass(aClass, true, false)) { + return false; + } //LanguageLevel effectiveLanguageLevel = element.getManager().getEffectiveLanguageLevel(); //boolean is15 = effectiveLanguageLevel != LanguageLevel.JDK_1_4 && effectiveLanguageLevel != LanguageLevel.JDK_1_3; boolean hasAnnotation = AnnotationUtil.isAnnotated(element, TEST_ANNOTATION_FQN, checkHierarchy, true); @@ -474,7 +488,7 @@ public class TestNGUtil { } public static boolean isTestNGClass(PsiClass psiClass) { - return hasTest(psiClass, true, false, false); + return hasTest(psiClass); } public static boolean checkTestNGInClasspath(PsiElement psiElement) { From e692a49a89b83f1654dbf9081854bef6a7f2fd83 Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Fri, 14 Oct 2016 20:27:24 +0200 Subject: [PATCH 12/66] EA-88367 - SOE: StringExpressionHelper.evaluateExpression --- .../codeInspection/dataFlow/StringExpressionHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StringExpressionHelper.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StringExpressionHelper.java index c9f5d4505709..a19e953e6437 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StringExpressionHelper.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StringExpressionHelper.java @@ -110,7 +110,7 @@ public class StringExpressionHelper { Collection elements = DfaUtil.getPossibleInitializationElements(expression); for (PsiElement element : elements) { - Pair expr = evaluateExpression(element); + Pair expr = evaluateExpression(element, visited); if (expr != null) return expr; } From 91e2807d869208558ff771da306daa4c8a5f5f9e Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Mon, 17 Oct 2016 10:38:09 +0200 Subject: [PATCH 13/66] clean up --- .../src/com/intellij/openapi/wm/impl/StripeButtonUI.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/StripeButtonUI.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/StripeButtonUI.java index 2c8022e7b864..69766e3b0192 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/StripeButtonUI.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/StripeButtonUI.java @@ -37,11 +37,12 @@ public final class StripeButtonUI extends MetalToggleButtonUI{ private static final Rectangle ourIconRect=new Rectangle(); private static final Rectangle ourTextRect=new Rectangle(); private static final Rectangle ourViewRect=new Rectangle(); - private static Insets ourViewInsets=new Insets(0,0,0,0); + private static Insets ourViewInsets = JBUI.emptyInsets(); private StripeButtonUI(){} /** Invoked by reflection */ + @SuppressWarnings({"MethodOverridesStaticMethodOfSuperclass", "unused"}) public static ComponentUI createUI(final JComponent c){ return ourInstance; } @@ -55,6 +56,7 @@ public final class StripeButtonUI extends MetalToggleButtonUI{ final ToolWindowAnchor anchor=button.getAnchor(); if(ToolWindowAnchor.LEFT==anchor||ToolWindowAnchor.RIGHT==anchor){ + //noinspection SuspiciousNameCombination return new Dimension(dim.height,dim.width); } else{ return dim; @@ -127,12 +129,14 @@ public final class StripeButtonUI extends MetalToggleButtonUI{ tr=g2.getTransform(); if(ToolWindowAnchor.RIGHT==anchor){ if(icon != null){ // do not rotate icon + //noinspection SuspiciousNameCombination icon.paintIcon(c, g2, ourIconRect.y, ourIconRect.x); } g2.rotate(Math.PI/2); g2.translate(0,-c.getWidth()); } else { if(icon != null){ // do not rotate icon + //noinspection SuspiciousNameCombination icon.paintIcon(c, g2, ourIconRect.y, c.getHeight() - ourIconRect.x - icon.getIconHeight()); } g2.rotate(-Math.PI/2); From e4a2b44942e7d523b8a9668fb8afdb1d50714098 Mon Sep 17 00:00:00 2001 From: Sergey Ignatov Date: Mon, 17 Oct 2016 12:06:42 +0300 Subject: [PATCH 14/66] [ui] use light grey for doc popup, coauthored with Olga B. --- .../src/com/intellij/codeInsight/hint/HintUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/codeInsight/hint/HintUtil.java b/platform/platform-impl/src/com/intellij/codeInsight/hint/HintUtil.java index 48c1678bd249..6aab28aa8d20 100644 --- a/platform/platform-impl/src/com/intellij/codeInsight/hint/HintUtil.java +++ b/platform/platform-impl/src/com/intellij/codeInsight/hint/HintUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,7 @@ import java.awt.*; import java.awt.event.MouseListener; public class HintUtil { - public static final Color INFORMATION_COLOR = new JBColor(new Color(253, 254, 226), new Color(0x4d4f51)); + public static final Color INFORMATION_COLOR = new JBColor(new Color(0xf8f8f8), new Color(0x4d4f51)); public static final Color QUESTION_COLOR = new JBColor(new Color(181, 208, 251), new Color(55, 108, 137)); public static final Color ERROR_COLOR = new JBColor(new Color(255, 220, 220), new Color(0x781732)); From 5f70a238bcf3d16843a18e0b04ff37e4e254712a Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Mon, 17 Oct 2016 11:28:19 +0200 Subject: [PATCH 15/66] IG: use isAssignableFrom() with substitutor instead of convertible check --- .../siyeh/ig/threading/AtomicFieldUpdaterIssuesInspection.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/AtomicFieldUpdaterIssuesInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/AtomicFieldUpdaterIssuesInspection.java index c3739d4bf34b..b72a0e62f291 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/AtomicFieldUpdaterIssuesInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/AtomicFieldUpdaterIssuesInspection.java @@ -129,7 +129,8 @@ public class AtomicFieldUpdaterIssuesInspection extends BaseInspection { } final PsiClassObjectAccessExpression objectAccessExpression = (PsiClassObjectAccessExpression)argument2; final PsiType type = objectAccessExpression.getOperand().getType(); - if (!TypeUtils.areConvertible(type, field.getType())) { + final PsiType substFieldType = classType.resolveGenerics().getSubstitutor().substitute(field.getType()); + if (!substFieldType.isAssignableFrom(type)) { registerError(lastArgument, InspectionGadgetsBundle.message("field.incorrect.type.problem.descriptor", fieldName, type.getPresentableText())); return; From 0686419e94a93e1627decbaca288aad772f1801f Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Mon, 17 Oct 2016 11:33:24 +0200 Subject: [PATCH 16/66] Cleanup (drops direct counter access) --- .../com/intellij/openapi/util/SimpleModificationTracker.java | 4 ++++ .../editor-ui-api/src/com/intellij/ide/ui/UISettings.java | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/platform/core-api/src/com/intellij/openapi/util/SimpleModificationTracker.java b/platform/core-api/src/com/intellij/openapi/util/SimpleModificationTracker.java index 91a933909066..f8b3461d8478 100644 --- a/platform/core-api/src/com/intellij/openapi/util/SimpleModificationTracker.java +++ b/platform/core-api/src/com/intellij/openapi/util/SimpleModificationTracker.java @@ -44,4 +44,8 @@ public class SimpleModificationTracker implements ModificationTracker { public void incModificationCount() { UPDATER.incrementAndGet(this); } + + public long incAndGetModificationCount() { + return UPDATER.incrementAndGet(this); + } } \ No newline at end of file diff --git a/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java b/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java index 95321651f571..c0c7055eddf6 100644 --- a/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java +++ b/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java @@ -173,8 +173,7 @@ public class UISettings extends SimpleModificationTracker implements PersistentS ColorBlindnessSupport support = ColorBlindnessSupport.get(COLOR_BLINDNESS); IconLoader.setFilter(support == null ? null : support.getFilter()); - incModificationCount(); - if (myCounter == 1) { + if (incAndGetModificationCount() == 1) { return; } From 97a784950b41232f9fa57ad72967a7c065a9b721 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Mon, 17 Oct 2016 11:36:36 +0200 Subject: [PATCH 17/66] Cleanup (obsolete assertion) --- .../intellij/openapi/util/SimpleModificationTracker.java | 8 +------- platform/util/src/com/intellij/Patches.java | 6 ------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/platform/core-api/src/com/intellij/openapi/util/SimpleModificationTracker.java b/platform/core-api/src/com/intellij/openapi/util/SimpleModificationTracker.java index f8b3461d8478..e1e9fff31cc5 100644 --- a/platform/core-api/src/com/intellij/openapi/util/SimpleModificationTracker.java +++ b/platform/core-api/src/com/intellij/openapi/util/SimpleModificationTracker.java @@ -15,7 +15,6 @@ */ package com.intellij.openapi.util; -import com.intellij.Patches; import com.intellij.util.xmlb.annotations.Transient; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; @@ -26,15 +25,10 @@ import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; */ @Transient public class SimpleModificationTracker implements ModificationTracker { - static { - //noinspection ConstantConditions - assert Patches.JDK_BUG_ID_7103570; - } - private static final AtomicIntegerFieldUpdater UPDATER = AtomicIntegerFieldUpdater.newUpdater(SimpleModificationTracker.class, "myCounter"); - public volatile int myCounter; // is public to work around JDK-7103570 + @SuppressWarnings("unused") private volatile int myCounter; @Override public long getModificationCount() { diff --git a/platform/util/src/com/intellij/Patches.java b/platform/util/src/com/intellij/Patches.java index ecb590c7b207..e59497d23f42 100644 --- a/platform/util/src/com/intellij/Patches.java +++ b/platform/util/src/com/intellij/Patches.java @@ -71,12 +71,6 @@ public class Patches { */ public static final boolean USE_REFLECTION_TO_ACCESS_JDK8 = Boolean.valueOf(true); - /** - * AtomicIntegerFieldUpdater does not work when SecurityManager is installed. - * See https://bugs.openjdk.java.net/browse/JDK-7103570. - */ - public static final boolean JDK_BUG_ID_7103570 = true; - /** * Support default methods in JDI * See JDK-8042123 From 991d5210e0566eb93843804acf89023250b42073 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Mon, 17 Oct 2016 11:38:12 +0200 Subject: [PATCH 18/66] Cleanup (forgotten modifier) --- platform/util/src/com/intellij/Patches.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/util/src/com/intellij/Patches.java b/platform/util/src/com/intellij/Patches.java index e59497d23f42..41dd4f41ba27 100644 --- a/platform/util/src/com/intellij/Patches.java +++ b/platform/util/src/com/intellij/Patches.java @@ -121,5 +121,5 @@ public class Patches { /** * Some HTTP connections lock the context class loader: https://bugs.openjdk.java.net/browse/JDK-8032832 */ - public static boolean JDK_BUG_ID_8032832 = SystemInfo.isJavaVersionAtLeast("1.8.0_20"); + public static final boolean JDK_BUG_ID_8032832 = SystemInfo.isJavaVersionAtLeast("1.8.0_20"); } \ No newline at end of file From de4563d0487efb54f02a028458e376104b32c847 Mon Sep 17 00:00:00 2001 From: nik Date: Mon, 17 Oct 2016 12:50:48 +0300 Subject: [PATCH 19/66] build scripts cleanup: unused methods removed, some method names corrected, obsolete todo's removed --- .../intellij/build/BuildTasks.groovy | 26 +---- .../build/IdeaCommunityBuilder.groovy | 4 +- .../intellij/build/ProductProperties.groovy | 2 - .../build/impl/BuildContextImpl.groovy | 4 +- .../intellij/build/impl/BuildTasksImpl.groovy | 100 ++++++++---------- .../CrossPlatformDistributionBuilder.groovy | 1 - .../impl/WindowsDistributionBuilder.groovy | 1 - build/scripts/layouts.gant | 5 +- 8 files changed, 55 insertions(+), 88 deletions(-) diff --git a/build/groovy/org/jetbrains/intellij/build/BuildTasks.groovy b/build/groovy/org/jetbrains/intellij/build/BuildTasks.groovy index 2f5bf5c79eff..dc56e6436be0 100644 --- a/build/groovy/org/jetbrains/intellij/build/BuildTasks.groovy +++ b/build/groovy/org/jetbrains/intellij/build/BuildTasks.groovy @@ -38,33 +38,11 @@ abstract class BuildTasks { */ abstract void zipSourcesOfModules(Collection modules, String targetFilePath) - /** - * Updates search/searchableOptions.xml file in {@code targetModuleName} module output directory - *
- * todo[nik] this is temporary solution until code from layouts.gant files moved to the new builders. After that this method will - * be called inside {@link #buildDistributions()} - */ - abstract void buildSearchableOptions(String targetModuleName, List modulesToIndex, List pathsToLicenses) - - /** - * Creates a copy of *ApplicationInfo.xml file with substituted __BUILD_NUMBER__ and __BUILD_DATE__ placeholders - *
- * todo[nik] this is temporary solution until code from layouts.gant files moved to the new builders. After that this method will - * be called inside {@link #buildDistributions()} - * @return path to the copied file - */ - abstract File patchApplicationInfo() - - /** - * Creates distribution for all operating systems from JAR files located at {@link BuildPaths#distAll} - */ - abstract void buildDistributions() - /** * Produces distributions for all operating systems from sources. This includes compiling required modules, packing their output into JAR * files accordingly to {@link ProductProperties#productLayout}, and creating distributions and installers for all OS. */ - abstract void compileModulesAndBuildDistributions() + abstract void buildDistributions() abstract void compileProjectAndTests(List includingTestsInModules) @@ -97,6 +75,6 @@ abstract class BuildTasks { ProductProperties productProperties = (ProductProperties) Class.forName(productPropertiesClassName).constructors[0].newInstance(projectHome) def context = BuildContext.createContext(binding.ant, binding.projectBuilder, binding.project, binding.global, "$projectHome/$communityHomeRelativePath", projectHome, productProperties, proprietaryBuildTools) - create(context).compileModulesAndBuildDistributions() + create(context).buildDistributions() } } \ No newline at end of file diff --git a/build/groovy/org/jetbrains/intellij/build/IdeaCommunityBuilder.groovy b/build/groovy/org/jetbrains/intellij/build/IdeaCommunityBuilder.groovy index 3e4038b752e7..c08dceb9ac7e 100644 --- a/build/groovy/org/jetbrains/intellij/build/IdeaCommunityBuilder.groovy +++ b/build/groovy/org/jetbrains/intellij/build/IdeaCommunityBuilder.groovy @@ -41,13 +41,13 @@ class IdeaCommunityBuilder { } void buildDistJars() { - BuildTasks.create(buildContext).compileModulesAndBuildDistributions() + BuildTasks.create(buildContext).buildDistributions() layoutAdditionalArtifacts() } void buildDistributions() { def tasks = BuildTasks.create(buildContext) - tasks.compileModulesAndBuildDistributions() + tasks.buildDistributions() layoutAdditionalArtifacts(true) tasks.buildUpdaterJar() } diff --git a/build/groovy/org/jetbrains/intellij/build/ProductProperties.groovy b/build/groovy/org/jetbrains/intellij/build/ProductProperties.groovy index 78d246f8b6b0..a31b68493867 100644 --- a/build/groovy/org/jetbrains/intellij/build/ProductProperties.groovy +++ b/build/groovy/org/jetbrains/intellij/build/ProductProperties.groovy @@ -164,8 +164,6 @@ abstract class ProductProperties { */ boolean enableYourkitAgentInEAP = false - List excludedPlugins = [] - /** * Specified additional modules (not included into the product layout) which need to be compiled when product is built. * todo[nik] get rid of this diff --git a/build/groovy/org/jetbrains/intellij/build/impl/BuildContextImpl.groovy b/build/groovy/org/jetbrains/intellij/build/impl/BuildContextImpl.groovy index 6238cc0a438c..369ea2a80951 100644 --- a/build/groovy/org/jetbrains/intellij/build/impl/BuildContextImpl.groovy +++ b/build/groovy/org/jetbrains/intellij/build/impl/BuildContextImpl.groovy @@ -311,9 +311,7 @@ class BuildContextImpl extends BuildContext { private boolean isJavaSupportedInProduct() { def productLayout = productProperties.productLayout - return productLayout.mainJarName == null || - //todo[nik] remove this condition later; currently build scripts for IDEA don't fully migrated to the new scheme - productLayout.includedPlatformModules.contains("execution-impl") + return productLayout.includedPlatformModules.contains("execution-impl") } @CompileDynamic diff --git a/build/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy b/build/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy index 9d5fcfb40b60..118e7c8f6699 100644 --- a/build/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy +++ b/build/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy @@ -91,12 +91,6 @@ class BuildTasksImpl extends BuildTasks { } } - @Override - void buildSearchableOptions(String targetModuleName, List modulesToIndex, List pathsToLicenses) { - buildSearchableOptions(new File(buildContext.projectBuilder.moduleOutput(buildContext.findRequiredModule(targetModuleName))), modulesToIndex, pathsToLicenses) - } - -//todo[nik] do we need 'cp' and 'jvmArgs' parameters? void buildSearchableOptions(File targetDirectory, List modulesToIndex, List pathsToLicenses) { buildContext.executeStep("Build searchable options index", BuildOptions.SEARCHABLE_OPTIONS_INDEX_STEP, { def javaRuntimeClasses = "${buildContext.projectBuilder.moduleOutput(buildContext.findModule("java-runtime"))}" @@ -187,7 +181,6 @@ idea.fatal.error.notification=disabled return propertiesFile } - @Override File patchApplicationInfo() { def sourceFile = buildContext.findApplicationInfoInSources() def targetFile = new File(buildContext.paths.temp, sourceFile.name) @@ -229,38 +222,6 @@ idea.fatal.error.notification=disabled src.filterLine { String it -> !it.contains('appender-ref ref="CONSOLE-WARN"') }.writeTo(dst.newWriter()).close() } - @Override - void buildDistributions() { - layoutShared() - - def propertiesFile = patchIdeaPropertiesFile() - List> tasks = [ - createDistributionForOsTask("win", { BuildContext context -> - context.windowsDistributionCustomizer?.with {new WindowsDistributionBuilder(context, it, propertiesFile)} - }), - createDistributionForOsTask("linux", { BuildContext context -> - context.linuxDistributionCustomizer?.with {new LinuxDistributionBuilder(context, it, propertiesFile)} - }), - createDistributionForOsTask("mac", { BuildContext context -> - context.macDistributionCustomizer?.with {new MacDistributionBuilder(context, it, propertiesFile)} - }) - ] - - List paths = runInParallel(tasks).findAll {it != null} - - if (buildContext.productProperties.buildCrossPlatformDistribution) { - if (paths.size() == 3) { - buildContext.executeStep("Build cross-platform distribution", BuildOptions.CROSS_PLATFORM_DISTRIBUTION_STEP) { - def crossPlatformBuilder = new CrossPlatformDistributionBuilder(buildContext) - crossPlatformBuilder.buildCrossPlatformZip(paths[0], paths[1], paths[2]) - } - } - else { - buildContext.messages.info("Skipping building cross-platform distribution because some OS-specific distributions were skipped") - } - } - } - private static BuildTaskRunnable createDistributionForOsTask(String taskName, Function factory) { new BuildTaskRunnable(taskName) { @Override @@ -279,8 +240,9 @@ idea.fatal.error.notification=disabled } @Override - void compileModulesAndBuildDistributions() { + void buildDistributions() { checkProductProperties() + def distributionJARsBuilder = new DistributionJARsBuilder(buildContext) compileModules(buildContext.productProperties.productLayout.includedPluginModules + distributionJARsBuilder.platformModules + buildContext.productProperties.additionalModulesToCompile, buildContext.productProperties.modulesToCompileTests) @@ -289,24 +251,56 @@ idea.fatal.error.notification=disabled distributionJARsBuilder.buildAdditionalArtifacts() } if (buildContext.productProperties.scrambleMainJar) { - if (buildContext.proprietaryBuildTools.scrambleTool != null) { - buildContext.proprietaryBuildTools.scrambleTool.scramble(buildContext.productProperties.productLayout.mainJarName, buildContext) + scramble() + } + + layoutShared() + + def propertiesFile = patchIdeaPropertiesFile() + List> tasks = [ + createDistributionForOsTask("win", { BuildContext context -> + context.windowsDistributionCustomizer?.with { new WindowsDistributionBuilder(context, it, propertiesFile) } + }), + createDistributionForOsTask("linux", { BuildContext context -> + context.linuxDistributionCustomizer?.with { new LinuxDistributionBuilder(context, it, propertiesFile) } + }), + createDistributionForOsTask("mac", { BuildContext context -> + context.macDistributionCustomizer?.with { new MacDistributionBuilder(context, it, propertiesFile) } + }) + ] + + List paths = runInParallel(tasks).findAll { it != null } + + if (buildContext.productProperties.buildCrossPlatformDistribution) { + if (paths.size() == 3) { + buildContext.executeStep("Build cross-platform distribution", BuildOptions.CROSS_PLATFORM_DISTRIBUTION_STEP) { + def crossPlatformBuilder = new CrossPlatformDistributionBuilder(buildContext) + crossPlatformBuilder.buildCrossPlatformZip(paths[0], paths[1], paths[2]) + } } else { - buildContext.messages.warning("Scrambling skipped: 'scrambleTool' isn't defined") + buildContext.messages.info("Skipping building cross-platform distribution because some OS-specific distributions were skipped") } - buildContext.ant.zip(destfile: "$buildContext.paths.artifacts/internalUtilities.zip") { - fileset(file: "$buildContext.paths.buildOutputRoot/internal/internalUtilities.jar") - fileset(dir: "$buildContext.paths.communityHome/lib") { - include(name: "junit-4*.jar") - include(name: "hamcrest-core-*.jar") - } - zipfileset(src: "$buildContext.paths.buildOutputRoot/internal/internalUtilities.jar") { - include(name: "*.xml") - } + } + } + + private void scramble() { + if (buildContext.proprietaryBuildTools.scrambleTool != null) { + buildContext.proprietaryBuildTools.scrambleTool.scramble(buildContext.productProperties.productLayout.mainJarName, buildContext) + } + else { + buildContext.messages.warning("Scrambling skipped: 'scrambleTool' isn't defined") + } + buildContext.ant.zip(destfile: "$buildContext.paths.artifacts/internalUtilities.zip") { + fileset(file: "$buildContext.paths.buildOutputRoot/internal/internalUtilities.jar") + fileset(dir: "$buildContext.paths.communityHome/lib") { + include(name: "junit-4*.jar") + include(name: "hamcrest-core-*.jar") + } + zipfileset(src: "$buildContext.paths.buildOutputRoot/internal/internalUtilities.jar") { + include(name: "*.xml") } } - buildDistributions() } private void checkProductProperties() { diff --git a/build/groovy/org/jetbrains/intellij/build/impl/CrossPlatformDistributionBuilder.groovy b/build/groovy/org/jetbrains/intellij/build/impl/CrossPlatformDistributionBuilder.groovy index 88a408519817..ba9494402aa4 100644 --- a/build/groovy/org/jetbrains/intellij/build/impl/CrossPlatformDistributionBuilder.groovy +++ b/build/groovy/org/jetbrains/intellij/build/impl/CrossPlatformDistributionBuilder.groovy @@ -59,7 +59,6 @@ class CrossPlatformDistributionBuilder { buildContext.ant.zip(zipfile: targetPath, duplicate: "fail") { fileset(dir: buildContext.paths.distAll) { exclude(name: "bin/idea.properties") - exclude(name: "lib/libpty/**") //todo[nik] this is temporary workaround until IDEA fully migrates to the new scheme } fileset(dir: zipDir) diff --git a/build/groovy/org/jetbrains/intellij/build/impl/WindowsDistributionBuilder.groovy b/build/groovy/org/jetbrains/intellij/build/impl/WindowsDistributionBuilder.groovy index d2e245facb84..55f3e4d1f2a9 100644 --- a/build/groovy/org/jetbrains/intellij/build/impl/WindowsDistributionBuilder.groovy +++ b/build/groovy/org/jetbrains/intellij/build/impl/WindowsDistributionBuilder.groovy @@ -101,7 +101,6 @@ class WindowsDistributionBuilder extends OsSpecificDistributionBuilder { private void generateScripts(String winDistPath) { String fullName = buildContext.applicationInfo.productName - //todo[nik] looks like names without .exe were also supported, do we need this? String vmOptionsFileName = "${buildContext.productProperties.baseFileName}%BITS%.exe" String classPath = "SET CLASS_PATH=%IDE_HOME%\\lib\\${buildContext.bootClassPathJarNames[0]}\n" diff --git a/build/scripts/layouts.gant b/build/scripts/layouts.gant index b9ce6b6cf519..93b097440515 100644 --- a/build/scripts/layouts.gant +++ b/build/scripts/layouts.gant @@ -16,6 +16,7 @@ import org.jetbrains.intellij.build.* +import org.jetbrains.intellij.build.impl.BuildTasksImpl import org.jetbrains.jps.util.JpsPathUtil import static org.jetbrains.jps.idea.IdeaProjectLoader.guessHome @@ -41,7 +42,7 @@ boolean setPluginAndIDEVersionInPluginXml() { } List getExcludedPlugins() { - return isDefined("productProperties") ? productProperties.excludedPlugins : [] + return [] } /** @@ -51,7 +52,7 @@ def layoutFull(BuildContext context) { binding.setVariable("productProperties", context.productProperties) String home = context.paths.communityHome String targetDirectory = context.paths.distAll - File patchedApplicationInfo = BuildTasks.create(context).patchApplicationInfo() + File patchedApplicationInfo = ((BuildTasksImpl)BuildTasks.create(context)).patchApplicationInfo() projectBuilder.stage("layout to $targetDirectory") List jpsCommonModules = ["jps-model-impl", "jps-model-serialization"] From 86eaaf22bfa1d6e4f46de11445b6682ba9ea7c41 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Mon, 17 Oct 2016 17:14:22 +0700 Subject: [PATCH 20/66] IDEA-160988 Add inspection to merge adjacent Stream API calls --- .../intention/impl/InlineStreamMapAction.java | 300 ++++++++++++++++++ .../inlineStreamMap/afterAsLongStreamMap.java | 9 + .../inlineStreamMap/afterBoxedForEach.java | 9 + .../inlineStreamMap/afterMapBoxed.java | 9 + .../inlineStreamMap/afterMapForEach.java | 8 + .../quickFix/inlineStreamMap/afterMapMap.java | 8 + .../inlineStreamMap/afterMapMapExpr.java | 8 + .../inlineStreamMap/afterMapMapMR.java | 8 + .../inlineStreamMap/afterMapMapToInt.java | 9 + .../afterMapToIntAsLongStream.java | 9 + .../inlineStreamMap/afterMapToIntFlatMap.java | 9 + .../beforeAsLongStreamMap.java | 9 + .../inlineStreamMap/beforeBoxedForEach.java | 9 + .../inlineStreamMap/beforeFlatMapForEach.java | 9 + .../inlineStreamMap/beforeMapBoxed.java | 9 + .../inlineStreamMap/beforeMapForEach.java | 8 + .../inlineStreamMap/beforeMapMap.java | 8 + .../inlineStreamMap/beforeMapMapExpr.java | 10 + .../inlineStreamMap/beforeMapMapMR.java | 8 + .../inlineStreamMap/beforeMapMapToInt.java | 9 + .../inlineStreamMap/beforeMapMapTwoExpr.java | 11 + .../beforeMapToIntAsLongStream.java | 9 + .../beforeMapToIntFlatMap.java | 9 + .../intention/InlineStreamMapActionTest.java | 28 ++ .../com/intellij/psi/CommonClassNames.java | 5 +- .../src/messages/CodeInsightBundle.properties | 3 + ...dRefCanBeReplacedWithLambdaInspection.java | 20 +- .../InlineStreamMapAction/after.java.template | 7 + .../before.java.template | 7 + .../InlineStreamMapAction/description.html | 7 + resources/src/META-INF/IdeaPlugin.xml | 4 + 31 files changed, 564 insertions(+), 11 deletions(-) create mode 100644 java/java-impl/src/com/intellij/codeInsight/intention/impl/InlineStreamMapAction.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterAsLongStreamMap.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterBoxedForEach.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapBoxed.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapForEach.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapMap.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapMapExpr.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapMapMR.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapMapToInt.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapToIntAsLongStream.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapToIntFlatMap.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeAsLongStreamMap.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeBoxedForEach.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeFlatMapForEach.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapBoxed.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapForEach.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapMap.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapMapExpr.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapMapMR.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapMapToInt.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapMapTwoExpr.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapToIntAsLongStream.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapToIntFlatMap.java create mode 100644 java/java-tests/testSrc/com/intellij/codeInsight/intention/InlineStreamMapActionTest.java create mode 100644 resources-en/src/intentionDescriptions/InlineStreamMapAction/after.java.template create mode 100644 resources-en/src/intentionDescriptions/InlineStreamMapAction/before.java.template create mode 100644 resources-en/src/intentionDescriptions/InlineStreamMapAction/description.html diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/InlineStreamMapAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/InlineStreamMapAction.java new file mode 100644 index 000000000000..7dbb7f3a3fce --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/InlineStreamMapAction.java @@ -0,0 +1,300 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.intention.impl; + +import com.intellij.codeInsight.CodeInsightBundle; +import com.intellij.codeInsight.FileModificationService; +import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.search.LocalSearchScope; +import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.refactoring.util.LambdaRefactoringUtil; +import com.intellij.util.IncorrectOperationException; +import com.siyeh.ig.psiutils.ParenthesesUtils; +import com.siyeh.ig.style.MethodRefCanBeReplacedWithLambdaInspection; +import one.util.streamex.StreamEx; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Set; + +public class InlineStreamMapAction extends PsiElementBaseIntentionAction { + private static final Logger LOG = Logger.getInstance(InlineStreamMapAction.class.getName()); + + private static final Set MAP_METHODS = + StreamEx.of("map", "mapToInt", "mapToLong", "mapToDouble", "mapToObj", "boxed", "asLongStream", "asDoubleStream").toSet(); + + private static final Set NEXT_METHODS = StreamEx + .of("flatMap", "flatMapToInt", "flatMapToLong", "flatMapToDouble", "forEach", "forEachOrdered", "anyMatch", "noneMatch", "allMatch") + .append(MAP_METHODS).toSet(); + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull final PsiElement element) { + if (!(element instanceof PsiIdentifier)) return false; + final PsiElement parent = element.getParent(); + if (!(parent instanceof PsiReferenceExpression)) return false; + final PsiElement gParent = parent.getParent(); + if (!(gParent instanceof PsiMethodCallExpression)) return false; + PsiMethodCallExpression curCall = (PsiMethodCallExpression)gParent; + if (!isMapCall(curCall)) return false; + PsiMethodCallExpression nextCall = getNextExpressionToMerge(curCall); + if(nextCall == null) return false; + String key = curCall.getArgumentList().getExpressions().length == 0 || nextCall.getArgumentList().getExpressions().length == 0 ? + "intention.inline.map.merge.text" : "intention.inline.map.inline.text"; + setText(CodeInsightBundle.message(key, element.getText(), nextCall.getMethodExpression().getReferenceName())); + return true; + } + + private static boolean isMapCall(@NotNull PsiMethodCallExpression methodCallExpression) { + String name = methodCallExpression.getMethodExpression().getReferenceName(); + if (name == null || !MAP_METHODS.contains(name)) return false; + + final PsiExpressionList argumentList = methodCallExpression.getArgumentList(); + final PsiExpression[] expressions = argumentList.getExpressions(); + if (!name.startsWith("map") && expressions.length == 0) return true; + if (expressions.length != 1) return false; + if (!isSupportedForConversion(expressions[0], true)) return false; + + final PsiMethod method = methodCallExpression.resolveMethod(); + if (method == null) return false; + final PsiClass containingClass = method.getContainingClass(); + return InheritanceUtil.isInheritor(containingClass, CommonClassNames.JAVA_UTIL_STREAM_BASE_STREAM); + } + + private static boolean isSupportedForConversion(PsiExpression expression, boolean requireExpressionLambda) { + if(expression instanceof PsiLambdaExpression) { + PsiLambdaExpression lambdaExpression = (PsiLambdaExpression)expression; + return lambdaExpression.getParameterList().getParametersCount() == 1 && + (!requireExpressionLambda || LambdaUtil.extractSingleExpressionFromBody(lambdaExpression.getBody()) != null); + } else if(expression instanceof PsiMethodReferenceExpression) { + PsiMethodReferenceExpression methodReference = (PsiMethodReferenceExpression)expression; + return !MethodRefCanBeReplacedWithLambdaInspection.isWithSideEffects(methodReference); + } + return false; + } + + @Nullable + private static PsiMethodCallExpression getNextExpressionToMerge(PsiMethodCallExpression methodCallExpression) { + PsiElement parent = methodCallExpression.getParent(); + if(!(parent instanceof PsiReferenceExpression)) return null; + PsiElement gParent = parent.getParent(); + if(!(gParent instanceof PsiMethodCallExpression)) return null; + String nextName = ((PsiReferenceExpression)parent).getReferenceName(); + PsiMethodCallExpression nextCall = (PsiMethodCallExpression)gParent; + if(nextName == null || !NEXT_METHODS.contains(nextName) || translateName(methodCallExpression, nextCall) == null) return null; + PsiExpressionList argumentList = (nextCall).getArgumentList(); + PsiExpression[] expressions = argumentList.getExpressions(); + if(expressions.length == 0) { + if (!nextName.equals("boxed") && !nextName.equals("asLongStream") && !nextName.equals("asDoubleStream")) return null; + return nextCall; + } + if (expressions.length != 1 || !isSupportedForConversion(expressions[0], false)) return null; + + return nextCall; + } + + /** + * Generate name of joint method call which combines two given calls + * + * @param prevCall previous call (assumed to be in MAP_METHODS) + * @param nextCall next call (assumed to be in NEXT_METHODS) + * @return a name of the resulting method + */ + @Nullable + private static String translateName(@NotNull PsiMethodCallExpression prevCall, @NotNull PsiMethodCallExpression nextCall) { + PsiMethod nextMethod = nextCall.resolveMethod(); + if (nextMethod == null) return null; + String nextName = nextMethod.getName(); + PsiMethod method = prevCall.resolveMethod(); + if (method == null) return null; + PsiClass prevClass = method.getContainingClass(); + if (prevClass == null) return null; + String prevClassName = prevClass.getQualifiedName(); + if (prevClassName == null) return null; + String prevName = method.getName(); + if (nextName.endsWith("Match") || nextName.startsWith("forEach")) return nextName; + if (nextName.equals("map")) { + return translateMap(prevName); + } + if (prevName.equals("map")) { + return translateMap(nextName); + } + if(MAP_METHODS.contains(nextName)) { + PsiType type = nextMethod.getReturnType(); + if(!(type instanceof PsiClassType)) return null; + PsiClass nextClass = ((PsiClassType)type).resolve(); + if(nextClass == null) return null; + String nextClassName = nextClass.getQualifiedName(); + if(nextClassName == null) return null; + if(prevClassName.equals(nextClassName)) return "map"; + switch(nextClassName) { + case CommonClassNames.JAVA_UTIL_STREAM_INT_STREAM: + return "mapToInt"; + case CommonClassNames.JAVA_UTIL_STREAM_LONG_STREAM: + return "mapToLong"; + case CommonClassNames.JAVA_UTIL_STREAM_DOUBLE_STREAM: + return "mapToDouble"; + case CommonClassNames.JAVA_UTIL_STREAM_STREAM: + return "mapToObj"; + default: + return null; + } + } + if(nextName.equals("flatMap") && prevClassName.equals(CommonClassNames.JAVA_UTIL_STREAM_STREAM)) { + String mapMethod = translateMap(prevName); + return "flatM"+mapMethod.substring(1); + } + return null; + } + + @NotNull + private static String translateMap(String nextMethod) { + switch (nextMethod) { + case "boxed": + return "mapToObj"; + case "asLongStream": + return "mapToLong"; + case "asDoubleStream": + return "mapToDouble"; + default: + return nextMethod; + } + } + + @Override + @NotNull + public String getFamilyName() { + return CodeInsightBundle.message("intention.inline.map.family"); + } + + @Override + public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException { + PsiMethodCallExpression mapCall = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class); + if(mapCall == null) return; + + PsiMethodCallExpression nextCall = getNextExpressionToMerge(mapCall); + if(nextCall == null) return; + + PsiExpression nextQualifier = nextCall.getMethodExpression().getQualifierExpression(); + if(nextQualifier == null) return; + + String newName = translateName(mapCall, nextCall); + if(newName == null) return; + + if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return; + + PsiLambdaExpression previousLambda = getLambda(mapCall); + + LOG.assertTrue(previousLambda != null); + PsiExpression previousBody = LambdaUtil.extractSingleExpressionFromBody(previousLambda.getBody()); + LOG.assertTrue(previousBody != null); + + PsiLambdaExpression lambda = getLambda(nextCall); + LOG.assertTrue(lambda != null); + + if(!lambda.isPhysical()) { + lambda = (PsiLambdaExpression)nextCall.getArgumentList().add(lambda); + } + PsiElement body = lambda.getBody(); + LOG.assertTrue(body != null); + + PsiParameter[] nextParameters = lambda.getParameterList().getParameters(); + LOG.assertTrue(nextParameters.length == 1); + PsiParameter[] prevParameters = previousLambda.getParameterList().getParameters(); + LOG.assertTrue(prevParameters.length == 1); + PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + for(PsiReference ref : ReferencesSearch.search(nextParameters[0], new LocalSearchScope(body)).findAll()) { + PsiElement e = ref.getElement(); + PsiExpression replacement = previousBody; + if (e.getParent() instanceof PsiExpression && + ParenthesesUtils.areParenthesesNeeded(previousBody, (PsiExpression)e.getParent(), false)) { + replacement = factory.createExpressionFromText("(a)", e); + PsiExpression parenthesized = ((PsiParenthesizedExpression)replacement).getExpression(); + LOG.assertTrue(parenthesized != null); + parenthesized.replace(previousBody); + } + e.replace(replacement); + } + nextParameters[0].replace(prevParameters[0]); + PsiElement nameElement = nextCall.getMethodExpression().getReferenceNameElement(); + if(nameElement != null && !nameElement.getText().equals(newName)) { + nameElement.replace(factory.createIdentifier(newName)); + } + PsiExpression prevQualifier = mapCall.getMethodExpression().getQualifierExpression(); + if(prevQualifier == null) { + nextQualifier.delete(); + } else { + nextQualifier.replace(prevQualifier); + } + CodeStyleManager.getInstance(project).reformat(lambda); + } + + @Nullable + private static PsiLambdaExpression getLambda(PsiMethodCallExpression call) { + PsiExpression[] expressions = call.getArgumentList().getExpressions(); + if(expressions.length == 1) { + PsiExpression expression = expressions[0]; + if(expression instanceof PsiLambdaExpression) return (PsiLambdaExpression)expression; + if(expression instanceof PsiMethodReferenceExpression) { + return LambdaRefactoringUtil.convertMethodReferenceToLambda((PsiMethodReferenceExpression)expression, false, true); + } + return null; + } + if(expressions.length != 0) return null; + PsiMethod method = call.resolveMethod(); + if(method == null) return null; + PsiClass containingClass = method.getContainingClass(); + if(containingClass == null) return null; + String className = containingClass.getQualifiedName(); + if(className == null) return null; + String varName; + String type; + switch (className) { + case CommonClassNames.JAVA_UTIL_STREAM_INT_STREAM: + varName = "i"; + type = CommonClassNames.JAVA_LANG_INTEGER; + break; + case CommonClassNames.JAVA_UTIL_STREAM_LONG_STREAM: + varName = "l"; + type = CommonClassNames.JAVA_LANG_LONG; + break; + case CommonClassNames.JAVA_UTIL_STREAM_DOUBLE_STREAM: + varName = "d"; + type = CommonClassNames.JAVA_LANG_DOUBLE; + break; + default: + return null; + } + varName = JavaCodeStyleManager.getInstance(call.getProject()).suggestUniqueVariableName(varName, call, true); + String expression; + if("boxed".equals(method.getName())) { + expression = varName+" -> ("+type+")"+varName; + } else if("asLongStream".equals(method.getName())) { + expression = varName+" -> (long)"+varName; + } else if("asDoubleStream".equals(method.getName())) { + expression = varName+" -> (double)"+varName; + } else return null; + PsiElementFactory factory = JavaPsiFacade.getElementFactory(call.getProject()); + return (PsiLambdaExpression)factory.createExpressionFromText(expression, call); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterAsLongStreamMap.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterAsLongStreamMap.java new file mode 100644 index 000000000000..54ce38e4db2a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterAsLongStreamMap.java @@ -0,0 +1,9 @@ +// "Merge 'asLongStream' call and 'map' call" "true" +import java.util.List; +import java.util.stream.IntStream; + +public class Main { + public static void test(List list) { + list.stream().map(cs -> (String)cs).mapToInt(String::length).mapToLong(i -> (long) i * 2).boxed().forEach(System.out::println); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterBoxedForEach.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterBoxedForEach.java new file mode 100644 index 000000000000..d2d8a82224de --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterBoxedForEach.java @@ -0,0 +1,9 @@ +// "Merge 'boxed' call and 'forEach' call" "true" +import java.util.List; +import java.util.stream.IntStream; + +public class Main { + public static void test(List list) { + list.stream().map(cs -> (String)cs).mapToInt(String::length).asLongStream().map(x -> x*2).forEach((l) -> System.out.println((Long) l)); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapBoxed.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapBoxed.java new file mode 100644 index 000000000000..6f11313cefb4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapBoxed.java @@ -0,0 +1,9 @@ +// "Merge 'map' call and 'boxed' call" "true" +import java.util.List; +import java.util.stream.IntStream; + +public class Main { + public static void test(List list) { + list.stream().map(cs -> (String)cs).mapToInt(String::length).asLongStream().mapToObj(x -> (Long) (x * 2)).forEach(System.out::println); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapForEach.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapForEach.java new file mode 100644 index 000000000000..26e0d0f5d22f --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapForEach.java @@ -0,0 +1,8 @@ +// "Inline 'map' body into the next 'forEach' call" "true" +import java.util.List; + +public class Main { + public static void test(List list) { + list.stream().map(cs -> cs.subSequence(1, 5)).forEach((charSequence) -> System.out.println(charSequence.length())); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapMap.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapMap.java new file mode 100644 index 000000000000..b8d02f286f7c --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapMap.java @@ -0,0 +1,8 @@ +// "Inline 'map' body into the next 'map' call" "true" +import java.util.List; + +public class Main { + public static void test(List list) { + list.stream().map(cs -> cs.subSequence(1, 5).length()).forEach(System.out::println); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapMapExpr.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapMapExpr.java new file mode 100644 index 000000000000..b8d02f286f7c --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapMapExpr.java @@ -0,0 +1,8 @@ +// "Inline 'map' body into the next 'map' call" "true" +import java.util.List; + +public class Main { + public static void test(List list) { + list.stream().map(cs -> cs.subSequence(1, 5).length()).forEach(System.out::println); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapMapMR.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapMapMR.java new file mode 100644 index 000000000000..c0fd79876619 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapMapMR.java @@ -0,0 +1,8 @@ +// "Inline 'map' body into the next 'map' call" "true" +import java.util.List; + +public class Main { + public static void test(List list) { + list.stream().map((cs) -> cs.subSequence(1, 5).length()).forEach(System.out::println); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapMapToInt.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapMapToInt.java new file mode 100644 index 000000000000..51f09ebafda9 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapMapToInt.java @@ -0,0 +1,9 @@ +// "Inline 'map' body into the next 'mapToInt' call" "true" +import java.util.List; +import java.util.stream.IntStream; + +public class Main { + public static void test(List list) { + list.stream().mapToInt((cs) -> ((String) cs).length()).asLongStream().map(x -> x*2).boxed().forEach(System.out::println); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapToIntAsLongStream.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapToIntAsLongStream.java new file mode 100644 index 000000000000..386a1c9a07ab --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapToIntAsLongStream.java @@ -0,0 +1,9 @@ +// "Merge 'mapToInt' call and 'asLongStream' call" "true" +import java.util.List; +import java.util.stream.IntStream; + +public class Main { + public static void test(List list) { + list.stream().map(cs -> (String)cs).mapToLong(s -> (long) s.length()).map(x -> x*2).boxed().forEach(System.out::println); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapToIntFlatMap.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapToIntFlatMap.java new file mode 100644 index 000000000000..84a406fbdd60 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/afterMapToIntFlatMap.java @@ -0,0 +1,9 @@ +// "Inline 'mapToInt' body into the next 'flatMap' call" "true" +import java.util.List; +import java.util.stream.IntStream; + +public class Main { + public static void test(List list) { + list.stream().map(cs -> (String)cs).flatMapToInt(s1 -> IntStream.range(0, s1.length())).forEach(System.out::println); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeAsLongStreamMap.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeAsLongStreamMap.java new file mode 100644 index 000000000000..b4da3d8be0b0 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeAsLongStreamMap.java @@ -0,0 +1,9 @@ +// "Merge 'asLongStream' call and 'map' call" "true" +import java.util.List; +import java.util.stream.IntStream; + +public class Main { + public static void test(List list) { + list.stream().map(cs -> (String)cs).mapToInt(String::length).asLongStream().map(x -> x*2).boxed().forEach(System.out::println); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeBoxedForEach.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeBoxedForEach.java new file mode 100644 index 000000000000..236777cd7dcf --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeBoxedForEach.java @@ -0,0 +1,9 @@ +// "Merge 'boxed' call and 'forEach' call" "true" +import java.util.List; +import java.util.stream.IntStream; + +public class Main { + public static void test(List list) { + list.stream().map(cs -> (String)cs).mapToInt(String::length).asLongStream().map(x -> x*2).boxed().forEach(System.out::println); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeFlatMapForEach.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeFlatMapForEach.java new file mode 100644 index 000000000000..bcfaff7752f8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeFlatMapForEach.java @@ -0,0 +1,9 @@ +// "Inline 'flatMap' body into the next 'forEach' call" "false" +import java.util.List; +import java.util.stream.IntStream; + +public class Main { + public static void test(List list) { + list.stream().map(cs -> (String)cs).mapToInt(String::length).flatMap(s -> IntStream.range(0, s)).forEach(System.out::println); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapBoxed.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapBoxed.java new file mode 100644 index 000000000000..abe11d46b724 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapBoxed.java @@ -0,0 +1,9 @@ +// "Merge 'map' call and 'boxed' call" "true" +import java.util.List; +import java.util.stream.IntStream; + +public class Main { + public static void test(List list) { + list.stream().map(cs -> (String)cs).mapToInt(String::length).asLongStream().map(x -> x*2).boxed().forEach(System.out::println); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapForEach.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapForEach.java new file mode 100644 index 000000000000..1f915897ee6e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapForEach.java @@ -0,0 +1,8 @@ +// "Inline 'map' body into the next 'forEach' call" "true" +import java.util.List; + +public class Main { + public static void test(List list) { + list.stream().map(cs -> cs.subSequence(1, 5)).map(CharSequence::length).forEach(System.out::println); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapMap.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapMap.java new file mode 100644 index 000000000000..231cc075b2db --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapMap.java @@ -0,0 +1,8 @@ +// "Inline 'map' body into the next 'map' call" "true" +import java.util.List; + +public class Main { + public static void test(List list) { + list.stream().map(cs -> cs.subSequence(1, 5)).map(cs -> cs.length()).forEach(System.out::println); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapMapExpr.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapMapExpr.java new file mode 100644 index 000000000000..c85780d55a36 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapMapExpr.java @@ -0,0 +1,10 @@ +// "Inline 'map' body into the next 'map' call" "true" +import java.util.List; + +public class Main { + public static void test(List list) { + list.stream().map(cs -> { + return cs.subSequence(1, 5); + }).map(cs -> cs.length()).forEach(System.out::println); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapMapMR.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapMapMR.java new file mode 100644 index 000000000000..a5651a40f0eb --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapMapMR.java @@ -0,0 +1,8 @@ +// "Inline 'map' body into the next 'map' call" "true" +import java.util.List; + +public class Main { + public static void test(List list) { + list.stream().map(cs -> cs.subSequence(1, 5)).map(CharSequence::length).forEach(System.out::println); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapMapToInt.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapMapToInt.java new file mode 100644 index 000000000000..62f169782191 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapMapToInt.java @@ -0,0 +1,9 @@ +// "Inline 'map' body into the next 'mapToInt' call" "true" +import java.util.List; +import java.util.stream.IntStream; + +public class Main { + public static void test(List list) { + list.stream().map(cs -> (String)cs).mapToInt(String::length).asLongStream().map(x -> x*2).boxed().forEach(System.out::println); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapMapTwoExpr.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapMapTwoExpr.java new file mode 100644 index 000000000000..ef277ee38ef9 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapMapTwoExpr.java @@ -0,0 +1,11 @@ +// "Inline 'map' body into the next 'map' call" "false" +import java.util.List; + +public class Main { + public static void test(List list) { + list.stream().map(cs -> { + cs = cs.subSequence(0, 10); + return cs.subSequence(1, 5); + }).map(cs -> cs.length()).forEach(System.out::println); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapToIntAsLongStream.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapToIntAsLongStream.java new file mode 100644 index 000000000000..a95324d26a1f --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapToIntAsLongStream.java @@ -0,0 +1,9 @@ +// "Merge 'mapToInt' call and 'asLongStream' call" "true" +import java.util.List; +import java.util.stream.IntStream; + +public class Main { + public static void test(List list) { + list.stream().map(cs -> (String)cs).mapToInt(String::length).asLongStream().map(x -> x*2).boxed().forEach(System.out::println); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapToIntFlatMap.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapToIntFlatMap.java new file mode 100644 index 000000000000..3bd66dd69a3b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap/beforeMapToIntFlatMap.java @@ -0,0 +1,9 @@ +// "Inline 'mapToInt' body into the next 'flatMap' call" "true" +import java.util.List; +import java.util.stream.IntStream; + +public class Main { + public static void test(List list) { + list.stream().map(cs -> (String)cs).mapToInt(String::length).flatMap(s -> IntStream.range(0, s)).forEach(System.out::println); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/intention/InlineStreamMapActionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/intention/InlineStreamMapActionTest.java new file mode 100644 index 000000000000..a96409ec6b74 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/intention/InlineStreamMapActionTest.java @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.intention; + +import com.intellij.codeInsight.daemon.LightIntentionActionTestCase; + +public class InlineStreamMapActionTest extends LightIntentionActionTestCase { + + public void test() throws Exception { doAllTests(); } + + @Override + protected String getBasePath() { + return "/codeInsight/daemonCodeAnalyzer/quickFix/inlineStreamMap"; + } +} diff --git a/platform/core-api/src/com/intellij/psi/CommonClassNames.java b/platform/core-api/src/com/intellij/psi/CommonClassNames.java index f2da8bc222e4..f1ff9187bee8 100644 --- a/platform/core-api/src/com/intellij/psi/CommonClassNames.java +++ b/platform/core-api/src/com/intellij/psi/CommonClassNames.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -105,6 +105,9 @@ public interface CommonClassNames { @NonNls String JAVA_UTIL_STREAM_BASE_STREAM = "java.util.stream.BaseStream"; @NonNls String JAVA_UTIL_STREAM_STREAM = "java.util.stream.Stream"; + @NonNls String JAVA_UTIL_STREAM_INT_STREAM = "java.util.stream.IntStream"; + @NonNls String JAVA_UTIL_STREAM_LONG_STREAM = "java.util.stream.LongStream"; + @NonNls String JAVA_UTIL_STREAM_DOUBLE_STREAM = "java.util.stream.DoubleStream"; @NonNls String JAVA_UTIL_STREAM_COLLECTORS = "java.util.stream.Collectors"; @NonNls String JAVA_UTIL_FUNCTION_PREDICATE = "java.util.function.Predicate"; diff --git a/platform/platform-resources-en/src/messages/CodeInsightBundle.properties b/platform/platform-resources-en/src/messages/CodeInsightBundle.properties index af978ba4e90c..534ffab3da98 100644 --- a/platform/platform-resources-en/src/messages/CodeInsightBundle.properties +++ b/platform/platform-resources-en/src/messages/CodeInsightBundle.properties @@ -174,6 +174,9 @@ intention.split.filter.text=Split into filter's chain intention.split.filter.family=Split filter intention.merge.filter.text=Merge filter's chain intention.merge.filter.family=Merge filters +intention.inline.map.inline.text=Inline ''{0}'' body into the next ''{1}'' call +intention.inline.map.merge.text=Merge ''{0}'' call and ''{1}'' call +intention.inline.map.family=Inline stream mapping method intention.introduce.variable.text=Introduce local variable intention.encapsulate.field.text=Encapsulate field intention.implement.abstract.method.family=Implement Abstract Method diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/style/MethodRefCanBeReplacedWithLambdaInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/style/MethodRefCanBeReplacedWithLambdaInspection.java index bb34d5cb9e9b..4d755dcf77d1 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/style/MethodRefCanBeReplacedWithLambdaInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/style/MethodRefCanBeReplacedWithLambdaInspection.java @@ -74,6 +74,16 @@ public class MethodRefCanBeReplacedWithLambdaInspection extends BaseInspection { return null; } + public static boolean isWithSideEffects(PsiMethodReferenceExpression methodReferenceExpression) { + final PsiExpression qualifierExpression = methodReferenceExpression.getQualifierExpression(); + if (qualifierExpression != null) { + final List sideEffects = new ArrayList<>(); + SideEffectChecker.checkSideEffects(qualifierExpression, sideEffects); + return !sideEffects.isEmpty(); + } + return false; + } + private static class MethodRefToLambdaVisitor extends BaseInspectionVisitor { @Override public void visitMethodReferenceExpression(PsiMethodReferenceExpression methodReferenceExpression) { @@ -91,16 +101,6 @@ public class MethodRefCanBeReplacedWithLambdaInspection extends BaseInspection { if (onTheFly || ApplicationManager.getApplication().isUnitTestMode()) return SideEffectsMethodRefToLambdaFix::new; return null; } - - private static boolean isWithSideEffects(PsiMethodReferenceExpression methodReferenceExpression) { - final PsiExpression qualifierExpression = methodReferenceExpression.getQualifierExpression(); - if (qualifierExpression != null) { - final List sideEffects = new ArrayList<>(); - SideEffectChecker.checkSideEffects(qualifierExpression, sideEffects); - return !sideEffects.isEmpty(); - } - return false; - } } private static class MethodRefToLambdaFix extends InspectionGadgetsFix { diff --git a/resources-en/src/intentionDescriptions/InlineStreamMapAction/after.java.template b/resources-en/src/intentionDescriptions/InlineStreamMapAction/after.java.template new file mode 100644 index 000000000000..96a6374ab244 --- /dev/null +++ b/resources-en/src/intentionDescriptions/InlineStreamMapAction/after.java.template @@ -0,0 +1,7 @@ +import java.util.List; + +public class X { + boolean test(List list) { + return list.stream().anyMatch(s -> s.toLowerCase().equals("test")); + } +} \ No newline at end of file diff --git a/resources-en/src/intentionDescriptions/InlineStreamMapAction/before.java.template b/resources-en/src/intentionDescriptions/InlineStreamMapAction/before.java.template new file mode 100644 index 000000000000..7c507d8efd91 --- /dev/null +++ b/resources-en/src/intentionDescriptions/InlineStreamMapAction/before.java.template @@ -0,0 +1,7 @@ +import java.util.List; + +public class X { + boolean test(List list) { + return list.stream().map(s -> s.toLowerCase()).anyMatch(s -> s.equals("test")); + } +} \ No newline at end of file diff --git a/resources-en/src/intentionDescriptions/InlineStreamMapAction/description.html b/resources-en/src/intentionDescriptions/InlineStreamMapAction/description.html new file mode 100644 index 000000000000..60ad2cec8290 --- /dev/null +++ b/resources-en/src/intentionDescriptions/InlineStreamMapAction/description.html @@ -0,0 +1,7 @@ + + +This intention inlines Stream.map() and similar calls into the next stream operation when possible. + +As during normal variable inline this intention may change the code semantics if mapping result is used more than once and has side-effects. + + \ No newline at end of file diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index 21853176ce56..98d0f7f154f8 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -873,6 +873,10 @@ com.intellij.codeInsight.intention.impl.MergeFilterChainAction Java/Streams + + com.intellij.codeInsight.intention.impl.InlineStreamMapAction + Java/Streams + com.intellij.codeInsight.intention.impl.InvertIfConditionAction Java/Control Flow From e437019267a5cbb519cec5b979f1228349c41300 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Mon, 17 Oct 2016 13:25:03 +0300 Subject: [PATCH 21/66] inspection settings: do not re-apply settings when panel was not shown --- .../codeInspection/ui/SingleInspectionProfilePanel.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java index e2c8abe2e6c3..b28890076aa8 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java @@ -74,7 +74,6 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.swing.FocusManager; import javax.swing.*; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeExpansionListener; @@ -84,8 +83,6 @@ import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import java.awt.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; import java.io.IOException; import java.io.StringReader; import java.util.*; @@ -1095,6 +1092,7 @@ public class SingleInspectionProfilePanel extends JPanel { } public boolean isModified() { + if (myTreeTable == null) return false; if (myModified) return true; if (myProfile.isChanged()) return true; if (myProfile.getParentProfile().isProjectLevel() != myProfile.isProjectLevel()) return true; From 03354ff73dc304cdb7a0fe840527049d5d31e71c Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Mon, 17 Oct 2016 17:30:22 +0700 Subject: [PATCH 22/66] AnonymousCanBeLambdaInspection#isLambdaForm, minor cleanup (IDEA-CR-14331) --- .../AnonymousCanBeLambdaInspection.java | 20 +++++-------- ...onymousHasLambdaAlternativeInspection.java | 28 +++++++++---------- 2 files changed, 20 insertions(+), 28 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java index 3c8cc9e04919..0379bfaa2990 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java @@ -195,9 +195,10 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection return canBeConvertedToLambda(aClass, acceptParameterizedFunctionTypes, true, ignoredRuntimeAnnotations); } - public static boolean isClassAndMethodSuitableForConversion(PsiAnonymousClass aClass, - PsiMethod method, - Set ignoredRuntimeAnnotations) { + public static boolean isLambdaForm(PsiAnonymousClass aClass, Set ignoredRuntimeAnnotations) { + PsiMethod[] methods = aClass.getMethods(); + if(methods.length != 1) return false; + PsiMethod method = methods[0]; return aClass.getFields().length == 0 && aClass.getInnerClasses().length == 0 && aClass.getInitializers().length == 0 && @@ -222,16 +223,9 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection } final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult); if (interfaceMethod != null && (acceptParameterizedFunctionTypes || !interfaceMethod.hasTypeParameters())) { - final PsiMethod[] methods = aClass.getMethods(); - if (methods.length == 1) { - final PsiMethod method = methods[0]; - if (isClassAndMethodSuitableForConversion(aClass, method, ignoredRuntimeAnnotations)) { - final PsiType inferredType = getInferredType(aClass, method); - if (inferredType == null) { - return false; - } - return true; - } + if (isLambdaForm(aClass, ignoredRuntimeAnnotations)) { + final PsiMethod method = aClass.getMethods()[0]; + return getInferredType(aClass, method) != null; } } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousHasLambdaAlternativeInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousHasLambdaAlternativeInspection.java index b259c5dc956d..ef5ad5741e6f 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousHasLambdaAlternativeInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousHasLambdaAlternativeInspection.java @@ -66,21 +66,19 @@ public class AnonymousHasLambdaAlternativeInspection extends BaseJavaBatchLocalI @Override public void visitAnonymousClass(final PsiAnonymousClass aClass) { super.visitAnonymousClass(aClass); - PsiMethod[] methods = aClass.getMethods(); - if(methods.length == 1) { - PsiMethod method = methods[0]; - PsiExpressionList argumentList = aClass.getArgumentList(); - if (AnonymousCanBeLambdaInspection.isClassAndMethodSuitableForConversion(aClass, method, Collections.emptySet()) && - argumentList != null && argumentList.getExpressions().length == 0) { - PsiClassType type = aClass.getBaseClassType(); - AnonymousLambdaAlternative alternative = getAlternative(type.resolve(), method); - if(alternative != null) { - final PsiElement lBrace = aClass.getLBrace(); - LOG.assertTrue(lBrace != null); - final TextRange rangeInElement = new TextRange(0, lBrace.getStartOffsetInParent() + aClass.getStartOffsetInParent() - 1); - holder.registerProblem(aClass.getParent(), "Anonymous #ref #loc can be replaced with "+alternative.myReplacementMessage, - ProblemHighlightType.LIKE_UNUSED_SYMBOL, rangeInElement, new ReplaceWithLambdaAlternativeFix(alternative)); - } + PsiExpressionList argumentList = aClass.getArgumentList(); + if (AnonymousCanBeLambdaInspection.isLambdaForm(aClass, Collections.emptySet()) && + argumentList != null && + argumentList.getExpressions().length == 0) { + PsiMethod method = aClass.getMethods()[0]; + PsiClassType type = aClass.getBaseClassType(); + AnonymousLambdaAlternative alternative = getAlternative(type.resolve(), method); + if(alternative != null) { + final PsiElement lBrace = aClass.getLBrace(); + LOG.assertTrue(lBrace != null); + final TextRange rangeInElement = new TextRange(0, lBrace.getStartOffsetInParent() + aClass.getStartOffsetInParent() - 1); + holder.registerProblem(aClass.getParent(), "Anonymous #ref #loc can be replaced with "+alternative.myReplacementMessage, + ProblemHighlightType.LIKE_UNUSED_SYMBOL, rangeInElement, new ReplaceWithLambdaAlternativeFix(alternative)); } } } From c4596d19b597c5025d980c46c579c0eae7ecd2fc Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Mon, 17 Oct 2016 12:23:31 +0200 Subject: [PATCH 23/66] getDefaultProfile -> getBaseProfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is not default — it is base. Do not add default default profile if there is at least on bundled/custom global profile — as before. --- .../ex/InspectionProfileTest.java | 6 +++--- .../codeInspection/ex/InspectionSchemeTest.kt | 8 +++++++- .../ex/InspectionProfileImpl.java | 6 +++--- .../ProjectInspectionProfileManager.kt | 4 ++-- .../ApplicationInspectionProfileManager.java | 18 +++--------------- .../com/intellij/testFramework/inspections.kt | 2 +- 6 files changed, 19 insertions(+), 25 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionProfileTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionProfileTest.java index 922d706384e2..975f15ccfe43 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionProfileTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionProfileTest.java @@ -82,7 +82,7 @@ public class InspectionProfileTest extends LightIdeaTestCase { } private static InspectionProfileImpl createProfile() { - return new InspectionProfileImpl(PROFILE, InspectionToolRegistrar.getInstance(), InspectionProfileManager.getInstance(), InspectionProfileImpl.getDefaultProfile(), null); + return new InspectionProfileImpl(PROFILE, InspectionToolRegistrar.getInstance(), InspectionProfileManager.getInstance(), InspectionProfileImpl.getBaseProfile(), null); } private static InspectionProfileImpl createProfile(@NotNull InspectionProfileImpl base) { return new InspectionProfileImpl(PROFILE, InspectionToolRegistrar.getInstance(), InspectionProfileManager.getInstance(), base, null); @@ -98,7 +98,7 @@ public class InspectionProfileTest extends LightIdeaTestCase { //normally on open project profile wrappers are init for both managers profileManager.updateProfile(localProfile); InspectionProfileImpl profile = new InspectionProfileImpl(PROFILE, InspectionToolRegistrar.getInstance(), projectProfileManager, - InspectionProfileImpl.getDefaultProfile(), null); + InspectionProfileImpl.getBaseProfile(), null); projectProfileManager.updateProfile(profile); projectProfileManager.setRootProfile(profile.getName()); @@ -579,7 +579,7 @@ public class InspectionProfileTest extends LightIdeaTestCase { } public void testDoNotInstantiateOnSave() throws Exception { - InspectionProfileImpl profile = new InspectionProfileImpl("profile", InspectionToolRegistrar.getInstance(), InspectionProfileManager.getInstance(), InspectionProfileImpl.getDefaultProfile(), null); + InspectionProfileImpl profile = new InspectionProfileImpl("profile", InspectionToolRegistrar.getInstance(), InspectionProfileManager.getInstance(), InspectionProfileImpl.getBaseProfile(), null); assertEquals(0, countInitializedTools(profile)); InspectionToolWrapper[] toolWrappers = profile.getInspectionTools(null); assertTrue(toolWrappers.length > 0); diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionSchemeTest.kt b/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionSchemeTest.kt index 974c9661cba7..58880e3e19fc 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionSchemeTest.kt +++ b/java/java-tests/testSrc/com/intellij/codeInspection/ex/InspectionSchemeTest.kt @@ -51,7 +51,7 @@ class InspectionSchemeTest { profileManager.forceInitProfiles(true) profileManager.initProfiles() - assertThat(profileManager.profiles).hasSize(2) + assertThat(profileManager.profiles).hasSize(1) val scheme = profileManager.profiles.first() as InspectionProfileImpl assertThat(scheme.name).isEqualTo("Bar") @@ -61,5 +61,11 @@ class InspectionSchemeTest { assertThat(schemeFile.readText()).isEqualTo(schemeData) profileManager.profiles + + schemeManagerFactory.process { + it.reload() + } + + assertThat(profileManager.profiles).hasSize(1) } } diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java index 5f6ac4764dba..0556319665dd 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java @@ -101,7 +101,7 @@ public class InspectionProfileImpl extends ProfileEx implements ModifiableModel, public InspectionProfileImpl(@NotNull String profileName, @NotNull InspectionToolRegistrar registrar, @NotNull ProfileManager profileManager) { - this(profileName, registrar, profileManager, getDefaultProfile(), null); + this(profileName, registrar, profileManager, getBaseProfile(), null); } public InspectionProfileImpl(@NotNull @NonNls String profileName) { @@ -125,7 +125,7 @@ public class InspectionProfileImpl extends ProfileEx implements ModifiableModel, @NotNull InspectionToolRegistrar registrar, @NotNull ProfileManager profileManager, @Nullable SchemeDataHolder dataHolder) { - this(profileName, registrar, profileManager, getDefaultProfile(), dataHolder); + this(profileName, registrar, profileManager, getBaseProfile(), dataHolder); } @NotNull @@ -163,7 +163,7 @@ public class InspectionProfileImpl extends ProfileEx implements ModifiableModel, } @NotNull - public static InspectionProfileImpl getDefaultProfile() { + public static InspectionProfileImpl getBaseProfile() { return InspectionProfileImplHolder.DEFAULT_PROFILE; } diff --git a/platform/analysis-impl/src/com/intellij/profile/codeInspection/ProjectInspectionProfileManager.kt b/platform/analysis-impl/src/com/intellij/profile/codeInspection/ProjectInspectionProfileManager.kt index f7f31fc78e52..f6a759bfbe13 100644 --- a/platform/analysis-impl/src/com/intellij/profile/codeInspection/ProjectInspectionProfileManager.kt +++ b/platform/analysis-impl/src/com/intellij/profile/codeInspection/ProjectInspectionProfileManager.kt @@ -98,7 +98,7 @@ class ProjectInspectionProfileManager(val project: Project, attributeProvider: Function, isBundled: Boolean): InspectionProfileImpl { val profile = InspectionProfileImpl(name, InspectionToolRegistrar.getInstance(), this@ProjectInspectionProfileManager, - InspectionProfileImpl.getDefaultProfile(), dataHolder) + InspectionProfileImpl.getBaseProfile(), dataHolder) profile.isProjectLevel = true return profile } @@ -306,7 +306,7 @@ class ProjectInspectionProfileManager(val project: Project, currentScheme = schemeManager.allSchemes.firstOrNull() if (currentScheme == null) { currentScheme = InspectionProfileImpl(PROJECT_DEFAULT_PROFILE_NAME, InspectionToolRegistrar.getInstance(), this, - InspectionProfileImpl.getDefaultProfile(), null) + InspectionProfileImpl.getBaseProfile(), null) currentScheme.copyFrom(applicationProfileManager.currentProfile as ProfileEx) currentScheme.isProjectLevel = true currentScheme.name = PROJECT_DEFAULT_PROFILE_NAME diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/ApplicationInspectionProfileManager.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/ApplicationInspectionProfileManager.java index 91b13c8643d4..cdb88bebf4aa 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/ApplicationInspectionProfileManager.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/ApplicationInspectionProfileManager.java @@ -60,8 +60,6 @@ import java.util.Collections; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; -import static com.intellij.codeInspection.ex.InspectionProfileImpl.getDefaultProfile; - @State( name = "InspectionProfileManager", storages = { @@ -92,7 +90,7 @@ public class ApplicationInspectionProfileManager extends BaseInspectionProfileMa mySchemeManager = schemeManagerFactory.create(INSPECTION_DIR, new InspectionProfileProcessor() { @NotNull @Override - public String getName(@NotNull Function attributeProvider, String fileNameWithoutExtension) { + public String getName(@NotNull Function attributeProvider, @NotNull String fileNameWithoutExtension) { return fileNameWithoutExtension; } @@ -170,13 +168,9 @@ public class ApplicationInspectionProfileManager extends BaseInspectionProfileMa loadBundledSchemes(); mySchemeManager.loadSchemes(); - createDefaultProfile(); - } - private void createDefaultProfile() { - final InspectionProfileImpl oldDefault = mySchemeManager.findSchemeByName(InspectionProfileImpl.DEFAULT_PROFILE_NAME); - if (oldDefault == null || !oldDefault.isProfileLocked()) { - getSchemeManager().addScheme(createSampleProfile(InspectionProfileImpl.DEFAULT_PROFILE_NAME, getDefaultProfile())); + if (mySchemeManager.isEmpty()) { + mySchemeManager.addScheme(createSampleProfile(InspectionProfileImpl.DEFAULT_PROFILE_NAME, InspectionProfileImpl.getBaseProfile())); } } @@ -239,12 +233,6 @@ public class ApplicationInspectionProfileManager extends BaseInspectionProfileMa return new InspectionProfileConvertor(this); } - @SuppressWarnings("unused") - @Deprecated - public InspectionProfileImpl createProfile() { - return createSampleProfile(InspectionProfileImpl.DEFAULT_PROFILE_NAME, getDefaultProfile()); - } - @Override public void setRootProfile(@Nullable String profileName) { mySchemeManager.setCurrentSchemeName(profileName); diff --git a/platform/testFramework/src/com/intellij/testFramework/inspections.kt b/platform/testFramework/src/com/intellij/testFramework/inspections.kt index f557aadd5720..d466de5c7871 100644 --- a/platform/testFramework/src/com/intellij/testFramework/inspections.kt +++ b/platform/testFramework/src/com/intellij/testFramework/inspections.kt @@ -40,7 +40,7 @@ fun configureInspections(tools: Array, Disposer.register(parentDisposable, Disposable { profileManager.deleteProfile(profile) profileManager.setCurrentProfile(null) - clearAllToolsIn(InspectionProfileImpl.getDefaultProfile()) + clearAllToolsIn(InspectionProfileImpl.getBaseProfile()) }) profileManager.addProfile(profile) From c80fd29dd8a9f11e91327a1aa63e7dc10c7d09af Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Mon, 17 Oct 2016 12:27:21 +0200 Subject: [PATCH 24/66] =?UTF-8?q?EclipseImlTest=20=E2=80=94=20fix=20test?= =?UTF-8?q?=20data?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iml/allProps/expected/expected.iml | 218 +++++++++--------- .../idea/eclipse/EclipseIml2ModulesTest.java | 3 +- .../idea/eclipse/EclipseImlTest.java | 13 +- 3 files changed, 113 insertions(+), 121 deletions(-) diff --git a/plugins/eclipse/testData/iml/allProps/expected/expected.iml b/plugins/eclipse/testData/iml/allProps/expected/expected.iml index 73c02983b9bd..dfd2fa8ea05d 100644 --- a/plugins/eclipse/testData/iml/allProps/expected/expected.iml +++ b/plugins/eclipse/testData/iml/allProps/expected/expected.iml @@ -1,111 +1,109 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/eclipse/testSources/org/jetbrains/idea/eclipse/EclipseIml2ModulesTest.java b/plugins/eclipse/testSources/org/jetbrains/idea/eclipse/EclipseIml2ModulesTest.java index 505d9b606840..d9d8bd451470 100644 --- a/plugins/eclipse/testSources/org/jetbrains/idea/eclipse/EclipseIml2ModulesTest.java +++ b/plugins/eclipse/testSources/org/jetbrains/idea/eclipse/EclipseIml2ModulesTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,6 @@ package org.jetbrains.idea.eclipse; import org.jetbrains.annotations.NotNull; public class EclipseIml2ModulesTest extends Eclipse2ModulesTest { - @Override protected String getTestPath() { return "iml"; diff --git a/plugins/eclipse/testSources/org/jetbrains/idea/eclipse/EclipseImlTest.java b/plugins/eclipse/testSources/org/jetbrains/idea/eclipse/EclipseImlTest.java index 9c880689ec62..c7696a6b3732 100644 --- a/plugins/eclipse/testSources/org/jetbrains/idea/eclipse/EclipseImlTest.java +++ b/plugins/eclipse/testSources/org/jetbrains/idea/eclipse/EclipseImlTest.java @@ -79,14 +79,9 @@ public class EclipseImlTest extends IdeaTestCase { String communityLib = FileUtil.toSystemIndependentName(PathManagerEx.findFileUnderCommunityHome("lib").getAbsolutePath()); fileText = fileText.replaceAll("\\$" + JUNIT + "\\$", communityLib); final Element classpathElement = JDOMUtil.loadDocument(fileText).getRootElement(); - final Module module = WriteCommandAction.runWriteCommandAction(null, new Computable() { - @Override - public Module compute() { - return ModuleManager.getInstance(project) - .newModule(new File(path) + File.separator + EclipseProjectFinder - .findProjectName(path) + IdeaXml.IML_EXT, StdModuleTypes.JAVA.getId()); - } - }); + final Module module = WriteCommandAction.runWriteCommandAction(null, (Computable)() -> ModuleManager.getInstance(project) + .newModule(new File(path) + File.separator + EclipseProjectFinder + .findProjectName(path) + IdeaXml.IML_EXT, StdModuleTypes.JAVA.getId())); final ModifiableRootModel rootModel = ModuleRootManager.getInstance(module).getModifiableModel(); EclipseClasspathReader classpathReader = new EclipseClasspathReader(path, project, null); classpathReader.init(rootModel); @@ -101,7 +96,7 @@ public class EclipseImlTest extends IdeaTestCase { PathMacroManager.getInstance(project).collapsePaths(actualImlElement); PathMacros.getInstance().removeMacro(JUNIT); - assertThat(actualImlElement).isEqualTo(FileUtil.loadFile(new File(project.getBaseDir().getPath() + "/expected", "expected.iml"))); + assertThat(actualImlElement).isEqualTo(new File(project.getBaseDir().getPath() + "/expected", "expected.iml")); } public void testWorkspaceOnly() throws Exception { From d6d959af71318bb962f4daf1479f043a812bbadb Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Mon, 17 Oct 2016 11:44:24 +0200 Subject: [PATCH 25/66] Cleanup (obsolete reflection) --- .../com/intellij/openapi/MnemonicWrapper.java | 28 ++--------- .../intellij/ide/dnd/TransferableList.java | 19 ++----- .../openapi/actionSystem/impl/ActionMenu.java | 7 +-- .../actionSystem/impl/ActionMenuItem.java | 7 +-- .../src/com/intellij/ui/KeyStrokeAdapter.java | 23 +-------- .../intellij/ui/plaf/gtk/GtkMenuItemUI.java | 31 ++++++------ .../com/intellij/ui/plaf/gtk/GtkMenuUI.java | 22 ++++----- .../intellij/ui/plaf/gtk/GtkPaintingUtil.java | 49 +++---------------- .../com/intellij/ui/plaf/gtk/IconWrapper.java | 15 +++--- .../com/intellij/ui/popup/MovablePopup.java | 21 +------- 10 files changed, 56 insertions(+), 166 deletions(-) diff --git a/platform/platform-api/src/com/intellij/openapi/MnemonicWrapper.java b/platform/platform-api/src/com/intellij/openapi/MnemonicWrapper.java index 2c78265d1726..e1bb409c0904 100644 --- a/platform/platform-api/src/com/intellij/openapi/MnemonicWrapper.java +++ b/platform/platform-api/src/com/intellij/openapi/MnemonicWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,19 +15,17 @@ */ package com.intellij.openapi; -import com.intellij.Patches; import com.intellij.ide.ui.UISettings; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.registry.Registry; import com.intellij.util.ui.UIUtil; import javax.swing.*; -import java.awt.Component; +import java.awt.*; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.lang.reflect.Method; /** * @author Sergey.Malenkov @@ -136,7 +134,7 @@ abstract class MnemonicWrapper implements Runnable, Propert sb.append(ch); } else if (i + 1 < length) { - code = getExtendedKeyCodeForChar(text.charAt(i + 1)); + code = KeyEvent.getExtendedKeyCodeForChar((int)text.charAt(i + 1)); index = sb.length(); } } @@ -200,26 +198,6 @@ abstract class MnemonicWrapper implements Runnable, Propert return stroke; } - // TODO: HACK because of Java7 required: - // replace later with KeyEvent.getExtendedKeyCodeForChar(ch) - private static int getExtendedKeyCodeForChar(int ch) { - //noinspection ConstantConditions - assert Patches.USE_REFLECTION_TO_ACCESS_JDK7; - try { - Method method = KeyEvent.class.getMethod("getExtendedKeyCodeForChar", int.class); - if (!method.isAccessible()) { - method.setAccessible(true); - } - return (Integer)method.invoke(KeyEvent.class, ch); - } - catch (Exception exception) { - if (ch >= 'a' && ch <= 'z') { - ch -= ('a' - 'A'); - } - return ch; - } - } - private static class MenuWrapper extends ButtonWrapper { private MenuWrapper(AbstractButton component) { super(component); diff --git a/platform/platform-impl/src/com/intellij/ide/dnd/TransferableList.java b/platform/platform-impl/src/com/intellij/ide/dnd/TransferableList.java index 1de9a4377ef7..e79b3f4469ca 100644 --- a/platform/platform-impl/src/com/intellij/ide/dnd/TransferableList.java +++ b/platform/platform-impl/src/com/intellij/ide/dnd/TransferableList.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ */ package com.intellij.ide.dnd; -import com.intellij.Patches; import org.jetbrains.annotations.NotNull; import java.awt.datatransfer.DataFlavor; @@ -31,20 +30,8 @@ import java.util.List; */ abstract public class TransferableList implements Transferable { private static final DataFlavor LIST_DATA_FLAVOR = new DataFlavor(List.class, "Transferable List"); - private static final DataFlavor ALL_HTML_DATA_FLAVOR = initHtmlDataFlavor("all"); // JDK7: DataFlavor.allHtmlFlavor - private static final DataFlavor PART_HTML_DATA_FLAVOR = initHtmlDataFlavor("fragment"); // JDK7: DataFlavor.fragmentHtmlFlavor - - private static DataFlavor initHtmlDataFlavor(String type) { - // some constants were added in JDK 7 without @since 1.7 tag - // http://bugs.openjdk.java.net/browse/JDK-7075105 - assert Patches.USE_REFLECTION_TO_ACCESS_JDK7; - try { - return new DataFlavor("text/html; class=java.lang.String;document=" + type + ";charset=Unicode"); - } - catch (Exception exception) { - return null; - } - } + private static final DataFlavor ALL_HTML_DATA_FLAVOR = DataFlavor.allHtmlFlavor; + private static final DataFlavor PART_HTML_DATA_FLAVOR = DataFlavor.fragmentHtmlFlavor; private final List myList; diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenu.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenu.java index d7f172573af4..642d25f597f1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenu.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenu.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,6 +40,7 @@ import javax.swing.*; import javax.swing.event.MenuEvent; import javax.swing.event.MenuListener; import javax.swing.plaf.MenuItemUI; +import javax.swing.plaf.synth.SynthMenuUI; import java.awt.*; import java.awt.event.AWTEventListener; import java.awt.event.ComponentEvent; @@ -178,8 +179,8 @@ public final class ActionMenu extends JBMenu { } @Override - public void setUI(final MenuItemUI ui) { - final MenuItemUI newUi = !myTopLevel && UIUtil.isUnderGTKLookAndFeel() && GtkMenuUI.isUiAcceptable(ui) ? new GtkMenuUI(ui) : ui; + public void setUI(MenuItemUI ui) { + MenuItemUI newUi = !myTopLevel && UIUtil.isUnderGTKLookAndFeel() && ui instanceof SynthMenuUI ? new GtkMenuUI((SynthMenuUI)ui) : ui; super.setUI(newUi); } diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenuItem.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenuItem.java index d3fb034d8f16..a50afb68e1b2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenuItem.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenuItem.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,6 +41,7 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.plaf.MenuItemUI; +import javax.swing.plaf.synth.SynthMenuItemUI; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -187,8 +188,8 @@ public class ActionMenuItem extends JBCheckBoxMenuItem { } @Override - public void setUI(final MenuItemUI ui) { - final MenuItemUI newUi = UIUtil.isUnderGTKLookAndFeel() && GtkMenuItemUI.isUiAcceptable(ui) ? new GtkMenuItemUI(ui) : ui; + public void setUI(MenuItemUI ui) { + MenuItemUI newUi = UIUtil.isUnderGTKLookAndFeel() && ui instanceof SynthMenuItemUI ? new GtkMenuItemUI((SynthMenuItemUI)ui) : ui; super.setUI(newUi); } diff --git a/platform/platform-impl/src/com/intellij/ui/KeyStrokeAdapter.java b/platform/platform-impl/src/com/intellij/ui/KeyStrokeAdapter.java index 020fb241b3ef..cf607d56271a 100644 --- a/platform/platform-impl/src/com/intellij/ui/KeyStrokeAdapter.java +++ b/platform/platform-impl/src/com/intellij/ui/KeyStrokeAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ */ package com.intellij.ui; -import com.intellij.Patches; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.registry.Registry; @@ -26,7 +25,6 @@ import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.util.Locale; import java.util.Map; import java.util.StringTokenizer; @@ -116,7 +114,7 @@ public class KeyStrokeAdapter implements KeyListener { if (Registry.is("actionSystem.extendedKeyCode.disabled")) { return null; } - code = getExtendedKeyCode(event); + code = event.getExtendedKeyCode(); if (code == event.getKeyCode()) { return null; } @@ -146,23 +144,6 @@ public class KeyStrokeAdapter implements KeyListener { return KeyEvent.VK_UNDEFINED == code ? null : KeyStroke.getKeyStroke(code, modifiers, released); } - // TODO: HACK because of Java7 required: - // replace later with event.getExtendedKeyCode() - private static int getExtendedKeyCode(KeyEvent event) { - //noinspection ConstantConditions - assert Patches.USE_REFLECTION_TO_ACCESS_JDK7; - try { - Method method = KeyEvent.class.getMethod("getExtendedKeyCode"); - if (!method.isAccessible()) { - method.setAccessible(true); - } - return (Integer)method.invoke(event); - } - catch (Exception exception) { - return event.getKeyCode(); - } - } - /** * Parses a string and returns the corresponding key stroke. * The string must have the following syntax: diff --git a/platform/platform-impl/src/com/intellij/ui/plaf/gtk/GtkMenuItemUI.java b/platform/platform-impl/src/com/intellij/ui/plaf/gtk/GtkMenuItemUI.java index fd74d0e4863b..e47797729099 100644 --- a/platform/platform-impl/src/com/intellij/ui/plaf/gtk/GtkMenuItemUI.java +++ b/platform/platform-impl/src/com/intellij/ui/plaf/gtk/GtkMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,24 +20,20 @@ import com.intellij.util.ui.EmptyIcon; import com.intellij.util.ui.UIUtil; import javax.swing.*; -import javax.swing.plaf.MenuItemUI; import javax.swing.plaf.basic.BasicMenuItemUI; +import javax.swing.plaf.synth.ColorType; import javax.swing.plaf.synth.SynthContext; +import javax.swing.plaf.synth.SynthMenuItemUI; import java.awt.*; public class GtkMenuItemUI extends BasicMenuItemUI { private static Icon myCachedCheckIcon = null; - private final BasicMenuItemUI myOriginalUI; + private final SynthMenuItemUI myOriginalUI; private JCheckBoxMenuItem myHiddenItem; - public GtkMenuItemUI(final MenuItemUI originalUI) { - assert isUiAcceptable(originalUI) : originalUI; - myOriginalUI = (BasicMenuItemUI)originalUI; - } - - public static boolean isUiAcceptable(final MenuItemUI ui) { - return ui instanceof BasicMenuItemUI && GtkPaintingUtil.isSynthUI(ui); + public GtkMenuItemUI(SynthMenuItemUI originalUI) { + myOriginalUI = originalUI; } @Override @@ -60,9 +56,9 @@ public class GtkMenuItemUI extends BasicMenuItemUI { resetCachedCheckIcon(); } - private static Icon getCheckIconFromContext(final BasicMenuItemUI originalUI, final JCheckBoxMenuItem item) { + private static Icon getCheckIconFromContext(final SynthMenuItemUI ui, final JCheckBoxMenuItem item) { if (myCachedCheckIcon == null) { - final SynthContext context = GtkPaintingUtil.getSynthContext(originalUI, item); + SynthContext context = ui.getContext(item); myCachedCheckIcon = context.getStyle().getIcon(context, "CheckBoxMenuItem.checkIcon"); } return myCachedCheckIcon; @@ -82,10 +78,12 @@ public class GtkMenuItemUI extends BasicMenuItemUI { if (UIUtil.isMurrineBasedTheme()) { acceleratorFont = menuItem.getFont(); - final Color fg = GtkPaintingUtil.getForeground(myOriginalUI, menuItem); + SynthContext context = myOriginalUI.getContext(menuItem); + Color fg = context.getStyle().getColor(context, ColorType.TEXT_FOREGROUND); acceleratorForeground = UIUtil.mix(fg, menuItem.getBackground(), menuItem.isSelected() ? 0.4 : 0.2); disabledForeground = fg; } + if (checkIcon != null && !(checkIcon instanceof IconWrapper) && !(checkIcon instanceof EmptyIcon)) { checkIcon = new IconWrapper(checkIcon, myOriginalUI); } @@ -97,8 +95,9 @@ public class GtkMenuItemUI extends BasicMenuItemUI { protected void paintText(final Graphics g, final JMenuItem menuItem, final Rectangle textRect, final String text) { if (!menuItem.isEnabled() && UIUtil.isMurrineBasedTheme()) { GtkPaintingUtil.paintDisabledText(myOriginalUI, g, menuItem, textRect, text); - return; } - super.paintText(g, menuItem, textRect, text); + else { + super.paintText(g, menuItem, textRect, text); + } } -} +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/ui/plaf/gtk/GtkMenuUI.java b/platform/platform-impl/src/com/intellij/ui/plaf/gtk/GtkMenuUI.java index 43ff684f408f..d2f887d3e0f3 100644 --- a/platform/platform-impl/src/com/intellij/ui/plaf/gtk/GtkMenuUI.java +++ b/platform/platform-impl/src/com/intellij/ui/plaf/gtk/GtkMenuUI.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,20 +18,15 @@ package com.intellij.ui.plaf.gtk; import com.intellij.util.ui.UIUtil; import javax.swing.*; -import javax.swing.plaf.MenuItemUI; import javax.swing.plaf.basic.BasicMenuUI; +import javax.swing.plaf.synth.SynthMenuUI; import java.awt.*; public class GtkMenuUI extends BasicMenuUI { - private final BasicMenuUI myOriginalUI; + private final SynthMenuUI myOriginalUI; - public GtkMenuUI(final MenuItemUI originalUI) { - assert isUiAcceptable(originalUI) : originalUI; - myOriginalUI = (BasicMenuUI)originalUI; - } - - public static boolean isUiAcceptable(final MenuItemUI ui) { - return ui instanceof BasicMenuUI && GtkPaintingUtil.isSynthUI(ui); + public GtkMenuUI(SynthMenuUI originalUI) { + myOriginalUI = originalUI; } @Override @@ -55,8 +50,9 @@ public class GtkMenuUI extends BasicMenuUI { protected void paintText(final Graphics g, final JMenuItem menuItem, final Rectangle textRect, final String text) { if (!menuItem.isEnabled() && UIUtil.isMurrineBasedTheme()) { GtkPaintingUtil.paintDisabledText(myOriginalUI, g, menuItem, textRect, text); - return; } - super.paintText(g, menuItem, textRect, text); + else { + super.paintText(g, menuItem, textRect, text); + } } -} +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/ui/plaf/gtk/GtkPaintingUtil.java b/platform/platform-impl/src/com/intellij/ui/plaf/gtk/GtkPaintingUtil.java index 7da57c850965..b5656f1671e8 100644 --- a/platform/platform-impl/src/com/intellij/ui/plaf/gtk/GtkPaintingUtil.java +++ b/platform/platform-impl/src/com/intellij/ui/plaf/gtk/GtkPaintingUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,31 +15,20 @@ */ package com.intellij.ui.plaf.gtk; -import com.intellij.Patches; import com.intellij.util.ui.UIUtil; import sun.swing.SwingUtilities2; import javax.swing.*; import javax.swing.plaf.MenuItemUI; -import javax.swing.plaf.basic.BasicMenuItemUI; import javax.swing.plaf.synth.ColorType; import javax.swing.plaf.synth.SynthContext; +import javax.swing.plaf.synth.SynthUI; import java.awt.*; -import java.lang.reflect.Method; -// todo[r.sh] get rid of SynthUI reflection after migration to JDK 7 public class GtkPaintingUtil { - private static final String V6_SYNTH_UI_CLASS = "sun.swing.plaf.synth.SynthUI"; - private static final String V7_SYNTH_UI_CLASS = "javax.swing.plaf.synth.SynthUI"; - private GtkPaintingUtil() { } - public static Color getForeground(final BasicMenuItemUI ui, final JMenuItem menuItem) { - final SynthContext context = getSynthContext(ui, menuItem); - return context.getStyle().getColor(context, ColorType.TEXT_FOREGROUND); - } - - public static void paintDisabledText(final BasicMenuItemUI originalUI, + public static void paintDisabledText(final SynthUI originalUI, final Graphics g, final JMenuItem menuItem, final Rectangle textRect, @@ -47,7 +36,8 @@ public class GtkPaintingUtil { final FontMetrics fm = SwingUtilities2.getFontMetrics(menuItem, g); final int index = menuItem.getDisplayedMnemonicIndex(); - final Color fg = getForeground(originalUI, menuItem); + final SynthContext context = originalUI.getContext(menuItem); + final Color fg = context.getStyle().getColor(context, ColorType.TEXT_FOREGROUND); final Color shadow = UIUtil.shade(menuItem.getBackground(), 1.24, 0.5); g.setColor(shadow); @@ -56,32 +46,7 @@ public class GtkPaintingUtil { SwingUtilities2.drawStringUnderlineCharAt(menuItem, g, text, index, textRect.x, textRect.y + fm.getAscent()); } - public static boolean isSynthUI(final MenuItemUI ui) { - Class aClass = ui.getClass(); - - while (aClass != null && aClass.getSimpleName().contains("Synth")) { - final Class[] interfaces = aClass.getInterfaces(); - for (int i = 0, length = interfaces.length; i < length; i++) { - final Class anInterface = interfaces[i]; - if (V6_SYNTH_UI_CLASS.equals(anInterface.getName()) || V7_SYNTH_UI_CLASS.equals(anInterface.getName())) { - return true; - } - } - aClass = aClass.getSuperclass(); - } - - return false; - } - public static SynthContext getSynthContext(final MenuItemUI ui, final JComponent item) { - assert Patches.USE_REFLECTION_TO_ACCESS_JDK7; - try { - final Method getContext = ui.getClass().getMethod("getContext", JComponent.class); - getContext.setAccessible(true); - return (SynthContext)getContext.invoke(ui, item); - } - catch (Exception e) { - throw new RuntimeException(e); - } + return ((SynthUI)ui).getContext(item); } -} +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/ui/plaf/gtk/IconWrapper.java b/platform/platform-impl/src/com/intellij/ui/plaf/gtk/IconWrapper.java index 66bbdab856bc..372a19f4a237 100644 --- a/platform/platform-impl/src/com/intellij/ui/plaf/gtk/IconWrapper.java +++ b/platform/platform-impl/src/com/intellij/ui/plaf/gtk/IconWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,16 +18,16 @@ package com.intellij.ui.plaf.gtk; import com.intellij.Patches; import javax.swing.*; -import javax.swing.plaf.MenuItemUI; import javax.swing.plaf.synth.SynthContext; +import javax.swing.plaf.synth.SynthUI; import java.awt.*; import java.lang.reflect.Method; public class IconWrapper implements Icon { private final Icon myIcon; - private final MenuItemUI myOriginalUI; + private final SynthUI myOriginalUI; - public IconWrapper(final Icon icon, final MenuItemUI originalUI) { + public IconWrapper(final Icon icon, final SynthUI originalUI) { myIcon = icon; myOriginalUI = originalUI; } @@ -36,10 +36,9 @@ public class IconWrapper implements Icon { public void paintIcon(final Component c, final Graphics g, final int x, final int y) { if (Patches.USE_REFLECTION_TO_ACCESS_JDK7) { try { - final Method paintIcon = myIcon.getClass().getMethod("paintIcon", SynthContext.class, Graphics.class, - int.class, int.class, int.class, int.class); + Method paintIcon = myIcon.getClass().getMethod("paintIcon", SynthContext.class, Graphics.class, int.class, int.class, int.class, int.class); paintIcon.setAccessible(true); - paintIcon.invoke(myIcon, GtkPaintingUtil.getSynthContext(myOriginalUI, (JComponent)c), g, x, y, getIconWidth(), getIconHeight()); + paintIcon.invoke(myIcon, myOriginalUI.getContext((JComponent)c), g, x, y, getIconWidth(), getIconHeight()); return; } catch (Exception ignore) { } @@ -56,4 +55,4 @@ public class IconWrapper implements Icon { public int getIconHeight() { return myIcon.getIconHeight(); } -} +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/ui/popup/MovablePopup.java b/platform/platform-impl/src/com/intellij/ui/popup/MovablePopup.java index 9dea75d03470..fb2ee27c02c0 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/MovablePopup.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/MovablePopup.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,8 +22,6 @@ import javax.swing.*; import java.awt.*; import java.util.ArrayDeque; -import static com.intellij.Patches.USE_REFLECTION_TO_ACCESS_JDK7; - /** * @author Sergey Malenkov */ @@ -166,7 +164,7 @@ public class MovablePopup { Window view = pop(owner); if (view == null) { view = new JWindow(owner); - setPopupType(view); + view.setType(Window.Type.POPUP); } setAlwaysOnTop(view, myAlwaysOnTop); setWindowFocusable(view, myWindowFocusable); @@ -265,21 +263,6 @@ public class MovablePopup { } } - // TODO: HACK because of Java7 required: - // replace later with window.setType(Window.Type.POPUP) - private static void setPopupType(@NotNull Window window) { - //noinspection ConstantConditions,ConstantAssertCondition - assert USE_REFLECTION_TO_ACCESS_JDK7; - try { - @SuppressWarnings("unchecked") - Class type = (Class)Class.forName("java.awt.Window$Type"); - Object value = Enum.valueOf(type, "POPUP"); - Window.class.getMethod("setType", type).invoke(window, value); - } - catch (Exception ignored) { - } - } - private static JRootPane getRootPane(Window window) { if (window instanceof RootPaneContainer) { RootPaneContainer container = (RootPaneContainer)window; From 9cd356673093bc869fc49fba7285f9325dc2f3c8 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Mon, 17 Oct 2016 17:49:09 +0700 Subject: [PATCH 26/66] ReplaceWithMapPutIfAbsentFix -> ReplaceConditionalMapPutFix; parameter methodName replaced with boolean (IDEA-CR-14446) --- .../java18api/Java8CollectionsApiInspection.java | 6 +++--- ...bsentFix.java => ReplaceConditionalMapPutFix.java} | 11 +++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) rename java/java-analysis-impl/src/com/intellij/codeInspection/java18api/{ReplaceWithMapPutIfAbsentFix.java => ReplaceConditionalMapPutFix.java} (92%) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/Java8CollectionsApiInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/Java8CollectionsApiInspection.java index 4a9a02fe601b..55008d12b530 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/Java8CollectionsApiInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/Java8CollectionsApiInspection.java @@ -239,14 +239,14 @@ public class Java8CollectionsApiInspection extends BaseJavaBatchLocalInspectionT (getArgument == null || EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(getArgument, putKeyArgument))) { LocalQuickFix fix = null; if (ExpressionUtils.isSimpleExpression(putValueArgument)) { - fix = new ReplaceWithMapPutIfAbsentFix(putMethodCall, "putIfAbsent"); + fix = new ReplaceConditionalMapPutFix(putMethodCall, false); } else if ((maybePutMethodCall.getParent() instanceof PsiExpressionStatement) && // only if result of put is not used LambdaGenerationUtil.canBeUncheckedLambda(putValueArgument)) { - fix = new ReplaceWithMapPutIfAbsentFix(putMethodCall, "computeIfAbsent"); + fix = new ReplaceConditionalMapPutFix(putMethodCall, true); } else if (mySuggestPutIfAbsentForComplexExpression) { - fix = new ReplaceWithMapPutIfAbsentFix(putMethodCall, "putIfAbsent"); + fix = new ReplaceConditionalMapPutFix(putMethodCall, false); } if(fix != null) { holder.registerProblem(context, QuickFixBundle.message("java.8.collections.api.inspection.description"), fix); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/ReplaceWithMapPutIfAbsentFix.java b/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/ReplaceConditionalMapPutFix.java similarity index 92% rename from java/java-analysis-impl/src/com/intellij/codeInspection/java18api/ReplaceWithMapPutIfAbsentFix.java rename to java/java-analysis-impl/src/com/intellij/codeInspection/java18api/ReplaceConditionalMapPutFix.java index 2d74c1f5aa7a..693906636de1 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/ReplaceWithMapPutIfAbsentFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/java18api/ReplaceConditionalMapPutFix.java @@ -32,14 +32,17 @@ import org.jetbrains.annotations.NotNull; /** * @author Dmitry Batkovich */ -public class ReplaceWithMapPutIfAbsentFix implements LocalQuickFix { +public class ReplaceConditionalMapPutFix implements LocalQuickFix { + private static final String COMPUTE_IF_ABSENT_METHOD = "computeIfAbsent"; + private static final String PUT_IF_ABSENT_METHOD = "putIfAbsent"; + private final SmartPsiElementPointer myPutExpressionPointer; private final String myMethodName; - public ReplaceWithMapPutIfAbsentFix(PsiMethodCallExpression putExpression, String methodName) { + public ReplaceConditionalMapPutFix(PsiMethodCallExpression putExpression, boolean useComputeIfAbsent) { final SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(putExpression.getProject()); myPutExpressionPointer = smartPointerManager.createSmartPsiElementPointer(putExpression); - myMethodName = methodName; // either "putIfAbsent" or "computeIfAbsent" + myMethodName = useComputeIfAbsent ? COMPUTE_IF_ABSENT_METHOD : PUT_IF_ABSENT_METHOD; } @Override @@ -74,7 +77,7 @@ public class ReplaceWithMapPutIfAbsentFix implements LocalQuickFix { final Couple boundText = getBoundText(putContainingElement, putExpression); String valueArgument = putExpression.getArgumentList().getExpressions()[1].getText(); - if(myMethodName.equals("computeIfAbsent")) { + if(myMethodName.equals(COMPUTE_IF_ABSENT_METHOD)) { String varName = JavaCodeStyleManager.getInstance(project).suggestUniqueVariableName("k", putExpression, true); valueArgument = varName + " -> " + valueArgument; } From db149751ae38f7613b321786d3046e997346802b Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Mon, 17 Oct 2016 12:54:42 +0200 Subject: [PATCH 27/66] [ui] correct scaling in GTK+ menu --- .../openapi/actionSystem/impl/ActionMenu.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenu.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenu.java index 642d25f597f1..0799d5f3a31a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenu.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenu.java @@ -33,6 +33,7 @@ import com.intellij.ui.plaf.beg.IdeaMenuUI; import com.intellij.ui.plaf.gtk.GtkMenuUI; import com.intellij.util.ReflectionUtil; import com.intellij.util.SingleAlarm; +import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; @@ -165,12 +166,12 @@ public final class ActionMenu extends JBMenu { if (myTopLevel && UIUtil.isUnderGTKLookAndFeel()) { Insets insets = getInsets(); - Insets newInsets = new Insets(insets.top, insets.left, insets.bottom, insets.right); - if (insets.top + insets.bottom < 6) { - newInsets.top = newInsets.bottom = 3; + @SuppressWarnings("UseDPIAwareInsets") Insets newInsets = new Insets(insets.top, insets.left, insets.bottom, insets.right); + if (insets.top + insets.bottom < JBUI.scale(6)) { + newInsets.top = newInsets.bottom = JBUI.scale(3); } - if (insets.left + insets.right < 12) { - newInsets.left = newInsets.right = 6; + if (insets.left + insets.right < JBUI.scale(12)) { + newInsets.left = newInsets.right = JBUI.scale(6); } if (!newInsets.equals(insets)) { setBorder(BorderFactory.createEmptyBorder(newInsets.top, newInsets.left, newInsets.bottom, newInsets.right)); From 83943e0376e394d1a2584b5b81717c72f2ee7187 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Mon, 17 Oct 2016 12:56:30 +0200 Subject: [PATCH 28/66] Cleanup (warnings) --- .../intellij/openapi/actionSystem/impl/ActionMenu.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenu.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenu.java index 0799d5f3a31a..fc506a13dbe4 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenu.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionMenu.java @@ -224,7 +224,8 @@ public final class ActionMenu extends JBMenu { } private void updateIcon() { - if (UISettings.getInstance().SHOW_ICONS_IN_MENUS) { + UISettings settings = UISettings.getInstance(); + if (settings != null && settings.SHOW_ICONS_IN_MENUS) { final Presentation presentation = myPresentation; final Icon icon = presentation.getIcon(); setIcon(icon); @@ -244,9 +245,7 @@ public final class ActionMenu extends JBMenu { } public static void showDescriptionInStatusBar(boolean isIncluded, Component component, String description) { - IdeFrame frame = component instanceof IdeFrame - ? (IdeFrame)component - : (IdeFrame)SwingUtilities.getAncestorOfClass(IdeFrame.class, component); + IdeFrame frame = (IdeFrame)(component instanceof IdeFrame ? component : SwingUtilities.getAncestorOfClass(IdeFrame.class, component)); StatusBar statusBar; if (frame != null && (statusBar = frame.getStatusBar()) != null) { statusBar.setInfo(isIncluded ? description : null); @@ -450,4 +449,4 @@ public final class ActionMenu extends JBMenu { Toolkit.getDefaultToolkit().removeAWTEventListener(this); } } -} +} \ No newline at end of file From 45f6ba5775193b736172c25569bf4579a9a396cd Mon Sep 17 00:00:00 2001 From: Sergey Simonchik Date: Mon, 17 Oct 2016 14:07:47 +0300 Subject: [PATCH 29/66] content entry editor: fast fail if getModel returns null (investigating EA-89693 - NPE: ContentEntryEditor.isExcludedOrUnderExcludedDirectory) (IDEA-CR-14528) --- .../openapi/roots/ui/configuration/ContentEntryEditor.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/ContentEntryEditor.java b/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/ContentEntryEditor.java index a9e9a497add1..00e499a60699 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/ContentEntryEditor.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/ContentEntryEditor.java @@ -289,7 +289,10 @@ public abstract class ContentEntryEditor implements ContentRootPanel.ActionCallb public boolean isExcludedOrUnderExcludedDirectory(@NotNull VirtualFile file) { ModifiableRootModel model = getModel(); - Project project = model != null ? model.getProject() : null; + if (model == null) { + throw new AssertionError(getClass() + ".getModel() returned null unexpectedly"); + } + Project project = model.getProject(); ContentEntry contentEntry = getContentEntry(); if (contentEntry == null) { return false; From 0cb5558e8529b4ee723c910e7a4ee295b0d13bc3 Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Mon, 17 Oct 2016 11:34:24 +0200 Subject: [PATCH 30/66] inplace introduce variable: ensure validity of created var EA-63354 - PIEAE: CompositePsiElement.getContainingFile --- .../IntroduceVariableBase.java | 24 +++++++++---------- .../JavaVariableInplaceIntroducer.java | 17 ++++++++----- .../brokenFormattingWithInValidation.java | 10 ++++++++ ...rokenFormattingWithInValidation_after.java | 10 ++++++++ .../InplaceIntroduceVariableTest.java | 9 +++++++ 5 files changed, 52 insertions(+), 18 deletions(-) create mode 100644 java/java-tests/testData/refactoring/inplaceIntroduceVariable/brokenFormattingWithInValidation.java create mode 100644 java/java-tests/testData/refactoring/inplaceIntroduceVariable/brokenFormattingWithInValidation_after.java diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java index 614f204c6a95..fa92d548a366 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java @@ -699,8 +699,7 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { final PsiElement chosenAnchor = chooseAnchor(settings.isReplaceAllOccurrences(), hasWriteAccess, nonWrite, anchorStatementIfAll, anchorStatement); - variable = ApplicationManager.getApplication().runWriteAction( - introduce(project, expr, topLevelEditor, chosenAnchor, occurrences, settings)); + variable = introduce(project, expr, topLevelEditor, chosenAnchor, occurrences, settings); } finally { final RefactoringEventData afterData = new RefactoringEventData(); @@ -794,12 +793,12 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { return parent3 instanceof JspHolderMethod; } - public static Computable introduce(final Project project, - final PsiExpression expr, - final Editor editor, - final PsiElement anchorStatement, - final PsiExpression[] occurrences, - final IntroduceVariableSettings settings) { + public static PsiVariable introduce(final Project project, + final PsiExpression expr, + final Editor editor, + final PsiElement anchorStatement, + final PsiExpression[] occurrences, + final IntroduceVariableSettings settings) { final PsiElement container = anchorStatement.getParent(); PsiElement child = anchorStatement; final boolean isInsideLoop = RefactoringUtil.isLoopOrIf(container); @@ -841,9 +840,9 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { final PsiCodeBlock newDeclarationScope = PsiTreeUtil.getParentOfType(container, PsiCodeBlock.class, false); final FieldConflictsResolver fieldConflictsResolver = new FieldConflictsResolver(settings.getEnteredName(), newDeclarationScope); - return new Computable() { + SmartPsiElementPointer pointer = ApplicationManager.getApplication().runWriteAction(new Computable> () { @Override - public PsiVariable compute() { + public SmartPsiElementPointer compute() { try { PsiStatement statement = null; if (!isInsideLoop && deleteSelf) { @@ -913,7 +912,7 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { PsiVariable var = (PsiVariable) declaration.getDeclaredElements()[0]; PsiUtil.setModifierProperty(var, PsiModifier.FINAL, settings.isDeclareFinal()); fieldConflictsResolver.fix(); - return var; + return SmartPointerManager.getInstance(project).createSmartPsiElementPointer(var); } catch (IncorrectOperationException e) { LOG.error(e); } @@ -945,7 +944,8 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { } return (PsiDeclarationStatement) container.addBefore(declaration, anchor); } - }; + }); + return pointer != null ? pointer.getElement() : null; } private static PsiType stripNullabilityAnnotationsFromTargetType(SmartTypePointer selectedType, final Project project) { diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/JavaVariableInplaceIntroducer.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/JavaVariableInplaceIntroducer.java index c6ad929835b0..a53c51acc6c8 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/JavaVariableInplaceIntroducer.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/JavaVariableInplaceIntroducer.java @@ -395,10 +395,19 @@ public class JavaVariableInplaceIntroducer extends AbstractJavaInplaceIntroducer @Override protected PsiVariable createFieldToStartTemplateOn(String[] names, PsiType psiType) { - final PsiVariable variable = ApplicationManager.getApplication().runWriteAction( - IntroduceVariableBase.introduce(myProject, myExpr, myEditor, myChosenAnchor.getElement(), getOccurrences(), mySettings)); + PsiVariable variable = IntroduceVariableBase.introduce(myProject, myExpr, myEditor, myChosenAnchor.getElement(), getOccurrences(), mySettings); + final PsiDeclarationStatement declarationStatement = PsiTreeUtil.getParentOfType(variable, PsiDeclarationStatement.class); + myPointer = declarationStatement != null ? SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(declarationStatement) : null; + myEditor.putUserData(ReassignVariableUtil.DECLARATION_KEY, myPointer); + setAdvertisementText(getAdvertisementText(declarationStatement, variable.getType(), myHasTypeSuggestion)); + PsiDocumentManager.getInstance(myProject).doPostponedOperationsAndUnblockDocument(myEditor.getDocument()); + final PsiVariable restoredVar = getVariable(); + if (restoredVar != null) { + variable = restoredVar; + } + if (isReplaceAllOccurrences()) { List occurrences = new ArrayList<>(); ReferencesSearch.search(variable).forEach(reference -> { @@ -407,10 +416,6 @@ public class JavaVariableInplaceIntroducer extends AbstractJavaInplaceIntroducer setOccurrenceMarkers(occurrences); } - final PsiDeclarationStatement declarationStatement = PsiTreeUtil.getParentOfType(variable, PsiDeclarationStatement.class); - myPointer = declarationStatement != null ? SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(declarationStatement) : null; - myEditor.putUserData(ReassignVariableUtil.DECLARATION_KEY, myPointer); - setAdvertisementText(getAdvertisementText(declarationStatement, variable.getType(), myHasTypeSuggestion)); final PsiIdentifier identifier = variable.getNameIdentifier(); if (identifier != null) { myEditor.getCaretModel().moveToOffset(identifier.getTextOffset()); diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/brokenFormattingWithInValidation.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/brokenFormattingWithInValidation.java new file mode 100644 index 000000000000..06b06cbd5f32 --- /dev/null +++ b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/brokenFormattingWithInValidation.java @@ -0,0 +1,10 @@ +class C { + void sort(int[] array) { + int j; + for (int i = 0; i < array.length; i++) { + j = 0; + while (j>array[i]) + + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/brokenFormattingWithInValidation_after.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/brokenFormattingWithInValidation_after.java new file mode 100644 index 000000000000..b36d03646293 --- /dev/null +++ b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/brokenFormattingWithInValidation_after.java @@ -0,0 +1,10 @@ +class C { + void sort(int[] array) { + int j; + for (int i = 0; i < array.length; i++) { + j = 0; + while (j array[i];&&(array[j] > array[i]) + + } + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/refactoring/InplaceIntroduceVariableTest.java b/java/java-tests/testSrc/com/intellij/refactoring/InplaceIntroduceVariableTest.java index 5e9ca964059c..44a86d4a2388 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/InplaceIntroduceVariableTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/InplaceIntroduceVariableTest.java @@ -205,6 +205,15 @@ public class InplaceIntroduceVariableTest extends AbstractJavaInplaceIntroduceTe doTestReplaceChoice(OccurrencesChooser.ReplaceChoice.ALL); } + public void testBrokenFormattingWithInValidation() throws Exception { + doTest(new Pass() { + @Override + public void pass(AbstractInplaceIntroducer introducer) { + type("bool"); + } + }); + } + public void testStopEditing() { doTestStopEditing(new Pass() { @Override From 943ee0e8b28bcd70797672cbbc72e6497af801f6 Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Mon, 17 Oct 2016 12:06:34 +0200 Subject: [PATCH 31/66] EA-89953 - assert: MessageBusImpl.checkNotDisposed --- .../sm/runner/GeneralTestEventsProcessor.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralTestEventsProcessor.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralTestEventsProcessor.java index ce2112e80170..c15b0c7ce445 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralTestEventsProcessor.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralTestEventsProcessor.java @@ -244,7 +244,12 @@ public abstract class GeneralTestEventsProcessor implements Disposable { } public void stopEventProcessing() { - UIUtil.invokeLaterIfNeeded(() -> myTransferToEDTQueue.drain()); + UIUtil.invokeLaterIfNeeded(() -> { + if (myProject.isDisposed()) { + return; + } + myTransferToEDTQueue.drain(); + }); } From 867b295cd7c1fd076eeea10a4c80e213d7d7cfb0 Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Mon, 17 Oct 2016 12:09:50 +0200 Subject: [PATCH 32/66] EA-89934 - IAE: PsiSearchHelperImpl.bulkProcessElementsWithWord --- .../refactoring/rename/inplace/VariableInplaceRenamer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenamer.java b/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenamer.java index 3cd1af45f6b3..f2b15f2d94eb 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenamer.java +++ b/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenamer.java @@ -36,6 +36,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; import com.intellij.psi.util.PsiUtilCore; @@ -101,7 +102,7 @@ public class VariableInplaceRenamer extends InplaceRefactoring { protected void collectAdditionalElementsToRename(final List> stringUsages) { final String stringToSearch = myElementToRename.getName(); final PsiFile currentFile = PsiDocumentManager.getInstance(myProject).getPsiFile(myEditor.getDocument()); - if (stringToSearch != null) { + if (!StringUtil.isEmptyOrSpaces(stringToSearch)) { TextOccurrencesUtil .processUsagesInStringsAndComments(myElementToRename, stringToSearch, true, (psiElement, textRange) -> { if (psiElement.getContainingFile() == currentFile) { From 313b33ec31a64812c7c4d5c94f8a532b79e709fe Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Mon, 17 Oct 2016 12:49:48 +0200 Subject: [PATCH 33/66] paste reference: ignore binding if reference was not created as it could be e.g. groovy method name = invalid java identifier EA-89854 - IOE: PsiJavaParserFacadeImpl.createExpressionFromText --- .../actions/JavaQualifiedNameProvider.java | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/java/java-impl/src/com/intellij/ide/actions/JavaQualifiedNameProvider.java b/java/java-impl/src/com/intellij/ide/actions/JavaQualifiedNameProvider.java index 67c09e9cf4c1..d0e9d102cbd1 100644 --- a/java/java-impl/src/com/intellij/ide/actions/JavaQualifiedNameProvider.java +++ b/java/java-impl/src/com/intellij/ide/actions/JavaQualifiedNameProvider.java @@ -219,30 +219,27 @@ public class JavaQualifiedNameProvider implements QualifiedNameProvider { final PsiExpression expression; try { expression = factory.createExpressionFromText(toInsert + suffix, elementAtCaret); - } - catch (IncorrectOperationException e) { - LOG.error(e); - return; - } - final PsiReferenceExpression referenceExpression = expression instanceof PsiMethodCallExpression - ? ((PsiMethodCallExpression)expression).getMethodExpression() - : expression instanceof PsiReferenceExpression - ? (PsiReferenceExpression)expression - : null; - if (referenceExpression == null || !referenceExpression.isValid()) { - toInsert = fqn; - } - else if (!isReferencedTo(referenceExpression, targetElement)) { - try { - referenceExpression.bindToElement(targetElement); - } - catch (IncorrectOperationException e) { - // failed to bind - } - if (!referenceExpression.isValid() || !isReferencedTo(referenceExpression, targetElement)) { + final PsiReferenceExpression referenceExpression = expression instanceof PsiMethodCallExpression + ? ((PsiMethodCallExpression)expression).getMethodExpression() + : expression instanceof PsiReferenceExpression + ? (PsiReferenceExpression)expression + : null; + if (referenceExpression == null || !referenceExpression.isValid()) { toInsert = fqn; } + else if (!isReferencedTo(referenceExpression, targetElement)) { + try { + referenceExpression.bindToElement(targetElement); + } + catch (IncorrectOperationException e) { + // failed to bind + } + if (!referenceExpression.isValid() || !isReferencedTo(referenceExpression, targetElement)) { + toInsert = fqn; + } + } } + catch (IncorrectOperationException ignored) {} } if (toInsert == null) toInsert = ""; From af57c348fb5fa8a2efdb65dabc25dcbb8550a993 Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Mon, 17 Oct 2016 12:58:27 +0200 Subject: [PATCH 34/66] avoid second processing of same elements in groovy/java --- .../ChangeSignatureProcessorBase.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureProcessorBase.java b/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureProcessorBase.java index d61fbe81425b..9cfba7716f3e 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureProcessorBase.java +++ b/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureProcessorBase.java @@ -93,18 +93,22 @@ public abstract class ChangeSignatureProcessorBase extends BaseRefactoringProces List infos = new ArrayList<>(); final ChangeSignatureUsageProcessor[] processors = ChangeSignatureUsageProcessor.EP_NAME.getExtensions(); for (ChangeSignatureUsageProcessor processor : processors) { - ContainerUtil.addAll(infos, filterUsages(processor.findUsages(changeInfo), processor)); + for (UsageInfo info : processor.findUsages(changeInfo)) { + LOG.assertTrue(info != null, processor); + infos.add(info); + } } + infos = filterUsages(infos); return infos.toArray(new UsageInfo[infos.size()]); } - protected static List filterUsages(UsageInfo[] infos, ChangeSignatureUsageProcessor processor) { + protected static List filterUsages(List infos) { Map moveRenameInfos = new HashMap<>(); Set usedElements = new HashSet<>(); - List result = new ArrayList<>(infos.length / 2); + List result = new ArrayList<>(infos.size() / 2); for (UsageInfo info : infos) { - LOG.assertTrue(info != null, processor); + LOG.assertTrue(info != null); PsiElement element = info.getElement(); if (info instanceof MoveRenameUsageInfo) { if (usedElements.contains(element)) continue; From 28ef6a3669896c860efebc3728fa09349bb74acc Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Mon, 17 Oct 2016 13:09:32 +0200 Subject: [PATCH 35/66] fix testdata --- plugins/testng/testData/inspection/dependsOn/Dependencies.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/testng/testData/inspection/dependsOn/Dependencies.java b/plugins/testng/testData/inspection/dependsOn/Dependencies.java index fc095a3ecd3d..d5110bc7507a 100644 --- a/plugins/testng/testData/inspection/dependsOn/Dependencies.java +++ b/plugins/testng/testData/inspection/dependsOn/Dependencies.java @@ -1,5 +1,5 @@ import org.testng.annotations.*; - class MyTest { +public class Dependencies { @Test(dependsOnMethods = "beforeMethod") public void testFoo() throws Exception { } From 1b2dbea09fce7b5b3b579717524f45ba159d76c9 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Fri, 14 Oct 2016 14:12:21 +0300 Subject: [PATCH 36/66] github: fix repository list loading * fix "repository already exists" check because of the organisation repository with the same name * behavior of the API changed some time ago --- .../plugins/github/api/GithubApiUtil.java | 33 ++++++------------- ...est.java => GithubRequestQueringTest.java} | 22 +++++++++++-- 2 files changed, 29 insertions(+), 26 deletions(-) rename plugins/github/test/org/jetbrains/plugins/github/{GithubRequestPagingTest.java => GithubRequestQueringTest.java} (63%) diff --git a/plugins/github/src/org/jetbrains/plugins/github/api/GithubApiUtil.java b/plugins/github/src/org/jetbrains/plugins/github/api/GithubApiUtil.java index 20ba58c85a2a..85eb2bfabf2d 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/api/GithubApiUtil.java +++ b/plugins/github/src/org/jetbrains/plugins/github/api/GithubApiUtil.java @@ -244,8 +244,14 @@ public class GithubApiUtil { @NotNull public static List getUserRepos(@NotNull GithubConnection connection) throws IOException { + return getUserRepos(connection, false); + } + + @NotNull + public static List getUserRepos(@NotNull GithubConnection connection, boolean allAssociated) throws IOException { try { - String path = "/user/repos?" + PER_PAGE; + String type = allAssociated ? "" : "type=owner&"; + String path = "/user/repos?" + type + PER_PAGE; return loadAll(connection, path, GithubRepo[].class, ACCEPT_V3_JSON); } catch (GithubConfusingException e) { @@ -257,7 +263,7 @@ public class GithubApiUtil { @NotNull public static List getUserRepos(@NotNull GithubConnection connection, @NotNull String user) throws IOException { try { - String path = "/users/" + user + "/repos?" + PER_PAGE; + String path = "/users/" + user + "/repos?type=owner&" + PER_PAGE; return loadAll(connection, path, GithubRepo[].class, ACCEPT_V3_JSON); } catch (GithubConfusingException e) { @@ -271,15 +277,10 @@ public class GithubApiUtil { try { List repos = new ArrayList<>(); - repos.addAll(getUserRepos(connection)); + repos.addAll(getUserRepos(connection, true)); // We already can return something useful from getUserRepos, so let's ignore errors. // One of this may not exist in GitHub enterprise - try { - repos.addAll(getMembershipRepos(connection)); - } - catch (GithubAuthenticationException | GithubStatusCodeException ignore) { - } try { repos.addAll(getWatchedRepos(connection)); } @@ -295,21 +296,7 @@ public class GithubApiUtil { } @NotNull - public static List getMembershipRepos(@NotNull GithubConnection connection) throws IOException { - String orgsPath = "/user/orgs?" + PER_PAGE; - List orgs = loadAll(connection, orgsPath, GithubOrg[].class); - - List repos = new ArrayList<>(); - for (GithubOrg org : orgs) { - String path = "/orgs/" + org.getLogin() + "/repos?type=member&" + PER_PAGE; - repos.addAll(loadAll(connection, path, GithubRepoOrg[].class, ACCEPT_V3_JSON)); - } - - return repos; - } - - @NotNull - public static List getWatchedRepos(@NotNull GithubConnection connection) throws IOException { + private static List getWatchedRepos(@NotNull GithubConnection connection) throws IOException { String pathWatched = "/user/subscriptions?" + PER_PAGE; return loadAll(connection, pathWatched, GithubRepo[].class, ACCEPT_V3_JSON); } diff --git a/plugins/github/test/org/jetbrains/plugins/github/GithubRequestPagingTest.java b/plugins/github/test/org/jetbrains/plugins/github/GithubRequestQueringTest.java similarity index 63% rename from plugins/github/test/org/jetbrains/plugins/github/GithubRequestPagingTest.java rename to plugins/github/test/org/jetbrains/plugins/github/GithubRequestQueringTest.java index fe205e2f4269..a85fee484d2c 100644 --- a/plugins/github/test/org/jetbrains/plugins/github/GithubRequestPagingTest.java +++ b/plugins/github/test/org/jetbrains/plugins/github/GithubRequestQueringTest.java @@ -15,6 +15,7 @@ */ package org.jetbrains.plugins.github; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.plugins.github.api.GithubApiUtil; import org.jetbrains.plugins.github.api.GithubConnection; import org.jetbrains.plugins.github.api.data.GithubRepo; @@ -28,15 +29,14 @@ import static org.junit.Assume.assumeNotNull; /** * @author Aleksey Pivovarov */ -public class GithubRequestPagingTest extends GithubTest { +public class GithubRequestQueringTest extends GithubTest { @Override protected void beforeTest() throws Exception { assumeNotNull(myLogin2); } - public void testAvailableRepos() throws Throwable { - + public void testPagination() throws Throwable { GithubConnection connection = new GithubConnection(myGitHubSettings.getAuthData(), true); try { List availableRepos = GithubApiUtil.getUserRepos(connection, myLogin2); @@ -56,4 +56,20 @@ public class GithubRequestPagingTest extends GithubTest { connection.close(); } } + + public void testOwnRepos() throws Throwable { + List result = GithubApiUtil.getUserRepos(new GithubConnection(myAuth)); + + assertTrue(ContainerUtil.exists(result, (it) -> it.getName().equals("example"))); + assertTrue(ContainerUtil.exists(result, (it) -> it.getName().equals("PullRequestTest"))); + assertFalse(ContainerUtil.exists(result, (it) -> it.getName().equals("org_repo"))); + } + + public void testAllRepos() throws Throwable { + List result = GithubApiUtil.getUserRepos(new GithubConnection(myAuth), true); + + assertTrue(ContainerUtil.exists(result, (it) -> it.getName().equals("example"))); + assertTrue(ContainerUtil.exists(result, (it) -> it.getName().equals("PullRequestTest"))); + assertTrue(ContainerUtil.exists(result, (it) -> it.getName().equals("org_repo"))); + } } From 9858aaf705f9f74e780b2c63ec7da1cc957db328 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Mon, 17 Oct 2016 15:22:30 +0300 Subject: [PATCH 37/66] null argument checker: filter out unused parameters --- .../dataFlow/NullParameterConstraintChecker.java | 15 ++++++++++++++- ...sntReportedWhenMethodOnlyThrowAnException.java | 10 ++++++++++ .../codeInspection/DataFlowInspectionTest.java | 1 + 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/NullLiteralArgumentDoesntReportedWhenMethodOnlyThrowAnException.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullParameterConstraintChecker.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullParameterConstraintChecker.java index 218656cfb91d..39ba4a0e1475 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullParameterConstraintChecker.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullParameterConstraintChecker.java @@ -18,6 +18,7 @@ package com.intellij.codeInspection.dataFlow; import com.intellij.codeInsight.NullableNotNullManager; import com.intellij.codeInspection.dataFlow.instructions.AssignInstruction; import com.intellij.codeInspection.dataFlow.instructions.Instruction; +import com.intellij.codeInspection.dataFlow.instructions.PushInstruction; import com.intellij.codeInspection.dataFlow.instructions.ReturnInstruction; import com.intellij.codeInspection.dataFlow.value.DfaValue; import com.intellij.codeInspection.dataFlow.value.DfaValueFactory; @@ -47,10 +48,12 @@ import java.util.Set; */ class NullParameterConstraintChecker extends DataFlowRunner { private final Set myPossiblyViolatedParameters; + private final Set myUsedParameters; private NullParameterConstraintChecker(Collection parameters, boolean isOnTheFly) { super(false, true, isOnTheFly); myPossiblyViolatedParameters = new THashSet<>(parameters); + myUsedParameters = new THashSet<>(); } @NotNull @@ -73,7 +76,7 @@ class NullParameterConstraintChecker extends DataFlowRunner { final NullParameterConstraintChecker checker = new NullParameterConstraintChecker(nullableParameters, true); checker.analyzeMethod(method.getBody(), new StandardInstructionVisitor()); - return checker.myPossiblyViolatedParameters.toArray(new PsiParameter[checker.myPossiblyViolatedParameters.size()]); + return checker.myPossiblyViolatedParameters.stream().filter(checker.myUsedParameters::contains).toArray(PsiParameter[]::new); } @NotNull @@ -81,6 +84,16 @@ class NullParameterConstraintChecker extends DataFlowRunner { protected DfaInstructionState[] acceptInstruction(@NotNull InstructionVisitor visitor, @NotNull DfaInstructionState instructionState) { Instruction instruction = instructionState.getInstruction(); + if (instruction instanceof PushInstruction) { + final DfaValue var = ((PushInstruction)instruction).getValue(); + if (var instanceof DfaVariableValue) { + final PsiModifierListOwner psiVar = ((DfaVariableValue)var).getPsiVariable(); + if (psiVar instanceof PsiParameter) { + myUsedParameters.add((PsiParameter)psiVar); + } + } + } + if (instruction instanceof AssignInstruction) { final DfaValue value = ((AssignInstruction)instruction).getAssignedValue(); if (value instanceof DfaVariableValue) { diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/NullLiteralArgumentDoesntReportedWhenMethodOnlyThrowAnException.java b/java/java-tests/testData/inspection/dataFlow/fixture/NullLiteralArgumentDoesntReportedWhenMethodOnlyThrowAnException.java new file mode 100644 index 000000000000..aff8882cc70a --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/NullLiteralArgumentDoesntReportedWhenMethodOnlyThrowAnException.java @@ -0,0 +1,10 @@ +class Test { + + void m() { + throwAnException(null); + } + + static void throwAnException(String arg) { + throw new RuntimeException(); + } +} diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java index 8d3e4c50b620..eb2a56ad9d22 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java @@ -405,4 +405,5 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase { public void testNullLiteralAndInferredMethodContract() { doTest(); } + public void testNullLiteralArgumentDoesntReportedWhenMethodOnlyThrowAnException() { doTest(); } } From f5b611b4a83a23b5fed0cc4a5897cd572dfca6bf Mon Sep 17 00:00:00 2001 From: Elizaveta Shashkova Date: Mon, 17 Oct 2016 15:27:48 +0300 Subject: [PATCH 38/66] Fix multiline commands and some exceptions in Python Console again (PY-20616, PY-21104, PY-21103) --- .../python/console/PyConsoleEnterHandler.kt | 34 +++++++++---------- .../python/PyConsoleEnterHandlerTest.kt | 14 +++++++- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/python/src/com/jetbrains/python/console/PyConsoleEnterHandler.kt b/python/src/com/jetbrains/python/console/PyConsoleEnterHandler.kt index be814fd57e35..75dce0ce2116 100644 --- a/python/src/com/jetbrains/python/console/PyConsoleEnterHandler.kt +++ b/python/src/com/jetbrains/python/console/PyConsoleEnterHandler.kt @@ -27,10 +27,9 @@ import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.psi.PsiWhiteSpace -import com.intellij.psi.impl.source.codeStyle.IndentHelperImpl import com.intellij.psi.util.PsiTreeUtil +import com.intellij.util.DocumentUtil import com.jetbrains.python.PyTokenTypes -import com.jetbrains.python.PythonFileType import com.jetbrains.python.psi.PyStatementListContainer import com.jetbrains.python.psi.PyStringLiteralExpression import com.jetbrains.python.psi.impl.PyPsiUtils @@ -40,12 +39,15 @@ import com.jetbrains.python.psi.impl.PyStringLiteralExpressionImpl class PyConsoleEnterHandler { fun handleEnterPressed(editor: EditorEx): Boolean { val project = editor.project ?: throw IllegalArgumentException() - if (editor.document.lineCount != 0) { // move to end of line + if (editor.document.lineCount > 0) { // move to end of line editor.selectionModel.removeSelection() val caretPosition = editor.caretModel.logicalPosition val lineEndOffset = editor.document.getLineEndOffset(caretPosition.line) editor.caretModel.moveToOffset(lineEndOffset) } + else { + return true; + } val psiMgr = PsiDocumentManager.getInstance(project) psiMgr.commitDocument(editor.document) @@ -64,24 +66,19 @@ class PyConsoleEnterHandler { } }.execute() + val firstLine = getLineAtOffset(editor.document, DocumentUtil.getFirstNonSpaceCharOffset(editor.document, 0)) + val isCellMagic = firstLine.trim().startsWith("%%") && !firstLine.trimEnd().endsWith("?") + val isMultiLineCommand = PsiTreeUtil.getParentOfType(atElement, PyStatementListContainer::class.java) != null || isCellMagic + + val hasCompleteStatement = atElement != null && !insideDocString && checkComplete(atElement) val prevLine = getLineAtOffset(editor.document, caretOffset) - val isCellMagic = prevLine.trim().startsWith("%%") && !prevLine.trimEnd().endsWith("?") - val isCellHelp = prevLine.trim().startsWith("%%") && prevLine.trimEnd().endsWith("?") - val isLineCellMagic = prevLine.trim().startsWith("%") - val hasCompleteStatement = atElement != null && !insideDocString && !isCellMagic && - (isCellHelp || isLineCellMagic || checkComplete(atElement)) - val currentLine = getLineAtOffset(editor.document, editor.expectedCaretOffset) - val indent = IndentHelperImpl.getIndent(project, PythonFileType.INSTANCE, currentLine, false) - - return indent == 0 || (hasCompleteStatement && prevLine.isBlank()) + return hasCompleteStatement && ((isMultiLineCommand && prevLine.isBlank()) || (!isMultiLineCommand)) } private fun isElementInsideDocString(atElement: PsiElement, caretOffset: Int): Boolean { - return (atElement.context is PyStringLiteralExpression && - (PyTokenTypes.TRIPLE_NODES.contains(atElement.node.elementType) - || atElement.node.elementType === PyTokenTypes.DOCSTRING) - && (atElement.textRange.endOffset > caretOffset || !isCompletDocString(atElement.text))) + return atElement.context is PyStringLiteralExpression && PyTokenTypes.TRIPLE_NODES.contains(atElement.node.elementType) + && (atElement.textRange.endOffset > caretOffset || !isCompleteDocString(atElement.text)) } private fun checkComplete(el: PsiElement): Boolean { @@ -89,8 +86,9 @@ class PyConsoleEnterHandler { if (compoundStatement != null) { return compoundStatement.statementList.statements.size != 0 } + if (el.parent == null) return false val topLevel = PyPsiUtils.getParentRightBefore(el, el.containingFile) - return topLevel != null && PsiTreeUtil.hasErrorElements(topLevel) + return topLevel != null && !PsiTreeUtil.hasErrorElements(topLevel) } private fun findFirstNoneSpaceElement(psiFile: PsiFile, offset: Int): PsiElement? { @@ -110,7 +108,7 @@ class PyConsoleEnterHandler { return doc.getText(TextRange(start, end)) } - private fun isCompletDocString(str: String): Boolean { + private fun isCompleteDocString(str: String): Boolean { val prefixLen = PyStringLiteralExpressionImpl.getPrefixLength(str) val text = str.substring(prefixLen) for (token in arrayOf("\"\"\"", "'''")) { diff --git a/python/testSrc/com/jetbrains/python/PyConsoleEnterHandlerTest.kt b/python/testSrc/com/jetbrains/python/PyConsoleEnterHandlerTest.kt index 24d25db3a6d1..dfb851f2738d 100644 --- a/python/testSrc/com/jetbrains/python/PyConsoleEnterHandlerTest.kt +++ b/python/testSrc/com/jetbrains/python/PyConsoleEnterHandlerTest.kt @@ -50,8 +50,11 @@ class PyConsoleEnterHandlerTest : PyTestCase() { fun testTripleQuotes() { assertFalse(push("'''abs")) + } - + fun testSingleQuote() { + assertTrue(push("'a'")) + assertTrue(push("a = 'abc'")) } fun testSimpleSingleLine() { @@ -109,6 +112,15 @@ class PyConsoleEnterHandlerTest : PyTestCase() { } + fun testMultiLineIf() { + assertFalse(push("if True:")) + assertFalse(push("")) + assertFalse(push("")) + assertFalse(push("")) + assertFalse(push("\ta = 1")) + assertTrue(push("")) + } + override fun tearDown() { Disposer.dispose(testRootDisposable) super.tearDown() From 46bfaec7622bbbcb102528cf167bbcd82c935ecf Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Thu, 13 Oct 2016 15:43:16 +0300 Subject: [PATCH 39/66] removed deprecated usages --- platform/core-api/src/com/intellij/openapi/util/Iconable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/core-api/src/com/intellij/openapi/util/Iconable.java b/platform/core-api/src/com/intellij/openapi/util/Iconable.java index 643223564c8d..d9147139ecf3 100644 --- a/platform/core-api/src/com/intellij/openapi/util/Iconable.java +++ b/platform/core-api/src/com/intellij/openapi/util/Iconable.java @@ -28,7 +28,7 @@ public interface Iconable { @Deprecated int ICON_FLAG_OPEN = 0x0004; @Deprecated int ICON_FLAG_CLOSED = 0x0008; - @MagicConstant(flags = {ICON_FLAG_VISIBILITY, ICON_FLAG_OPEN, ICON_FLAG_CLOSED, ICON_FLAG_READ_STATUS}) + @MagicConstant(flags = {ICON_FLAG_VISIBILITY, ICON_FLAG_READ_STATUS}) @interface IconFlags {} Icon getIcon(@IconFlags int flags); From 56579ee98ddf5b342978ee28945b38cd09783bf0 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Mon, 17 Oct 2016 14:18:29 +0300 Subject: [PATCH 40/66] concurrency issues in EA-89861 - NPE: TIntObjectHashMap.index --- .../src/com/intellij/openapi/util/Iconable.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/platform/core-api/src/com/intellij/openapi/util/Iconable.java b/platform/core-api/src/com/intellij/openapi/util/Iconable.java index d9147139ecf3..b2de180119b0 100644 --- a/platform/core-api/src/com/intellij/openapi/util/Iconable.java +++ b/platform/core-api/src/com/intellij/openapi/util/Iconable.java @@ -15,7 +15,8 @@ */ package com.intellij.openapi.util; -import gnu.trove.TIntObjectHashMap; +import com.intellij.util.containers.ConcurrentIntObjectMap; +import com.intellij.util.containers.ContainerUtil; import org.intellij.lang.annotations.MagicConstant; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -34,18 +35,18 @@ public interface Iconable { Icon getIcon(@IconFlags int flags); class LastComputedIcon { - private static final Key> LAST_COMPUTED_ICON = Key.create("lastComputedIcon"); + private static final Key> LAST_COMPUTED_ICON = Key.create("lastComputedIcon"); @Nullable public static Icon get(@NotNull UserDataHolder holder, int flags) { - TIntObjectHashMap map = holder.getUserData(LAST_COMPUTED_ICON); + ConcurrentIntObjectMap map = holder.getUserData(LAST_COMPUTED_ICON); return map == null ? null : map.get(flags); } public static void put(@NotNull UserDataHolder holder, Icon icon, int flags) { - TIntObjectHashMap map = holder.getUserData(LAST_COMPUTED_ICON); + ConcurrentIntObjectMap map = holder.getUserData(LAST_COMPUTED_ICON); if (map == null) { - map = ((UserDataHolderEx)holder).putUserDataIfAbsent(LAST_COMPUTED_ICON, new TIntObjectHashMap()); + map = ((UserDataHolderEx)holder).putUserDataIfAbsent(LAST_COMPUTED_ICON, ContainerUtil.createConcurrentIntObjectMap()); } map.put(flags, icon); } From a0c0082210315f6c2cd7c6e172927f6d3359f97d Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Mon, 17 Oct 2016 14:25:36 +0300 Subject: [PATCH 41/66] removed suppressions for unused methods esp. since some of them are --- .../openapi/vfs/DeprecatedVirtualFileSystem.java | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/platform/core-api/src/com/intellij/openapi/vfs/DeprecatedVirtualFileSystem.java b/platform/core-api/src/com/intellij/openapi/vfs/DeprecatedVirtualFileSystem.java index d206dd51cf4a..f33c224d7b44 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/DeprecatedVirtualFileSystem.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/DeprecatedVirtualFileSystem.java @@ -49,7 +49,6 @@ public abstract class DeprecatedVirtualFileSystem extends VirtualFileSystem { myEventDispatcher.removeListener(listener); } - @SuppressWarnings("unused") protected void firePropertyChanged(Object requestor, @NotNull VirtualFile file, @NotNull String propertyName, @@ -60,42 +59,36 @@ public abstract class DeprecatedVirtualFileSystem extends VirtualFileSystem { myEventDispatcher.getMulticaster().propertyChanged(event); } - @SuppressWarnings("unused") protected void fireContentsChanged(Object requestor, @NotNull VirtualFile file, long oldModificationStamp) { assertWriteAccessAllowed(); VirtualFileEvent event = new VirtualFileEvent(requestor, file, file.getParent(), oldModificationStamp, file.getModificationStamp()); myEventDispatcher.getMulticaster().contentsChanged(event); } - @SuppressWarnings("unused") protected void fireFileCreated(@Nullable Object requestor, @NotNull VirtualFile file) { assertWriteAccessAllowed(); VirtualFileEvent event = new VirtualFileEvent(requestor, file, file.getName(), file.getParent()); myEventDispatcher.getMulticaster().fileCreated(event); } - @SuppressWarnings("unused") protected void fireFileDeleted(Object requestor, @NotNull VirtualFile file, @NotNull String fileName, VirtualFile parent) { assertWriteAccessAllowed(); VirtualFileEvent event = new VirtualFileEvent(requestor, file, fileName, parent); myEventDispatcher.getMulticaster().fileDeleted(event); } - @SuppressWarnings("unused") protected void fireFileMoved(Object requestor, @NotNull VirtualFile file, VirtualFile oldParent) { assertWriteAccessAllowed(); VirtualFileMoveEvent event = new VirtualFileMoveEvent(requestor, file, oldParent, file.getParent()); myEventDispatcher.getMulticaster().fileMoved(event); } - @SuppressWarnings("unused") protected void fireFileCopied(@Nullable Object requestor, @NotNull VirtualFile originalFile, @NotNull VirtualFile createdFile) { assertWriteAccessAllowed(); VirtualFileCopyEvent event = new VirtualFileCopyEvent(requestor, originalFile, createdFile); myEventDispatcher.getMulticaster().fileCopied(event); } - @SuppressWarnings("unused") protected void fireBeforePropertyChange(Object requestor, @NotNull VirtualFile file, @NotNull String propertyName, @@ -106,21 +99,18 @@ public abstract class DeprecatedVirtualFileSystem extends VirtualFileSystem { myEventDispatcher.getMulticaster().beforePropertyChange(event); } - @SuppressWarnings("unused") protected void fireBeforeContentsChange(Object requestor, @NotNull VirtualFile file) { assertWriteAccessAllowed(); VirtualFileEvent event = new VirtualFileEvent(requestor, file, file.getName(), file.getParent()); myEventDispatcher.getMulticaster().beforeContentsChange(event); } - @SuppressWarnings("unused") protected void fireBeforeFileDeletion(Object requestor, @NotNull VirtualFile file) { assertWriteAccessAllowed(); VirtualFileEvent event = new VirtualFileEvent(requestor, file, file.getName(), file.getParent()); myEventDispatcher.getMulticaster().beforeFileDeletion(event); } - @SuppressWarnings("unused") protected void fireBeforeFileMovement(Object requestor, @NotNull VirtualFile file, VirtualFile newParent) { assertWriteAccessAllowed(); VirtualFileMoveEvent event = new VirtualFileMoveEvent(requestor, file, file.getParent(), newParent); @@ -166,7 +156,7 @@ public abstract class DeprecatedVirtualFileSystem extends VirtualFileSystem { @NotNull @Override public VirtualFile copyFile(Object requestor, @NotNull VirtualFile vFile, @NotNull VirtualFile newParent, @NotNull String copyName) throws IOException { - throw unsupported("copyFile() not supported", vFile); + throw unsupported("copyFile", vFile); } private UnsupportedOperationException unsupported(String op, VirtualFile vFile) { From fafb9d1efe7befb154d2b9748306be0b49006cea Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Mon, 17 Oct 2016 14:55:45 +0300 Subject: [PATCH 42/66] notnull --- .../quickFix/LightQuickFixTestCase.java | 24 +++++++++++-------- .../daemon/quickFix/QuickFixTestCase.java | 15 ++++++------ 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/LightQuickFixTestCase.java b/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/LightQuickFixTestCase.java index 8c9619b112b5..19ec879a4f35 100644 --- a/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/LightQuickFixTestCase.java +++ b/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/LightQuickFixTestCase.java @@ -60,7 +60,7 @@ public abstract class LightQuickFixTestCase extends LightDaemonAnalyzerTestCase return ActionHint.parse(file, contents); } - private static void doTestFor(final String testName, final QuickFixTestCase quickFixTestCase) { + private static void doTestFor(@NotNull String testName, @NotNull QuickFixTestCase quickFixTestCase) { final String relativePath = ObjectUtils.notNull(quickFixTestCase.getBasePath(), "") + "/" + BEFORE_PREFIX + testName; final String testFullPath = quickFixTestCase.getTestDataPath().replace(File.separatorChar, '/') + relativePath; final File testFile = new File(testFullPath); @@ -204,7 +204,7 @@ public abstract class LightQuickFixTestCase extends LightDaemonAnalyzerTestCase return files; } - protected void doSingleTest(String fileSuffix) { + protected void doSingleTest(@NotNull String fileSuffix) { doTestFor(fileSuffix, createWrapper()); } @@ -212,10 +212,12 @@ public abstract class LightQuickFixTestCase extends LightDaemonAnalyzerTestCase doTestFor(fileSuffix, createWrapper(testDataPath)); } + @NotNull protected QuickFixTestCase createWrapper() { return createWrapper(null); } + @NotNull protected QuickFixTestCase createWrapper(final String testDataPath) { return new QuickFixTestCase() { public String myTestDataPath = testDataPath; @@ -225,6 +227,7 @@ public abstract class LightQuickFixTestCase extends LightDaemonAnalyzerTestCase return LightQuickFixTestCase.this.getBasePath(); } + @NotNull @Override public String getTestDataPath() { if (myTestDataPath == null) { @@ -240,27 +243,27 @@ public abstract class LightQuickFixTestCase extends LightDaemonAnalyzerTestCase } @Override - public void beforeActionStarted(String testName, String contents) { + public void beforeActionStarted(@NotNull String testName, @NotNull String contents) { LightQuickFixTestCase.this.beforeActionStarted(testName, contents); } @Override - public void afterActionCompleted(String testName, String contents) { + public void afterActionCompleted(@NotNull String testName, @NotNull String contents) { LightQuickFixTestCase.this.afterActionCompleted(testName, contents); } @Override - public void doAction(ActionHint actionHint, String testFullPath, String testName) throws Exception { + public void doAction(@NotNull ActionHint actionHint, @NotNull String testFullPath, @NotNull String testName) throws Exception { LightQuickFixTestCase.this.doAction(actionHint, testFullPath, testName); } @Override - public void checkResultByFile(String s, @NotNull String expectedFilePath, boolean b) throws Exception { - LightQuickFixTestCase.this.checkResultByFile(s, expectedFilePath, b); + public void checkResultByFile(@NotNull String message, @NotNull String expectedFilePath, boolean ignoreTrailingSpaces) throws Exception { + LightQuickFixTestCase.this.checkResultByFile(message, expectedFilePath, ignoreTrailingSpaces); } @Override - public IntentionAction findActionWithText(String text) { + public IntentionAction findActionWithText(@NotNull String text) { return LightQuickFixTestCase.this.findActionWithText(text); } @@ -270,7 +273,7 @@ public abstract class LightQuickFixTestCase extends LightDaemonAnalyzerTestCase } @Override - public void invoke(IntentionAction action) { + public void invoke(@NotNull IntentionAction action) { LightQuickFixTestCase.invoke(action); } @@ -287,7 +290,7 @@ public abstract class LightQuickFixTestCase extends LightDaemonAnalyzerTestCase } @Override - public void configureFromFileText(String name, String contents) throws IOException { + public void configureFromFileText(@NotNull String name, @NotNull String contents) throws IOException { LightPlatformCodeInsightTestCase.configureFromFileText(name, contents, true); } @@ -313,6 +316,7 @@ public abstract class LightQuickFixTestCase extends LightDaemonAnalyzerTestCase return getAvailableActions(getEditor(), getFile()); } + @NotNull public static List getAvailableActions(@NotNull Editor editor, @NotNull PsiFile file) { return CodeInsightTestFixtureImpl.getAvailableIntentions(editor, file); } diff --git a/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/QuickFixTestCase.java b/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/QuickFixTestCase.java index 8c00ae4149dc..e69b636fecd6 100644 --- a/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/QuickFixTestCase.java +++ b/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/QuickFixTestCase.java @@ -31,24 +31,25 @@ import java.util.List; public interface QuickFixTestCase { String getBasePath(); + @NotNull String getTestDataPath(); @NotNull ActionHint parseActionHintImpl(@NotNull PsiFile file, @NotNull String contents); - void beforeActionStarted(String testName, String contents); + void beforeActionStarted(@NotNull String testName, @NotNull String contents); - void afterActionCompleted(String testName, String contents); + void afterActionCompleted(@NotNull String testName, @NotNull String contents); - void doAction(ActionHint actionHint, String testFullPath, String testName) throws Exception; + void doAction(@NotNull ActionHint actionHint, @NotNull String testFullPath, @NotNull String testName) throws Exception; - void checkResultByFile(String s, @NotNull String expectedFilePath, boolean b) throws Exception; + void checkResultByFile(@NotNull String message, @NotNull String expectedFilePath, boolean ignoreTrailingSpaces) throws Exception; - IntentionAction findActionWithText(String text); + IntentionAction findActionWithText(@NotNull String text); boolean shouldBeAvailableAfterExecution(); - void invoke(IntentionAction action); + void invoke(@NotNull IntentionAction action); @NotNull List doHighlighting(); @@ -58,7 +59,7 @@ public interface QuickFixTestCase { void bringRealEditorBack(); - void configureFromFileText(String name, String contents) throws Throwable; + void configureFromFileText(@NotNull String name, @NotNull String contents) throws Throwable; PsiFile getFile(); From c1ba655f17bc66d4c93aca8277b92376888cecff Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Mon, 17 Oct 2016 15:33:07 +0300 Subject: [PATCH 43/66] cleanup --- .../intellij/openapi/util/SimpleModificationTracker.java | 5 +++-- .../com/intellij/openapi/fileTypes/FileTypeManager.java | 7 +++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/platform/core-api/src/com/intellij/openapi/util/SimpleModificationTracker.java b/platform/core-api/src/com/intellij/openapi/util/SimpleModificationTracker.java index e1e9fff31cc5..b83dc9cfe765 100644 --- a/platform/core-api/src/com/intellij/openapi/util/SimpleModificationTracker.java +++ b/platform/core-api/src/com/intellij/openapi/util/SimpleModificationTracker.java @@ -28,7 +28,8 @@ public class SimpleModificationTracker implements ModificationTracker { private static final AtomicIntegerFieldUpdater UPDATER = AtomicIntegerFieldUpdater.newUpdater(SimpleModificationTracker.class, "myCounter"); - @SuppressWarnings("unused") private volatile int myCounter; + @SuppressWarnings("unused") + private volatile int myCounter; @Override public long getModificationCount() { @@ -36,7 +37,7 @@ public class SimpleModificationTracker implements ModificationTracker { } public void incModificationCount() { - UPDATER.incrementAndGet(this); + incAndGetModificationCount(); } public long incAndGetModificationCount() { diff --git a/platform/platform-api/src/com/intellij/openapi/fileTypes/FileTypeManager.java b/platform/platform-api/src/com/intellij/openapi/fileTypes/FileTypeManager.java index ecc24a8a5579..0791b837820e 100644 --- a/platform/platform-api/src/com/intellij/openapi/fileTypes/FileTypeManager.java +++ b/platform/platform-api/src/com/intellij/openapi/fileTypes/FileTypeManager.java @@ -19,7 +19,6 @@ import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.CachedSingletonsRegistry; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Getter; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.messages.Topic; import org.jetbrains.annotations.NonNls; @@ -36,7 +35,7 @@ import java.util.List; public abstract class FileTypeManager extends FileTypeRegistry { static { - FileTypeRegistry.ourInstanceGetter = () -> FileTypeManager.getInstance(); + FileTypeRegistry.ourInstanceGetter = () -> getInstance(); } private static FileTypeManager ourInstance = CachedSingletonsRegistry.markCachedField(FileTypeManager.class); @@ -59,7 +58,7 @@ public abstract class FileTypeManager extends FileTypeRegistry { } /** - * @deprecated use {@link com.intellij.openapi.fileTypes.FileTypeFactory} instead + * @deprecated use {@link FileTypeFactory} instead */ public abstract void registerFileType(@NotNull FileType type, @NotNull List defaultAssociations); @@ -69,7 +68,7 @@ public abstract class FileTypeManager extends FileTypeRegistry { * @param type The file type to register. * @param defaultAssociatedExtensions The list of extensions which cause the file to be * treated as the specified file type. The extensions should not start with '.'. - * @deprecated use {@link com.intellij.openapi.fileTypes.FileTypeFactory} instead + * @deprecated use {@link FileTypeFactory} instead */ public final void registerFileType(@NotNull FileType type, @NonNls @Nullable String... defaultAssociatedExtensions) { List matchers = new ArrayList<>(); From dd6384ac01c07b8eb9b263c8f842d7b75736b971 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Mon, 17 Oct 2016 15:43:07 +0300 Subject: [PATCH 44/66] made assertWriteAcquired() generate expensive garbage in tests and debug only --- .../openapi/editor/impl/IntervalTreeImpl.java | 10 ++++++++-- .../openapi/editor/impl/RangeMarkerTree.java | 16 ++++++---------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/platform/core-impl/src/com/intellij/openapi/editor/impl/IntervalTreeImpl.java b/platform/core-impl/src/com/intellij/openapi/editor/impl/IntervalTreeImpl.java index 3233e3359ea3..1ce0f42443a5 100644 --- a/platform/core-impl/src/com/intellij/openapi/editor/impl/IntervalTreeImpl.java +++ b/platform/core-impl/src/com/intellij/openapi/editor/impl/IntervalTreeImpl.java @@ -15,6 +15,8 @@ */ package com.intellij.openapi.editor.impl; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.ex.MarkupIterator; import com.intellij.openapi.util.Getter; import com.intellij.util.IncorrectOperationException; @@ -42,6 +44,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * User: cdr */ abstract class IntervalTreeImpl extends RedBlackTree implements IntervalTree { + static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.RangeMarkerTree"); + static final boolean DEBUG = LOG.isDebugEnabled() || ApplicationManager.getApplication() != null && (ApplicationManager.getApplication().isUnitTestMode() || ApplicationManager.getApplication().isInternal()); private int keySize; // number of all intervals, counting all duplicates, some of them maybe gced final ReadWriteLock l = new ReentrantReadWriteLock(); @@ -66,7 +70,7 @@ abstract class IntervalTreeImpl extends RedBlackTree< @NotNull private final IntervalTreeImpl myIntervalTree; - public IntervalNode(@NotNull IntervalTreeImpl intervalTree, @NotNull E key, int start, int end) { + IntervalNode(@NotNull IntervalTreeImpl intervalTree, @NotNull E key, int start, int end) { // maxEnd == 0 so to not disrupt existing maxes myIntervalTree = intervalTree; myStart = start; @@ -374,7 +378,9 @@ abstract class IntervalTreeImpl extends RedBlackTree< } private void assertUnderWriteLock() { - assert isAcquired(l.writeLock()) : l.writeLock(); + if (DEBUG) { + assert isAcquired(l.writeLock()) : l.writeLock(); + } } private static boolean isAcquired(@NotNull Lock l) { String s = l.toString(); diff --git a/platform/core-impl/src/com/intellij/openapi/editor/impl/RangeMarkerTree.java b/platform/core-impl/src/com/intellij/openapi/editor/impl/RangeMarkerTree.java index 14ef8ccc977e..e012f11af497 100644 --- a/platform/core-impl/src/com/intellij/openapi/editor/impl/RangeMarkerTree.java +++ b/platform/core-impl/src/com/intellij/openapi/editor/impl/RangeMarkerTree.java @@ -17,7 +17,6 @@ package com.intellij.openapi.editor.impl; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.impl.ApplicationInfoImpl; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.ex.PrioritizedDocumentListener; @@ -38,9 +37,6 @@ import java.util.concurrent.atomic.AtomicInteger; * User: cdr */ public class RangeMarkerTree extends IntervalTreeImpl { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.RangeMarkerTree"); - private static final boolean DEBUG = LOG.isDebugEnabled() || ApplicationManager.getApplication() != null && (ApplicationManager.getApplication().isUnitTestMode() || ApplicationManager.getApplication().isInternal()); - private final PrioritizedDocumentListener myListener; private final Document myDocument; @@ -159,12 +155,12 @@ public class RangeMarkerTree extends IntervalTreeImpl rangeMarkerTree, - @NotNull T key, - int start, - int end, - boolean greedyToLeft, - boolean greedyToRight) { + RMNode(@NotNull RangeMarkerTree rangeMarkerTree, + @NotNull T key, + int start, + int end, + boolean greedyToLeft, + boolean greedyToRight) { super(rangeMarkerTree, key, start, end); setFlag(EXPAND_TO_LEFT_FLAG, greedyToLeft); setFlag(EXPAND_TO_RIGHT_FLAG, greedyToRight); From e863423acfb6656a6414ac061f4dc901d2d0a1cd Mon Sep 17 00:00:00 2001 From: Anton Tarasov Date: Mon, 17 Oct 2016 15:45:15 +0300 Subject: [PATCH 45/66] IDEA-162563 Action menus do not have icons under HiDPI linux --- .../src/com/intellij/util/ui/EmptyIcon.java | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/platform/util/src/com/intellij/util/ui/EmptyIcon.java b/platform/util/src/com/intellij/util/ui/EmptyIcon.java index 843eb9a08a05..7b630b1068cb 100644 --- a/platform/util/src/com/intellij/util/ui/EmptyIcon.java +++ b/platform/util/src/com/intellij/util/ui/EmptyIcon.java @@ -32,7 +32,7 @@ import java.util.Map; * @see ColorIcon */ public class EmptyIcon implements Icon, ScalableIcon { - private static final Map cache = new HashMap(); + private static final Map cache = new HashMap(); public static final Icon ICON_16 = create(16); public static final Icon ICON_18 = create(18); @@ -45,19 +45,25 @@ public class EmptyIcon implements Icon, ScalableIcon { private EmptyIcon myScaledCache; public static Icon create(int size) { - Icon icon = cache.get(size); - if (icon == null && size < 129) { - cache.put(size, icon = new EmptyIcon(size, size)); + return create(size, size, true); + } + + private static Icon create(int width, int height, boolean autoScale) { + int size = (width == height) ? width : -1; + EmptyIcon icon = cache.get(size); + if (icon == null) { + icon = new EmptyIcon(width, height); + if (size < JBUI.scale(129) && size > 0) cache.put(size, icon); } - return icon == null ? new EmptyIcon(size, size) : icon; + return autoScale ? icon.scale(JBUI.scale(1f)) : icon; } public static Icon create(int width, int height) { - return width == height ? create(width) : new EmptyIcon(width, height); + return create(width, height, true); } public static Icon create(@NotNull Icon base) { - return create(base.getIconWidth(), base.getIconHeight()); + return create(base.getIconWidth(), base.getIconHeight(), false); } /** From 11b8f5678a4f57af5b13807d4c5d47c30ca605f2 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Mon, 17 Oct 2016 14:43:46 +0200 Subject: [PATCH 46/66] add signup param --- .../src/com/intellij/diagnostic/JetBrainsAccountDialog.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/diagnostic/JetBrainsAccountDialog.kt b/platform/platform-impl/src/com/intellij/diagnostic/JetBrainsAccountDialog.kt index 99dbed90dab6..cc009b7cd6f6 100644 --- a/platform/platform-impl/src/com/intellij/diagnostic/JetBrainsAccountDialog.kt +++ b/platform/platform-impl/src/com/intellij/diagnostic/JetBrainsAccountDialog.kt @@ -49,7 +49,7 @@ fun showJetBrainsAccountDialog(parent: Component, project: Project? = null): Dia link("Forgot password?") { BrowserUtil.browse("https://account.jetbrains.com/forgot-password?username=${userField.text.trim().encodeUrlQueryParameter()}") } } } - noteRow("""Do not have an account? Sign Up""") + noteRow("""Do not have an account? Sign Up""") } return dialog( From b301b92958586eaa1a55555e2ce1bcd1608d6fae Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Mon, 17 Oct 2016 14:51:32 +0200 Subject: [PATCH 47/66] WI-33574 Authentication popup doesn't appear in case password is missing --- .../src/com/intellij/credentialStore/CredentialAttributes.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-api/src/com/intellij/credentialStore/CredentialAttributes.kt b/platform/platform-api/src/com/intellij/credentialStore/CredentialAttributes.kt index 4498a7ea53c7..c156af30d340 100644 --- a/platform/platform-api/src/com/intellij/credentialStore/CredentialAttributes.kt +++ b/platform/platform-api/src/com/intellij/credentialStore/CredentialAttributes.kt @@ -62,7 +62,7 @@ fun CredentialAttributes(requestor: Class<*>, userName: String?) = CredentialAtt fun Credentials?.isFulfilled() = this != null && userName != null && !password.isNullOrEmpty() fun Credentials?.hasOnlyUserName() = this != null && userName != null && password.isNullOrEmpty() -fun Credentials?.isEmpty() = this == null || (userName == null && password == null) +fun Credentials?.isEmpty() = this == null || (userName == null && password.isNullOrEmpty()) // input will be cleared @JvmOverloads From 6112e0cdae4cd1120abc241b7b5e43c685b37113 Mon Sep 17 00:00:00 2001 From: Elizaveta Shashkova Date: Mon, 17 Oct 2016 15:48:53 +0300 Subject: [PATCH 48/66] Tests: fix import error --- python/helpers/pydev/_pydevd_bundle/pydevd_signature.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_signature.py b/python/helpers/pydev/_pydevd_bundle/pydevd_signature.py index 8203abca50f4..2ec20ed63b10 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_signature.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_signature.py @@ -7,7 +7,7 @@ else: trace._warn = lambda *args: None # workaround for http://bugs.python.org/issue17143 (PY-8706) from _pydevd_bundle.pydevd_comm import CMD_SIGNATURE_CALL_TRACE, NetCommand -from _pydevd_bundle import pydevd_vars +from _pydevd_bundle import pydevd_xml from _pydevd_bundle.pydevd_constants import xrange, dict_iter_items from _pydevd_bundle import pydevd_utils from _pydevd_bundle.pydevd_utils import get_clsname_for_code @@ -153,13 +153,13 @@ class CallSignatureCache(object): def create_signature_message(signature): cmdTextList = [""] - cmdTextList.append('' % (pydevd_vars.make_valid_xml_value(signature.file), pydevd_vars.make_valid_xml_value(signature.name))) + cmdTextList.append('' % (pydevd_xml.make_valid_xml_value(signature.file), pydevd_xml.make_valid_xml_value(signature.name))) for arg in signature.args: - cmdTextList.append('' % (pydevd_vars.make_valid_xml_value(arg[0]), pydevd_vars.make_valid_xml_value(arg[1]))) + cmdTextList.append('' % (pydevd_xml.make_valid_xml_value(arg[0]), pydevd_xml.make_valid_xml_value(arg[1]))) if signature.return_type is not None: - cmdTextList.append('' % (pydevd_vars.make_valid_xml_value(signature.return_type))) + cmdTextList.append('' % (pydevd_xml.make_valid_xml_value(signature.return_type))) cmdTextList.append("") cmdText = ''.join(cmdTextList) From 06bb6defe16a07f1231a1683b86cc730332824b8 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Fri, 14 Oct 2016 17:58:58 +0300 Subject: [PATCH 49/66] vcs: cleanup - remove unnecessary parameter --- .../intellij/openapi/vcs/actions/AnnotateToggleAction.java | 4 ++-- .../intellij/openapi/vcs/actions/AnnotationFieldGutter.java | 4 ++-- .../openapi/vcs/actions/HighlightedAdditionalColumn.java | 3 +-- .../openapi/vcs/actions/MergeSourceAvailableMarkerGutter.java | 3 +-- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java index 005cd6512791..e09a45efe5dd 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java @@ -132,7 +132,7 @@ public class AnnotateToggleAction extends ToggleAction implements DumbAware { final CurrentRevisionAnnotationFieldGutter currentRevisionGutter = new CurrentRevisionAnnotationFieldGutter(fileAnnotation, revisionAspect, presentation, bgColorMap); final MergeSourceAvailableMarkerGutter mergeSourceGutter = - new MergeSourceAvailableMarkerGutter(fileAnnotation, null, presentation, bgColorMap); + new MergeSourceAvailableMarkerGutter(fileAnnotation, presentation, bgColorMap); SwitchAnnotationSourceAction switchAction = new SwitchAnnotationSourceAction(switcher, editorGutter); presentation.addAction(switchAction); @@ -155,7 +155,7 @@ public class AnnotateToggleAction extends ToggleAction implements DumbAware { if (historyIds != null) { gutters.add(new HistoryIdColumn(fileAnnotation, presentation, bgColorMap, historyIds)); } - gutters.add(new HighlightedAdditionalColumn(fileAnnotation, null, presentation, bgColorMap)); + gutters.add(new HighlightedAdditionalColumn(fileAnnotation, presentation, bgColorMap)); final AnnotateActionGroup actionGroup = new AnnotateActionGroup(gutters, editorGutter, bgColorMap); presentation.addAction(actionGroup, 1); gutters.add(new ExtraFieldGutter(fileAnnotation, presentation, bgColorMap, actionGroup)); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationFieldGutter.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationFieldGutter.java index 2ce48f59a1b5..55a051dafd0d 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationFieldGutter.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationFieldGutter.java @@ -42,13 +42,13 @@ import java.util.Map; */ public class AnnotationFieldGutter implements ActiveAnnotationGutter { @NotNull protected final FileAnnotation myAnnotation; - protected final LineAnnotationAspect myAspect; + @Nullable protected final LineAnnotationAspect myAspect; @NotNull private final TextAnnotationPresentation myPresentation; private final boolean myIsGutterAction; @Nullable private Couple> myColorScheme; AnnotationFieldGutter(@NotNull FileAnnotation annotation, - LineAnnotationAspect aspect, + @Nullable LineAnnotationAspect aspect, @NotNull TextAnnotationPresentation presentation, @Nullable Couple> colorScheme) { myAnnotation = annotation; diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/HighlightedAdditionalColumn.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/HighlightedAdditionalColumn.java index ebc9ed5e8638..0e0fab8bfd65 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/HighlightedAdditionalColumn.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/HighlightedAdditionalColumn.java @@ -28,10 +28,9 @@ import java.util.Map; class HighlightedAdditionalColumn extends AnnotationFieldGutter { HighlightedAdditionalColumn(FileAnnotation annotation, - LineAnnotationAspect aspect, TextAnnotationPresentation presentation, Couple> colorScheme) { - super(annotation, aspect, presentation, colorScheme); + super(annotation, null, presentation, colorScheme); } @Override diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/MergeSourceAvailableMarkerGutter.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/MergeSourceAvailableMarkerGutter.java index 6c2ef0729559..3e78713832d3 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/MergeSourceAvailableMarkerGutter.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/MergeSourceAvailableMarkerGutter.java @@ -33,10 +33,9 @@ class MergeSourceAvailableMarkerGutter extends AnnotationFieldGutter implements private boolean myTurnedOn; MergeSourceAvailableMarkerGutter(FileAnnotation annotation, - LineAnnotationAspect aspect, TextAnnotationPresentation highlighting, Couple> colorScheme) { - super(annotation, aspect, highlighting, colorScheme); + super(annotation, null, highlighting, colorScheme); } @Override From 2bb64c1b33372413905008fcfac1b9c13cf9e8d8 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Fri, 14 Oct 2016 18:21:25 +0300 Subject: [PATCH 50/66] vcs: extract LineAnnotationAspect-related logic from AnnotationFieldGutter * move name shortener to the ShortNameType --- .../vcs/actions/AnnotateToggleAction.java | 2 +- .../vcs/actions/AnnotationFieldGutter.java | 91 ++++-------------- .../actions/AspectAnnotationFieldGutter.java | 94 +++++++++++++++++++ .../CurrentRevisionAnnotationFieldGutter.java | 2 +- .../openapi/vcs/actions/ExtraFieldGutter.java | 2 +- .../actions/HighlightedAdditionalColumn.java | 3 +- .../openapi/vcs/actions/HistoryIdColumn.java | 7 +- .../MergeSourceAvailableMarkerGutter.java | 7 +- .../openapi/vcs/actions/ShortNameType.java | 35 +++++++ .../openapi/vcs/actions/ShowShortenNames.java | 4 - 10 files changed, 161 insertions(+), 86 deletions(-) create mode 100644 platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AspectAnnotationFieldGutter.java diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java index e09a45efe5dd..3554612676d1 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java @@ -148,7 +148,7 @@ public class AnnotateToggleAction extends ToggleAction implements DumbAware { final LineAnnotationAspect[] aspects = fileAnnotation.getAspects(); for (LineAnnotationAspect aspect : aspects) { - gutters.add(new AnnotationFieldGutter(fileAnnotation, aspect, presentation, bgColorMap)); + gutters.add(new AspectAnnotationFieldGutter(fileAnnotation, aspect, presentation, bgColorMap)); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationFieldGutter.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationFieldGutter.java index 55a051dafd0d..0e0c2b0cdb18 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationFieldGutter.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationFieldGutter.java @@ -17,18 +17,14 @@ package com.intellij.openapi.vcs.actions; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.EditorGutterAction; import com.intellij.openapi.editor.colors.ColorKey; import com.intellij.openapi.editor.colors.EditorFontType; import com.intellij.openapi.util.Couple; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.ProjectLevelVcsManager; import com.intellij.openapi.vcs.annotate.FileAnnotation; -import com.intellij.openapi.vcs.annotate.LineAnnotationAspect; import com.intellij.openapi.vcs.annotate.TextAnnotationPresentation; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.vcsUtil.VcsUtil; -import com.intellij.xml.util.XmlStringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,103 +36,63 @@ import java.util.Map; * @author Irina Chernushina * @author Konstantin Bulenkov */ -public class AnnotationFieldGutter implements ActiveAnnotationGutter { +public abstract class AnnotationFieldGutter implements ActiveAnnotationGutter { @NotNull protected final FileAnnotation myAnnotation; - @Nullable protected final LineAnnotationAspect myAspect; @NotNull private final TextAnnotationPresentation myPresentation; - private final boolean myIsGutterAction; @Nullable private Couple> myColorScheme; AnnotationFieldGutter(@NotNull FileAnnotation annotation, - @Nullable LineAnnotationAspect aspect, @NotNull TextAnnotationPresentation presentation, @Nullable Couple> colorScheme) { myAnnotation = annotation; - myAspect = aspect; myPresentation = presentation; - myIsGutterAction = myAspect instanceof EditorGutterAction; myColorScheme = colorScheme; } public boolean isGutterAction() { - return myIsGutterAction; - } - - public String getLineText(int line, Editor editor) { - final String value = isAvailable() ? myAspect.getValue(line) : ""; - if (myAspect.getId() == LineAnnotationAspect.AUTHOR && ShowShortenNames.isSet()) { - return shorten(value, ShowShortenNames.getType()); - } - return value; - } - - @Nullable - public static String shorten(String name, ShortNameType type) { - if (name != null) { - // Vasya Pupkin -> Vasya Pupkin - final int[] ind = {name.indexOf('<'), name.indexOf('@'), name.indexOf('>')}; - if (0 < ind[0] && ind[0] < ind[1] && ind[1] < ind[2]) { - return shorten(name.substring(0, ind[0]).trim(), type); - } - - // vasya.pupkin@email.com --> vasya pupkin - if (!name.contains(" ") && name.contains("@")) { //simple e-mail check. john@localhost - final String firstPart = name.substring(0, name.indexOf('@')).replace('.', ' ').replace('_', ' ').replace('-', ' '); - if (firstPart.length() < name.length()) { - return shorten(firstPart, type); - } else { - return firstPart; - } - } - - final List strings = StringUtil.split(name.replace('.', ' ').replace('_', ' ').replace('-', ' '), " "); - if (strings.size() > 1) { - //Middle name check: Vasya S. Pupkin - return StringUtil.capitalize(type == ShortNameType.FIRSTNAME ? strings.get(0) : strings.get(strings.size() - 1)); - } - } - return name; + return false; } @Nullable + @Override public String getToolTip(final int line, final Editor editor) { - return isAvailable() ? XmlStringUtil.escapeString(myAnnotation.getToolTip(line)) : null; + return null; } + @Override public void doAction(int line) { - if (myIsGutterAction) { - ((EditorGutterAction)myAspect).doAction(line); - } } + @Override public Cursor getCursor(final int line) { - if (myIsGutterAction) { - return ((EditorGutterAction)myAspect).getCursor(line); - } else { - return Cursor.getDefaultCursor(); - } - + return Cursor.getDefaultCursor(); } + @Override public EditorFontType getStyle(final int line, final Editor editor) { return myPresentation.getFontType(line); } @Nullable + @Override public ColorKey getColor(final int line, final Editor editor) { return myPresentation.getColor(line); } + @Override public List getPopupActions(int line, final Editor editor) { return myPresentation.getActions(line); } + @Override public void gutterClosed() { - ProjectLevelVcsManager.getInstance(myAnnotation.getProject()).getAnnotationLocalChangesListener().unregisterAnnotation(myAnnotation.getFile(), myAnnotation); + ProjectLevelVcsManager.getInstance(myAnnotation.getProject()).getAnnotationLocalChangesListener() + .unregisterAnnotation(myAnnotation.getFile(), myAnnotation); myAnnotation.dispose(); } @Nullable + @Override public Color getBgColor(int line, Editor editor) { if (myColorScheme == null) return null; ColorMode type = ShowAnnotationColorsAction.getType(); @@ -147,23 +103,16 @@ public class AnnotationFieldGutter implements ActiveAnnotationGutter { return colorMap.get(number); } + public boolean isShowByDefault() { + return true; + } + public boolean isAvailable() { - if (myAspect == null) return false; - return VcsUtil.isAspectAvailableByDefault(getID(), myAspect.isShowByDefault()); + return VcsUtil.isAspectAvailableByDefault(getID(), isShowByDefault()); } @Nullable public String getID() { - return myAspect == null ? null : myAspect.getId(); - } - - - public static void main(String[] args) { - assert shorten("Vasya Pavlovich Pupkin ", ShortNameType.FIRSTNAME).equals("Vasya"); - assert shorten("Vasya Pavlovich Pupkin ", ShortNameType.LASTNAME).equals("Pupkin"); - assert shorten("Vasya Pavlovich Pupkin", ShortNameType.FIRSTNAME).equals("Vasya"); - assert shorten("Vasya Pavlovich Pupkin", ShortNameType.LASTNAME).equals("Pupkin"); - assert shorten("vasya.pupkin@localhost.com", ShortNameType.LASTNAME).equals("Pupkin"); - assert shorten("vasya.pupkin@localhost.com", ShortNameType.FIRSTNAME).equals("Vasya"); + return null; } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AspectAnnotationFieldGutter.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AspectAnnotationFieldGutter.java new file mode 100644 index 000000000000..086d8e927918 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AspectAnnotationFieldGutter.java @@ -0,0 +1,94 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.vcs.actions; + +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.EditorGutterAction; +import com.intellij.openapi.util.Couple; +import com.intellij.openapi.vcs.annotate.FileAnnotation; +import com.intellij.openapi.vcs.annotate.LineAnnotationAspect; +import com.intellij.openapi.vcs.annotate.TextAnnotationPresentation; +import com.intellij.openapi.vcs.history.VcsRevisionNumber; +import com.intellij.xml.util.XmlStringUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.*; +import java.util.Map; + +/** + * @author Irina Chernushina + * @author Konstantin Bulenkov + */ +public class AspectAnnotationFieldGutter extends AnnotationFieldGutter { + @NotNull protected final LineAnnotationAspect myAspect; + private final boolean myIsGutterAction; + + public AspectAnnotationFieldGutter(@NotNull FileAnnotation annotation, + @NotNull LineAnnotationAspect aspect, + @NotNull TextAnnotationPresentation presentation, + @Nullable Couple> colorScheme) { + super(annotation, presentation, colorScheme); + myAspect = aspect; + myIsGutterAction = myAspect instanceof EditorGutterAction; + } + + @Override + public boolean isGutterAction() { + return myIsGutterAction; + } + + @Override + public String getLineText(int line, Editor editor) { + final String value = isAvailable() ? myAspect.getValue(line) : ""; + if (myAspect.getId() == LineAnnotationAspect.AUTHOR) { + return ShortNameType.shorten(value, ShowShortenNames.getType()); + } + return value; + } + + @Nullable + @Override + public String getToolTip(final int line, final Editor editor) { + return isAvailable() ? XmlStringUtil.escapeString(myAnnotation.getToolTip(line)) : null; + } + + @Override + public void doAction(int line) { + if (myIsGutterAction) { + ((EditorGutterAction)myAspect).doAction(line); + } + } + + @Override + public Cursor getCursor(final int line) { + if (myIsGutterAction) { + return ((EditorGutterAction)myAspect).getCursor(line); + } + return super.getCursor(line); + } + + @Override + public boolean isShowByDefault() { + return myAspect.isShowByDefault(); + } + + @Nullable + @Override + public String getID() { + return myAspect.getId(); + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/CurrentRevisionAnnotationFieldGutter.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/CurrentRevisionAnnotationFieldGutter.java index 78bbc5fdcbf9..8a64690392d9 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/CurrentRevisionAnnotationFieldGutter.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/CurrentRevisionAnnotationFieldGutter.java @@ -34,7 +34,7 @@ import java.util.Map; * * @author Konstantin Bulenkov */ -class CurrentRevisionAnnotationFieldGutter extends AnnotationFieldGutter implements Consumer { +class CurrentRevisionAnnotationFieldGutter extends AspectAnnotationFieldGutter implements Consumer { // merge source showing is turned on private boolean myTurnedOn; diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/ExtraFieldGutter.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/ExtraFieldGutter.java index 77828f9fbd9a..dde2dd29a882 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/ExtraFieldGutter.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/ExtraFieldGutter.java @@ -32,7 +32,7 @@ public class ExtraFieldGutter extends AnnotationFieldGutter { public ExtraFieldGutter(FileAnnotation fileAnnotation, AnnotationPresentation presentation, Couple> bgColorMap, AnnotateActionGroup actionGroup) { - super(fileAnnotation, null, presentation, bgColorMap); + super(fileAnnotation, presentation, bgColorMap); myActionGroup = actionGroup; } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/HighlightedAdditionalColumn.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/HighlightedAdditionalColumn.java index 0e0fab8bfd65..56adfb62444c 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/HighlightedAdditionalColumn.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/HighlightedAdditionalColumn.java @@ -18,7 +18,6 @@ package com.intellij.openapi.vcs.actions; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.util.Couple; import com.intellij.openapi.vcs.annotate.FileAnnotation; -import com.intellij.openapi.vcs.annotate.LineAnnotationAspect; import com.intellij.openapi.vcs.annotate.TextAnnotationPresentation; import com.intellij.openapi.vcs.history.VcsRevisionNumber; @@ -30,7 +29,7 @@ class HighlightedAdditionalColumn extends AnnotationFieldGutter { HighlightedAdditionalColumn(FileAnnotation annotation, TextAnnotationPresentation presentation, Couple> colorScheme) { - super(annotation, null, presentation, colorScheme); + super(annotation, presentation, colorScheme); } @Override diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/HistoryIdColumn.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/HistoryIdColumn.java index 8fe73dc6f0bd..f9013b0c5ba7 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/HistoryIdColumn.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/HistoryIdColumn.java @@ -21,7 +21,6 @@ import com.intellij.openapi.vcs.VcsBundle; import com.intellij.openapi.vcs.annotate.FileAnnotation; import com.intellij.openapi.vcs.annotate.TextAnnotationPresentation; import com.intellij.openapi.vcs.history.VcsRevisionNumber; -import com.intellij.vcsUtil.VcsUtil; import java.awt.*; import java.util.Map; @@ -36,7 +35,7 @@ class HistoryIdColumn extends AnnotationFieldGutter { final TextAnnotationPresentation presentation, Couple> colorScheme, Map ids) { - super(annotation, null, presentation, colorScheme); + super(annotation, presentation, colorScheme); myHistoryIds = ids; } @@ -59,8 +58,8 @@ class HistoryIdColumn extends AnnotationFieldGutter { } @Override - public boolean isAvailable() { - return VcsUtil.isAspectAvailableByDefault(getID(), false); + public boolean isShowByDefault() { + return false; } @Override diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/MergeSourceAvailableMarkerGutter.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/MergeSourceAvailableMarkerGutter.java index 3e78713832d3..375f1d110885 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/MergeSourceAvailableMarkerGutter.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/MergeSourceAvailableMarkerGutter.java @@ -18,7 +18,10 @@ package com.intellij.openapi.vcs.actions; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.colors.ColorKey; import com.intellij.openapi.util.Couple; -import com.intellij.openapi.vcs.annotate.*; +import com.intellij.openapi.vcs.annotate.AnnotationSource; +import com.intellij.openapi.vcs.annotate.AnnotationSourceSwitcher; +import com.intellij.openapi.vcs.annotate.FileAnnotation; +import com.intellij.openapi.vcs.annotate.TextAnnotationPresentation; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.util.Consumer; @@ -35,7 +38,7 @@ class MergeSourceAvailableMarkerGutter extends AnnotationFieldGutter implements MergeSourceAvailableMarkerGutter(FileAnnotation annotation, TextAnnotationPresentation highlighting, Couple> colorScheme) { - super(annotation, null, highlighting, colorScheme); + super(annotation, highlighting, colorScheme); } @Override diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/ShortNameType.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/ShortNameType.java index c6936797d08b..255c5d275e88 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/ShortNameType.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/ShortNameType.java @@ -16,6 +16,11 @@ package com.intellij.openapi.vcs.actions; import com.intellij.ide.util.PropertiesComponent; +import com.intellij.openapi.util.text.StringUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; /** * @author Konstantin Bulenkov @@ -45,4 +50,34 @@ public enum ShortNameType { void set() { PropertiesComponent.getInstance().setValue(KEY, myId); } + + @Nullable + public static String shorten(@Nullable String name, @NotNull ShortNameType type) { + if (type == NONE) return name; + if (name != null) { + // Vasya Pupkin -> Vasya Pupkin + final int[] ind = {name.indexOf('<'), name.indexOf('@'), name.indexOf('>')}; + if (0 < ind[0] && ind[0] < ind[1] && ind[1] < ind[2]) { + return shorten(name.substring(0, ind[0]).trim(), type); + } + + // vasya.pupkin@email.com --> vasya pupkin + if (!name.contains(" ") && name.contains("@")) { //simple e-mail check. john@localhost + final String firstPart = name.substring(0, name.indexOf('@')).replace('.', ' ').replace('_', ' ').replace('-', ' '); + if (firstPart.length() < name.length()) { + return shorten(firstPart, type); + } + else { + return firstPart; + } + } + + final List strings = StringUtil.split(name.replace('.', ' ').replace('_', ' ').replace('-', ' '), " "); + if (strings.size() > 1) { + //Middle name check: Vasya S. Pupkin + return StringUtil.capitalize(type == ShortNameType.FIRSTNAME ? strings.get(0) : strings.get(strings.size() - 1)); + } + } + return name; + } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/ShowShortenNames.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/ShowShortenNames.java index fe5af07d7279..e10aa4ec1641 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/ShowShortenNames.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/ShowShortenNames.java @@ -47,10 +47,6 @@ public class ShowShortenNames extends ActionGroup { return myChildren; } - public static boolean isSet() { - return getType() != ShortNameType.NONE; - } - public static ShortNameType getType() { for (ShortNameType type : ShortNameType.values()) { if (type.isSet()) { From 4ef5206325f6368ed28deb9bf2f341b6f2f05b4a Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Mon, 17 Oct 2016 15:23:45 +0300 Subject: [PATCH 51/66] vcs: simplify - remove recursive method calls --- .../openapi/vcs/actions/ShortNameType.java | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/ShortNameType.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/ShortNameType.java index 255c5d275e88..e7cd4804df2e 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/ShortNameType.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/ShortNameType.java @@ -53,31 +53,31 @@ public enum ShortNameType { @Nullable public static String shorten(@Nullable String name, @NotNull ShortNameType type) { + if (name == null) return null; if (type == NONE) return name; - if (name != null) { - // Vasya Pupkin -> Vasya Pupkin - final int[] ind = {name.indexOf('<'), name.indexOf('@'), name.indexOf('>')}; - if (0 < ind[0] && ind[0] < ind[1] && ind[1] < ind[2]) { - return shorten(name.substring(0, ind[0]).trim(), type); - } - // vasya.pupkin@email.com --> vasya pupkin - if (!name.contains(" ") && name.contains("@")) { //simple e-mail check. john@localhost - final String firstPart = name.substring(0, name.indexOf('@')).replace('.', ' ').replace('_', ' ').replace('-', ' '); - if (firstPart.length() < name.length()) { - return shorten(firstPart, type); - } - else { - return firstPart; - } - } - - final List strings = StringUtil.split(name.replace('.', ' ').replace('_', ' ').replace('-', ' '), " "); - if (strings.size() > 1) { - //Middle name check: Vasya S. Pupkin - return StringUtil.capitalize(type == ShortNameType.FIRSTNAME ? strings.get(0) : strings.get(strings.size() - 1)); - } + // Vasya Pupkin -> Vasya Pupkin + final int[] ind = {name.indexOf('<'), name.indexOf('@'), name.indexOf('>')}; + if (0 < ind[0] && ind[0] < ind[1] && ind[1] < ind[2]) { + name = name.substring(0, ind[0]).trim(); } - return name; + + // vasya.pupkin@email.com --> vasya pupkin + if (!name.contains(" ") && name.contains("@")) { //simple e-mail check. john@localhost + name = name.substring(0, name.indexOf('@')); + } + name = name.replace('.', ' ').replace('_', ' ').replace('-', ' '); + + final List strings = StringUtil.split(name, " "); + if (strings.size() < 2) return name; + + String shortName; + if (type == FIRSTNAME) { + shortName = strings.get(0); + } + else { + shortName = strings.get(strings.size() - 1); + } + return StringUtil.capitalize(shortName); } } From 93a8b7dd218acaa591e3e6b2bc5ea45c0c512bee Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Fri, 14 Oct 2016 17:56:31 +0300 Subject: [PATCH 52/66] vcs: allow to get total line number in up-to-date revision --- .../openapi/localVcs/UpToDateLineNumberProvider.java | 1 + .../openapi/vcs/actions/AnnotateDiffViewerAction.java | 5 +++++ .../openapi/vcs/impl/UpToDateLineNumberProviderImpl.java | 9 +++++++++ 3 files changed, 15 insertions(+) diff --git a/platform/vcs-api/src/com/intellij/openapi/localVcs/UpToDateLineNumberProvider.java b/platform/vcs-api/src/com/intellij/openapi/localVcs/UpToDateLineNumberProvider.java index a10f34645ab6..e367ad0ecf7b 100644 --- a/platform/vcs-api/src/com/intellij/openapi/localVcs/UpToDateLineNumberProvider.java +++ b/platform/vcs-api/src/com/intellij/openapi/localVcs/UpToDateLineNumberProvider.java @@ -19,6 +19,7 @@ public interface UpToDateLineNumberProvider { int ABSENT_LINE_NUMBER = -1; int FAKE_LINE_NUMBER = -2; + int getLineCount(); int getLineNumber(int currentNumber); boolean isLineChanged(int currentNumber); boolean isRangeChanged(final int start, final int end); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateDiffViewerAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateDiffViewerAction.java index c172a7dca544..9768edae6d92 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateDiffViewerAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateDiffViewerAction.java @@ -515,6 +515,11 @@ public class AnnotateDiffViewerAction extends ToggleAction implements DumbAware } return myLocalChangesProvider.isRangeChanged(line1, line2); } + + @Override + public int getLineCount() { + return myLocalChangesProvider.getLineCount(); + } } private static class ThreesideAnnotatorFactory extends ThreesideViewerAnnotatorFactory { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/UpToDateLineNumberProviderImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/UpToDateLineNumberProviderImpl.java index 921d6432da7c..a53fa7a1019c 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/UpToDateLineNumberProviderImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/UpToDateLineNumberProviderImpl.java @@ -58,4 +58,13 @@ public class UpToDateLineNumberProviderImpl implements UpToDateLineNumberProvide } return tracker.transferLineToVcs(currentNumber, false); } + + @Override + public int getLineCount() { + LineStatusTracker tracker = myLineStatusTrackerManagerI.getLineStatusTracker(myDocument); + if (tracker == null || !tracker.isOperational()) { + return myDocument.getLineCount(); + } + return tracker.getVcsDocument().getLineCount(); + } } From 1bfe8a5380cea86481437c650f6828a5d19d75d3 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Mon, 17 Oct 2016 15:35:36 +0300 Subject: [PATCH 53/66] vcs: remove duplication --- .../impl/UpToDateLineNumberProviderImpl.java | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/UpToDateLineNumberProviderImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/UpToDateLineNumberProviderImpl.java index a53fa7a1019c..9cf5a451b544 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/UpToDateLineNumberProviderImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/UpToDateLineNumberProviderImpl.java @@ -19,10 +19,8 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.localVcs.UpToDateLineNumberProvider; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.ex.LineStatusTracker; +import org.jetbrains.annotations.Nullable; -/** - * author: lesya - */ public class UpToDateLineNumberProviderImpl implements UpToDateLineNumberProvider { private final Document myDocument; private final LineStatusTrackerManagerI myLineStatusTrackerManagerI; @@ -34,37 +32,51 @@ public class UpToDateLineNumberProviderImpl implements UpToDateLineNumberProvide @Override public boolean isRangeChanged(final int start, final int end) { - LineStatusTracker tracker = myLineStatusTrackerManagerI.getLineStatusTracker(myDocument); - if (tracker == null || !tracker.isOperational()) { + LineStatusTracker tracker = getTracker(); + if (tracker == null) { return false; } - return tracker.isRangeModified(start, end); + else { + return tracker.isRangeModified(start, end); + } } @Override public boolean isLineChanged(int currentNumber) { - LineStatusTracker tracker = myLineStatusTrackerManagerI.getLineStatusTracker(myDocument); - if (tracker == null || !tracker.isOperational()) { + LineStatusTracker tracker = getTracker(); + if (tracker == null) { return false; } - return tracker.isLineModified(currentNumber); + else { + return tracker.isLineModified(currentNumber); + } } @Override public int getLineNumber(int currentNumber) { - LineStatusTracker tracker = myLineStatusTrackerManagerI.getLineStatusTracker(myDocument); - if (tracker == null || !tracker.isOperational()) { + LineStatusTracker tracker = getTracker(); + if (tracker == null) { return currentNumber; } - return tracker.transferLineToVcs(currentNumber, false); + else { + return tracker.transferLineToVcs(currentNumber, false); + } } @Override public int getLineCount() { - LineStatusTracker tracker = myLineStatusTrackerManagerI.getLineStatusTracker(myDocument); - if (tracker == null || !tracker.isOperational()) { + LineStatusTracker tracker = getTracker(); + if (tracker == null) { return myDocument.getLineCount(); } - return tracker.getVcsDocument().getLineCount(); + else { + return tracker.getVcsDocument().getLineCount(); + } + } + + @Nullable + private LineStatusTracker getTracker() { + LineStatusTracker tracker = myLineStatusTrackerManagerI.getLineStatusTracker(myDocument); + return tracker != null && tracker.isOperational() ? tracker : null; } } From f7bcc376c76bf909f8d3c264a6067f94c8df492d Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Mon, 17 Oct 2016 16:06:42 +0300 Subject: [PATCH 54/66] vcs: move dispose logic out of AnnotationFieldGutter --- .../vcs/actions/AnnotateToggleAction.java | 22 +++++++++++++++++-- .../vcs/actions/AnnotationFieldGutter.java | 4 +--- .../vcs/actions/AnnotationPresentation.java | 20 +++++++++++++++-- .../annotate/TextAnnotationPresentation.java | 1 + 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java index 3554612676d1..4e9450b0305e 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.vcs.actions; +import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.Separator; import com.intellij.openapi.actionSystem.ToggleAction; @@ -27,12 +28,14 @@ import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Couple; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vcs.AbstractVcs; import com.intellij.openapi.vcs.ProjectLevelVcsManager; import com.intellij.openapi.vcs.annotate.AnnotationGutterActionProvider; import com.intellij.openapi.vcs.annotate.AnnotationSourceSwitcher; import com.intellij.openapi.vcs.annotate.FileAnnotation; import com.intellij.openapi.vcs.annotate.LineAnnotationAspect; +import com.intellij.openapi.vcs.changes.VcsAnnotationLocalChangesListener; import com.intellij.openapi.vcs.history.VcsFileRevision; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vcs.impl.UpToDateLineNumberProviderImpl; @@ -89,8 +92,23 @@ public class AnnotateToggleAction extends ToggleAction implements DumbAware { @NotNull final FileAnnotation fileAnnotation, @NotNull final AbstractVcs vcs, @Nullable final UpToDateLineNumberProvider upToDateLineNumberProvider) { + Disposable disposable = new Disposable() { + @Override + public void dispose() { + fileAnnotation.dispose(); + } + }; + if (fileAnnotation.getFile() != null && fileAnnotation.getFile().isInLocalFileSystem()) { - ProjectLevelVcsManager.getInstance(project).getAnnotationLocalChangesListener().registerAnnotation(fileAnnotation.getFile(), fileAnnotation); + VcsAnnotationLocalChangesListener changesListener = ProjectLevelVcsManager.getInstance(project).getAnnotationLocalChangesListener(); + + changesListener.registerAnnotation(fileAnnotation.getFile(), fileAnnotation); + Disposer.register(disposable, new Disposable() { + @Override + public void dispose() { + changesListener.unregisterAnnotation(fileAnnotation.getFile(), fileAnnotation); + } + }); } editor.getGutter().closeAllAnnotations(); @@ -116,7 +134,7 @@ public class AnnotateToggleAction extends ToggleAction implements DumbAware { upToDateLineNumberProvider : new UpToDateLineNumberProviderImpl(editor.getDocument(), project); - final AnnotationPresentation presentation = new AnnotationPresentation(fileAnnotation, getUpToDateLineNumber, switcher); + final AnnotationPresentation presentation = new AnnotationPresentation(fileAnnotation, getUpToDateLineNumber, switcher, disposable); if (currentFile != null && vcs.getCommittedChangesProvider() != null) { presentation.addAction(new ShowDiffFromAnnotation(fileAnnotation, vcs, currentFile)); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationFieldGutter.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationFieldGutter.java index 0e0c2b0cdb18..cb7bd75d0cb1 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationFieldGutter.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationFieldGutter.java @@ -86,9 +86,7 @@ public abstract class AnnotationFieldGutter implements ActiveAnnotationGutter { @Override public void gutterClosed() { - ProjectLevelVcsManager.getInstance(myAnnotation.getProject()).getAnnotationLocalChangesListener() - .unregisterAnnotation(myAnnotation.getFile(), myAnnotation); - myAnnotation.dispose(); + myPresentation.gutterClosed(); } @Nullable diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationPresentation.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationPresentation.java index 08d7b20d0121..28bb81e686a7 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationPresentation.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotationPresentation.java @@ -15,13 +15,14 @@ */ package com.intellij.openapi.vcs.actions; +import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.editor.colors.ColorKey; import com.intellij.openapi.editor.colors.EditorFontType; import com.intellij.openapi.localVcs.UpToDateLineNumberProvider; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vcs.annotate.*; import com.intellij.openapi.vcs.history.VcsRevisionNumber; -import com.intellij.util.Consumer; import com.intellij.util.ObjectUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -35,25 +36,33 @@ class AnnotationPresentation implements TextAnnotationPresentation { @Nullable private final AnnotationSourceSwitcher mySwitcher; private final ArrayList myActions = new ArrayList<>(); + @NotNull private final Disposable myDisposable; + private boolean myDisposed = false; + AnnotationPresentation(@NotNull FileAnnotation fileAnnotation, @NotNull UpToDateLineNumberProvider upToDateLineNumberProvider, - @Nullable final AnnotationSourceSwitcher switcher) { + @Nullable AnnotationSourceSwitcher switcher, + @NotNull Disposable disposable) { myUpToDateLineNumberProvider = upToDateLineNumberProvider; myFileAnnotation = fileAnnotation; mySwitcher = switcher; + myDisposable = disposable; } + @Override public EditorFontType getFontType(final int line) { VcsRevisionNumber revision = myFileAnnotation.originalRevision(line); VcsRevisionNumber currentRevision = myFileAnnotation.getCurrentRevision(); return currentRevision != null && currentRevision.equals(revision) ? EditorFontType.BOLD : EditorFontType.PLAIN; } + @Override public ColorKey getColor(final int line) { if (mySwitcher == null) return AnnotationSource.LOCAL.getColor(); return mySwitcher.getAnnotationSource(line).getColor(); } + @Override public List getActions(int line) { int correctedNumber = myUpToDateLineNumberProvider.getLineNumber(line); for (AnAction action : myActions) { @@ -79,4 +88,11 @@ class AnnotationPresentation implements TextAnnotationPresentation { public void addAction(AnAction action, int index) { myActions.add(index, action); } + + @Override + public void gutterClosed() { + if (myDisposed) return; + myDisposed = true; + Disposer.dispose(myDisposable); + } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/annotate/TextAnnotationPresentation.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/annotate/TextAnnotationPresentation.java index 1fca77641739..8d610295d65b 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/annotate/TextAnnotationPresentation.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/annotate/TextAnnotationPresentation.java @@ -25,4 +25,5 @@ public interface TextAnnotationPresentation { EditorFontType getFontType(int line); ColorKey getColor(int line); List getActions(int line); + void gutterClosed(); } From 2485cd2b474232f00cf6b3eb8f502be50924496c Mon Sep 17 00:00:00 2001 From: nik Date: Mon, 17 Oct 2016 16:21:22 +0300 Subject: [PATCH 55/66] build scripts: include actual build number into *.exe launcher attributes (IDEA-162192) --- .../jetbrains/intellij/build/impl/BuildTasksImpl.groovy | 7 ++++--- .../intellij/build/impl/DistributionJARsBuilder.groovy | 6 ++++-- .../intellij/build/impl/WindowsDistributionBuilder.groovy | 6 ++++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/build/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy b/build/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy index 118e7c8f6699..31c0f15ec0e3 100644 --- a/build/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy +++ b/build/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy @@ -243,7 +243,8 @@ idea.fatal.error.notification=disabled void buildDistributions() { checkProductProperties() - def distributionJARsBuilder = new DistributionJARsBuilder(buildContext) + def patchedApplicationInfo = patchApplicationInfo() + def distributionJARsBuilder = new DistributionJARsBuilder(buildContext, patchedApplicationInfo) compileModules(buildContext.productProperties.productLayout.includedPluginModules + distributionJARsBuilder.platformModules + buildContext.productProperties.additionalModulesToCompile, buildContext.productProperties.modulesToCompileTests) buildContext.messages.block("Build platform and plugin JARs") { @@ -259,7 +260,7 @@ idea.fatal.error.notification=disabled def propertiesFile = patchIdeaPropertiesFile() List> tasks = [ createDistributionForOsTask("win", { BuildContext context -> - context.windowsDistributionCustomizer?.with { new WindowsDistributionBuilder(context, it, propertiesFile) } + context.windowsDistributionCustomizer?.with { new WindowsDistributionBuilder(context, it, propertiesFile, patchedApplicationInfo) } }), createDistributionForOsTask("linux", { BuildContext context -> context.linuxDistributionCustomizer?.with { new LinuxDistributionBuilder(context, it, propertiesFile) } @@ -472,7 +473,7 @@ idea.fatal.error.notification=disabled @Override void buildUnpackedDistribution(String targetDirectory) { - def jarsBuilder = new DistributionJARsBuilder(buildContext) + def jarsBuilder = new DistributionJARsBuilder(buildContext, patchApplicationInfo()) jarsBuilder.buildJARs() layoutShared() diff --git a/build/groovy/org/jetbrains/intellij/build/impl/DistributionJARsBuilder.groovy b/build/groovy/org/jetbrains/intellij/build/impl/DistributionJARsBuilder.groovy index 6175bb62da95..212a9f4541ea 100644 --- a/build/groovy/org/jetbrains/intellij/build/impl/DistributionJARsBuilder.groovy +++ b/build/groovy/org/jetbrains/intellij/build/impl/DistributionJARsBuilder.groovy @@ -39,8 +39,10 @@ class DistributionJARsBuilder { private final BuildContext buildContext private final Set usedModules = new LinkedHashSet<>() private final PlatformLayout platform + private final File patchedApplicationInfo - DistributionJARsBuilder(BuildContext buildContext) { + DistributionJARsBuilder(BuildContext buildContext, File patchedApplicationInfo) { + this.patchedApplicationInfo = patchedApplicationInfo this.buildContext = buildContext buildContext.ant.patternset(id: RESOURCES_INCLUDED) { include(name: "**/*Bundle*.properties") @@ -205,7 +207,7 @@ class DistributionJARsBuilder { layoutBuilder.patchModuleOutput(productLayout.searchableOptionsModule, FileUtil.toSystemIndependentName(searchableOptionsDir.absolutePath)) } - def applicationInfoFile = FileUtil.toSystemIndependentName(buildTasks.patchApplicationInfo().absolutePath) + def applicationInfoFile = FileUtil.toSystemIndependentName(patchedApplicationInfo.absolutePath) def applicationInfoDir = "$buildContext.paths.temp/applicationInfo" ant.copy(file: applicationInfoFile, todir: "$applicationInfoDir/idea") layoutBuilder.patchModuleOutput(buildContext.productProperties.applicationInfoModule, applicationInfoDir) diff --git a/build/groovy/org/jetbrains/intellij/build/impl/WindowsDistributionBuilder.groovy b/build/groovy/org/jetbrains/intellij/build/impl/WindowsDistributionBuilder.groovy index 55f3e4d1f2a9..efc5f29bd160 100644 --- a/build/groovy/org/jetbrains/intellij/build/impl/WindowsDistributionBuilder.groovy +++ b/build/groovy/org/jetbrains/intellij/build/impl/WindowsDistributionBuilder.groovy @@ -28,9 +28,11 @@ import org.jetbrains.jps.model.module.JpsModuleSourceRoot class WindowsDistributionBuilder extends OsSpecificDistributionBuilder { private final WindowsDistributionCustomizer customizer private final File ideaProperties + private final File patchedApplicationInfo - WindowsDistributionBuilder(BuildContext buildContext, WindowsDistributionCustomizer customizer, File ideaProperties) { + WindowsDistributionBuilder(BuildContext buildContext, WindowsDistributionCustomizer customizer, File ideaProperties, File patchedApplicationInfo) { super(BuildOptions.OS_WINDOWS, "Windows", buildContext) + this.patchedApplicationInfo = patchedApplicationInfo this.customizer = customizer this.ideaProperties = ideaProperties } @@ -184,7 +186,7 @@ IDS_VM_OPTIONS=$vmOptions buildContext.ant.java(classname: "com.pme.launcher.LauncherGeneratorMain", fork: "true", failonerror: "true") { sysproperty(key: "java.awt.headless", value: "true") arg(value: inputPath) - arg(value: buildContext.findApplicationInfoInSources().absolutePath) + arg(value: patchedApplicationInfo.absolutePath) arg(value: "$communityHome/native/WinLauncher/WinLauncher/resource.h") arg(value: launcherPropertiesPath) arg(value: outputPath) From 3fb83747e17ea1e90a5946fe9dbf9ebcb0c054bb Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Mon, 17 Oct 2016 16:51:36 +0300 Subject: [PATCH 56/66] merge: use non-greedy approach for "magic" conflict resolve * it produces lots of confusing false-positive results * move "greedy" approach under registry key --- .../diff/comparison/ComparisonMergeUtil.java | 8 ++- .../diff/comparison/MergeResolveUtil.kt | 62 ++++++++++++++++++- .../src/com/intellij/diff/util/DiffUtil.java | 32 ++++++++++ .../diff/comparison/MergeResolveUtilTest.kt | 41 ++++++++---- .../util/resources/misc/registry.properties | 2 + 5 files changed, 129 insertions(+), 16 deletions(-) diff --git a/platform/diff-impl/src/com/intellij/diff/comparison/ComparisonMergeUtil.java b/platform/diff-impl/src/com/intellij/diff/comparison/ComparisonMergeUtil.java index 1b1b926d6f9d..7c8340f04f45 100644 --- a/platform/diff-impl/src/com/intellij/diff/comparison/ComparisonMergeUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/comparison/ComparisonMergeUtil.java @@ -20,6 +20,7 @@ import com.intellij.diff.util.MergeRange; import com.intellij.diff.util.Range; import com.intellij.diff.util.Side; import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.util.registry.Registry; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -160,6 +161,11 @@ public class ComparisonMergeUtil { public static CharSequence tryResolveConflict(@NotNull CharSequence leftText, @NotNull CharSequence baseText, @NotNull CharSequence rightText) { - return MergeResolveUtil.tryResolveConflict(leftText, baseText, rightText); + if (Registry.is("diff.merge.resolve.conflict.action.use.greedy.approach")) { + return MergeResolveUtil.tryGreedyResolve(leftText, baseText, rightText); + } + else { + return MergeResolveUtil.tryResolve(leftText, baseText, rightText); + } } } \ No newline at end of file diff --git a/platform/diff-impl/src/com/intellij/diff/comparison/MergeResolveUtil.kt b/platform/diff-impl/src/com/intellij/diff/comparison/MergeResolveUtil.kt index 4096c79fc003..df886328b8e6 100644 --- a/platform/diff-impl/src/com/intellij/diff/comparison/MergeResolveUtil.kt +++ b/platform/diff-impl/src/com/intellij/diff/comparison/MergeResolveUtil.kt @@ -16,13 +16,29 @@ package com.intellij.diff.comparison import com.intellij.diff.fragments.DiffFragment +import com.intellij.diff.util.DiffUtil import com.intellij.diff.util.Side import com.intellij.diff.util.Side.LEFT import com.intellij.diff.util.Side.RIGHT +import com.intellij.diff.util.TextDiffType +import com.intellij.diff.util.ThreeSide import com.intellij.openapi.progress.DumbProgressIndicator import com.intellij.util.text.MergingCharSequence object MergeResolveUtil { + @JvmStatic + fun tryResolve(leftText: CharSequence, baseText: CharSequence, rightText: CharSequence): CharSequence? { + try { + val resolved = tryResolve(leftText, baseText, rightText, ComparisonPolicy.DEFAULT) + if (resolved != null) return resolved + + return tryResolve(leftText, baseText, rightText, ComparisonPolicy.IGNORE_WHITESPACES) + } + catch (e: DiffTooBigException) { + return null + } + } + /* * Here we assume, that resolve results are explicitly verified by user and can be safely undone. * Thus we trade higher chances of incorrect resolve for higher chances of correct resolve. @@ -36,12 +52,12 @@ object MergeResolveUtil { * modifications can be considered as "insertion + deletion" and resolved accordingly. */ @JvmStatic - fun tryResolveConflict(leftText: CharSequence, baseText: CharSequence, rightText: CharSequence): CharSequence? { + fun tryGreedyResolve(leftText: CharSequence, baseText: CharSequence, rightText: CharSequence): CharSequence? { try { - val resolved = Helper(leftText, baseText, rightText).execute(ComparisonPolicy.DEFAULT) + val resolved = tryGreedyResolve(leftText, baseText, rightText, ComparisonPolicy.DEFAULT) if (resolved != null) return resolved - return Helper(leftText, baseText, rightText).execute(ComparisonPolicy.IGNORE_WHITESPACES) + return tryGreedyResolve(leftText, baseText, rightText, ComparisonPolicy.IGNORE_WHITESPACES) } catch (e: DiffTooBigException) { return null @@ -49,6 +65,46 @@ object MergeResolveUtil { } } +private fun tryResolve(leftText: CharSequence, baseText: CharSequence, rightText: CharSequence, + policy: ComparisonPolicy): CharSequence? { + val texts = listOf(leftText, baseText, rightText) + + val changes = ByWord.compare(leftText, baseText, rightText, policy, DumbProgressIndicator.INSTANCE) + + val newContent = StringBuilder() + + var last = 0 + for (fragment in changes) { + val type = DiffUtil.getWordMergeType(fragment, texts, policy) + if (type.diffType == TextDiffType.CONFLICT) return null; + + val baseStart = fragment.getStartOffset(ThreeSide.BASE) + val baseEnd = fragment.getEndOffset(ThreeSide.BASE) + + newContent.append(baseText, last, baseStart) + + if (type.isChange(Side.LEFT)) { + val leftStart = fragment.getStartOffset(ThreeSide.LEFT) + val leftEnd = fragment.getEndOffset(ThreeSide.LEFT) + newContent.append(leftText, leftStart, leftEnd) + } + else { + val rightStart = fragment.getStartOffset(ThreeSide.RIGHT) + val rightEnd = fragment.getEndOffset(ThreeSide.RIGHT) + newContent.append(rightText, rightStart, rightEnd) + } + last = baseEnd + } + + newContent.append(baseText, last, baseText.length) + return newContent.toString() +} + +private fun tryGreedyResolve(leftText: CharSequence, baseText: CharSequence, rightText: CharSequence, + policy: ComparisonPolicy): CharSequence? { + return Helper(leftText, baseText, rightText).execute(policy) +} + private class Helper(val leftText: CharSequence, val baseText: CharSequence, val rightText: CharSequence) { val newContent = StringBuilder() diff --git a/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java b/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java index fc8d62f14922..3ade1e10cfd3 100644 --- a/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java @@ -23,6 +23,7 @@ import com.intellij.diff.SuppressiveDiffTool; import com.intellij.diff.comparison.ByWord; import com.intellij.diff.comparison.ComparisonManager; import com.intellij.diff.comparison.ComparisonPolicy; +import com.intellij.diff.comparison.ComparisonUtil; import com.intellij.diff.contents.DiffContent; import com.intellij.diff.contents.DocumentContent; import com.intellij.diff.contents.EmptyContent; @@ -30,6 +31,7 @@ import com.intellij.diff.contents.FileContent; import com.intellij.diff.fragments.DiffFragment; import com.intellij.diff.fragments.LineFragment; import com.intellij.diff.fragments.MergeLineFragment; +import com.intellij.diff.fragments.MergeWordFragment; import com.intellij.diff.impl.DiffSettingsHolder; import com.intellij.diff.impl.DiffSettingsHolder.DiffSettings; import com.intellij.diff.requests.ContentDiffRequest; @@ -1069,6 +1071,36 @@ public class DiffUtil { return fragment.getStartLine(side) == fragment.getEndLine(side); } + @NotNull + public static MergeConflictType getWordMergeType(@NotNull MergeWordFragment fragment, + @NotNull List texts, + @NotNull ComparisonPolicy policy) { + return getMergeType((side) -> isWordMergeIntervalEmpty(fragment, side), + (side1, side2) -> compareWordMergeContents(fragment, texts, policy, side1, side2)); + } + + private static boolean compareWordMergeContents(@NotNull MergeWordFragment fragment, + @NotNull List texts, + @NotNull ComparisonPolicy policy, + @NotNull ThreeSide side1, + @NotNull ThreeSide side2) { + int start1 = fragment.getStartOffset(side1); + int end1 = fragment.getEndOffset(side1); + int start2 = fragment.getStartOffset(side2); + int end2 = fragment.getEndOffset(side2); + + CharSequence document1 = side1.select(texts); + CharSequence document2 = side2.select(texts); + + CharSequence content1 = document1.subSequence(start1, end1); + CharSequence content2 = document2.subSequence(start2, end2); + return ComparisonUtil.isEquals(content1, content2, policy); + } + + private static boolean isWordMergeIntervalEmpty(@NotNull MergeWordFragment fragment, @NotNull ThreeSide side) { + return fragment.getStartOffset(side) == fragment.getEndOffset(side); + } + // // Writable // diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/MergeResolveUtilTest.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/MergeResolveUtilTest.kt index 1283f7ed8f46..c5c299bd593a 100644 --- a/platform/diff-impl/tests/com/intellij/diff/comparison/MergeResolveUtilTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/MergeResolveUtilTest.kt @@ -118,35 +118,35 @@ class MergeResolveUtilTest : DiffTestCase() { } fun testNonFailureConflicts() { - test( + testGreedy( "x X x", "x x", "x X Y x", "x Y x" ) - test( + testGreedy( "x X x", "x x", "x Y X x", "x Y x" ) - test( + testGreedy( "x X Y x", "x X x", "x Y x", "x x" ) - test( + testGreedy( "x X Y Z x", "x X x", "x Z x", "x x" ) - test( + testGreedy( "x A B C D E F G H K x", "x C F K x", "x A D H x", @@ -157,21 +157,21 @@ class MergeResolveUtilTest : DiffTestCase() { fun testConfusingConflicts() { // these cases might be a failure as well - test( + testGreedy( "x X x", "x x", "x Z x", "xZ x" ) - test( + testGreedy( "x X X x", "x X Y X x", "x x", "x Y x" ) - test( + testGreedy( "x X x", "x x", "x Y x", @@ -179,7 +179,7 @@ class MergeResolveUtilTest : DiffTestCase() { ) - test( + testGreedy( "x X X x", "x Y x", "x X Y x", @@ -187,8 +187,25 @@ class MergeResolveUtilTest : DiffTestCase() { ) } - private fun test(base: String, left: String, right: String, expected: String?) { - val actual = MergeResolveUtil.tryResolveConflict(left, base, right) - assertEquals(expected, actual?.toString()) + private fun testGreedy(base: String, left: String, right: String, expected: String?) { + test(base, left, right, expected, true); + } + + private fun test(base: String, left: String, right: String, expected: String?, isGreedy: Boolean = false) { + val simpleResult = MergeResolveUtil.tryResolve(left, base, right) + val magicResult = MergeResolveUtil.tryGreedyResolve(left, base, right); + + if (expected == null) { + assertNull(simpleResult) + assertNull(magicResult) + } + else if (isGreedy) { + assertNull(simpleResult) + assertEquals(expected, magicResult) + } + else { + assertEquals(expected, simpleResult) + assertEquals(expected, magicResult) + } } } diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index a21ae20afa6b..82cf604b0d4b 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -491,6 +491,8 @@ 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.merge.resolve.conflict.action.visible.description=Allows to resolve some conflict in merge in one click (with a high probability of wrong result) +diff.merge.resolve.conflict.action.use.greedy.approach=false +diff.merge.resolve.conflict.action.use.greedy.approach.description=Use greedy heuristic in attempt to resolve conflict. This leads to higher amounts of false-positive and true-positive results. diff.enable.psi.highlighting=true diff.enable.psi.highlighting.description=Enable advanced highlighting and code navigation in VCS content in diff viewers. diff.pass.rich.editor.context=false From 1468b5f0efb9fb3d1515f30cb465d8c6de776397 Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Mon, 17 Oct 2016 17:47:54 +0300 Subject: [PATCH 57/66] bring back notifications about forcibly enabled soft wraps (not working since commit 1c70e25) --- .../openapi/fileEditor/impl/text/AsyncEditorLoader.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/AsyncEditorLoader.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/AsyncEditorLoader.java index 92e3b69e2a0e..609204ff1942 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/AsyncEditorLoader.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/AsyncEditorLoader.java @@ -30,6 +30,7 @@ import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Ref; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.psi.PsiDocumentManager; +import com.intellij.ui.EditorNotifications; import com.intellij.util.concurrency.AppExecutorUtil; import org.jetbrains.annotations.NotNull; @@ -172,6 +173,7 @@ public class AsyncEditorLoader { if (FileEditorManager.getInstance(myProject).getSelectedTextEditor() == myEditor) { IdeFocusManager.getInstance(myProject).requestFocus(myTextEditor.getPreferredFocusedComponent(), true); } + EditorNotifications.getInstance(myProject).updateNotifications(myTextEditor.myFile); } public static void performWhenLoaded(@NotNull Editor editor, @NotNull Runnable runnable) { From e3f9dc0f962da0a2e1bd22f903e2914c7b075d51 Mon Sep 17 00:00:00 2001 From: Elizaveta Shashkova Date: Mon, 17 Oct 2016 17:51:26 +0300 Subject: [PATCH 58/66] Tests: fix python console npe --- .../python/console/PydevConsoleRunnerImpl.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java b/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java index 578323d58e0a..28596f9af9a8 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java @@ -63,11 +63,15 @@ import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.*; +import com.intellij.openapi.util.Couple; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.io.StreamUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.ToolWindow; import com.intellij.psi.PsiFile; import com.intellij.remote.RemoteProcess; import com.intellij.remote.Tunnelable; @@ -107,8 +111,10 @@ import java.awt.event.KeyEvent; import java.io.File; import java.io.IOException; import java.net.ServerSocket; -import java.util.*; +import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Scanner; import java.util.stream.Collectors; import static com.intellij.execution.runners.AbstractConsoleRunnerWithHistory.registerActionShortcuts; @@ -317,10 +323,9 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner { private void showContentDescriptor(RunContentDescriptor contentDescriptor) { - PythonConsoleToolWindow toolWindow = PythonConsoleToolWindow.getInstance(myProject); - if (toolWindow != null) { - toolWindow - .init(PythonConsoleToolWindow.getToolWindow(myProject), contentDescriptor); + ToolWindow toolwindow = PythonConsoleToolWindow.getToolWindow(myProject); + if (toolwindow != null) { + PythonConsoleToolWindow.getInstance(myProject).init(toolwindow, contentDescriptor); } else { ExecutionManager From aa7405f26a98763b2d49f5ee20e2e313d8e86e06 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Mon, 17 Oct 2016 16:59:26 +0200 Subject: [PATCH 59/66] Cleanup (layout) --- .../java/decompiler/IdeaDecompiler.kt | 51 +++++++++++ .../java/decompiler/LegalNoticeDialog.java | 89 ------------------- 2 files changed, 51 insertions(+), 89 deletions(-) delete mode 100644 plugins/java-decompiler/plugin/src/org/jetbrains/java/decompiler/LegalNoticeDialog.java diff --git a/plugins/java-decompiler/plugin/src/org/jetbrains/java/decompiler/IdeaDecompiler.kt b/plugins/java-decompiler/plugin/src/org/jetbrains/java/decompiler/IdeaDecompiler.kt index 20e7fe6b5b7d..403347778b5a 100644 --- a/plugins/java-decompiler/plugin/src/org/jetbrains/java/decompiler/IdeaDecompiler.kt +++ b/plugins/java-decompiler/plugin/src/org/jetbrains/java/decompiler/IdeaDecompiler.kt @@ -16,6 +16,7 @@ package org.jetbrains.java.decompiler import com.intellij.execution.filters.LineNumbersMapping +import com.intellij.icons.AllIcons import com.intellij.ide.highlighter.JavaFileType import com.intellij.ide.plugins.PluginManagerCore import com.intellij.ide.util.PropertiesComponent @@ -28,6 +29,7 @@ import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.project.DefaultProjectFactory +import com.intellij.openapi.project.Project import com.intellij.openapi.ui.DialogWrapper import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.registry.Registry @@ -40,17 +42,26 @@ import com.intellij.psi.PsiPackage import com.intellij.psi.codeStyle.CodeStyleSettingsManager import com.intellij.psi.compiled.ClassFileDecompilers import com.intellij.psi.impl.compiled.ClsFileImpl +import com.intellij.ui.Gray +import com.intellij.ui.components.JBLabel +import com.intellij.ui.components.JBPanel import com.intellij.util.containers.ContainerUtil +import com.intellij.util.ui.JBUI +import com.intellij.util.ui.UIUtil import org.jetbrains.annotations.TestOnly import org.jetbrains.java.decompiler.main.decompiler.BaseDecompiler import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences import org.jetbrains.java.decompiler.main.extern.IResultSaver +import java.awt.BorderLayout import java.io.File import java.util.* import java.util.concurrent.Callable import java.util.concurrent.Future import java.util.jar.Manifest +import javax.swing.BorderFactory +import javax.swing.JComponent +import javax.swing.JEditorPane class IdeaDecompiler : ClassFileDecompilers.Light() { companion object { @@ -239,4 +250,44 @@ class IdeaDecompiler : ClassFileDecompilers.Light() { return -1 } } + + private class LegalNoticeDialog(project: Project, file: VirtualFile) : DialogWrapper(project) { + companion object { + val POSTPONE_EXIT_CODE = DialogWrapper.CANCEL_EXIT_CODE + val DECLINE_EXIT_CODE = DialogWrapper.NEXT_USER_EXIT_CODE + } + + private var myMessage: JEditorPane? = null + + init { + title = IdeaDecompilerBundle.message("legal.notice.title", StringUtil.last(file.path, 40, true)) + setOKButtonText(IdeaDecompilerBundle.message("legal.notice.action.accept")) + setCancelButtonText(IdeaDecompilerBundle.message("legal.notice.action.postpone")) + init() + pack() + } + + override fun createCenterPanel(): JComponent? { + val iconPanel = JBPanel>(BorderLayout()) + iconPanel.add(JBLabel(AllIcons.General.WarningDialog), BorderLayout.NORTH) + + val message = JEditorPane() + myMessage = message + message.editorKit = UIUtil.getHTMLEditorKit() + message.isEditable = false + message.preferredSize = JBUI.size(500, 100) + message.border = BorderFactory.createLineBorder(Gray._200) + message.text = "
${IdeaDecompilerBundle.message("legal.notice.text")}
" + + val panel = JBPanel>(BorderLayout(JBUI.scale(10), 0)) + panel.add(iconPanel, BorderLayout.WEST) + panel.add(message, BorderLayout.CENTER) + return panel + } + + override fun createActions() = + arrayOf(okAction, DialogWrapperExitAction(IdeaDecompilerBundle.message("legal.notice.action.reject"), DECLINE_EXIT_CODE), cancelAction) + + override fun getPreferredFocusedComponent() = myMessage + } } \ No newline at end of file diff --git a/plugins/java-decompiler/plugin/src/org/jetbrains/java/decompiler/LegalNoticeDialog.java b/plugins/java-decompiler/plugin/src/org/jetbrains/java/decompiler/LegalNoticeDialog.java deleted file mode 100644 index ec0492a90e49..000000000000 --- a/plugins/java-decompiler/plugin/src/org/jetbrains/java/decompiler/LegalNoticeDialog.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2000-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler; - -import com.intellij.icons.AllIcons; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.ui.Gray; -import com.intellij.ui.components.JBLabel; -import com.intellij.ui.components.JBPanel; -import com.intellij.util.ui.JBUI; -import com.intellij.util.ui.UIUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; - -class LegalNoticeDialog extends DialogWrapper { - public static final int POSTPONE_EXIT_CODE = CANCEL_EXIT_CODE; - public static final int DECLINE_EXIT_CODE = NEXT_USER_EXIT_CODE; - - private JEditorPane myMessage; - - public LegalNoticeDialog(Project project, VirtualFile file) { - super(project); - setTitle(IdeaDecompilerBundle.message("legal.notice.title", StringUtil.last(file.getPath(), 40, true))); - setOKButtonText(IdeaDecompilerBundle.message("legal.notice.action.accept")); - setCancelButtonText(IdeaDecompilerBundle.message("legal.notice.action.postpone")); - init(); - pack(); - } - - @Nullable - @Override - protected JComponent createCenterPanel() { - JPanel iconPanel = new JBPanel(new BorderLayout()); - iconPanel.add(new JBLabel(AllIcons.General.WarningDialog), BorderLayout.NORTH); - - myMessage = new JEditorPane(); - myMessage.setEditorKit(UIUtil.getHTMLEditorKit()); - myMessage.setEditable(false); - myMessage.setPreferredSize(JBUI.size(500, 100)); - myMessage.setBorder(BorderFactory.createLineBorder(Gray._200)); - String text = "
" + IdeaDecompilerBundle.message("legal.notice.text") + "
"; - myMessage.setText(text); - - JPanel panel = new JBPanel(new BorderLayout(10, 0)); - panel.add(iconPanel, BorderLayout.WEST); - panel.add(myMessage, BorderLayout.CENTER); - return panel; - } - - @NotNull - @Override - protected Action[] createActions() { - return new Action[]{ - getOKAction(), - new DialogWrapperAction(IdeaDecompilerBundle.message("legal.notice.action.reject")) { - @Override - protected void doAction(ActionEvent e) { - close(DECLINE_EXIT_CODE); - } - }, - getCancelAction()}; - } - - @Nullable - @Override - public JComponent getPreferredFocusedComponent() { - return myMessage; - } -} \ No newline at end of file From 7a82d4a283b7ffd4b13a4bf8994198b68620f23b Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Mon, 17 Oct 2016 16:55:53 +0200 Subject: [PATCH 60/66] avoid extra copying data during append --- .../com/intellij/util/indexing/ValueContainerMap.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/util/indexing/ValueContainerMap.java b/platform/lang-impl/src/com/intellij/util/indexing/ValueContainerMap.java index 7350f2bc7d80..e6789513ee35 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/ValueContainerMap.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/ValueContainerMap.java @@ -15,9 +15,7 @@ */ package com.intellij.util.indexing; -import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; import com.intellij.util.io.DataExternalizer; -import com.intellij.util.io.DataOutputStream; import com.intellij.util.io.KeyDescriptor; import com.intellij.util.io.PersistentHashMap; import org.jetbrains.annotations.NotNull; @@ -56,15 +54,10 @@ class ValueContainerMap extends PersistentHashMap Date: Mon, 17 Oct 2016 17:02:07 +0200 Subject: [PATCH 61/66] avoid extra field initialization --- .../util/src/com/intellij/util/io/CompressedAppendableFile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/util/src/com/intellij/util/io/CompressedAppendableFile.java b/platform/util/src/com/intellij/util/io/CompressedAppendableFile.java index 5dd735458341..b9deac071df9 100644 --- a/platform/util/src/com/intellij/util/io/CompressedAppendableFile.java +++ b/platform/util/src/com/intellij/util/io/CompressedAppendableFile.java @@ -291,7 +291,7 @@ public class CompressedAppendableFile { private synchronized void loadAppendBuffer() throws IOException { if (myNextChunkBuffer != null) return; - myNextChunkBuffer = new byte[myAppendBufferLength]; + File tempAppendFile = getIncompleteChunkFile(); if (tempAppendFile.exists()) { myBufferPosition = (int)tempAppendFile.length(); From 3a2a8f142cf34f09242275d4efbc8d2b31c2fdc3 Mon Sep 17 00:00:00 2001 From: Valentina Kiryushkina Date: Fri, 8 Apr 2016 18:52:46 +0300 Subject: [PATCH 62/66] PY-19242 Add autocompletion for % format strings * Extract separate completion provider for formatted string arguments add patterns * Add tests --- .../PyStringFormatCompletionContributor.java | 364 +++++++++++++----- .../formatStringWithFormatModifier.after.py | 1 + .../formatStringWithFormatModifier.py | 1 + .../percentStringDictCallStringKey.after.py | 1 + .../percentStringDictCallStringKey.py | 1 + .../percentStringDictLiteralArgument.after.py | 1 + .../percentStringDictLiteralArgument.py | 1 + ...percentStringDictLiteralStringKey.after.py | 1 + .../percentStringDictLiteralStringKey.py | 1 + .../percentStringWithDictCallArg.after.py | 1 + .../percentStringWithDictCallArg.py | 1 + .../percentStringWithDictLiteralArg.after.py | 1 + .../percentStringWithDictLiteralArg.py | 1 + .../percentStringWithModifiers.after.py | 1 + .../completion/percentStringWithModifiers.py | 1 + ...percentStringWithParenDictCallArg.after.py | 1 + .../percentStringWithParenDictCallArg.py | 1 + .../python/PythonCompletionTest.java | 49 ++- 18 files changed, 336 insertions(+), 93 deletions(-) create mode 100644 python/testData/completion/formatStringWithFormatModifier.after.py create mode 100644 python/testData/completion/formatStringWithFormatModifier.py create mode 100644 python/testData/completion/percentStringDictCallStringKey.after.py create mode 100644 python/testData/completion/percentStringDictCallStringKey.py create mode 100644 python/testData/completion/percentStringDictLiteralArgument.after.py create mode 100644 python/testData/completion/percentStringDictLiteralArgument.py create mode 100644 python/testData/completion/percentStringDictLiteralStringKey.after.py create mode 100644 python/testData/completion/percentStringDictLiteralStringKey.py create mode 100644 python/testData/completion/percentStringWithDictCallArg.after.py create mode 100644 python/testData/completion/percentStringWithDictCallArg.py create mode 100644 python/testData/completion/percentStringWithDictLiteralArg.after.py create mode 100644 python/testData/completion/percentStringWithDictLiteralArg.py create mode 100644 python/testData/completion/percentStringWithModifiers.after.py create mode 100644 python/testData/completion/percentStringWithModifiers.py create mode 100644 python/testData/completion/percentStringWithParenDictCallArg.after.py create mode 100644 python/testData/completion/percentStringWithParenDictCallArg.py diff --git a/python/src/com/jetbrains/python/codeInsight/completion/PyStringFormatCompletionContributor.java b/python/src/com/jetbrains/python/codeInsight/completion/PyStringFormatCompletionContributor.java index cd86794044da..718c1ef2e28c 100644 --- a/python/src/com/jetbrains/python/codeInsight/completion/PyStringFormatCompletionContributor.java +++ b/python/src/com/jetbrains/python/codeInsight/completion/PyStringFormatCompletionContributor.java @@ -17,38 +17,205 @@ package com.jetbrains.python.codeInsight.completion; import com.intellij.codeInsight.completion.*; -import com.intellij.codeInsight.lookup.AutoCompletionPolicy; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.patterns.PatternCondition; +import com.intellij.patterns.PsiElementPattern; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ObjectUtils; import com.intellij.util.ProcessingContext; +import com.jetbrains.python.PyNames; import com.jetbrains.python.inspections.PyStringFormatParser; import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.impl.PyPsiUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import static com.intellij.patterns.PlatformPatterns.psiElement; import static com.intellij.patterns.StandardPatterns.or; +import static java.util.Arrays.asList; public class PyStringFormatCompletionContributor extends CompletionContributor { + private static final String DICT_NAME = "dict"; + + private static final PatternCondition FORMAT_CALL_PATTERN_CONDITION = + new PatternCondition("isFormatFunction") { + + @Override + public boolean accepts(@NotNull PyReferenceExpression expression, ProcessingContext context) { + String expressionName = expression.getName(); + return expressionName != null && expressionName.equals(PyNames.FORMAT); + } + }; + + private static final PatternCondition DICT_CALL_PATTERN_CONDITION = + new PatternCondition("isDictCall") { + + @Override + public boolean accepts(@NotNull PyReferenceExpression expression, ProcessingContext context) { + String expressionName = expression.getName(); + return expressionName != null && expressionName.equals(DICT_NAME); + } + }; + + private static final PsiElementPattern.Capture FORMAT_STRING_CAPTURE = + psiElement(PyStringLiteralExpression.class) + .withParent(psiElement(PyReferenceExpression.class).with(FORMAT_CALL_PATTERN_CONDITION)) + .withSuperParent(2, PyCallExpression.class); + + private static final PsiElementPattern.Capture PERCENT_STRING_CAPTURE = + psiElement(PyStringLiteralExpression.class).beforeLeaf(psiElement().withText("%")).withParent(PyBinaryExpression.class); + + + @Nullable private static final PatternCondition PERCENT_BINARY_EXPRESSION_PATTERN = + new PatternCondition("isBinaryFormatExpression") { + @Override + public boolean accepts(@NotNull PyBinaryExpression expression, ProcessingContext context) { + return expression.isOperator("%"); + } + }; + + private static final PsiElementPattern.Capture DICT_FUNCTION_KEYWORD_ARGUMENT_CAPTURE = + (psiElement(PyKeywordArgument.class)) + .withSuperParent(3, + psiElement(PyBinaryExpression.class) + .withChild(psiElement(PyCallExpression.class) + .withChild(psiElement(PyReferenceExpression.class) + .with(DICT_CALL_PATTERN_CONDITION))) + .with(PERCENT_BINARY_EXPRESSION_PATTERN)); + + private static final PsiElementPattern.Capture DICT_FUNCTION_REFERENCE_ARGUMENT_CAPTURE = + (psiElement(PyReferenceExpression.class)) + .withSuperParent(3, + psiElement(PyBinaryExpression.class) + .withChild(psiElement(PyCallExpression.class) + .withChild(psiElement(PyReferenceExpression.class) + .with(DICT_CALL_PATTERN_CONDITION))) + .with(PERCENT_BINARY_EXPRESSION_PATTERN)); + + private static final PsiElementPattern.Capture FORMAT_FUNCTION_ARGUMENT_CAPTURE = + psiElement(PyKeywordArgument.class) + .withSuperParent(2, psiElement(PyCallExpression.class) + .withChild(psiElement(PyReferenceExpression.class).with(FORMAT_CALL_PATTERN_CONDITION))); + + // to provide completion for: "{foo}".format(fo) + private static final PsiElementPattern.Capture FORMAT_FUNCTION_REFERENCE_ARGUMENT_CAPTURE = + psiElement(PyReferenceExpression.class) + .withSuperParent(2, psiElement(PyCallExpression.class) + .withChild(psiElement(PyReferenceExpression.class).with(FORMAT_CALL_PATTERN_CONDITION))); + + private static final PsiElementPattern.Capture DICT_LITERAL_STRING_KEY_CAPTURE = + psiElement(PyStringLiteralExpression.class) + .withParent(or(psiElement(PyKeyValueExpression.class) + .withParent(psiElement(PyDictLiteralExpression.class) + .withParent(psiElement(PyBinaryExpression.class).with(PERCENT_BINARY_EXPRESSION_PATTERN))), + psiElement(PyDictLiteralExpression.class) + .withParent(psiElement(PyBinaryExpression.class).with(PERCENT_BINARY_EXPRESSION_PATTERN)))); + + // to provide completion for: "%(foo)s % {"f"} + private static final PsiElementPattern.Capture SET_LITERAL_STRING_KEY_CAPTURE = + psiElement(PyStringLiteralExpression.class) + .withParent(psiElement(PySetLiteralExpression.class).withParent(psiElement(PyBinaryExpression.class) + .with(PERCENT_BINARY_EXPRESSION_PATTERN))); + public PyStringFormatCompletionContributor() { extend( CompletionType.BASIC, - or(psiElement().inside(PyArgumentList - .class), psiElement().inside(PyStringLiteralExpression.class)), - new FormattedStringCompletionProvider() + or( + psiElement().inside(PERCENT_STRING_CAPTURE), + psiElement().inside(FORMAT_STRING_CAPTURE)), + new StringFormatCompletionProvider() + ); + + extend( + CompletionType.BASIC, + or(psiElement().inside(DICT_LITERAL_STRING_KEY_CAPTURE), + psiElement().inside(DICT_FUNCTION_KEYWORD_ARGUMENT_CAPTURE), + psiElement().inside(DICT_FUNCTION_REFERENCE_ARGUMENT_CAPTURE), + psiElement().inside(SET_LITERAL_STRING_KEY_CAPTURE), + psiElement().inside(FORMAT_FUNCTION_ARGUMENT_CAPTURE), + psiElement().inside(FORMAT_FUNCTION_REFERENCE_ARGUMENT_CAPTURE) + ), + new StringFormatArgumentsCompletionProvider() ); } + private static class StringFormatArgumentsCompletionProvider extends CompletionProvider { - private static class FormattedStringCompletionProvider extends CompletionProvider { + @Override + protected void addCompletions(@NotNull CompletionParameters parameters, + ProcessingContext context, + @NotNull CompletionResultSet result) { + final PsiElement original = parameters.getOriginalPosition(); + if (original != null) { + result = result.withPrefixMatcher(getPrefix(parameters.getOffset(), parameters.getOriginalFile())); + final PsiElement parent = original.getParent(); + if (parent.getParent() instanceof PyKeyValueExpression || parent instanceof PyStringLiteralExpression) { + final PyBinaryExpression binExpr = PsiTreeUtil.getParentOfType(parent, PyBinaryExpression.class); + if (binExpr != null) { + final PyStringLiteralExpression strExpr = PyUtil.as(binExpr.getLeftExpression(), PyStringLiteralExpression.class); + if (strExpr != null) { + result.addAllElements(getPercentLookupBuilders(strExpr)); + } + } + } + else if (PyUtil.instanceOf(parent, PyKeywordArgument.class, PyReferenceExpression.class)) { + result.addAllElements(getElementsFromString(PsiTreeUtil.getParentOfType(original, PyArgumentList.class))); + } + } + } + + @NotNull + private static List getElementsFromString(@Nullable final PyArgumentList argumentList) { + if (argumentList != null) { + final PyReferenceExpression refExpr = PsiTreeUtil.getPrevSiblingOfType(argumentList, PyReferenceExpression.class); + final PyStringLiteralExpression strExpr = PsiTreeUtil.getChildOfType(refExpr, PyStringLiteralExpression.class); + if (strExpr != null) { + return getFormatLookupBuilders(strExpr); + } + else { + final PyBinaryExpression binExpr = PsiTreeUtil.getParentOfType(refExpr, PyBinaryExpression.class); + if (binExpr != null) { + final PyStringLiteralExpression stringLiteralExpr = PyUtil.as(binExpr.getLeftExpression(), PyStringLiteralExpression.class); + if (stringLiteralExpr != null) { + return getPercentLookupBuilders(stringLiteralExpr); + } + } + } + } + return Collections.emptyList(); + } + + @NotNull + private static List getFormatLookupBuilders(@NotNull final PyStringLiteralExpression expression) { + final Map chunks = PyStringFormatParser.getKeywordSubstitutions( + PyStringFormatParser.filterSubstitutions(PyStringFormatParser.parseNewStyleFormat(expression.getStringValue()))); + return getLookupBuilders(chunks); + } + + private static List getPercentLookupBuilders(@NotNull final PyStringLiteralExpression expression) { + final Map chunks = PyStringFormatParser.getKeywordSubstitutions( + PyStringFormatParser.filterSubstitutions(PyStringFormatParser.parsePercentFormat(expression.getStringValue()))); + return getLookupBuilders(chunks); + } + + @NotNull + private static List getLookupBuilders(@NotNull final Map chunks) { + return chunks.keySet().stream() + .map(PyStringFormatCompletionContributor::createLookUpElement) + .collect(Collectors.toList()); + } + } + + private static class StringFormatCompletionProvider extends CompletionProvider { @Override protected void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext context, @@ -56,63 +223,111 @@ public class PyStringFormatCompletionContributor extends CompletionContributor { final PsiElement original = parameters.getOriginalPosition(); if (original != null) { final PsiElement parent = original.getParent(); + result = result.withPrefixMatcher(getPrefix(parameters.getOffset(), parent.getContainingFile())); if (parent instanceof PyStringLiteralExpression) { - final int stringOffset = parameters.getOffset() - parameters.getPosition().getTextRange().getStartOffset(); - if (isInsideSubstitutionChunk((PyStringLiteralExpression)parent, - stringOffset)) { - final PyExpression[] arguments = getFormatFunctionKeyWordArguments(original); - for (PyExpression argument : arguments) { - result = result.withPrefixMatcher(getPrefix(parameters.getOffset(), argument.getContainingFile())); - addKeysFromStarArgument(result, argument); - addKeyWordArgument(result, argument); - } - } - } - else if (PyUtil.instanceOf(parent, PyKeywordArgument.class, PyReferenceExpression.class)) { - final PyArgumentList argumentList = PsiTreeUtil.getParentOfType(original, PyArgumentList.class); - result = result.withPrefixMatcher(getPrefix(parameters.getOffset(), parent.getContainingFile())); - addElementsFromFormattedString(result, argumentList); + result.addAllElements(addCompletionsForSubstitutions(parameters, original, (PyStringLiteralExpression)parent)); } } } - private static boolean isInsideSubstitutionChunk(@NotNull final PyStringLiteralExpression expression, final int offset) { - final List substitutions = PyStringFormatParser.filterSubstitutions( - PyStringFormatParser.parseNewStyleFormat(expression.getStringValue())); - for (PyStringFormatParser.SubstitutionChunk substitution: substitutions) { - if (offset >= substitution.getStartIndex() && offset <= substitution.getEndIndex()) { - return true; - } - } - return false; - } - @NotNull - private static PyExpression[] getFormatFunctionKeyWordArguments(final PsiElement original) { - final PsiElement pyReferenceExpression = PsiTreeUtil.getParentOfType(original, PyReferenceExpression.class); - final PyArgumentList argumentList = PsiTreeUtil.getNextSiblingOfType(pyReferenceExpression, PyArgumentList.class); - if (argumentList != null) { - return argumentList.getArguments(); - } - return PyExpression.EMPTY_ARRAY; - } + private static List addCompletionsForSubstitutions(@NotNull final CompletionParameters parameters, + @NotNull final PsiElement original, + @NotNull final PyStringLiteralExpression stringExpression) { + final int stringOffset = getCaretStartOffsetInsideString(parameters, stringExpression); - private static void addKeysFromStarArgument(@NotNull final CompletionResultSet result, @NotNull final PyExpression arg) { - if (arg instanceof PyStarArgument) { - final PyDictLiteralExpression dict = ObjectUtils.chooseNotNull(PsiTreeUtil.getChildOfType(arg, PyDictLiteralExpression.class), - getDictFromReference(arg)); - if (dict != null) { - for (PyKeyValueExpression keyValue: dict.getElements()) { - if (keyValue.getKey() instanceof PyStringLiteralExpression) { - final String key = ((PyStringLiteralExpression) keyValue.getKey()).getStringValue(); - result.addElement(createLookUpElement(key)); + if (isInsideFormatSubstitutionChunk(stringExpression, stringOffset)) { + final PyExpression[] arguments = getFormatFunctionKeyWordArguments(original); + final ArrayList elements = new ArrayList<>(); + for (PyExpression argument : arguments) { + if (argument instanceof PyKeywordArgument) { + elements.add(getKeywordArgument((PyKeywordArgument)argument)); + } + else if (argument instanceof PyStarArgument) { + elements.addAll(getKeysFromStarArgument((PyStarArgument)argument)); + } + } + return elements; + } + + if (isInsidePercentSubstitutionChunk(stringExpression, stringOffset)) { + final PyBinaryExpression binExpr = PyUtil.as(PsiTreeUtil.getParentOfType(stringExpression, PyBinaryExpression.class), + PyBinaryExpression.class); + if (binExpr != null) { + final PyExpression rightExpr = PyPsiUtils.flattenParens(binExpr.getRightExpression()); + + final PyDictLiteralExpression dict = PyUtil.as(rightExpr, PyDictLiteralExpression.class); + if (dict != null) { + return getElementsFromDict(dict); + } + + final PyCallExpression callExpression = PyUtil.as(rightExpr, PyCallExpression.class); + if (callExpression != null) { + final PyExpression callee = callExpression.getCallee(); + if (callee != null && callee.getName() != null && callee.getName().equals(DICT_NAME)) { + final PyExpression[] arguments = callExpression.getArguments(); + return asList(arguments).stream() + .filter(a -> a instanceof PyKeywordArgument) + .map(a -> getKeywordArgument((PyKeywordArgument)a)) + .filter(e -> e != null) + .collect(Collectors.toList()); } } } } + return Collections.emptyList(); } + private static int getCaretStartOffsetInsideString(@NotNull final CompletionParameters parameters, + @NotNull final PyStringLiteralExpression parent) { + final int caretAbsoluteOffset = parameters.getOffset(); + final int stringExprStartOffset = parameters.getPosition().getTextRange().getStartOffset(); + final int stringValueStartOffset = parent.getStringValueTextRange().getStartOffset(); + return caretAbsoluteOffset - stringExprStartOffset - stringValueStartOffset; + } + + private static boolean isInsideFormatSubstitutionChunk(@NotNull final PyStringLiteralExpression expression, final int offset) { + List substitutions = PyStringFormatParser.filterSubstitutions( + PyStringFormatParser.parseNewStyleFormat(expression.getStringValue())); + return isInsideSubstitutionChunk(offset, substitutions); + } + + private static boolean isInsidePercentSubstitutionChunk(@NotNull final PyStringLiteralExpression expression, final int offset) { + List substitutions = PyStringFormatParser.filterSubstitutions( + PyStringFormatParser.parsePercentFormat(expression.getStringValue())); + return isInsideSubstitutionChunk(offset, substitutions); + } + + private static boolean isInsideSubstitutionChunk(int offset, @NotNull List substitutions) { + return substitutions.stream().anyMatch(s -> offset >= s.getStartIndex() && offset <= s.getEndIndex()); + } + + @NotNull + private static PyExpression[] getFormatFunctionKeyWordArguments(@NotNull final PsiElement original) { + final PsiElement pyReferenceExpression = PsiTreeUtil.getParentOfType(original, PyReferenceExpression.class); + final PyArgumentList argumentList = PsiTreeUtil.getNextSiblingOfType(pyReferenceExpression, PyArgumentList.class); + return argumentList != null ? argumentList.getArguments() : PyExpression.EMPTY_ARRAY; + } + + @NotNull + private static List getKeysFromStarArgument(@NotNull final PyStarArgument arg) { + final PyDictLiteralExpression dict = ObjectUtils.chooseNotNull(PsiTreeUtil.getChildOfType(arg, PyDictLiteralExpression.class), + getDictFromReference(arg)); + + return dict != null ? getElementsFromDict(dict) : Collections.emptyList(); + } + + @NotNull + private static List getElementsFromDict(@NotNull final PyDictLiteralExpression dict) { + return asList(dict.getElements()).stream() + .map(e -> PyUtil.as(e.getKey(), PyStringLiteralExpression.class)) + .filter(k-> k != null) + .map(k -> createLookUpElement(k.getStringValue())) + .collect(Collectors.toList()); + } + + @Nullable private static PyDictLiteralExpression getDictFromReference(@NotNull final PyExpression arg) { final PyReferenceExpression referenceExpression = PsiTreeUtil.getChildOfType(arg, PyReferenceExpression.class); if (referenceExpression != null) { @@ -125,58 +340,31 @@ public class PyStringFormatCompletionContributor extends CompletionContributor { return null; } - private static void addKeyWordArgument(@NotNull final CompletionResultSet result, @NotNull final PyExpression arg) { - if (arg instanceof PyKeywordArgument) { - final String keyword = ((PyKeywordArgument)arg).getKeyword(); - if (keyword!= null) { - result.addElement(createLookUpElement(keyword)); - } - } + @Nullable + private static LookupElement getKeywordArgument(@NotNull final PyKeywordArgument arg) { + final String keyword = arg.getKeyword(); + return keyword != null ? createLookUpElement(keyword) : null; } - - @NotNull - private static LookupElement createLookUpElement(@NotNull final String element) { - return LookupElementBuilder - .create(element) - .withTypeText("arg") - .withAutoCompletionPolicy(AutoCompletionPolicy.ALWAYS_AUTOCOMPLETE); - } - - private static void addElementsFromFormattedString(@NotNull final CompletionResultSet result, - @Nullable final PyArgumentList argumentList) { - if (argumentList != null) { - final PyReferenceExpression pyReferenceExpression = PsiTreeUtil.getPrevSiblingOfType(argumentList, PyReferenceExpression.class); - final PyStringLiteralExpression formattedString = PsiTreeUtil.getChildOfType(pyReferenceExpression, PyStringLiteralExpression.class); - if (formattedString != null) { - result.addAllElements(getLookupBuilders(formattedString)); - } - } - } - - @NotNull - private static List getLookupBuilders(@NotNull final PyStringLiteralExpression literalExpression) { - final Map chunks = PyStringFormatParser.getKeywordSubstitutions( - PyStringFormatParser.filterSubstitutions(PyStringFormatParser.parseNewStyleFormat(literalExpression.getStringValue()))); - final List keys = new ArrayList<>(); - for (String chunk: chunks.keySet()) { - keys.add(createLookUpElement(chunk)); - } - return keys; - } - } + @NotNull + private static LookupElement createLookUpElement(@NotNull final String element) { + return LookupElementBuilder + .create(element) + .withTypeText("arg"); + } + + @NotNull private static String getPrefix(int offset, @NotNull final PsiFile file) { if (offset > 0) { offset--; } final String text = file.getText(); final StringBuilder prefixBuilder = new StringBuilder(); - while(offset > 0 && Character.isLetterOrDigit(text.charAt(offset))) { + while (offset > 0 && Character.isLetterOrDigit(text.charAt(offset))) { prefixBuilder.insert(0, text.charAt(offset)); offset--; } return prefixBuilder.toString(); } - } diff --git a/python/testData/completion/formatStringWithFormatModifier.after.py b/python/testData/completion/formatStringWithFormatModifier.after.py new file mode 100644 index 000000000000..fd3cbf8745f0 --- /dev/null +++ b/python/testData/completion/formatStringWithFormatModifier.after.py @@ -0,0 +1 @@ +r"{completed}".format(completed="hood") \ No newline at end of file diff --git a/python/testData/completion/formatStringWithFormatModifier.py b/python/testData/completion/formatStringWithFormatModifier.py new file mode 100644 index 000000000000..326bd387e8ec --- /dev/null +++ b/python/testData/completion/formatStringWithFormatModifier.py @@ -0,0 +1 @@ +r"{compl}".format(completed="hood") \ No newline at end of file diff --git a/python/testData/completion/percentStringDictCallStringKey.after.py b/python/testData/completion/percentStringDictCallStringKey.after.py new file mode 100644 index 000000000000..b511e971f8f9 --- /dev/null +++ b/python/testData/completion/percentStringDictCallStringKey.after.py @@ -0,0 +1 @@ +"%(completion)s" % dict(completion) \ No newline at end of file diff --git a/python/testData/completion/percentStringDictCallStringKey.py b/python/testData/completion/percentStringDictCallStringKey.py new file mode 100644 index 000000000000..85f51a432740 --- /dev/null +++ b/python/testData/completion/percentStringDictCallStringKey.py @@ -0,0 +1 @@ +"%(completion)s" % dict(complet) \ No newline at end of file diff --git a/python/testData/completion/percentStringDictLiteralArgument.after.py b/python/testData/completion/percentStringDictLiteralArgument.after.py new file mode 100644 index 000000000000..d7e3feb45085 --- /dev/null +++ b/python/testData/completion/percentStringDictLiteralArgument.after.py @@ -0,0 +1 @@ +"format: %(fooo)s" % {"boo":1, "fooo"} \ No newline at end of file diff --git a/python/testData/completion/percentStringDictLiteralArgument.py b/python/testData/completion/percentStringDictLiteralArgument.py new file mode 100644 index 000000000000..de95e3756ebc --- /dev/null +++ b/python/testData/completion/percentStringDictLiteralArgument.py @@ -0,0 +1 @@ +"format: %(fooo)s" % {"boo":1, "foo"} \ No newline at end of file diff --git a/python/testData/completion/percentStringDictLiteralStringKey.after.py b/python/testData/completion/percentStringDictLiteralStringKey.after.py new file mode 100644 index 000000000000..a6b1a9f515a8 --- /dev/null +++ b/python/testData/completion/percentStringDictLiteralStringKey.after.py @@ -0,0 +1 @@ +"format: %(fooo)s" % {"fooo"} \ No newline at end of file diff --git a/python/testData/completion/percentStringDictLiteralStringKey.py b/python/testData/completion/percentStringDictLiteralStringKey.py new file mode 100644 index 000000000000..cdb848d3a4d1 --- /dev/null +++ b/python/testData/completion/percentStringDictLiteralStringKey.py @@ -0,0 +1 @@ +"format: %(fooo)s" % {"fo"} \ No newline at end of file diff --git a/python/testData/completion/percentStringWithDictCallArg.after.py b/python/testData/completion/percentStringWithDictCallArg.after.py new file mode 100644 index 000000000000..1746bbfe255b --- /dev/null +++ b/python/testData/completion/percentStringWithDictCallArg.after.py @@ -0,0 +1 @@ +"to be %(completed)s" % dict(completed="smth") \ No newline at end of file diff --git a/python/testData/completion/percentStringWithDictCallArg.py b/python/testData/completion/percentStringWithDictCallArg.py new file mode 100644 index 000000000000..c5b1e67ec3c5 --- /dev/null +++ b/python/testData/completion/percentStringWithDictCallArg.py @@ -0,0 +1 @@ +"to be %(comple)s" % dict(completed="smth") \ No newline at end of file diff --git a/python/testData/completion/percentStringWithDictLiteralArg.after.py b/python/testData/completion/percentStringWithDictLiteralArg.after.py new file mode 100644 index 000000000000..0fc03b14c9c7 --- /dev/null +++ b/python/testData/completion/percentStringWithDictLiteralArg.after.py @@ -0,0 +1 @@ +"format: %(completion)s" % {"completion": "smth"} \ No newline at end of file diff --git a/python/testData/completion/percentStringWithDictLiteralArg.py b/python/testData/completion/percentStringWithDictLiteralArg.py new file mode 100644 index 000000000000..78d3719c68cf --- /dev/null +++ b/python/testData/completion/percentStringWithDictLiteralArg.py @@ -0,0 +1 @@ +"format: %(complet)s" % {"completion": "smth"} \ No newline at end of file diff --git a/python/testData/completion/percentStringWithModifiers.after.py b/python/testData/completion/percentStringWithModifiers.after.py new file mode 100644 index 000000000000..cf1d5fa339dd --- /dev/null +++ b/python/testData/completion/percentStringWithModifiers.after.py @@ -0,0 +1 @@ +r"%(completion)s" % dict(completion="smth") \ No newline at end of file diff --git a/python/testData/completion/percentStringWithModifiers.py b/python/testData/completion/percentStringWithModifiers.py new file mode 100644 index 000000000000..100deae1f4a6 --- /dev/null +++ b/python/testData/completion/percentStringWithModifiers.py @@ -0,0 +1 @@ +r"%(compl)s" % dict(completion="smth") \ No newline at end of file diff --git a/python/testData/completion/percentStringWithParenDictCallArg.after.py b/python/testData/completion/percentStringWithParenDictCallArg.after.py new file mode 100644 index 000000000000..281d52c5862c --- /dev/null +++ b/python/testData/completion/percentStringWithParenDictCallArg.after.py @@ -0,0 +1 @@ +"to be %(completion)s" % dict(completion="smth") \ No newline at end of file diff --git a/python/testData/completion/percentStringWithParenDictCallArg.py b/python/testData/completion/percentStringWithParenDictCallArg.py new file mode 100644 index 000000000000..81d8f5a4aabe --- /dev/null +++ b/python/testData/completion/percentStringWithParenDictCallArg.py @@ -0,0 +1 @@ +"to be %(complet)s" % dict(completion="smth") \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java index d64a6be42a1e..62fa374323fd 100644 --- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java @@ -864,31 +864,70 @@ public class PythonCompletionTest extends PyTestCase { doTest(); } - //PY-3077 + // PY-3077 public void testFormatString() { doTest(); } - //PY-3077 + // PY-3077 public void testFormatFuncArgument() { doTest(); } - //PY-3077 + // PY-3077 public void testFormatStringFromStarArg() { doTest(); } - //PY-3077 + // PY-3077 public void testFormatStringOutsideBraces() { doTest(); } - //PY-3077 + // PY-3077 public void testFormatStringFromRef() { doTest(); } + // PY-3077 + public void testFormatStringWithFormatModifier() { + doTest(); + } + + // PY-3077 + public void testPercentStringWithDictLiteralArg() { + doTest(); + } + + // PY-3077 + public void testPercentStringWithDictCallArg() { + doTest(); + } + + // PY-3077 + public void testPercentStringWithParenDictCallArg() { + doTest(); + } + + // PY-3077 + public void testPercentStringWithModifiers() { + doTest(); + } + + // PY-3077 + public void testPercentStringDictLiteralStringKey() { + doTest(); + } + + // PY-3077 + public void testPercentStringDictCallStringKey() { + doTest(); + } + + public void testPercentStringDictLiteralArgument() { + doTest(); + } + // PY-17437 public void testStrFormat() { doTest(); From 6d04482ac77da4bf620d5c3652ad247bb760961b Mon Sep 17 00:00:00 2001 From: Valentina Kiryushkina Date: Wed, 22 Jun 2016 14:20:30 +0300 Subject: [PATCH 63/66] PY-19839 Fix patterns for keyword substitution for print, yield, true, false keywords Add tests --- .../PyDocstringCompletionContributor.java | 4 +- .../PyKeywordCompletionContributor.java | 8 ++-- .../completion/percentStringDictFuncKeys.py | 1 + .../completion/percentStringDictRefKeys.py | 3 ++ .../percentStringDictWithDictLiteralArg.py | 1 + .../percentStringDictWithListArg.py | 1 + ...rcentStringDictWithPackedDictLiteralArg.py | 2 + .../percentStringDictWithZipCall.py | 1 + .../python/PythonCompletionTest.java | 37 +++++++++++++++++++ 9 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 python/testData/completion/percentStringDictFuncKeys.py create mode 100644 python/testData/completion/percentStringDictRefKeys.py create mode 100644 python/testData/completion/percentStringDictWithDictLiteralArg.py create mode 100644 python/testData/completion/percentStringDictWithListArg.py create mode 100644 python/testData/completion/percentStringDictWithPackedDictLiteralArg.py create mode 100644 python/testData/completion/percentStringDictWithZipCall.py diff --git a/python/src/com/jetbrains/python/codeInsight/completion/PyDocstringCompletionContributor.java b/python/src/com/jetbrains/python/codeInsight/completion/PyDocstringCompletionContributor.java index f18e7debb496..fa2bf7eb1d16 100644 --- a/python/src/com/jetbrains/python/codeInsight/completion/PyDocstringCompletionContributor.java +++ b/python/src/com/jetbrains/python/codeInsight/completion/PyDocstringCompletionContributor.java @@ -28,10 +28,10 @@ import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ProcessingContext; import com.jetbrains.python.documentation.docstrings.DocStringParameterReference; +import com.jetbrains.python.documentation.docstrings.DocStringTagCompletionContributor; import com.jetbrains.python.documentation.docstrings.DocStringTypeReference; import com.jetbrains.python.psi.PyDocStringOwner; import com.jetbrains.python.psi.PyNamedParameter; -import com.jetbrains.python.psi.PyStringLiteralExpression; import com.jetbrains.python.refactoring.PyRefactoringUtil; import org.jetbrains.annotations.NotNull; @@ -47,7 +47,7 @@ import static com.intellij.patterns.StandardPatterns.or; public class PyDocstringCompletionContributor extends CompletionContributor { public PyDocstringCompletionContributor() { extend(CompletionType.BASIC, - or(psiElement().inside(PyStringLiteralExpression.class), psiComment()), + or(psiElement().withParent(DocStringTagCompletionContributor.DOCSTRING_PATTERN), psiComment()), new IdentifierCompletionProvider()); } diff --git a/python/src/com/jetbrains/python/codeInsight/completion/PyKeywordCompletionContributor.java b/python/src/com/jetbrains/python/codeInsight/completion/PyKeywordCompletionContributor.java index d1e4a81efc9c..ff500a61e5b4 100644 --- a/python/src/com/jetbrains/python/codeInsight/completion/PyKeywordCompletionContributor.java +++ b/python/src/com/jetbrains/python/codeInsight/completion/PyKeywordCompletionContributor.java @@ -419,7 +419,8 @@ public class PyKeywordCompletionContributor extends CompletionContributor { .andNot(IN_PARAM_LIST) .andNot(IN_ARG_LIST) .andNot(BEFORE_COND) - .andNot(AFTER_QUALIFIER); + .andNot(AFTER_QUALIFIER) + .andNot(IN_STRING_LITERAL); extend( CompletionType.BASIC, @@ -609,6 +610,7 @@ public class PyKeywordCompletionContributor extends CompletionContributor { .and(NOT_PARAMETER_OR_DEFAULT_VALUE) .andNot(AFTER_QUALIFIER) .andNot(IN_FUNCTION_HEADER) + .andNot(IN_STRING_LITERAL) , new PyKeywordCompletionProvider(TailType.NONE, PyNames.TRUE, PyNames.FALSE, PyNames.NONE)); extend(CompletionType.BASIC, @@ -708,7 +710,7 @@ public class PyKeywordCompletionContributor extends CompletionContributor { psiElement() .inside(false, psiElement(PyAugAssignmentStatement.class), psiElement(PyTargetExpression.class)) .afterLeaf(psiElement().withElementType(PyTokenTypes.AUG_ASSIGN_OPERATIONS)), - psiElement().inside(true, psiElement(PyParenthesizedExpression.class))), + psiElement().inside(true, psiElement(PyParenthesizedExpression.class))).andNot(IN_STRING_LITERAL), new PyKeywordCompletionProvider(PyNames.YIELD)); } @@ -758,7 +760,7 @@ public class PyKeywordCompletionContributor extends CompletionContributor { psiElement() .withLanguage(PythonLanguage.getInstance()) .and(psiElement()).afterLeaf(psiElement().afterLeaf(PyNames.FOR)), - new PyKeywordCompletionProvider("in")); + new PyKeywordCompletionProvider(PyNames.IN)); } diff --git a/python/testData/completion/percentStringDictFuncKeys.py b/python/testData/completion/percentStringDictFuncKeys.py new file mode 100644 index 000000000000..779cf0f0de4d --- /dev/null +++ b/python/testData/completion/percentStringDictFuncKeys.py @@ -0,0 +1 @@ +print("Other string %()d" % {str(4): int('4')}) \ No newline at end of file diff --git a/python/testData/completion/percentStringDictRefKeys.py b/python/testData/completion/percentStringDictRefKeys.py new file mode 100644 index 000000000000..5dc9af3f05cb --- /dev/null +++ b/python/testData/completion/percentStringDictRefKeys.py @@ -0,0 +1,3 @@ +f = "fst" +s1 = "snd" +print("first is %()s, second is %(snd)s" % {f: 1, s1: 2}) \ No newline at end of file diff --git a/python/testData/completion/percentStringDictWithDictLiteralArg.py b/python/testData/completion/percentStringDictWithDictLiteralArg.py new file mode 100644 index 000000000000..82be749c8908 --- /dev/null +++ b/python/testData/completion/percentStringDictWithDictLiteralArg.py @@ -0,0 +1 @@ +print("Other string %()d" % dict({'three': 3, 'one': 1, 'two': 2})) \ No newline at end of file diff --git a/python/testData/completion/percentStringDictWithListArg.py b/python/testData/completion/percentStringDictWithListArg.py new file mode 100644 index 000000000000..ab917d16be2f --- /dev/null +++ b/python/testData/completion/percentStringDictWithListArg.py @@ -0,0 +1 @@ +print("Other string %()d" % dict([('two', 2), ('one', 1), ('three', 3)])) \ No newline at end of file diff --git a/python/testData/completion/percentStringDictWithPackedDictLiteralArg.py b/python/testData/completion/percentStringDictWithPackedDictLiteralArg.py new file mode 100644 index 000000000000..8b798b097532 --- /dev/null +++ b/python/testData/completion/percentStringDictWithPackedDictLiteralArg.py @@ -0,0 +1,2 @@ +d = {'three': 3, 'one': 1, 'two': 2} +print("Other string %()d" % dict(**d)) \ No newline at end of file diff --git a/python/testData/completion/percentStringDictWithZipCall.py b/python/testData/completion/percentStringDictWithZipCall.py new file mode 100644 index 000000000000..81308afb3a3b --- /dev/null +++ b/python/testData/completion/percentStringDictWithZipCall.py @@ -0,0 +1 @@ +print("Other string %()d" % dict(zip(['one', 'two', 'three'], [1, 2, 3]))) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java index 62fa374323fd..c36c0b839008 100644 --- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java @@ -924,9 +924,46 @@ public class PythonCompletionTest extends PyTestCase { doTest(); } + // PY-3077 public void testPercentStringDictLiteralArgument() { doTest(); } + + // PY-19839 + public void testPercentStringDictRefKeys() { + final List variants = doTestByFile(); + assertNullOrEmpty(variants); + } + + // PY-19839 + public void testPercentStringDictFuncKeys() { + final List variants = doTestByFile(); + assertNullOrEmpty(variants); + } + + // PY-19839 + public void testPercentStringDictWithZipCall() { + final List variants = doTestByFile(); + assertNullOrEmpty(variants); + } + + // PY-19839 + public void testPercentStringDictWithListArg() { + final List variants = doTestByFile(); + assertNullOrEmpty(variants); + } + + // PY-19839 + public void testPercentStringDictWithDictLiteralArg() { + final List variants = doTestByFile(); + assertNullOrEmpty(variants); + } + + // PY-19839 + public void testPercentStringDictWithPackedDictLiteralArg() { + final List variants = doTestByFile(); + assertNullOrEmpty(variants); + } // PY-17437 public void testStrFormat() { From 966bf9a042f1cc19775c5727289b090dd57055b8 Mon Sep 17 00:00:00 2001 From: Valentina Kiryushkina Date: Thu, 23 Jun 2016 18:26:27 +0300 Subject: [PATCH 64/66] Fix PY-19842 If 'Tab' is used for choosing in auto-completion, then next symbol is deleted: 1) Parse string value of string literal when constructing substitution chunk reference --- .../python/codeInsight/PySubstitutionChunkReference.java | 4 ++-- .../codeInsight/PythonFormattedStringReferenceProvider.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java b/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java index d32c798fab60..c052d9680976 100644 --- a/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java +++ b/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java @@ -62,9 +62,9 @@ public class PySubstitutionChunkReference extends PsiReferenceBase chunks = PyStringFormatParser.filterSubstitutions( - PyStringFormatParser.parseNewStyleFormat(element.getText())); + PyStringFormatParser.parseNewStyleFormat(element.getStringValue())); return getReferencesFromChunks(element, chunks, false); } private static PsiReference[] getReferencesFromPercentString(@NotNull final PyStringLiteralExpression element) { final List - chunks = PyStringFormatParser.filterSubstitutions(PyStringFormatParser.parsePercentFormat(element.getText())); + chunks = PyStringFormatParser.filterSubstitutions(PyStringFormatParser.parsePercentFormat(element.getStringValue())); return getReferencesFromChunks(element, chunks, true); } From db2f6375cb10a4cde268c19bdf9a0914f01bdf92 Mon Sep 17 00:00:00 2001 From: nik Date: Mon, 17 Oct 2016 16:46:31 +0300 Subject: [PATCH 65/66] ContentManager: unused meaningless method marked as deprecated --- .../src/com/intellij/ui/content/ContentManager.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/platform/platform-api/src/com/intellij/ui/content/ContentManager.java b/platform/platform-api/src/com/intellij/ui/content/ContentManager.java index 4a6dcbcf1e8e..08386ba6c5b6 100644 --- a/platform/platform-api/src/com/intellij/ui/content/ContentManager.java +++ b/platform/platform-api/src/com/intellij/ui/content/ContentManager.java @@ -34,6 +34,10 @@ public interface ContentManager extends Disposable, BusyObject { void addContent(@NotNull Content content); void addContent(@NotNull Content content, final int order); + + /** + * @deprecated use {@link #addContent(Content)} instead, {@code constraints} parameter isn't used anyway + */ void addContent(@NotNull Content content, Object constraints); boolean removeContent(@NotNull Content content, final boolean dispose); From 70401e7ffa1beedf2639efe75089d6ad3c67626b Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Mon, 17 Oct 2016 18:43:14 +0300 Subject: [PATCH 66/66] IDEA-162678 Version Control Local Changes (Git) does not grab focus when opened --- .../com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java index 5670ecc8410b..2ef6de9cbb90 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java @@ -451,13 +451,12 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements toolWindow.setSplitMode(true, null); } - final ActionCallback activation = toolWindow.setActivation(new ActionCallback()); - + // ToolWindow activation is not needed anymore and should be removed in 2017 + toolWindow.setActivation(new ActionCallback()).setDone(); final DumbAwareRunnable runnable = () -> { if (toolWindow.isDisposed()) return; toolWindow.ensureContentInitialized(); - activation.setDone(); }; if (visible || ApplicationManager.getApplication().isUnitTestMode()) { runnable.run();