From a2cc5bad5ac786d2663e5b988d7613a6fc467840 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Mon, 21 Apr 2014 14:21:01 +0200 Subject: [PATCH 01/34] expand description --- .../src/inspectionDescriptions/WrongPackageStatement.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources-en/src/inspectionDescriptions/WrongPackageStatement.html b/resources-en/src/inspectionDescriptions/WrongPackageStatement.html index 01fb18e9f597..78ff05272bfe 100644 --- a/resources-en/src/inspectionDescriptions/WrongPackageStatement.html +++ b/resources-en/src/inspectionDescriptions/WrongPackageStatement.html @@ -1,5 +1,6 @@ -Detects package statements that do not correspond to the project directory structure. +Detects package statements that do not correspond to the project directory structure + and reports classes without package statements. \ No newline at end of file From 6e6811147a84b2534f05c6240ecbe871fea93d03 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Mon, 21 Apr 2014 14:21:26 +0200 Subject: [PATCH 02/34] remove superfluous inspection --- .../src/META-INF/InspectionGadgets.xml | 4 - .../ClassInTopLevelPackageInspectionBase.java | 81 ------------------- .../ClassInTopLevelPackageInspection.java | 27 ------- .../ClassInTopLevelPackage.html | 8 -- 4 files changed, 120 deletions(-) delete mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ClassInTopLevelPackageInspectionBase.java delete mode 100644 plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/ClassInTopLevelPackageInspection.java delete mode 100644 plugins/InspectionGadgets/src/inspectionDescriptions/ClassInTopLevelPackage.html diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml index a9bfaa130c66..2b47e3a3c33d 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml @@ -394,10 +394,6 @@ - diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ClassInTopLevelPackageInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ClassInTopLevelPackageInspectionBase.java deleted file mode 100644 index dec9e2e662d2..000000000000 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ClassInTopLevelPackageInspectionBase.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2000-2013 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.siyeh.ig.classlayout; - -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiJavaFile; -import com.intellij.psi.util.FileTypeUtils; -import com.siyeh.InspectionGadgetsBundle; -import com.siyeh.ig.BaseInspection; -import com.siyeh.ig.BaseInspectionVisitor; -import com.siyeh.ig.psiutils.ClassUtils; -import org.jetbrains.annotations.NotNull; - -public class ClassInTopLevelPackageInspectionBase extends BaseInspection { - @Override - @NotNull - public String getID() { - return "ClassWithoutPackageStatement"; - } - - @Override - @NotNull - public String getDisplayName() { - return InspectionGadgetsBundle.message( - "class.in.top.level.package.display.name"); - } - - @Override - @NotNull - protected String buildErrorString(Object... infos) { - return InspectionGadgetsBundle.message( - "class.in.top.level.package.problem.descriptor"); - } - - @Override - protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { - return true; - } - - @Override - public BaseInspectionVisitor buildVisitor() { - return new ClassInTopLevelPackageVisitor(); - } - - private static class ClassInTopLevelPackageVisitor - extends BaseInspectionVisitor { - - @Override - public void visitClass(@NotNull PsiClass aClass) { - // no call to super, so that it doesn't drill down to inner classes - if (FileTypeUtils.isInServerPageFile(aClass)) { - return; - } - if (ClassUtils.isInnerClass(aClass)) { - return; - } - final PsiFile file = aClass.getContainingFile(); - if (!(file instanceof PsiJavaFile)) { - return; - } - if (((PsiJavaFile)file).getPackageStatement() != null) { - return; - } - registerClassError(aClass); - } - } -} diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/ClassInTopLevelPackageInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/ClassInTopLevelPackageInspection.java deleted file mode 100644 index 828b4c68d43f..000000000000 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/ClassInTopLevelPackageInspection.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers - * - * 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.siyeh.ig.classlayout; - -import com.siyeh.ig.InspectionGadgetsFix; -import com.siyeh.ig.fixes.MoveClassFix; - -public class ClassInTopLevelPackageInspection extends ClassInTopLevelPackageInspectionBase { - - @Override - protected InspectionGadgetsFix buildFix(Object... infos) { - return new MoveClassFix(); - } -} \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/ClassInTopLevelPackage.html b/plugins/InspectionGadgets/src/inspectionDescriptions/ClassInTopLevelPackage.html deleted file mode 100644 index c6263f25c4d5..000000000000 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/ClassInTopLevelPackage.html +++ /dev/null @@ -1,8 +0,0 @@ - - -Reports any classes which do not contain package declarations. - -

- - - \ No newline at end of file From 448e9f8d02ae67f60b1e752ce763d5df61d250d2 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Mon, 21 Apr 2014 15:01:55 +0200 Subject: [PATCH 03/34] don't report on incomplete code or comparisons that are already reported by "Constant conditions & exceptions" --- ...intlessArithmeticExpressionInspection.java | 97 ++----------------- .../PointlessArithmeticExpression.java | 1 + .../expected.xml | 84 ---------------- 3 files changed, 10 insertions(+), 172 deletions(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/PointlessArithmeticExpressionInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/PointlessArithmeticExpressionInspection.java index 6b2f34b1aa47..bebf5a559d15 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/PointlessArithmeticExpressionInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/PointlessArithmeticExpressionInspection.java @@ -20,8 +20,8 @@ import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; -import com.intellij.psi.util.ConstantExpressionUtil; import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.PsiUtilCore; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; @@ -116,20 +116,19 @@ public class PointlessArithmeticExpressionInspection } else if (tokenType.equals(JavaTokenType.ASTERISK) && isZero(operand) || tokenType.equals(JavaTokenType.PERC) && (isOne(operand) || EquivalenceChecker.expressionsAreEquivalent(previousOperand, operand))) { - return PsiType.LONG.equals(polyadicExpression.getType()) ? "0L" : "0"; - } - else if (tokenType.equals(JavaTokenType.LE) || tokenType.equals(JavaTokenType.GE) || - tokenType.equals(JavaTokenType.LT) || tokenType.equals(JavaTokenType.GT)) { - return (tokenType.equals(JavaTokenType.LT) || tokenType.equals(JavaTokenType.GT)) ? "false" : "true"; + fromTarget = operands[0]; + untilTarget = operands[length - 1]; + replacement = PsiType.LONG.equals(polyadicExpression.getType()) ? "0L" : "0"; + break; } previousOperand = operand; } - return buildReplacementExpression(polyadicExpression, fromTarget, untilTarget, replacement).trim(); + return getText(polyadicExpression, fromTarget, untilTarget, replacement).trim(); } - public static String buildReplacementExpression(PsiPolyadicExpression expression, PsiElement fromTarget, PsiElement untilTarget, - String replacement) { + public static String getText(PsiPolyadicExpression expression, PsiElement fromTarget, PsiElement untilTarget, + @NotNull @NonNls String replacement) { final StringBuilder result = new StringBuilder(); boolean stop = false; for (PsiElement child : expression.getChildren()) { @@ -198,13 +197,10 @@ public class PointlessArithmeticExpressionInspection if (!arithmeticTokens.contains(expression.getOperationTokenType())) { return; } - if (ExpressionUtils.hasStringType(expression)) { + if (ExpressionUtils.hasStringType(expression) || PsiUtilCore.hasErrorElementChild(expression)) { return; } final PsiExpression[] operands = expression.getOperands(); - if (operands.length < 2) { - return; - } final IElementType tokenType = expression.getOperationTokenType(); final boolean isPointless; if (tokenType.equals(JavaTokenType.PLUS)) { @@ -222,14 +218,6 @@ public class PointlessArithmeticExpressionInspection else if (tokenType.equals(JavaTokenType.PERC)) { isPointless = modExpressionIsPointless(operands); } - else if (tokenType.equals(JavaTokenType.LE) || - tokenType.equals(JavaTokenType.GE) || - tokenType.equals(JavaTokenType.GT) || - tokenType.equals(JavaTokenType.LT)) { - final PsiExpression lhs = operands[0]; - final PsiExpression rhs = operands[1]; - isPointless = comparisonExpressionIsPointless(lhs, rhs, tokenType); - } else { isPointless = false; } @@ -292,45 +280,6 @@ public class PointlessArithmeticExpressionInspection } return false; } - - private boolean comparisonExpressionIsPointless( - PsiExpression lhs, PsiExpression rhs, IElementType comparison) { - if (PsiType.INT.equals(lhs.getType()) && - PsiType.INT.equals(rhs.getType())) { - return intComparisonIsPointless(lhs, rhs, comparison); - } - else if (PsiType.LONG.equals(lhs.getType()) && - PsiType.LONG.equals(rhs.getType())) { - return longComparisonIsPointless(lhs, rhs, comparison); - } - return false; - } - - private boolean intComparisonIsPointless( - PsiExpression lhs, PsiExpression rhs, IElementType comparison) { - if (isMaxInt(lhs) || isMinInt(rhs)) { - return JavaTokenType.GE.equals(comparison) || - JavaTokenType.LT.equals(comparison); - } - if (isMinInt(lhs) || isMaxInt(rhs)) { - return JavaTokenType.LE.equals(comparison) || - JavaTokenType.GT.equals(comparison); - } - return false; - } - - private boolean longComparisonIsPointless( - PsiExpression lhs, PsiExpression rhs, IElementType comparison) { - if (isMaxLong(lhs) || isMinLong(rhs)) { - return JavaTokenType.GE.equals(comparison) || - JavaTokenType.LT.equals(comparison); - } - if (isMinLong(lhs) || isMaxLong(rhs)) { - return JavaTokenType.LE.equals(comparison) || - JavaTokenType.GT.equals(comparison); - } - return false; - } } boolean isZero(PsiExpression expression) { @@ -346,32 +295,4 @@ public class PointlessArithmeticExpressionInspection } return ExpressionUtils.isOne(expression); } - - private static boolean isMinInt(PsiExpression expression) { - final Integer value = (Integer) - ConstantExpressionUtil.computeCastTo( - expression, PsiType.INT); - return value != null && value.intValue() == Integer.MIN_VALUE; - } - - private static boolean isMaxInt(PsiExpression expression) { - final Integer value = (Integer) - ConstantExpressionUtil.computeCastTo( - expression, PsiType.INT); - return value != null && value.intValue() == Integer.MAX_VALUE; - } - - private static boolean isMinLong(PsiExpression expression) { - final Long value = (Long) - ConstantExpressionUtil.computeCastTo( - expression, PsiType.LONG); - return value != null && value.longValue() == Long.MIN_VALUE; - } - - private static boolean isMaxLong(PsiExpression expression) { - final Long value = (Long) - ConstantExpressionUtil.computeCastTo( - expression, PsiType.LONG); - return value != null && value.longValue() == Long.MAX_VALUE; - } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/pointless_arithmetic_expression/PointlessArithmeticExpression.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/pointless_arithmetic_expression/PointlessArithmeticExpression.java index d7577b1c9c79..019805adcf17 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/pointless_arithmetic_expression/PointlessArithmeticExpression.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/pointless_arithmetic_expression/PointlessArithmeticExpression.java @@ -123,4 +123,5 @@ class Expanded {{ System.out.println(u * 1); long g = 8L / 8L; long h = 9L * 0L; + int a = 8 * 0 * 8 * ; // don't warn }} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/pointless_arithmetic_expression/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/pointless_arithmetic_expression/expected.xml index 58e0898bdcdd..f6af12237ea0 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/pointless_arithmetic_expression/expected.xml +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/pointless_arithmetic_expression/expected.xml @@ -36,90 +36,6 @@ <code>j%1</code> can be replaced with '0' #loc - - PointlessArithmeticExpression.java - 30 - Pointless arithmetic expression - <code>k<=Integer.MAX_VALUE</code> can be replaced with 'true' #loc - - - - PointlessArithmeticExpression.java - 34 - Pointless arithmetic expression - <code>k>=Integer.MIN_VALUE</code> can be replaced with 'true' #loc - - - - PointlessArithmeticExpression.java - 38 - Pointless arithmetic expression - <code>k>Integer.MAX_VALUE</code> can be replaced with 'false' #loc - - - - PointlessArithmeticExpression.java - 42 - Pointless arithmetic expression - <code>k<Integer.MIN_VALUE</code> can be replaced with 'false' #loc - - - - PointlessArithmeticExpression.java - 46 - Pointless arithmetic expression - <code>Integer.MAX_VALUE >= k</code> can be replaced with 'true' #loc - - - - PointlessArithmeticExpression.java - 50 - Pointless arithmetic expression - <code>Integer.MIN_VALUE <= k</code> can be replaced with 'true' #loc - - - - PointlessArithmeticExpression.java - 54 - Pointless arithmetic expression - <code>Integer.MAX_VALUE < k</code> can be replaced with 'false' #loc - - - - PointlessArithmeticExpression.java - 58 - Pointless arithmetic expression - <code>Integer.MIN_VALUE > k</code> can be replaced with 'false' #loc - - - - PointlessArithmeticExpression.java - 70 - Pointless arithmetic expression - <code>i > Integer.MAX_VALUE</code> can be replaced with 'false' #loc - - - - PointlessArithmeticExpression.java - 76 - Pointless arithmetic expression - <code>i <= Integer.MAX_VALUE</code> can be replaced with 'true' #loc - - - - PointlessArithmeticExpression.java - 80 - Pointless arithmetic expression - <code>i >= Integer.MIN_VALUE</code> can be replaced with 'true' #loc - - - - PointlessArithmeticExpression.java - 83 - Pointless arithmetic expression - <code>i < Integer.MIN_VALUE</code> can be replaced with 'false' #loc - - PointlessArithmeticExpression.java 93 From e31a4c1a11574c27cf96ff97c0abbf6d1e2725aa Mon Sep 17 00:00:00 2001 From: Rustam Vishnyakov Date: Mon, 21 Apr 2014 19:26:50 +0400 Subject: [PATCH 04/34] Correctly handle custom folding "desc" attribute followed by "defaultstate" --- .../lang/customFolding/NetBeansCustomFoldingProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/lang/customFolding/NetBeansCustomFoldingProvider.java b/platform/lang-impl/src/com/intellij/lang/customFolding/NetBeansCustomFoldingProvider.java index 4ce8a5aff6f4..44f619f609cf 100644 --- a/platform/lang-impl/src/com/intellij/lang/customFolding/NetBeansCustomFoldingProvider.java +++ b/platform/lang-impl/src/com/intellij/lang/customFolding/NetBeansCustomFoldingProvider.java @@ -34,7 +34,7 @@ public class NetBeansCustomFoldingProvider extends CustomFoldingProvider { @Override public String getPlaceholderText(String elementText) { - String customText = elementText.replaceFirst(".*desc\\s*=\\s*\"(.*)\".*", "$1").trim(); + String customText = elementText.replaceFirst(".*desc\\s*=\\s*\"([^\"]*)\".*", "$1").trim(); return customText.isEmpty() ? "..." : customText; } From 40fa2607b3d8be9b7842ce39f124b0392605f8ca Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Mon, 21 Apr 2014 17:56:32 +0200 Subject: [PATCH 05/34] don't create content entry for temp directory of temp project when opening single file --- .../com/intellij/platform/PlatformProjectOpenProcessor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/platform/PlatformProjectOpenProcessor.java b/platform/platform-impl/src/com/intellij/platform/PlatformProjectOpenProcessor.java index 67d44770a1a2..4ffac2cb8eb9 100644 --- a/platform/platform-impl/src/com/intellij/platform/PlatformProjectOpenProcessor.java +++ b/platform/platform-impl/src/com/intellij/platform/PlatformProjectOpenProcessor.java @@ -27,9 +27,9 @@ import com.intellij.openapi.project.DumbAwareRunnable; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.project.ex.ProjectManagerEx; +import com.intellij.openapi.roots.ContentEntry; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.ModuleRootManager; -import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Disposer; @@ -199,6 +199,8 @@ public class PlatformProjectOpenProcessor extends ProjectOpenProcessor { @Override public void run() { ModifiableRootModel model = ModuleRootManager.getInstance(module).getModifiableModel(); + ContentEntry[] entries = model.getContentEntries(); + if (entries.length == 1) model.removeContentEntry(entries[0]); // remove custom content entry created for temp directory model.addContentEntry(virtualFile); model.commit(); } From 58d2f1563fe7da1f2b748b11d48654c6ac336fac Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Mon, 21 Apr 2014 18:02:16 +0200 Subject: [PATCH 06/34] fix bg --- .../intellij/ide/ui/laf/darcula/ui/DarculaProgressBarUI.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaProgressBarUI.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaProgressBarUI.java index 0c4025bcdeae..37f5ed91042e 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaProgressBarUI.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaProgressBarUI.java @@ -89,6 +89,9 @@ public class DarculaProgressBarUI extends BasicProgressBarUI { } area.subtract(new Area(new RoundRectangle2D.Double(0,0,w, h, 9,9))); ((Graphics2D)g).setPaint(c.getParent().getBackground()); + if (c.isOpaque()) { + ((Graphics2D)g).fill(area); + } g.drawRoundRect(1,1, w-3, h-3, 8,8); g.translate(0, -(c.getHeight() - h)/2); From 5cbd491c127d67129b5fa714504494e2463e2d50 Mon Sep 17 00:00:00 2001 From: "Gregory.Shrago" Date: Mon, 21 Apr 2014 21:23:26 +0400 Subject: [PATCH 07/34] minor fix --- .../com/intellij/ide/customize/CustomizeUIThemeStepPanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java index 6ac22dcfd271..c5184dda2394 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java @@ -187,7 +187,7 @@ public class CustomizeUIThemeStepPanel extends AbstractCustomizeWizardStep { @Nullable private static UIManager.LookAndFeelInfo getLookAndFeelInfo(String name) { - if (DEFAULT.equals(name)) return new UIManager.LookAndFeelInfo(DEFAULT, "apple.laf.AquaLookAndFeel"); + if (DEFAULT.equals(name)) return new UIManager.LookAndFeelInfo(DEFAULT, "com.apple.laf.AquaLookAndFeel"); if (DARCULA.equals(name)) return new UIManager.LookAndFeelInfo(DARCULA, DarculaLaf.class.getName()); if (INTELLIJ.equals(name)) return new UIManager.LookAndFeelInfo(INTELLIJ, IntelliJLaf.class.getName()); if (ALLOY.equals(name)) return new UIManager.LookAndFeelInfo(ALLOY, "com.incors.plaf.alloy.AlloyIdea"); From bfef59ed2cd1231896868fe5b4ba004360771c61 Mon Sep 17 00:00:00 2001 From: Vassiliy Date: Mon, 21 Apr 2014 23:34:35 +0400 Subject: [PATCH 08/34] Start plugins wizard #19 usability improvements --- .../CustomizeFeaturedPluginsStepPanel.java | 93 +++++++++++++++---- .../customize/CustomizeIDEWizardDialog.java | 7 +- .../customize/CustomizePluginsStepPanel.java | 44 +++++---- .../intellij/ide/customize/PluginGroups.java | 4 +- 4 files changed, 108 insertions(+), 40 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java index 186c7bdecb20..d3e340e3b1e2 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java @@ -16,14 +16,22 @@ package com.intellij.ide.customize; import com.intellij.CommonBundle; +import com.intellij.icons.AllIcons; import com.intellij.ide.plugins.IdeaPluginDescriptor; import com.intellij.ide.plugins.PluginNode; import com.intellij.openapi.progress.util.ProgressIndicatorBase; +import com.intellij.openapi.ui.VerticalFlowLayout; import com.intellij.openapi.updateSettings.impl.PluginDownloader; +import com.intellij.ui.ColorUtil; +import com.intellij.ui.JBColor; +import com.intellij.ui.border.CustomLineBorder; import com.intellij.ui.components.JBScrollPane; +import com.intellij.ui.components.labels.LinkLabel; +import com.intellij.ui.components.labels.LinkListener; import com.intellij.util.ConcurrencyUtil; import javax.swing.*; +import javax.swing.border.CompoundBorder; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -32,22 +40,29 @@ import java.util.Map; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardStep { - private static ScheduledExecutorService ourService = new ScheduledThreadPoolExecutor(4, ConcurrencyUtil.newNamedThreadFactory("FeaturedPlugins", true, Thread.NORM_PRIORITY)); + private static final int COLS = 3; + private static ScheduledExecutorService ourService = new ScheduledThreadPoolExecutor(4, ConcurrencyUtil.newNamedThreadFactory( + "FeaturedPlugins", true, Thread.NORM_PRIORITY)); - public CustomizeFeaturedPluginsStepPanel() { - setLayout(new GridLayout(0, 3, GAP, GAP)); - JPanel gridPanel = new JPanel(new GridLayout(0, 3, GAP, GAP)); + public final AtomicBoolean myCanceled = new AtomicBoolean(false); + + + public CustomizeFeaturedPluginsStepPanel() throws OfflineException { + setLayout(new GridLayout(1, 1)); + JPanel gridPanel = new JPanel(new GridLayout(0, 3)); JBScrollPane scrollPane = new JBScrollPane(gridPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); scrollPane.getVerticalScrollBar().setUnitIncrement(10); Map config = PluginGroups.getInstance().getFeaturedPlugins(); + boolean isEmptyOrOffline = true; + List pluginsFromRepository = PluginGroups.getInstance().getPluginsFromRepository(); for (Map.Entry entry : config.entrySet()) { JPanel groupPanel = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); - gbc.insets = new Insets(0, 0, 10, 0); gbc.fill = GridBagConstraints.BOTH; gbc.anchor = GridBagConstraints.WEST; gbc.gridwidth = GridBagConstraints.REMAINDER; @@ -61,10 +76,10 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt final String description = s.substring(i + 1, j); final String pluginId = s.substring(j + 1); IdeaPluginDescriptor foundDescriptor = null; - List pluginsFromRepository = PluginGroups.getInstance().getPluginsFromRepository(); for (IdeaPluginDescriptor descriptor : pluginsFromRepository) { if (descriptor.getPluginId().getIdString().equals(pluginId)) { foundDescriptor = descriptor; + isEmptyOrOffline = false; break; } } @@ -88,13 +103,26 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt final JButton installButton = new JButton("Install"); final JProgressBar progressBar = new JProgressBar(0, 100); progressBar.setStringPainted(true); - buttonWrapper.add(installButton, "button"); - buttonWrapper.add(progressBar, "progress"); + JPanel progressPanel = new JPanel(new VerticalFlowLayout(true, false)); + progressPanel.add(progressBar); + final LinkLabel cancelLink = new LinkLabel("Cancel", AllIcons.Actions.Cancel); + JPanel linkWrapper = new JPanel(new FlowLayout(FlowLayout.CENTER)); + linkWrapper.add(cancelLink); + progressPanel.add(linkWrapper); + + JPanel buttonPanel = new JPanel(new VerticalFlowLayout()); + buttonPanel.add(installButton); + + buttonWrapper.add(buttonPanel, "button"); + buttonWrapper.add(progressPanel, "progress"); + wrapperLayout.show(buttonWrapper, "button"); final ProgressIndicatorBase indicator = new ProgressIndicatorBase(true) { + @Override public void start() { + myCanceled.set(false); super.start(); SwingUtilities.invokeLater(new Runnable() { @Override @@ -104,18 +132,15 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt }); } - @Override - public void setIndeterminate(boolean indeterminate) { - super.setIndeterminate(indeterminate); - } - @Override public void processFinish() { super.processFinish(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - progressBar.setString("Installed"); + wrapperLayout.show(buttonWrapper, "button"); + installButton.setEnabled(false); + installButton.setText("Installed"); } }); } @@ -135,11 +160,14 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt @Override public void cancel() { + stop(); + myCanceled.set(true); super.cancel(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { wrapperLayout.show(buttonWrapper, "button"); + progressBar.setValue(0); } }); } @@ -152,6 +180,7 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt @Override public void run() { try { + indicator.start(); PluginNode node = new PluginNode(descriptor.getPluginId()); node.setUrl(descriptor.getUrl()); PluginDownloader downloader = PluginDownloader.createDownloader(node); @@ -160,7 +189,9 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt indicator.processFinish(); } catch (Exception ignored) { - onFail(); + if (!myCanceled.get()) { + onFail(); + } } } @@ -169,28 +200,48 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt SwingUtilities.invokeLater(new Runnable() { @Override public void run() { + indicator.stop(); wrapperLayout.show(buttonWrapper, "progress"); progressBar.setString("Cannot download plugin"); } }); } }, 0, TimeUnit.SECONDS); - //PluginGroups.getInstance().setFeaturedPluginEnabled(pluginId, installButton.isSelected()); } }); + cancelLink.setListener(new LinkListener() { + @Override + public void linkSelected(LinkLabel aSource, Object aLinkData) { + indicator.cancel(); + } + }, null); + gbc.insets.bottom = -5; groupPanel.add(titleLabel, gbc); + gbc.insets.bottom = 10; groupPanel.add(topicLabel, gbc); groupPanel.add(descriptionLabel, gbc); gbc.weighty = 1; groupPanel.add(Box.createVerticalGlue(), gbc); gbc.weighty = 0; groupPanel.add(buttonWrapper, gbc); - gbc.weighty = 1; - groupPanel.add(Box.createVerticalGlue(), gbc); - groupPanel.setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP)); gridPanel.add(groupPanel); } - setLayout(new GridLayout(1, 1)); + int cursor = 0; + Component[] components = gridPanel.getComponents(); + int rowCount = components.length / COLS; + for (Component component : components) { + ((JComponent)component).setBorder( + new CompoundBorder(new CustomLineBorder(ColorUtil.withAlpha(JBColor.foreground(), .2), 0, 0, cursor / 3 < rowCount ? 1 : 0, + cursor % COLS != COLS - 1 ? 1 : 0) { + @Override + protected Color getColor() { + return ColorUtil.withAlpha(JBColor.foreground(), .2); + } + }, BorderFactory.createEmptyBorder(GAP, GAP, 0, GAP))); + cursor++; + } + + if (isEmptyOrOffline) throw new OfflineException(); add(scrollPane); } @@ -210,4 +261,6 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt public String getHTMLFooter() { return "New plugins can also be downloaded in " + CommonBundle.settingsTitle() + " | Plugins"; } + + static class OfflineException extends Exception {}; } diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java index 9b077846433b..be3519eedff3 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java @@ -70,7 +70,12 @@ public class CustomizeIDEWizardDialog extends DialogWrapper implements ActionLis mySteps.add(new CustomizeKeyboardSchemeStepPanel()); } mySteps.add(new CustomizePluginsStepPanel()); - mySteps.add(new CustomizeFeaturedPluginsStepPanel()); + try { + mySteps.add(new CustomizeFeaturedPluginsStepPanel()); + } + catch (CustomizeFeaturedPluginsStepPanel.OfflineException e) { + //skip featured step if we're offline + } } @Override diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizePluginsStepPanel.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizePluginsStepPanel.java index 65b2987c8526..055a1d67e2cf 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizePluginsStepPanel.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizePluginsStepPanel.java @@ -60,20 +60,19 @@ public class CustomizePluginsStepPanel extends AbstractCustomizeWizardStep imple add(scrollPane, MAIN); add(myCustomizePanel, CUSTOMIZE); - //PluginManager.loadDisabledPlugins(new File(PathManager.getConfigPath()).getPath(), myDisabledPluginIds); - //for (IdeaPluginDescriptor pluginDescriptor : myAllPlugins) { - // if (pluginDescriptor.getPluginId().getIdString().equals("com.intellij")) { - //// skip 'IDEA CORE' plugin - //continue; - //} - // //PluginManager.initClassLoader(PluginGroups.class.getClassLoader(), (IdeaPluginDescriptorImpl)pluginDescriptor); - //} Map> groups = PluginGroups.getInstance().getTree(); for (Map.Entry> entry : groups.entrySet()) { final String group = entry.getKey(); if (PluginGroups.CORE.equals(group)) continue; - JPanel groupPanel = new JPanel(new GridBagLayout()); + JPanel groupPanel = new JPanel(new GridBagLayout()) { + @Override + public Color getBackground() { + Color color = UIManager.getColor("Panel.background"); + return isGroupEnabled(group)? color : ColorUtil.darker(color, 1); + } + }; + gridPanel.setOpaque(true); GridBagConstraints gbc = new GridBagConstraints(); gbc.insets = new Insets(0, 0, 10, 0); gbc.fill = GridBagConstraints.BOTH; @@ -113,6 +112,7 @@ public class CustomizePluginsStepPanel extends AbstractCustomizeWizardStep imple } else { JPanel buttonsPanel = new JPanel(new GridLayout(1, 2, 10, 5)); + buttonsPanel.setOpaque(false); LinkLabel customizeButton = createLink(CUSTOMIZE_COMMAND + ":" + group, CUSTOMIZE_TEXT_PROVIDER); buttonsPanel.add(customizeButton); LinkLabel disableAllButton = createLink(SWITCH_COMMAND + ":" + group, getGroupSwitchTextProvider(group)); @@ -213,22 +213,20 @@ public class CustomizePluginsStepPanel extends AbstractCustomizeWizardStep imple return null; } - - - - - private class IdSetPanel extends JPanel { + private class IdSetPanel extends JPanel implements LinkListener { private JLabel myTitleLabel = new JLabel(); private JPanel myContentPanel = new JPanel(new GridLayout(0, 3, 5, 5)); private JButton mySaveButton = new JButton("Save Changes and Go Back"); + private String myGroup; private IdSetPanel() { setLayout(new VerticalFlowLayout(VerticalFlowLayout.TOP, 0, GAP, true, false)); add(myTitleLabel); add(myContentPanel); - JPanel buttonPanel = new JPanel(new BorderLayout()); - buttonPanel.add(mySaveButton, BorderLayout.WEST); - buttonPanel.add(Box.createHorizontalGlue(), BorderLayout.CENTER); + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 25, 5)); + buttonPanel.add(mySaveButton); + buttonPanel.add(new LinkLabel("Enable All", null, this, "enable")); + buttonPanel.add(new LinkLabel("Disable All", null, this, "disable")); add(buttonPanel); mySaveButton.addActionListener(new ActionListener() { @Override @@ -238,7 +236,19 @@ public class CustomizePluginsStepPanel extends AbstractCustomizeWizardStep imple }); } + @Override + public void linkSelected(LinkLabel aSource, String command) { + if (myGroup == null) return; + boolean enable = "enable".equals(command); + List idSets = PluginGroups.getInstance().getSets(myGroup); + for (IdSet set : idSets) { + PluginGroups.getInstance().setIdSetEnabled(set, enable); + } + CustomizePluginsStepPanel.this.repaint(); + } + void update(String group) { + myGroup = group; myTitleLabel.setText("

" + group + "

"); myContentPanel.removeAll(); List idSets = PluginGroups.getInstance().getSets(group); diff --git a/platform/platform-impl/src/com/intellij/ide/customize/PluginGroups.java b/platform/platform-impl/src/com/intellij/ide/customize/PluginGroups.java index c649cc1d0842..e3af6525cda5 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/PluginGroups.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/PluginGroups.java @@ -32,7 +32,7 @@ import java.util.*; class PluginGroups { static final String CORE = "Core"; - private static final int MAX_DESCR_LENGTH = 80; + private static final int MAX_DESCR_LENGTH = 50; private static PluginGroups instance = null; @@ -62,7 +62,7 @@ class PluginGroups { myPluginsFromRepository.addAll(RepositoryHelper.loadPluginsFromRepository(null)); } catch (Exception e) { - e.printStackTrace(); + //OK, it's offline } PluginManager.loadDisabledPlugins(new File(PathManager.getConfigPath()).getPath(), myDisabledPluginIds); From 7fcce270cc86f175ae6f8a29c7fe153b73dd0fcd Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Mon, 21 Apr 2014 21:49:05 +0200 Subject: [PATCH 09/34] fix darcula lafinfo --- .../intellij/ide/ui/laf/darcula/DarculaLookAndFeelInfo.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/DarculaLookAndFeelInfo.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/DarculaLookAndFeelInfo.java index cf8492750b9b..880ff1accb5a 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/DarculaLookAndFeelInfo.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/DarculaLookAndFeelInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -24,7 +24,7 @@ import javax.swing.*; * @author Konstantin Bulenkov */ public class DarculaLookAndFeelInfo extends UIManager.LookAndFeelInfo { - @NonNls public static final String CLASS_NAME = "idea.dark.laf.classname"; + @NonNls public static final String CLASS_NAME = DarculaLaf.class.getName(); public DarculaLookAndFeelInfo(){ super(IdeBundle.message("idea.dark.look.and.feel"), CLASS_NAME); } From 89d771e1afe5be2a9bad64b8acc4098ad552604f Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 21 Apr 2014 13:41:07 +0200 Subject: [PATCH 10/34] @NotNull --- .../ide/fileTemplates/JavaCreateFromTemplateHandler.java | 4 +++- .../ide/fileTemplates/CreateFromTemplateHandler.java | 5 ++++- .../ide/fileTemplates/DefaultCreateFromTemplateHandler.java | 4 +++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/java/java-impl/src/com/intellij/ide/fileTemplates/JavaCreateFromTemplateHandler.java b/java/java-impl/src/com/intellij/ide/fileTemplates/JavaCreateFromTemplateHandler.java index 26e4adfa0bbb..f511eac9320d 100644 --- a/java/java-impl/src/com/intellij/ide/fileTemplates/JavaCreateFromTemplateHandler.java +++ b/java/java-impl/src/com/intellij/ide/fileTemplates/JavaCreateFromTemplateHandler.java @@ -26,6 +26,7 @@ import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.impl.file.JavaDirectoryServiceImpl; import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; import java.util.Map; @@ -100,8 +101,9 @@ public class JavaCreateFromTemplateHandler implements CreateFromTemplateHandler return fileType.equals(StdFileTypes.JAVA) && !FileTemplateUtil.INTERNAL_PACKAGE_INFO_TEMPLATE_NAME.equals(template.getName()); } + @NotNull public PsiElement createFromTemplate(final Project project, final PsiDirectory directory, final String fileName, FileTemplate template, - String templateText, Map props) throws IncorrectOperationException { + String templateText, @NotNull Map props) throws IncorrectOperationException { String extension = template.getExtension(); PsiElement result = createClassOrInterface(project, directory, templateText, template.isReformatCode(), extension); hackAwayEmptyPackage((PsiJavaFile)result.getContainingFile(), template, props); diff --git a/platform/lang-impl/src/com/intellij/ide/fileTemplates/CreateFromTemplateHandler.java b/platform/lang-impl/src/com/intellij/ide/fileTemplates/CreateFromTemplateHandler.java index b0ceafd439d7..dc728e661ef0 100644 --- a/platform/lang-impl/src/com/intellij/ide/fileTemplates/CreateFromTemplateHandler.java +++ b/platform/lang-impl/src/com/intellij/ide/fileTemplates/CreateFromTemplateHandler.java @@ -21,6 +21,7 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; import java.util.Map; @@ -31,8 +32,10 @@ public interface CreateFromTemplateHandler { ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.createFromTemplateHandler"); boolean handlesTemplate(FileTemplate template); + + @NotNull PsiElement createFromTemplate(Project project, PsiDirectory directory, final String fileName, FileTemplate template, String templateText, - Map props) throws IncorrectOperationException; + @NotNull Map props) throws IncorrectOperationException; boolean canCreate(final PsiDirectory[] dirs); boolean isNameRequired(); diff --git a/platform/lang-impl/src/com/intellij/ide/fileTemplates/DefaultCreateFromTemplateHandler.java b/platform/lang-impl/src/com/intellij/ide/fileTemplates/DefaultCreateFromTemplateHandler.java index a97351fbebb8..d12e887f3371 100644 --- a/platform/lang-impl/src/com/intellij/ide/fileTemplates/DefaultCreateFromTemplateHandler.java +++ b/platform/lang-impl/src/com/intellij/ide/fileTemplates/DefaultCreateFromTemplateHandler.java @@ -27,6 +27,7 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileFactory; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; import java.util.Map; @@ -39,10 +40,11 @@ public class DefaultCreateFromTemplateHandler implements CreateFromTemplateHandl return true; } + @NotNull @Override public PsiElement createFromTemplate(final Project project, final PsiDirectory directory, String fileName, final FileTemplate template, final String templateText, - final Map props) throws IncorrectOperationException { + @NotNull final Map props) throws IncorrectOperationException { fileName = checkAppendExtension(fileName, template); if (FileTypeManager.getInstance().isFileIgnored(fileName)) { From e8c981ae82f2bef4de126cc35705cc5efe54e302 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 21 Apr 2014 16:12:38 +0200 Subject: [PATCH 11/34] create LookupUi and move there some ui-related code from LookupImpl; skip this code in tests --- .../codeInsight/lookup/impl/LookupImpl.java | 436 +--------------- .../codeInsight/lookup/impl/LookupUi.java | 466 ++++++++++++++++++ 2 files changed, 489 insertions(+), 413 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupUi.java diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java index 0402bba74698..8df95370fdaa 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java @@ -16,7 +16,6 @@ package com.intellij.codeInsight.lookup.impl; -import com.intellij.codeInsight.CodeInsightBundle; import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInsight.completion.CodeCompletionFeatures; import com.intellij.codeInsight.completion.CompletionLookupArranger; @@ -27,25 +26,19 @@ import com.intellij.codeInsight.hint.HintManager; import com.intellij.codeInsight.hint.HintManagerImpl; import com.intellij.codeInsight.lookup.*; import com.intellij.featureStatistics.FeatureUsageTracker; -import com.intellij.icons.AllIcons; -import com.intellij.ide.DataManager; import com.intellij.ide.IdeEventQueue; import com.intellij.ide.ui.UISettings; import com.intellij.lang.LangBundle; import com.intellij.openapi.Disposable; -import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.event.*; import com.intellij.openapi.editor.event.DocumentAdapter; -import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; -import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; @@ -57,20 +50,12 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.impl.DebugUtil; import com.intellij.ui.*; import com.intellij.ui.awt.RelativePoint; -import com.intellij.ui.components.JBLayeredPane; import com.intellij.ui.components.JBList; -import com.intellij.ui.components.JBScrollPane; -import com.intellij.ui.plaf.beg.BegPopupMenuBorder; import com.intellij.ui.popup.AbstractPopup; -import com.intellij.util.Alarm; import com.intellij.util.CollectConsumer; -import com.intellij.util.PlatformIcons; import com.intellij.util.containers.ConcurrentHashMap; import com.intellij.util.containers.ConcurrentWeakHashMap; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.ui.AbstractLayoutManager; -import com.intellij.util.ui.AsyncProcessIcon; -import com.intellij.util.ui.ButtonlessScrollBarUI; import com.intellij.util.ui.update.Activatable; import com.intellij.util.ui.update.UiNotifyConnector; import org.jetbrains.annotations.NotNull; @@ -78,13 +63,11 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.border.EmptyBorder; -import javax.swing.border.LineBorder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import java.awt.*; -import java.awt.event.*; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -120,8 +103,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable return myExtender; } }; - private final LookupCellRenderer myCellRenderer; - private Boolean myPositionedAbove = null; + final LookupCellRenderer myCellRenderer; private final List myListeners = ContainerUtil.createLockFreeCopyOnWriteList(); @@ -131,12 +113,10 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable private boolean myHidden = false; private boolean mySelectionTouched; private FocusDegree myFocusDegree = FocusDegree.FOCUSED; - private final AsyncProcessIcon myProcessIcon = new AsyncProcessIcon("Completion progress"); - private final JPanel myIconPanel = new JPanel(new BorderLayout()); private volatile boolean myCalculating; private final Advertiser myAdComponent; private volatile String myAdText; - private volatile int myLookupTextWidth = 50; + volatile int myLookupTextWidth = 50; private boolean myChangeGuard; private volatile LookupArranger myArranger; private LookupArranger myPresentableArranger; @@ -144,18 +124,11 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable ContainerUtil.identityStrategy()); private final Map myCustomFonts = new ConcurrentWeakHashMap( ContainerUtil.identityStrategy()); - private LookupHint myElementHint = null; - private final Alarm myHintAlarm = new Alarm(); - private final JLabel mySortingLabel = new JLabel(); - private final JScrollPane myScrollPane; - final LookupLayeredPane myLayeredPane = new LookupLayeredPane(); - private final JButton myScrollBarIncreaseButton; private boolean myStartCompletionWhenNothingMatches; - private boolean myResizePending; - private int myMaximumHeight = Integer.MAX_VALUE; + boolean myResizePending; private boolean myFinishing; - private boolean myUpdating; - private final ModalityState myModalityState; + boolean myUpdating; + private LookupUi myUi; public LookupImpl(Project project, Editor editor, @NotNull LookupArranger arranger) { super(new JPanel(new BorderLayout())); @@ -169,7 +142,6 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable myArranger = arranger; myPresentableArranger = arranger; - myIconPanel.setVisible(false); myCellRenderer = new LookupCellRenderer(this); myList.setCellRenderer(myCellRenderer); @@ -181,36 +153,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable myList.getExpandableItemsHandler(); - myScrollBarIncreaseButton = new JButton(); - myScrollBarIncreaseButton.setFocusable(false); - myScrollBarIncreaseButton.setRequestFocusEnabled(false); - - myScrollPane = new JBScrollPane(myList); - myScrollPane.setViewportBorder(new EmptyBorder(0, 0, 0, 0)); - myScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - myScrollPane.getVerticalScrollBar().setPreferredSize(new Dimension(13, -1)); - myScrollPane.getVerticalScrollBar().setUI(new ButtonlessScrollBarUI() { - @Override - protected JButton createIncreaseButton(int orientation) { - return myScrollBarIncreaseButton; - } - }); - getComponent().add(myLayeredPane, BorderLayout.CENTER); - - //IDEA-82111 - fixMouseCheaters(); - - myLayeredPane.mainPanel.add(myScrollPane, BorderLayout.CENTER); - myScrollPane.setBorder(null); - myAdComponent = new Advertiser(); - JComponent adComponent = myAdComponent.getAdComponent(); - adComponent.setBorder(new EmptyBorder(0, 1, 1, 2 + AllIcons.Ide.LookupRelevance.getIconWidth())); - myLayeredPane.mainPanel.add(adComponent, BorderLayout.SOUTH); - getComponent().setBorder(new BegPopupMenuBorder()); - - myIconPanel.setBackground(Color.LIGHT_GRAY); - myIconPanel.add(myProcessIcon); myOffsets = new LookupOffsets(editor); @@ -218,14 +161,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable addEmptyItem(model); updateListHeight(model); - addListeners(); - - mySortingLabel.setBorder(new LineBorder(new JBColor(Color.LIGHT_GRAY, JBColor.background()))); - mySortingLabel.setOpaque(true); - new ChangeLookupSorting().installOn(mySortingLabel); - updateSorting(); - myModalityState = ModalityState.stateForComponent(getComponent()); } private CollectionListModel getListModel() { @@ -233,34 +169,6 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable return (CollectionListModel)myList.getModel(); } - //Yes, it's possible to move focus to the hint. It's inconvenient, it doesn't make sense, but it's possible. - // This fix is for those jerks - private void fixMouseCheaters() { - getComponent().addFocusListener(new FocusAdapter() { - @Override - public void focusGained(FocusEvent e) { - final ActionCallback done = IdeFocusManager.getInstance(myProject).requestFocus(myEditor.getContentComponent(), true); - IdeFocusManager.getInstance(myProject).typeAheadUntil(done); - new Alarm(LookupImpl.this).addRequest(new Runnable() { - @Override - public void run() { - if (!done.isDone()) { - done.setDone(); - } - } - }, 300, myModalityState); - } - }); - } - - void updateSorting() { - final boolean lexi = UISettings.getInstance().SORT_LOOKUP_ELEMENTS_LEXICOGRAPHICALLY; - mySortingLabel.setIcon(lexi ? AllIcons.Ide.LookupAlphanumeric : AllIcons.Ide.LookupRelevance); - mySortingLabel.setToolTipText(lexi ? "Click to sort variants by relevance" : "Click to sort variants alphabetically"); - - resort(false); - } - public void setArranger(LookupArranger arranger) { myArranger = arranger; } @@ -284,22 +192,8 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable public void setCalculating(final boolean calculating) { myCalculating = calculating; - Runnable setVisible = new Runnable() { - @Override - public void run() { - myIconPanel.setVisible(myCalculating); - } - }; - if (myCalculating) { - new Alarm(this).addRequest(setVisible, 100, myModalityState); - } else { - setVisible.run(); - } - - if (calculating) { - myProcessIcon.resume(); - } else { - myProcessIcon.suspend(); + if (myUi != null) { + myUi.setCalculating(calculating); } } @@ -564,50 +458,6 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable return myMatchers.get(item); } - // in layered pane coordinate system. - private Rectangle calculatePosition() { - Dimension dim = getComponent().getPreferredSize(); - int lookupStart = getLookupStart(); - if (lookupStart < 0 || lookupStart > myEditor.getDocument().getTextLength()) { - LOG.error(lookupStart + "; offset=" + myEditor.getCaretModel().getOffset() + "; element=" + - getPsiElement()); - } - - LogicalPosition pos = myEditor.offsetToLogicalPosition(lookupStart); - Point location = myEditor.logicalPositionToXY(pos); - location.y += myEditor.getLineHeight(); - location.x -= myCellRenderer.getIconIndent() + getComponent().getInsets().left; - - SwingUtilities.convertPointToScreen(location, myEditor.getContentComponent()); - final Rectangle screenRectangle = ScreenUtil.getScreenRectangle(location); - - if (!isPositionedAboveCaret()) { - int shiftLow = screenRectangle.height - (location.y + dim.height); - myPositionedAbove = shiftLow < 0 && shiftLow < location.y - dim.height && location.y >= dim.height; - } - if (isPositionedAboveCaret()) { - location.y -= dim.height + myEditor.getLineHeight(); - if (pos.line == 0) { - location.y += 1; - //otherwise the lookup won't intersect with the editor and every editor's resize (e.g. after typing in console) will close the lookup - } - } - - if (!screenRectangle.contains(location)) { - location = ScreenUtil.findNearestPointOnBorder(screenRectangle, location); - } - - final JRootPane rootPane = myEditor.getComponent().getRootPane(); - if (rootPane == null) { - LOG.error(myEditor.isDisposed() + "; shown=" + myShown + "; disposed=" + myDisposed + "; editorShowing=" + myEditor.getContentComponent().isShowing()); - } - Rectangle candidate = new Rectangle(location, dim); - ScreenUtil.cropRectangleToFitTheScreen(candidate); - - SwingUtilities.convertPointFromScreen(location, rootPane.getLayeredPane()); - return new Rectangle(location.x, location.y, dim.width, candidate.height); - } - public void finishLookup(final char completionChar) { finishLookup(completionChar, (LookupElement)myList.getSelectedValue()); } @@ -794,7 +644,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable return false; } if (isVisible()) { - updateLookupLocation(); + HintManagerImpl.updateLocation(this, myEditor, myUi.calculatePosition().getLocation()); } checkValid(); return true; @@ -835,12 +685,9 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable myAdComponent.showRandomText(); - getComponent().setBorder(null); - updateScrollbarVisibility(); - - Rectangle bounds = calculatePosition(); - myMaximumHeight = bounds.height; - Point p = bounds.getLocation(); + myUi = new LookupUi(this, myAdComponent, myList, myProject); + myUi.setCalculating(myCalculating); + Point p = myUi.calculatePosition().getLocation(); HintManagerImpl.getInstanceImpl().showEditorHint(this, myEditor, p, HintManager.HIDE_BY_ESCAPE | HintManager.UPDATE_BY_SCROLLING, 0, false, HintManagerImpl.createHintHint(myEditor, p, this, HintManager.UNDER).setAwtTooltip(false)); @@ -923,8 +770,6 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable @Override public void valueChanged(ListSelectionEvent e){ - myHintAlarm.cancelAllRequests(); - final LookupElement item = getCurrentItem(); if (oldItem != item && !myList.isEmpty()) { // do not update on temporary model wipe fireCurrentItemChanged(item); @@ -933,9 +778,6 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable } oldItem = item; } - if (item != null) { - updateHint(item); - } } }); @@ -956,53 +798,6 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable return true; } }.installOn(myList); - - final Alarm alarm = new Alarm(this); - myScrollPane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() { - @Override - public void adjustmentValueChanged(AdjustmentEvent e) { - if (!myShown || myUpdating || myDisposed) return; - alarm.addRequest(new Runnable() { - @Override - public void run() { - refreshUi(false, false); - } - }, 300, myModalityState); - } - }); - } - - private void updateHint(@NotNull final LookupElement item) { - checkValid(); - if (myElementHint != null) { - myLayeredPane.remove(myElementHint); - myElementHint = null; - final JRootPane rootPane = getComponent().getRootPane(); - if (rootPane != null) { - rootPane.revalidate(); - rootPane.repaint(); - } - } - if (!isFocused()) { - return; - } - - final Collection actions = getActionsFor(item); - if (!actions.isEmpty()) { - myHintAlarm.addRequest(new Runnable() { - @Override - public void run() { - assert !myDisposed; - if (!ShowHideIntentionIconLookupAction.shouldShowLookupHint() || - ((CompletionExtender)myList.getExpandableItemsHandler()).isShowing()) { - return; - } - myElementHint = new LookupHint(); - myLayeredPane.add(myElementHint, 20, 0); - myLayeredPane.layoutHint(); - } - }, 500, myModalityState); - } } @Override @@ -1075,7 +870,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable } } - private void fireCurrentItemChanged(LookupElement item){ + void fireCurrentItemChanged(LookupElement item){ if (!myListeners.isEmpty()){ LookupEvent event = new LookupEvent(this, item, (char)0); for (LookupListener listener : myListeners) { @@ -1208,7 +1003,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable @Override public boolean isPositionedAboveCaret(){ - return myPositionedAbove != null && myPositionedAbove.booleanValue(); + return myUi != null && myUi.isPositionedAboveCaret(); } @Override @@ -1279,8 +1074,6 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable } myOffsets.disposeMarkers(); - Disposer.dispose(myProcessIcon); - Disposer.dispose(myHintAlarm); myDisposed = true; disposeTrace = DebugUtil.currentStackTrace() + "\n============"; //noinspection AssignmentToStaticFieldFromInstanceMethod @@ -1291,59 +1084,19 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable assert !myUpdating; myUpdating = true; try { - doRefreshUi(mayCheckReused, onExplicitAction); + final boolean reused = mayCheckReused && checkReused(); + boolean selectionVisible = isSelectionVisible(); + boolean itemsChanged = updateList(onExplicitAction, reused); + if (isVisible()) { + LOG.assertTrue(!ApplicationManager.getApplication().isUnitTestMode()); + myUi.refreshUi(selectionVisible, itemsChanged, reused, onExplicitAction); + } } finally { myUpdating = false; } } - private void doRefreshUi(boolean mayCheckReused, boolean onExplicitAction) { - final boolean reused = mayCheckReused && checkReused(); - - boolean selectionVisible = isSelectionVisible(); - - boolean itemsChanged = updateList(onExplicitAction, reused); - - if (isVisible()) { - LOG.assertTrue(!ApplicationManager.getApplication().isUnitTestMode()); - - if (myEditor.getComponent().getRootPane() == null) { - return; - } - - updateScrollbarVisibility(); - - if (myResizePending || itemsChanged) { - myMaximumHeight = Integer.MAX_VALUE; - } - Rectangle rectangle = calculatePosition(); - myMaximumHeight = rectangle.height; - - if (myResizePending || itemsChanged) { - myResizePending = false; - pack(); - } - HintManagerImpl.updateLocation(this, myEditor, rectangle.getLocation()); - - if (reused || selectionVisible || onExplicitAction) { - ensureSelectionVisible(false); - } - } - } - - private void updateLookupLocation() { - Rectangle rectangle = calculatePosition(); - myMaximumHeight = rectangle.height; - HintManagerImpl.updateLocation(this, myEditor, rectangle.getLocation()); - } - - private void updateScrollbarVisibility() { - boolean showSorting = isCompletion() && getListModel().getSize() >= 3; - mySortingLabel.setVisible(showSorting); - myScrollPane.setVerticalScrollBarPolicy(showSorting ? ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS : ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); - } - public void markReused() { myAdComponent.clearAdvertisements(); synchronized (myList) { @@ -1368,7 +1121,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable if (ApplicationManager.getApplication().isDispatchThread()) { runnable.run(); } else { - ApplicationManager.getApplication().invokeLater(runnable, myModalityState); + ApplicationManager.getApplication().invokeLater(runnable); } } @@ -1406,155 +1159,12 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable return true; } - private class LookupLayeredPane extends JBLayeredPane { - final JPanel mainPanel = new JPanel(new BorderLayout()); - - private LookupLayeredPane() { - add(mainPanel, 0, 0); - add(myIconPanel, 42, 0); - add(mySortingLabel, 10, 0); - - setLayout(new AbstractLayoutManager() { - @Override - public Dimension preferredLayoutSize(@Nullable Container parent) { - int maxCellWidth = myLookupTextWidth + myCellRenderer.getIconIndent(); - int scrollBarWidth = myScrollPane.getPreferredSize().width - myScrollPane.getViewport().getPreferredSize().width; - int listWidth = Math.min(scrollBarWidth + maxCellWidth, UISettings.getInstance().MAX_LOOKUP_WIDTH2); - - Dimension adSize = myAdComponent.getAdComponent().getPreferredSize(); - - int panelHeight = myList.getPreferredScrollableViewportSize().height + adSize.height; - if (getListModel().getSize() > myList.getVisibleRowCount() && myList.getVisibleRowCount() >= 5) { - panelHeight -= myList.getFixedCellHeight() / 2; - } - return new Dimension(Math.max(listWidth, adSize.width), Math.min(panelHeight, myMaximumHeight)); - } - - @Override - public void layoutContainer(Container parent) { - Dimension size = getSize(); - mainPanel.setSize(size); - mainPanel.validate(); - - if (!myResizePending) { - Dimension preferredSize = preferredLayoutSize(null); - if (preferredSize.width != size.width) { - UISettings.getInstance().MAX_LOOKUP_WIDTH2 = Math.max(500, size.width); - } - - int listHeight = myList.getLastVisibleIndex() - myList.getFirstVisibleIndex() + 1; - if (listHeight != getListModel().getSize() && listHeight != myList.getVisibleRowCount() && preferredSize.height != size.height) { - UISettings.getInstance().MAX_LOOKUP_LIST_HEIGHT = Math.max(5, listHeight); - } - } - - myList.setFixedCellWidth(myScrollPane.getViewport().getWidth()); - layoutStatusIcons(); - layoutHint(); - } - }); - } - - private void layoutStatusIcons() { - int adHeight = myAdComponent.getAdComponent().getPreferredSize().height; - Dimension buttonSize = adHeight > 0 || !mySortingLabel.isVisible() ? new Dimension(0, 0) : new Dimension( - AllIcons.Ide.LookupRelevance.getIconWidth(), AllIcons.Ide.LookupRelevance.getIconHeight()); - myScrollBarIncreaseButton.setPreferredSize(buttonSize); - myScrollBarIncreaseButton.setMinimumSize(buttonSize); - myScrollBarIncreaseButton.setMaximumSize(buttonSize); - JScrollBar scrollBar = myScrollPane.getVerticalScrollBar(); - scrollBar.revalidate(); - scrollBar.repaint(); - - final Dimension iconSize = myProcessIcon.getPreferredSize(); - myIconPanel.setBounds(getWidth() - iconSize.width - (scrollBar.isVisible() ? scrollBar.getWidth() : 0), 0, iconSize.width, iconSize.height); - - final Dimension sortSize = mySortingLabel.getPreferredSize(); - final Point sbLocation = SwingUtilities.convertPoint(scrollBar, 0, 0, myLayeredPane); - - final int sortHeight = Math.max(adHeight, mySortingLabel.getPreferredSize().height); - mySortingLabel.setBounds(sbLocation.x, getHeight() - sortHeight, sortSize.width, sortHeight); - } - - void layoutHint() { - if (myElementHint != null && getCurrentItem() != null) { - final Rectangle bounds = getCurrentItemBounds(); - myElementHint.setSize(myElementHint.getPreferredSize()); - - JScrollBar sb = myScrollPane.getVerticalScrollBar(); - int x = bounds.x + bounds.width - myElementHint.getWidth() + (sb.isVisible() ? sb.getWidth() : 0); - x = Math.min(x, getWidth() - myElementHint.getWidth()); - myElementHint.setLocation(new Point(x, bounds.y)); - } - } - } - - private class LookupHint extends JLabel { - private final Border INACTIVE_BORDER = BorderFactory.createEmptyBorder(2, 2, 2, 2); - private final Border ACTIVE_BORDER = BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(Color.BLACK, 1), BorderFactory.createEmptyBorder(1, 1, 1, 1)); - private LookupHint() { - setOpaque(false); - setBorder(INACTIVE_BORDER); - setIcon(AllIcons.Actions.IntentionBulb); - String acceleratorsText = KeymapUtil.getFirstKeyboardShortcutText( - ActionManager.getInstance().getAction(IdeActions.ACTION_SHOW_INTENTION_ACTIONS)); - if (acceleratorsText.length() > 0) { - setToolTipText(CodeInsightBundle.message("lightbulb.tooltip", acceleratorsText)); - } - - addMouseListener(new MouseAdapter() { - @Override - public void mouseEntered(MouseEvent e) { - setBorder(ACTIVE_BORDER); - } - - @Override - public void mouseExited(MouseEvent e) { - setBorder(INACTIVE_BORDER); - } - @Override - public void mousePressed(MouseEvent e) { - if (!e.isPopupTrigger() && e.getButton() == MouseEvent.BUTTON1) { - showElementActions(); - } - } - }); - } - } - public Map getRelevanceStrings() { synchronized (myList) { return myPresentableArranger.getRelevanceStrings(); } } - private class ChangeLookupSorting extends ClickListener { - - @Override - public boolean onClick(@NotNull MouseEvent e, int clickCount) { - DataContext context = DataManager.getInstance().getDataContext(mySortingLabel); - DefaultActionGroup group = new DefaultActionGroup(); - group.add(createSortingAction(true)); - group.add(createSortingAction(false)); - JBPopupFactory.getInstance().createActionGroupPopup("Change sorting", group, context, JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, false).showInBestPositionFor( - context); - return true; - } - - private AnAction createSortingAction(boolean checked) { - boolean currentSetting = UISettings.getInstance().SORT_LOOKUP_ELEMENTS_LEXICOGRAPHICALLY; - final boolean newSetting = checked ? currentSetting : !currentSetting; - return new AnAction(newSetting ? "Sort lexicographically" : "Sort by relevance", null, checked ? PlatformIcons.CHECK_ICON : null) { - @Override - public void actionPerformed(AnActionEvent e) { - FeatureUsageTracker.getInstance().triggerFeatureUsed(CodeCompletionFeatures.EDITING_COMPLETION_CHANGE_SORTING); - UISettings.getInstance().SORT_LOOKUP_ELEMENTS_LEXICOGRAPHICALLY = newSetting; - updateSorting(); - } - }; - } - } - public enum FocusDegree { FOCUSED, SEMI_FOCUSED, UNFOCUSED } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupUi.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupUi.java new file mode 100644 index 000000000000..2851eb35a6c3 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupUi.java @@ -0,0 +1,466 @@ +/* + * Copyright 2000-2014 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.lookup.impl; + +import com.intellij.codeInsight.CodeInsightBundle; +import com.intellij.codeInsight.completion.CodeCompletionFeatures; +import com.intellij.codeInsight.completion.ShowHideIntentionIconLookupAction; +import com.intellij.codeInsight.hint.HintManagerImpl; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupElementAction; +import com.intellij.featureStatistics.FeatureUsageTracker; +import com.intellij.icons.AllIcons; +import com.intellij.ide.DataManager; +import com.intellij.ide.ui.UISettings; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.LogicalPosition; +import com.intellij.openapi.keymap.KeymapUtil; +import com.intellij.openapi.project.DumbAwareAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.util.ActionCallback; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.wm.IdeFocusManager; +import com.intellij.ui.ClickListener; +import com.intellij.ui.JBColor; +import com.intellij.ui.ScreenUtil; +import com.intellij.ui.components.JBLayeredPane; +import com.intellij.ui.components.JBList; +import com.intellij.ui.components.JBScrollPane; +import com.intellij.util.Alarm; +import com.intellij.util.PlatformIcons; +import com.intellij.util.ui.AbstractLayoutManager; +import com.intellij.util.ui.AsyncProcessIcon; +import com.intellij.util.ui.ButtonlessScrollBarUI; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import javax.swing.border.LineBorder; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import java.awt.*; +import java.awt.event.*; +import java.util.Collection; + +/** + * @author peter + */ +class LookupUi { + private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.lookup.impl.LookupUi"); + private final LookupImpl myLookup; + private final Advertiser myAdvertiser; + private final JBList myList; + private final Project myProject; + private final ModalityState myModalityState; + private final Alarm myHintAlarm = new Alarm(); + private final JLabel mySortingLabel = new JLabel(); + private final JScrollPane myScrollPane; + private final JButton myScrollBarIncreaseButton; + private final AsyncProcessIcon myProcessIcon = new AsyncProcessIcon("Completion progress"); + private final JPanel myIconPanel = new JPanel(new BorderLayout()); + private final LookupLayeredPane myLayeredPane = new LookupLayeredPane(); + + private LookupHint myElementHint = null; + private int myMaximumHeight = Integer.MAX_VALUE; + private Boolean myPositionedAbove = null; + + LookupUi(LookupImpl lookup, Advertiser advertiser, JBList list, Project project) { + myLookup = lookup; + myAdvertiser = advertiser; + myList = list; + myProject = project; + + myIconPanel.setVisible(false); + myIconPanel.setBackground(Color.LIGHT_GRAY); + myIconPanel.add(myProcessIcon); + + JComponent adComponent = advertiser.getAdComponent(); + adComponent.setBorder(new EmptyBorder(0, 1, 1, 2 + AllIcons.Ide.LookupRelevance.getIconWidth())); + myLayeredPane.mainPanel.add(adComponent, BorderLayout.SOUTH); + + myScrollBarIncreaseButton = new JButton(); + myScrollBarIncreaseButton.setFocusable(false); + myScrollBarIncreaseButton.setRequestFocusEnabled(false); + + myScrollPane = new JBScrollPane(lookup.getList()); + myScrollPane.setViewportBorder(new EmptyBorder(0, 0, 0, 0)); + myScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + myScrollPane.getVerticalScrollBar().setPreferredSize(new Dimension(13, -1)); + myScrollPane.getVerticalScrollBar().setUI(new ButtonlessScrollBarUI() { + @Override + protected JButton createIncreaseButton(int orientation) { + return myScrollBarIncreaseButton; + } + }); + lookup.getComponent().add(myLayeredPane, BorderLayout.CENTER); + + //IDEA-82111 + fixMouseCheaters(); + + myLayeredPane.mainPanel.add(myScrollPane, BorderLayout.CENTER); + myScrollPane.setBorder(null); + + mySortingLabel.setBorder(new LineBorder(new JBColor(Color.LIGHT_GRAY, JBColor.background()))); + mySortingLabel.setOpaque(true); + new ChangeLookupSorting().installOn(mySortingLabel); + updateSorting(); + myModalityState = ModalityState.stateForComponent(myLookup.getComponent()); + + addListeners(); + + updateScrollbarVisibility(); + + Disposer.register(lookup, myProcessIcon); + Disposer.register(lookup, myHintAlarm); + } + + private void addListeners() { + myList.addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + myHintAlarm.cancelAllRequests(); + + final LookupElement item = myLookup.getCurrentItem(); + if (item != null) { + updateHint(item); + } + } + }); + + final Alarm alarm = new Alarm(myLookup); + myScrollPane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() { + @Override + public void adjustmentValueChanged(AdjustmentEvent e) { + if (myLookup.myUpdating || myLookup.isLookupDisposed()) return; + alarm.addRequest(new Runnable() { + @Override + public void run() { + myLookup.refreshUi(false, false); + } + }, 300, myModalityState); + } + }); + } + + private void updateScrollbarVisibility() { + boolean showSorting = myLookup.isCompletion() && myList.getModel().getSize() >= 3; + mySortingLabel.setVisible(showSorting); + myScrollPane.setVerticalScrollBarPolicy(showSorting ? ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS : ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); + } + + private void updateHint(@NotNull final LookupElement item) { + myLookup.checkValid(); + if (myElementHint != null) { + myLayeredPane.remove(myElementHint); + myElementHint = null; + final JRootPane rootPane = myLookup.getComponent().getRootPane(); + if (rootPane != null) { + rootPane.revalidate(); + rootPane.repaint(); + } + } + if (!myLookup.isFocused()) { + return; + } + + final Collection actions = myLookup.getActionsFor(item); + if (!actions.isEmpty()) { + myHintAlarm.addRequest(new Runnable() { + @Override + public void run() { + if (!ShowHideIntentionIconLookupAction.shouldShowLookupHint() || + ((CompletionExtender)myList.getExpandableItemsHandler()).isShowing()) { + return; + } + myElementHint = new LookupHint(); + myLayeredPane.add(myElementHint, 20, 0); + myLayeredPane.layoutHint(); + } + }, 500, myModalityState); + } + } + + //Yes, it's possible to move focus to the hint. It's inconvenient, it doesn't make sense, but it's possible. + // This fix is for those jerks + private void fixMouseCheaters() { + myLookup.getComponent().addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + final ActionCallback done = IdeFocusManager.getInstance(myProject).requestFocus(myLookup.getEditor().getContentComponent(), true); + IdeFocusManager.getInstance(myProject).typeAheadUntil(done); + new Alarm(myLookup).addRequest(new Runnable() { + @Override + public void run() { + if (!done.isDone()) { + done.setDone(); + } + } + }, 300, myModalityState); + } + }); + } + + void setCalculating(final boolean calculating) { + Runnable setVisible = new Runnable() { + @Override + public void run() { + myIconPanel.setVisible(myLookup.isCalculating()); + } + }; + if (myLookup.isCalculating()) { + new Alarm(myLookup).addRequest(setVisible, 100, myModalityState); + } else { + setVisible.run(); + } + + if (calculating) { + myProcessIcon.resume(); + } else { + myProcessIcon.suspend(); + } + } + + private void updateSorting() { + final boolean lexi = UISettings.getInstance().SORT_LOOKUP_ELEMENTS_LEXICOGRAPHICALLY; + mySortingLabel.setIcon(lexi ? AllIcons.Ide.LookupAlphanumeric : AllIcons.Ide.LookupRelevance); + mySortingLabel.setToolTipText(lexi ? "Click to sort variants by relevance" : "Click to sort variants alphabetically"); + + myLookup.resort(false); + } + + void refreshUi(boolean selectionVisible, boolean itemsChanged, boolean reused, boolean onExplicitAction) { + if (myLookup.getEditor().getComponent().getRootPane() == null) { + return; + } + + updateScrollbarVisibility(); + + if (myLookup.myResizePending || itemsChanged) { + myMaximumHeight = Integer.MAX_VALUE; + } + Rectangle rectangle = calculatePosition(); + myMaximumHeight = rectangle.height; + + if (myLookup.myResizePending || itemsChanged) { + myLookup.myResizePending = false; + myLookup.pack(); + } + HintManagerImpl.updateLocation(myLookup, myLookup.getEditor(), rectangle.getLocation()); + + if (reused || selectionVisible || onExplicitAction) { + myLookup.ensureSelectionVisible(false); + } + } + + boolean isPositionedAboveCaret() { + return myPositionedAbove != null && myPositionedAbove.booleanValue(); + } + + // in layered pane coordinate system. + Rectangle calculatePosition() { + Dimension dim = myLookup.getComponent().getPreferredSize(); + int lookupStart = myLookup.getLookupStart(); + Editor editor = myLookup.getEditor(); + if (lookupStart < 0 || lookupStart > editor.getDocument().getTextLength()) { + LOG.error(lookupStart + "; offset=" + editor.getCaretModel().getOffset() + "; element=" + + myLookup.getPsiElement()); + } + + LogicalPosition pos = editor.offsetToLogicalPosition(lookupStart); + Point location = editor.logicalPositionToXY(pos); + location.y += editor.getLineHeight(); + location.x -= myLookup.myCellRenderer.getIconIndent() + myLookup.getComponent().getInsets().left; + + SwingUtilities.convertPointToScreen(location, editor.getContentComponent()); + final Rectangle screenRectangle = ScreenUtil.getScreenRectangle(location); + + if (!isPositionedAboveCaret()) { + int shiftLow = screenRectangle.height - (location.y + dim.height); + myPositionedAbove = shiftLow < 0 && shiftLow < location.y - dim.height && location.y >= dim.height; + } + if (isPositionedAboveCaret()) { + location.y -= dim.height + editor.getLineHeight(); + if (pos.line == 0) { + location.y += 1; + //otherwise the lookup won't intersect with the editor and every editor's resize (e.g. after typing in console) will close the lookup + } + } + + if (!screenRectangle.contains(location)) { + location = ScreenUtil.findNearestPointOnBorder(screenRectangle, location); + } + + final JRootPane rootPane = editor.getComponent().getRootPane(); + if (rootPane == null) { + LOG.error("editor.disposed=" + editor.isDisposed() + "; lookup.disposed=" + myLookup.isLookupDisposed() + "; editorShowing=" + editor.getContentComponent().isShowing()); + } + Rectangle candidate = new Rectangle(location, dim); + ScreenUtil.cropRectangleToFitTheScreen(candidate); + + SwingUtilities.convertPointFromScreen(location, rootPane.getLayeredPane()); + myMaximumHeight = candidate.height; + return new Rectangle(location.x, location.y, dim.width, candidate.height); + } + + private class LookupLayeredPane extends JBLayeredPane { + final JPanel mainPanel = new JPanel(new BorderLayout()); + + private LookupLayeredPane() { + add(mainPanel, 0, 0); + add(myIconPanel, 42, 0); + add(mySortingLabel, 10, 0); + + setLayout(new AbstractLayoutManager() { + @Override + public Dimension preferredLayoutSize(@Nullable Container parent) { + int maxCellWidth = myLookup.myLookupTextWidth + myLookup.myCellRenderer.getIconIndent(); + int scrollBarWidth = myScrollPane.getPreferredSize().width - myScrollPane.getViewport().getPreferredSize().width; + int listWidth = Math.min(scrollBarWidth + maxCellWidth, UISettings.getInstance().MAX_LOOKUP_WIDTH2); + + Dimension adSize = myAdvertiser.getAdComponent().getPreferredSize(); + + int panelHeight = myList.getPreferredScrollableViewportSize().height + adSize.height; + if (myList.getModel().getSize() > myList.getVisibleRowCount() && myList.getVisibleRowCount() >= 5) { + panelHeight -= myList.getFixedCellHeight() / 2; + } + return new Dimension(Math.max(listWidth, adSize.width), Math.min(panelHeight, myMaximumHeight)); + } + + @Override + public void layoutContainer(Container parent) { + Dimension size = getSize(); + mainPanel.setSize(size); + mainPanel.validate(); + + if (!myLookup.myResizePending) { + Dimension preferredSize = preferredLayoutSize(null); + if (preferredSize.width != size.width) { + UISettings.getInstance().MAX_LOOKUP_WIDTH2 = Math.max(500, size.width); + } + + int listHeight = myList.getLastVisibleIndex() - myList.getFirstVisibleIndex() + 1; + if (listHeight != myList.getModel().getSize() && listHeight != myList.getVisibleRowCount() && preferredSize.height != size.height) { + UISettings.getInstance().MAX_LOOKUP_LIST_HEIGHT = Math.max(5, listHeight); + } + } + + myList.setFixedCellWidth(myScrollPane.getViewport().getWidth()); + layoutStatusIcons(); + layoutHint(); + } + }); + } + + private void layoutStatusIcons() { + int adHeight = myAdvertiser.getAdComponent().getPreferredSize().height; + Dimension buttonSize = adHeight > 0 || !mySortingLabel.isVisible() ? new Dimension(0, 0) : new Dimension( + AllIcons.Ide.LookupRelevance.getIconWidth(), AllIcons.Ide.LookupRelevance.getIconHeight()); + myScrollBarIncreaseButton.setPreferredSize(buttonSize); + myScrollBarIncreaseButton.setMinimumSize(buttonSize); + myScrollBarIncreaseButton.setMaximumSize(buttonSize); + JScrollBar scrollBar = myScrollPane.getVerticalScrollBar(); + scrollBar.revalidate(); + scrollBar.repaint(); + + final Dimension iconSize = myProcessIcon.getPreferredSize(); + myIconPanel.setBounds(getWidth() - iconSize.width - (scrollBar.isVisible() ? scrollBar.getWidth() : 0), 0, iconSize.width, iconSize.height); + + final Dimension sortSize = mySortingLabel.getPreferredSize(); + final Point sbLocation = SwingUtilities.convertPoint(scrollBar, 0, 0, myLayeredPane); + + final int sortHeight = Math.max(adHeight, mySortingLabel.getPreferredSize().height); + mySortingLabel.setBounds(sbLocation.x, getHeight() - sortHeight, sortSize.width, sortHeight); + } + + void layoutHint() { + if (myElementHint != null && myLookup.getCurrentItem() != null) { + final Rectangle bounds = myLookup.getCurrentItemBounds(); + myElementHint.setSize(myElementHint.getPreferredSize()); + + JScrollBar sb = myScrollPane.getVerticalScrollBar(); + int x = bounds.x + bounds.width - myElementHint.getWidth() + (sb.isVisible() ? sb.getWidth() : 0); + x = Math.min(x, getWidth() - myElementHint.getWidth()); + myElementHint.setLocation(new Point(x, bounds.y)); + } + } + } + + private class LookupHint extends JLabel { + private final Border INACTIVE_BORDER = BorderFactory.createEmptyBorder(2, 2, 2, 2); + private final Border ACTIVE_BORDER = BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(Color.BLACK, 1), BorderFactory.createEmptyBorder(1, 1, 1, 1)); + private LookupHint() { + setOpaque(false); + setBorder(INACTIVE_BORDER); + setIcon(AllIcons.Actions.IntentionBulb); + String acceleratorsText = KeymapUtil.getFirstKeyboardShortcutText( + ActionManager.getInstance().getAction(IdeActions.ACTION_SHOW_INTENTION_ACTIONS)); + if (acceleratorsText.length() > 0) { + setToolTipText(CodeInsightBundle.message("lightbulb.tooltip", acceleratorsText)); + } + + addMouseListener(new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + setBorder(ACTIVE_BORDER); + } + + @Override + public void mouseExited(MouseEvent e) { + setBorder(INACTIVE_BORDER); + } + @Override + public void mousePressed(MouseEvent e) { + if (!e.isPopupTrigger() && e.getButton() == MouseEvent.BUTTON1) { + myLookup.showElementActions(); + } + } + }); + } + } + + private class ChangeLookupSorting extends ClickListener { + + @Override + public boolean onClick(@NotNull MouseEvent e, int clickCount) { + DataContext context = DataManager.getInstance().getDataContext(mySortingLabel); + DefaultActionGroup group = new DefaultActionGroup(); + group.add(createSortingAction(true)); + group.add(createSortingAction(false)); + JBPopupFactory.getInstance().createActionGroupPopup("Change sorting", group, context, JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, false).showInBestPositionFor( + context); + return true; + } + + private AnAction createSortingAction(boolean checked) { + boolean currentSetting = UISettings.getInstance().SORT_LOOKUP_ELEMENTS_LEXICOGRAPHICALLY; + final boolean newSetting = checked ? currentSetting : !currentSetting; + return new DumbAwareAction(newSetting ? "Sort lexicographically" : "Sort by relevance", null, checked ? PlatformIcons.CHECK_ICON : null) { + @Override + public void actionPerformed(AnActionEvent e) { + FeatureUsageTracker.getInstance().triggerFeatureUsed(CodeCompletionFeatures.EDITING_COMPLETION_CHANGE_SORTING); + UISettings.getInstance().SORT_LOOKUP_ELEMENTS_LEXICOGRAPHICALLY = newSetting; + updateSorting(); + } + }; + } + } +} From 1a345735473f21df7c1100f78cc893649598ed6c Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 21 Apr 2014 16:41:39 +0200 Subject: [PATCH 12/34] IDEA-120139 Autopopup doesn't popup sometimes after completion --- .../codeInsight/completion/JavaAutoPopupTest.groovy | 11 +++++++++++ .../completion/CodeCompletionHandlerBase.java | 3 ++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy index d789ce2ad957..5910f178f2cf 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy @@ -1624,4 +1624,15 @@ class Foo { def tabKeyPresentation = KeyEvent.getKeyText(TemplateSettings.TAB_CHAR as int) assert p.typeText == " [$tabKeyPresentation] " } + + public void "test autopopup after package completion"() { + myFixture.addClass("package foo.bar.goo; class Foo {}") + myFixture.configureByText "a.java", "class Foo { { foo.b } }" + myFixture.completeBasic() + assert myFixture.editor.document.text.contains('foo.bar. ') + joinAutopopup() + joinCompletion() + assert lookup + assert myFixture.lookupElementStrings == ['goo'] + } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java index 392e35ac5941..25f0bf27092b 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java @@ -430,7 +430,8 @@ public class CodeCompletionHandlerBase { // the insert handler may have started a live template with completion if (CompletionService.getCompletionService().getCurrentCompletion() == null && - !ApplicationManager.getApplication().isUnitTestMode()) { + // ...or scheduled another autopopup + !CompletionServiceImpl.isPhase(CompletionPhase.CommittingDocuments.class)) { CompletionServiceImpl.setCompletionPhase(hasModifiers? new CompletionPhase.InsertedSingleItem(indicator, restorePrefix) : CompletionPhase.NoCompletion); } checkNotSync(indicator, items); From 094f040cc585e52fefc438d68357279647376b79 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 21 Apr 2014 18:12:35 +0200 Subject: [PATCH 13/34] IDEA-122362 'getClass()' should be suggested in smart completion if Class is expected --- .../completion/PreferByKindWeigher.java | 6 +-- .../scope/JavaCompletionProcessor.java | 28 ++++++++++--- .../com/intellij/psi/util/PsiTypesUtil.java | 39 +++++++++++-------- .../GetClassWhenClassExpected-out.java | 7 ++++ .../smartType/GetClassWhenClassExpected.java | 7 ++++ .../completion/SmartTypeCompletionTest.java | 1 + 6 files changed, 63 insertions(+), 25 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/completion/smartType/GetClassWhenClassExpected-out.java create mode 100644 java/java-tests/testData/codeInsight/completion/smartType/GetClassWhenClassExpected.java diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/PreferByKindWeigher.java b/java/java-impl/src/com/intellij/codeInsight/completion/PreferByKindWeigher.java index e8c828a6c4bc..2960bd797dea 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/PreferByKindWeigher.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/PreferByKindWeigher.java @@ -144,10 +144,8 @@ public class PreferByKindWeigher extends LookupElementWeigher { } } - if (myCompletionType == CompletionType.SMART) { - if (object instanceof PsiLocalVariable || object instanceof PsiParameter || object instanceof PsiThisExpression) { - return MyResult.localOrParameter; - } + if (object instanceof PsiLocalVariable || object instanceof PsiParameter || object instanceof PsiThisExpression) { + return MyResult.localOrParameter; } if (object instanceof String && item.getUserData(JavaCompletionUtil.SUPER_METHOD_PARAMETERS) == Boolean.TRUE) { diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/scope/JavaCompletionProcessor.java b/java/java-impl/src/com/intellij/codeInsight/completion/scope/JavaCompletionProcessor.java index fb7878dba26c..4e13baf779f2 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/scope/JavaCompletionProcessor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/scope/JavaCompletionProcessor.java @@ -25,6 +25,7 @@ import com.intellij.openapi.util.Key; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.filters.ElementFilter; +import com.intellij.psi.impl.light.LightMethodBuilder; import com.intellij.psi.impl.source.resolve.JavaResolveUtil; import com.intellij.psi.infos.CandidateInfo; import com.intellij.psi.scope.BaseScopeProcessor; @@ -100,14 +101,11 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme if (qualifier instanceof PsiSuperExpression) { final PsiJavaCodeReferenceElement qSuper = ((PsiSuperExpression)qualifier).getQualifier(); if (qSuper == null) { - myQualifierClass = JavaResolveUtil.getContextClass( myElement); + myQualifierClass = JavaResolveUtil.getContextClass(myElement); } else { final PsiElement target = qSuper.resolve(); myQualifierClass = target instanceof PsiClass ? (PsiClass)target : null; } - if (myQualifierClass != null) { - myQualifierType = JavaPsiFacade.getInstance(element.getProject()).getElementFactory().createType(myQualifierClass); - } } else if (qualifier != null) { setQualifierType(qualifier.getType()); @@ -117,8 +115,13 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme myQualifierClass = (PsiClass)target; } } + } else { + myQualifierClass = JavaResolveUtil.getContextClass(myElement); } } + if (myQualifierClass != null && myQualifierType == null) { + myQualifierType = JavaPsiFacade.getElementFactory(element.getProject()).createType(myQualifierClass); + } if (myOptions.checkInitialized) { myNonInitializedFields.addAll(getNonInitializedFields(element)); @@ -219,6 +222,19 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme return true; } + if (element instanceof PsiMethod) { + PsiMethod method = (PsiMethod)element; + if (PsiTypesUtil.isGetClass(method) && PsiUtil.isLanguageLevel5OrHigher(myElement)) { + PsiType patchedType = PsiTypesUtil.createJavaLangClassType(myElement, myQualifierType, false); + if (patchedType != null) { + element = new LightMethodBuilder(element.getManager(), method.getName()). + addModifier(PsiModifier.PUBLIC). + setMethodReturnType(patchedType). + setContainingClass(method.getContainingClass()); + } + } + } + if (satisfies(element, state) && isAccessible(element)) { CompletionElement element1 = new CompletionElement(element, state.get(PsiSubstitutor.KEY)); if (myResultNames.add(element1.getUniqueId())) { @@ -288,7 +304,9 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme if (!(element instanceof PsiMember)) return true; PsiMember member = (PsiMember)element; - return JavaPsiFacade.getInstance(element.getProject()).getResolveHelper().isAccessible(member, member.getModifierList(), myElement, myQualifierClass, myDeclarationHolder); + PsiClass accessObjectClass = member instanceof PsiClass ? null : myQualifierClass; + return JavaPsiFacade.getInstance(element.getProject()).getResolveHelper().isAccessible(member, member.getModifierList(), myElement, + accessObjectClass, myDeclarationHolder); } public void setCompletionElements(@NotNull Object[] elements) { diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiTypesUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiTypesUtil.java index c448926a5874..0330fd9be8d2 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiTypesUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiTypesUtil.java @@ -22,7 +22,6 @@ import com.intellij.openapi.util.Condition; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; -import com.intellij.util.containers.HashMap; import gnu.trove.THashMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -151,9 +150,7 @@ public class PsiTypesUtil { @Nullable Condition condition, @NotNull LanguageLevel languageLevel) { //JLS3 15.8.2 - if (languageLevel.isAtLeast(LanguageLevel.JDK_1_5) && - GET_CLASS_METHOD.equals(method.getName()) && - CommonClassNames.JAVA_LANG_OBJECT.equals(method.getContainingClass().getQualifiedName())) { + if (languageLevel.isAtLeast(LanguageLevel.JDK_1_5) && isGetClass(method)) { PsiExpression qualifier = methodExpression.getQualifierExpression(); PsiType qualifierType = null; final Project project = call.getProject(); @@ -169,18 +166,28 @@ public class PsiTypesUtil { qualifierType = JavaPsiFacade.getInstance(project).getElementFactory().createType((PsiClass)parent.getPsi()); } } - if (qualifierType != null) { - PsiClass javaLangClass = JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_LANG_CLASS, call.getResolveScope()); - if (javaLangClass != null && javaLangClass.getTypeParameters().length == 1) { - Map map = new HashMap(); - map.put(javaLangClass.getTypeParameters()[0], PsiWildcardType.createExtends(call.getManager(), qualifierType)); - PsiSubstitutor substitutor = JavaPsiFacade.getInstance(project).getElementFactory().createSubstitutor(map); - final PsiClassType classType = JavaPsiFacade.getInstance(project).getElementFactory() - .createType(javaLangClass, substitutor, languageLevel); - final PsiElement parent = call.getParent(); - return parent instanceof PsiReferenceExpression && parent.getParent() instanceof PsiMethodCallExpression || parent instanceof PsiExpressionList - ? PsiUtil.captureToplevelWildcards(classType, methodExpression) : classType; - } + PsiElement parent = call.getParent(); + boolean captureTopLevelWildcards = parent instanceof PsiReferenceExpression && parent.getParent() instanceof PsiMethodCallExpression || + parent instanceof PsiExpressionList; + return createJavaLangClassType(methodExpression, qualifierType, captureTopLevelWildcards); + } + return null; + } + + public static boolean isGetClass(PsiMethod method) { + return GET_CLASS_METHOD.equals(method.getName()) && CommonClassNames.JAVA_LANG_OBJECT.equals(method.getContainingClass().getQualifiedName()); + } + + @Nullable + public static PsiType createJavaLangClassType(@NotNull PsiElement context, @Nullable PsiType qualifierType, boolean captureTopLevelWildcards) { + if (qualifierType != null) { + JavaPsiFacade facade = JavaPsiFacade.getInstance(context.getProject()); + PsiClass javaLangClass = facade.findClass(CommonClassNames.JAVA_LANG_CLASS, context.getResolveScope()); + if (javaLangClass != null && javaLangClass.getTypeParameters().length == 1) { + PsiSubstitutor substitutor = PsiSubstitutor.EMPTY. + put(javaLangClass.getTypeParameters()[0], PsiWildcardType.createExtends(context.getManager(), qualifierType)); + final PsiClassType classType = facade.getElementFactory().createType(javaLangClass, substitutor, PsiUtil.getLanguageLevel(context)); + return captureTopLevelWildcards ? PsiUtil.captureToplevelWildcards(classType, context) : classType; } } return null; diff --git a/java/java-tests/testData/codeInsight/completion/smartType/GetClassWhenClassExpected-out.java b/java/java-tests/testData/codeInsight/completion/smartType/GetClassWhenClassExpected-out.java new file mode 100644 index 000000000000..39fbcc47c65b --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/GetClassWhenClassExpected-out.java @@ -0,0 +1,7 @@ +class A { +} +class B extends A { + void m() { + Class c = getClass(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/smartType/GetClassWhenClassExpected.java b/java/java-tests/testData/codeInsight/completion/smartType/GetClassWhenClassExpected.java new file mode 100644 index 000000000000..918046761375 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/GetClassWhenClassExpected.java @@ -0,0 +1,7 @@ +class A { +} +class B extends A { + void m() { + Class c = g + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java index 6779aacee87d..478cc941567c 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java @@ -1022,6 +1022,7 @@ public class SmartTypeCompletionTest extends LightFixtureCompletionTestCase { public void testDuplicateMembersFromSuperClass() throws Throwable { doTest(); } public void testInnerAfterNew() throws Throwable { doTest(); } public void testEverythingInStringConcatenation() throws Throwable { doTest(); } + public void testGetClassWhenClassExpected() { doTest(); } public void testMemberImportStatically() { configureByTestName(); From d9aed3f444effaf7fcb78b6b6961dbeee83a0955 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 21 Apr 2014 20:10:41 +0200 Subject: [PATCH 14/34] process class names just once in completion --- .../completion/AllClassesGetter.java | 23 ++++--- .../impl/search/AllClassesSearchExecutor.java | 62 +++++++++++-------- 2 files changed, 48 insertions(+), 37 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/AllClassesGetter.java b/java/java-impl/src/com/intellij/codeInsight/completion/AllClassesGetter.java index a5431b56082a..270d6f93f767 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/AllClassesGetter.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/AllClassesGetter.java @@ -22,20 +22,20 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; +import com.intellij.psi.impl.search.AllClassesSearchExecutor; import com.intellij.psi.impl.source.PostprocessReformattingAspect; import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.search.searches.AllClassesSearch; import com.intellij.util.Consumer; import com.intellij.util.IncorrectOperationException; import com.intellij.util.Processor; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; +import java.util.LinkedHashSet; import java.util.Set; /** @@ -184,18 +184,17 @@ public class AllClassesGetter { @NotNull Project project, @NotNull GlobalSearchScope scope, @NotNull Processor processor) { - AllClassesSearch.search(scope, project, new Condition() { + final Set names = new THashSet(10000); + AllClassesSearchExecutor.processClassNames(project, scope, new Consumer() { @Override - public boolean value(String s) { - return prefixMatcher.isStartMatch(s); + public void consume(String s) { + if (prefixMatcher.prefixMatches(s)) { + names.add(s); + } } - }).forEach(processor); - AllClassesSearch.search(scope, project, new Condition() { - @Override - public boolean value(String s) { - return prefixMatcher.prefixMatches(s); - } - }).forEach(processor); + }); + LinkedHashSet sorted = CompletionUtil.sortMatching(prefixMatcher, names); + AllClassesSearchExecutor.processClassesByNames(project, scope, sorted, processor); } diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/search/AllClassesSearchExecutor.java b/java/java-indexing-impl/src/com/intellij/psi/impl/search/AllClassesSearchExecutor.java index 09b60c173f9b..293d3350040b 100644 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/search/AllClassesSearchExecutor.java +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/search/AllClassesSearchExecutor.java @@ -30,6 +30,7 @@ import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.PsiShortNamesCache; import com.intellij.psi.search.SearchScope; import com.intellij.psi.search.searches.AllClassesSearch; +import com.intellij.util.Consumer; import com.intellij.util.Processor; import com.intellij.util.QueryExecutor; import com.intellij.util.indexing.IdFilter; @@ -57,39 +58,28 @@ public class AllClassesSearchExecutor implements QueryExecutor processor) { - Project project = parameters.getProject(); - final PsiShortNamesCache cache = PsiShortNamesCache.getInstance(project); - final ProgressIndicator indicator = ProgressIndicatorProvider.getGlobalProgressIndicator(); - final Set names = new THashSet(10000); - cache.processAllClassNames(new Processor() { - int i = 0; - + processClassNames(parameters.getProject(), scope, new Consumer() { @Override - public boolean process(String s) { - if (indicator != null && i++ % 512 == 0) { - indicator.checkCanceled(); - } + public void consume(String s) { if (parameters.nameMatches(s)) { names.add(s); } - return true; - } - }, scope, IdFilter.getProjectIdFilter(project, true)); - - if (indicator != null) { - indicator.checkCanceled(); - } - - List sorted = new ArrayList(names); - Collections.sort(sorted, new Comparator() { - @Override - public int compare(final String o1, final String o2) { - return o1.compareToIgnoreCase(o2); } }); - for (final String name : sorted) { + List sorted = new ArrayList(names); + Collections.sort(sorted, String.CASE_INSENSITIVE_ORDER); + + return processClassesByNames(parameters.getProject(), scope, sorted, processor); + } + + public static boolean processClassesByNames(Project project, + final GlobalSearchScope scope, + Collection names, + Processor processor) { + final PsiShortNamesCache cache = PsiShortNamesCache.getInstance(project); + for (final String name : names) { ProgressIndicatorProvider.checkCanceled(); final PsiClass[] classes = ApplicationManager.getApplication().runReadAction(new Computable() { @Override @@ -107,6 +97,28 @@ public class AllClassesSearchExecutor implements QueryExecutor consumer) { + final ProgressIndicator indicator = ProgressIndicatorProvider.getGlobalProgressIndicator(); + + PsiShortNamesCache.getInstance(project).processAllClassNames(new Processor() { + int i = 0; + + @Override + public boolean process(String s) { + if (indicator != null && i++ % 512 == 0) { + indicator.checkCanceled(); + } + consumer.consume(s); + return true; + } + }, scope, IdFilter.getProjectIdFilter(project, true)); + + if (indicator != null) { + indicator.checkCanceled(); + } + return project; + } + private static boolean processScopeRootForAllClasses(@NotNull final PsiElement scopeRoot, @NotNull final Processor processor) { final boolean[] stopped = {false}; From 99bc2e9ec8ee3c631cb137710e4c3c464c788f4b Mon Sep 17 00:00:00 2001 From: "Gregory.Shrago" Date: Tue, 22 Apr 2014 00:46:13 +0400 Subject: [PATCH 15/34] open some API --- .../options/colors/SimpleEditorPreview.java | 14 +++++++++----- .../ide/customize/AbstractCustomizeWizardStep.java | 6 +++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/application/options/colors/SimpleEditorPreview.java b/platform/lang-impl/src/com/intellij/application/options/colors/SimpleEditorPreview.java index 6f54db66fbf8..22eca017d876 100644 --- a/platform/lang-impl/src/com/intellij/application/options/colors/SimpleEditorPreview.java +++ b/platform/lang-impl/src/com/intellij/application/options/colors/SimpleEditorPreview.java @@ -19,7 +19,10 @@ package com.intellij.application.options.colors; import com.intellij.application.options.colors.highlighting.HighlightData; import com.intellij.application.options.colors.highlighting.HighlightsExtractor; import com.intellij.ide.highlighter.HighlighterFactory; -import com.intellij.openapi.editor.*; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.EditorFactory; +import com.intellij.openapi.editor.LogicalPosition; +import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.editor.colors.CodeInsightColors; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.colors.TextAttributesKey; @@ -42,10 +45,7 @@ import javax.swing.*; import java.awt.*; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.Map; +import java.util.*; import java.util.List; public class SimpleEditorPreview implements PreviewPanel{ @@ -91,6 +91,10 @@ public class SimpleEditorPreview implements PreviewPanel{ } } + public EditorEx getEditor() { + return myEditor; + } + private void addMouseMotionListener(final Editor view, final SyntaxHighlighter highlighter, final HighlightData[] data, final boolean isBackgroundImportant) { diff --git a/platform/platform-impl/src/com/intellij/ide/customize/AbstractCustomizeWizardStep.java b/platform/platform-impl/src/com/intellij/ide/customize/AbstractCustomizeWizardStep.java index 15a690a57233..af703fa4bfef 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/AbstractCustomizeWizardStep.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/AbstractCustomizeWizardStep.java @@ -29,11 +29,11 @@ import java.awt.event.MouseEvent; public abstract class AbstractCustomizeWizardStep extends JPanel { protected static final int GAP = 20; - abstract String getTitle(); + protected abstract String getTitle(); - abstract String getHTMLHeader(); + protected abstract String getHTMLHeader(); - abstract String getHTMLFooter(); + protected abstract String getHTMLFooter(); private static Color getSelectionBackground() { return ColorUtil.mix(UIUtil.getListSelectionBackground(), UIUtil.getLabelBackground(), .75); From ed33f35011b5fbca6bbef787448463d4a26fd583 Mon Sep 17 00:00:00 2001 From: "Gregory.Shrago" Date: Tue, 22 Apr 2014 00:59:38 +0400 Subject: [PATCH 16/34] fix Keymap page on Mac/Darcula. see same in LafManager for more info. --- .../com/intellij/ide/customize/CustomizeUIThemeStepPanel.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java index c5184dda2394..84329788785b 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java @@ -26,6 +26,7 @@ import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.SystemInfo; import com.intellij.util.IconUtil; import com.intellij.util.PlatformUtils; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -161,6 +162,7 @@ public class CustomizeUIThemeStepPanel extends AbstractCustomizeWizardStep { } Window window = SwingUtilities.getWindowAncestor(component); if (window != null) { + window.setBackground(new Color(UIUtil.getPanelBackground().getRGB())); SwingUtilities.updateComponentTreeUI(window); } if (ApplicationManager.getApplication() != null) { From e9eb1717855817a70bf3d23526fe1218d2c75e7b Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Tue, 22 Apr 2014 00:32:31 +0200 Subject: [PATCH 17/34] initial --- .../src/com/intellij/util/ui/JBImageIcon.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 platform/util/src/com/intellij/util/ui/JBImageIcon.java diff --git a/platform/util/src/com/intellij/util/ui/JBImageIcon.java b/platform/util/src/com/intellij/util/ui/JBImageIcon.java new file mode 100644 index 000000000000..8d8bc6e40b00 --- /dev/null +++ b/platform/util/src/com/intellij/util/ui/JBImageIcon.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.util.ui; + +import javax.swing.*; +import java.awt.*; +import java.awt.image.ImageObserver; + +/** + * HiDPI-aware image icon + * + * @author Konstantin Bulenkov + */ +public class JBImageIcon extends ImageIcon { + public JBImageIcon(Image image) { + super(image); + } + + @Override + public final synchronized void paintIcon(final Component c, final Graphics g, final int x, final int y) { + final ImageObserver observer = getImageObserver(); + + UIUtil.drawImage(g, getImage(), x, y, observer == null ? c : observer); + } +} From e2461d569f61169be70f84e0c9b954936f495e39 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Tue, 22 Apr 2014 00:33:15 +0200 Subject: [PATCH 18/34] use JBImageIcon --- .../com/intellij/openapi/util/IconLoader.java | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/platform/util/src/com/intellij/openapi/util/IconLoader.java b/platform/util/src/com/intellij/openapi/util/IconLoader.java index b611d7a51891..32584463a71e 100644 --- a/platform/util/src/com/intellij/openapi/util/IconLoader.java +++ b/platform/util/src/com/intellij/openapi/util/IconLoader.java @@ -24,6 +24,7 @@ import com.intellij.util.ReflectionUtil; import com.intellij.util.RetinaImage; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.WeakHashMap; +import com.intellij.util.ui.JBImageIcon; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -33,7 +34,6 @@ import javax.swing.*; import java.awt.*; import java.awt.image.BufferedImage; import java.awt.image.FilteredImageSource; -import java.awt.image.ImageObserver; import java.awt.image.ImageProducer; import java.lang.ref.Reference; import java.lang.reflect.Field; @@ -98,7 +98,7 @@ public final class IconLoader { @Deprecated public static Icon getIcon(@NotNull final Image image) { - return new MyImageIcon(image); + return new JBImageIcon(image); } public static void setUseDarkIcons(boolean useDarkIcons) { @@ -279,7 +279,7 @@ public final class IconLoader { Image img = createDisabled(image); if (UIUtil.isRetina()) img = RetinaImage.createFrom(img, 2, ImageLoader.ourComponent); - disabledIcon = new MyImageIcon(img); + disabledIcon = new JBImageIcon(img); ourIcon2DisabledIcon.put(icon, disabledIcon); } return disabledIcon; @@ -383,19 +383,6 @@ public final class IconLoader { } } - private static final class MyImageIcon extends ImageIcon { - public MyImageIcon(final Image image) { - super(image); - } - - @Override - public final synchronized void paintIcon(final Component c, final Graphics g, final int x, final int y) { - final ImageObserver observer = getImageObserver(); - - UIUtil.drawImage(g, getImage(), x, y, observer == null ? c : observer); - } - } - public abstract static class LazyIcon implements Icon { private boolean myWasComputed; private Icon myIcon; From e5eb7aa43ae6a13ce94bfe9c361548292a1fdd73 Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Mon, 21 Apr 2014 19:52:40 +0400 Subject: [PATCH 19/34] EA-31437 - assert: DocumentFoldingInfo.writeExternal --- .../folding/impl/OffsetsElementSignatureProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/OffsetsElementSignatureProvider.java b/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/OffsetsElementSignatureProvider.java index cae4fe686078..5a9defd95d0c 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/OffsetsElementSignatureProvider.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/OffsetsElementSignatureProvider.java @@ -206,7 +206,7 @@ public class OffsetsElementSignatureProvider extends AbstractElementSignaturePro buffer.append(ELEMENT_TOKENS_SEPARATOR).append(index); PsiFile containingFile = element.getContainingFile(); if (containingFile != null && containingFile.getViewProvider().getLanguages().size() > 1) { - buffer.append(ELEMENT_TOKENS_SEPARATOR).append(element.getLanguage().getID()); + buffer.append(ELEMENT_TOKENS_SEPARATOR).append(containingFile.getLanguage().getID()); } return buffer.toString(); } From 89ca1576e0d4566810120f777bdeb55e89f537e1 Mon Sep 17 00:00:00 2001 From: Andrey Starovoyt Date: Tue, 22 Apr 2014 11:05:08 +0400 Subject: [PATCH 20/34] Extract Java live postfix templates provider --- .../PostfixTemplateCompletionContributor.java | 21 +- .../PostfixTemplatesCompletionProvider.java | 7 +- .../PostfixTemplatesConfigurable.java | 18 +- .../JavaPostfixTemplateProvider.java | 167 +++++++++ .../templates/LanguagePostfixTemplate.java | 27 ++ .../templates/PostfixLiveTemplate.java | 323 ++++++++++-------- .../postfix/templates/PostfixTemplate.java | 4 - .../templates/PostfixTemplateProvider.java | 64 ++++ resources/src/META-INF/PostfixTemplates.xml | 34 +- 9 files changed, 463 insertions(+), 202 deletions(-) create mode 100644 java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/JavaPostfixTemplateProvider.java create mode 100644 java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/LanguagePostfixTemplate.java create mode 100644 java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateProvider.java diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplateCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplateCompletionContributor.java index b6a7b5f687ce..eb6b585f0ec1 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplateCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplateCompletionContributor.java @@ -21,34 +21,19 @@ import com.intellij.codeInsight.template.CustomLiveTemplate; import com.intellij.codeInsight.template.impl.TemplateManagerImpl; import com.intellij.codeInsight.template.postfix.templates.PostfixLiveTemplate; import com.intellij.openapi.editor.Editor; -import com.intellij.patterns.ElementPattern; -import com.intellij.psi.JavaTokenType; -import com.intellij.psi.PsiElement; +import com.intellij.patterns.PlatformPatterns; import com.intellij.psi.PsiFile; -import com.intellij.psi.impl.source.tree.ElementType; -import com.intellij.psi.tree.TokenSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import static com.intellij.patterns.PsiJavaPatterns.psiElement; -import static com.intellij.patterns.StandardPatterns.string; - public class PostfixTemplateCompletionContributor extends CompletionContributor { - private static final TokenSet SUITABLE_ELEMENTS = TokenSet.orSet(ElementType.KEYWORD_BIT_SET, - ElementType.LITERAL_BIT_SET, - TokenSet.create(JavaTokenType.IDENTIFIER)); - public PostfixTemplateCompletionContributor() { - extend(CompletionType.BASIC, identifierAfterDot(), new PostfixTemplatesCompletionProvider()); + extend(CompletionType.BASIC, PlatformPatterns.psiElement(), new PostfixTemplatesCompletionProvider()); } @Nullable - public static PostfixLiveTemplate getPostfixLiveTemplate(@NotNull PsiFile file, @NotNull Editor editor) { + public static PostfixLiveTemplate getPostfixLiveTemplate(@NotNull PsiFile file, @NotNull Editor editor) { PostfixLiveTemplate postfixLiveTemplate = CustomLiveTemplate.EP_NAME.findExtension(PostfixLiveTemplate.class); return postfixLiveTemplate != null && TemplateManagerImpl.isApplicable(postfixLiveTemplate, editor, file) ? postfixLiveTemplate : null; } - - private static ElementPattern identifierAfterDot() { - return psiElement().withElementType(SUITABLE_ELEMENTS).afterLeaf(psiElement().withText(string().contains("."))); - } } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplatesCompletionProvider.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplatesCompletionProvider.java index db253a6808b1..3e99fbf66db2 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplatesCompletionProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplatesCompletionProvider.java @@ -42,11 +42,12 @@ class PostfixTemplatesCompletionProvider extends CompletionProvider templates = Arrays.asList(PostfixTemplate.EP_NAME.getExtensions()); + + LanguageExtensionPoint[] extensions = new ExtensionPointName(LanguagePostfixTemplate.EP_NAME).getExtensions(); + + List templates = ContainerUtil.newArrayList(); + for (LanguageExtensionPoint extension : extensions) { + templates.addAll(((PostfixTemplateProvider)extension.getInstance()).getTemplates()); + } + ContainerUtil.sort(templates, new Comparator() { @Override public int compare(PostfixTemplate o1, PostfixTemplate o2) { @@ -184,7 +196,7 @@ public class PostfixTemplatesConfigurable implements SearchableConfigurable, Edi private static String shortcutToString(char shortcut) { if (shortcut == TemplateSettings.SPACE_CHAR) { return SPACE; - } + } if (shortcut == TemplateSettings.ENTER_CHAR) { return ENTER; } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/JavaPostfixTemplateProvider.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/JavaPostfixTemplateProvider.java new file mode 100644 index 000000000000..cb628dd2c276 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/JavaPostfixTemplateProvider.java @@ -0,0 +1,167 @@ +/* + * Copyright 2000-2014 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.template.postfix.templates; + +import com.intellij.codeInsight.completion.CompletionInitializationContext; +import com.intellij.codeInsight.completion.JavaCompletionContributor; +import com.intellij.codeInsight.template.CustomTemplateCallback; +import com.intellij.codeInsight.template.postfix.util.Aliases; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.util.Ref; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.Set; + + +public class JavaPostfixTemplateProvider implements PostfixTemplateProvider { + + private static final Logger LOG = Logger.getInstance(JavaPostfixTemplateProvider.class); + + private final Map myMapTemplates; + + public JavaPostfixTemplateProvider() { + myMapTemplates = ContainerUtil.newHashMap(); + for (PostfixTemplate template : getInitializeTemplateSet()) { + register(template.getKey(), template); + Aliases aliases = template.getClass().getAnnotation(Aliases.class); + if (aliases != null) { + for (String key : aliases.value()) { + register(key, template); + } + } + } + } + + @NotNull + protected Set getInitializeTemplateSet() { + return ContainerUtil.newHashSet(new AssertStatementPostfixTemplate(), + new CastExpressionPostfixTemplate(), + new ElseStatementPostfixTemplate(), + new ForAscendingPostfixTemplate(), + new ForDescendingPostfixTemplate(), + new ForeachPostfixTemplate(), + new FormatPostfixTemplate(), + new IfStatementPostfixTemplate(), + new InstanceofExpressionPostfixTemplate(), + new IntroduceFieldPostfixTemplate(), + new IntroduceVariablePostfixTemplate(), + new IsNullCheckPostfixTemplate(), + new NotExpressionPostfixTemplate(), + new NotNullCheckPostfixTemplate(), + new ParenthesizedExpressionPostfixTemplate(), + new ReturnStatementPostfixTemplate(), + new SoutPostfixTemplate(), + new SwitchStatementPostfixTemplate(), + new SynchronizedStatementPostfixTemplate(), + new ThrowExceptionPostfixTemplate(), + new TryStatementPostfixTemplate(), + new TryWithResourcesPostfixTemplate(), + new WhileStatementPostfixTemplate()); + } + + @NotNull + @Override + public Set getTemplates() { + return ContainerUtil.newHashSet(myMapTemplates.values()); + } + + @NotNull + @Override + public Set getKeys() { + return myMapTemplates.keySet(); + } + + + @Nullable + @Override + public PostfixTemplate get(@Nullable String key) { + return myMapTemplates.get(key); + } + + @Override + public boolean isTerminalSymbol(char currentChar) { + return currentChar == '.' || currentChar == '!'; + } + + @NotNull + @Override + public PsiElement preExpand(@NotNull Editor editor, @NotNull PsiElement context, int offset, @NotNull final String key) { + + return addSemicolonIfNeeded(editor, editor.getDocument(), context, offset - key.length()); + } + + @NotNull + @Override + public PsiFile preCheck(@NotNull Editor editor, @NotNull PsiFile file, int currentOffset) { + Document document = file.getViewProvider().getDocument(); + assert document != null; + CharSequence sequence = document.getCharsSequence(); + StringBuilder fileContentWithoutKey = new StringBuilder(sequence); + if (isSemicolonNeeded(file, editor)) { + fileContentWithoutKey.insert(currentOffset, ';'); + file = PostfixLiveTemplate.copyFile(file, fileContentWithoutKey); + } + + return file; + } + + private void register(@NotNull String key, @NotNull PostfixTemplate template) { + PostfixTemplate registered = myMapTemplates.put(key, template); + if (registered != null) { + LOG.error("Can't register postfix template. Duplicated key: " + template.getKey()); + } + } + + @NotNull + private static PsiElement addSemicolonIfNeeded(@NotNull final Editor editor, + @NotNull final Document document, + @NotNull final PsiElement context, + final int offset) { + ApplicationManager.getApplication().assertIsDispatchThread(); + + final Ref newContext = Ref.create(context); + final PsiFile file = context.getContainingFile(); + if (isSemicolonNeeded(file, editor)) { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() { + public void run() { + document.insertString(offset, ";"); + PsiDocumentManager.getInstance(context.getProject()).commitDocument(document); + newContext.set(CustomTemplateCallback.getContext(file, offset - 1)); + } + }); + } + }); + } + return newContext.get(); + } + + private static boolean isSemicolonNeeded(@NotNull PsiFile file, @NotNull Editor editor) { + return JavaCompletionContributor.semicolonNeeded(editor, file, CompletionInitializationContext.calcStartOffset(editor)); + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/LanguagePostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/LanguagePostfixTemplate.java new file mode 100644 index 000000000000..d3780e1f3f12 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/LanguagePostfixTemplate.java @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2014 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.template.postfix.templates; + +import com.intellij.lang.LanguageExtension; + +public class LanguagePostfixTemplate extends LanguageExtension { + public static final LanguagePostfixTemplate INSTANCE = new LanguagePostfixTemplate(); + public static final String EP_NAME = "com.intellij.codeInsight.template.postfixTemplateProvider"; + + private LanguagePostfixTemplate() { + super(EP_NAME); + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixLiveTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixLiveTemplate.java index d738fb8d227b..349eeda3ffb7 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixLiveTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixLiveTemplate.java @@ -15,17 +15,15 @@ */ package com.intellij.codeInsight.template.postfix.templates; -import com.intellij.codeInsight.completion.CompletionInitializationContext; -import com.intellij.codeInsight.completion.JavaCompletionContributor; +import com.google.common.collect.Sets; import com.intellij.codeInsight.template.CustomLiveTemplateBase; import com.intellij.codeInsight.template.CustomTemplateCallback; import com.intellij.codeInsight.template.impl.CustomLiveTemplateLookupElement; import com.intellij.codeInsight.template.impl.TemplateSettings; import com.intellij.codeInsight.template.postfix.completion.PostfixTemplateLookupElement; import com.intellij.codeInsight.template.postfix.settings.PostfixTemplatesSettings; -import com.intellij.codeInsight.template.postfix.util.Aliases; import com.intellij.featureStatistics.FeatureUsageTracker; -import com.intellij.lang.java.JavaLanguage; +import com.intellij.lang.Language; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.undo.UndoConstants; @@ -33,7 +31,6 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; @@ -42,71 +39,53 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileFactory; import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; +import java.util.Collections; import java.util.Set; public class PostfixLiveTemplate extends CustomLiveTemplateBase { public static final String POSTFIX_TEMPLATE_ID = "POSTFIX_TEMPLATE_ID"; - private static final Logger LOG = Logger.getInstance(PostfixLiveTemplate.class); - private final HashMap myTemplates = ContainerUtil.newHashMap(); + private static final LanguagePostfixTemplate templates = LanguagePostfixTemplate.INSTANCE; - public PostfixLiveTemplate() { - for (PostfixTemplate template : PostfixTemplate.EP_NAME.getExtensions()) { - register(template.getKey(), template); - Aliases aliases = template.getClass().getAnnotation(Aliases.class); - if (aliases != null) { - for (String key : aliases.value()) { - register(key, template); - } + @NotNull + public Set getAllTemplateKeys(PsiFile file, int offset) { + Set keys = Sets.newHashSet(); + Language language = PsiUtilCore.getLanguageAtOffset(file, offset); + + for (PostfixTemplateProvider provider : templates.allForLanguage(language)) { + keys.addAll(provider.getKeys()); + } + return keys; + } + + public boolean hasNotEmptyKey(PsiFile file, int offset) { + Language language = PsiUtilCore.getLanguageAtOffset(file, offset); + for (PostfixTemplateProvider provider : templates.allForLanguage(language)) { + if (StringUtil + .isNotEmpty(computeTemplateKeyWithoutContextChecking(provider, file.getText(), offset + 1))) { + return true; } } - } - - private void register(@NotNull String key, @NotNull PostfixTemplate template) { - PostfixTemplate registered = myTemplates.put(key, template); - if (registered != null) { - LOG.error("Can't register postfix template. Duplicated key: " + template.getKey()); - } - } - - @Nullable - @Override - public String computeTemplateKey(@NotNull CustomTemplateCallback callback) { - Editor editor = callback.getEditor(); - String key = computeTemplateKeyWithoutContextChecking(editor.getDocument().getCharsSequence(), editor.getCaretModel().getOffset()); - if (key == null) return null; - return isApplicableTemplate(getTemplateByKey(key), key, callback.getContext().getContainingFile(), editor) ? key : null; - } - - @Nullable - @Override - public String computeTemplateKeyWithoutContextChecking(@NotNull CustomTemplateCallback callback) { - Editor editor = callback.getEditor(); - return computeTemplateKeyWithoutContextChecking(editor.getDocument().getCharsSequence(), editor.getCaretModel().getOffset()); - } - - @Override - public boolean supportsMultiCaret() { return false; } @Nullable - public String computeTemplateKeyWithoutContextChecking(@NotNull CharSequence documentContent, int currentOffset) { + public String computeTemplateKeyWithoutContextChecking(PostfixTemplateProvider provider, + @NotNull CharSequence documentContent, + int currentOffset) { int startOffset = currentOffset; if (documentContent.length() < startOffset) { return null; } + while (startOffset > 0) { char currentChar = documentContent.charAt(startOffset - 1); if (!Character.isJavaIdentifierPart(currentChar)) { - if (currentChar != '.' && currentChar != '!') { + if (!provider.isTerminalSymbol(currentChar)) { return null; } startOffset--; @@ -117,19 +96,82 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase { return String.valueOf(documentContent.subSequence(startOffset, currentOffset)); } + @Nullable + @Override + public String computeTemplateKey(@NotNull CustomTemplateCallback callback) { + Editor editor = callback.getEditor(); + CharSequence charsSequence = editor.getDocument().getCharsSequence(); + int offset = editor.getCaretModel().getOffset(); + Language language = getLanguage(callback); + for (PostfixTemplateProvider provider : templates.allForLanguage(language)) { + String key = computeTemplateKeyWithoutContextChecking(provider, charsSequence, offset); + if (key != null && isApplicableTemplate(provider, key, callback.getFile(), editor)) { + return key; + } + } + + return null; + } + + @Nullable + @Override + public String computeTemplateKeyWithoutContextChecking(@NotNull CustomTemplateCallback callback) { + Editor editor = callback.getEditor(); + return computeTemplateKeyWithoutContextChecking(callback.getFile(), editor, editor.getCaretModel().getOffset()); + } + + @Override + public boolean supportsMultiCaret() { + return false; + } + + @Nullable + public String computeTemplateKeyWithoutContextChecking(PsiFile file, Editor editor, int currentOffset) { + Language language = PsiUtilCore.getLanguageAtOffset(file, currentOffset); + for (PostfixTemplateProvider provider : templates.allForLanguage(language)) { + String key = computeTemplateKeyWithoutContextChecking(provider, editor.getDocument().getCharsSequence(), currentOffset); + if (key != null) return key; + } + return null; + } + @Override public void expand(@NotNull final String key, @NotNull final CustomTemplateCallback callback) { ApplicationManager.getApplication().assertIsDispatchThread(); - FeatureUsageTracker.getInstance().triggerFeatureUsed("editing.completion.postfix"); - final PostfixTemplate template = getTemplateByKey(key); + Editor editor = callback.getEditor(); + Language language = getLanguage(callback); + for (PostfixTemplateProvider provider : templates.allForLanguage(language)) { + PostfixTemplate postfixTemplate = provider.get(key); + if (postfixTemplate != null) { + expandForProvider(provider, key, callback); + return; + } + } + + // don't care about errors in multiCaret mode + if (editor.getCaretModel().getAllCarets().size() == 1) { + LOG.error("Template not found by key: " + key); + } + } + + private static Language getLanguage(CustomTemplateCallback callback) { + return PsiUtilCore.getLanguageAtOffset(callback.getFile(), callback.getEditor().getCaretModel().getOffset()); + } + + public void expandForProvider( + @NotNull PostfixTemplateProvider provider, + @NotNull final String key, + @NotNull final CustomTemplateCallback callback) { final Editor editor = callback.getEditor(); final PsiFile file = callback.getContext().getContainingFile(); - if (isApplicableTemplate(template, key, file, editor)) { + if (isApplicableTemplate(provider, key, file, editor)) { int currentOffset = editor.getCaretModel().getOffset(); PsiElement newContext = deleteTemplateKey(file, editor.getDocument(), currentOffset, key); - newContext = addSemicolonIfNeeded(editor, editor.getDocument(), newContext, currentOffset - key.length()); + newContext = provider.preExpand(editor, newContext, currentOffset, key); + PostfixTemplate template = provider.get(key); + assert template != null; expandTemplate(template, editor, newContext); } // don't care about errors in multiCaret mode @@ -141,11 +183,10 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase { @Override public boolean isApplicable(PsiFile file, int offset, boolean wrapping) { PostfixTemplatesSettings settings = PostfixTemplatesSettings.getInstance(); - if (wrapping || file == null || settings == null || !settings.isPostfixTemplatesEnabled() || - PsiUtilCore.getLanguageAtOffset(file, offset) != JavaLanguage.INSTANCE) { + if (wrapping || file == null || settings == null || !settings.isPostfixTemplatesEnabled()) { return false; } - return StringUtil.isNotEmpty(computeTemplateKeyWithoutContextChecking(file.getText(), offset + 1)); + return hasNotEmptyKey(file, offset); } @Override @@ -177,35 +218,46 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase { @NotNull @Override - public Collection getLookupElements(@NotNull PsiFile file, @NotNull Editor editor, int offset) { - String key = computeTemplateKeyWithoutContextChecking(editor.getDocument().getCharsSequence(), offset); + public Collection getLookupElements(@NotNull PsiFile file, + @NotNull Editor editor, + int offset) { + Collection result = ContainerUtil.newHashSet(); + Language language = PsiUtilCore.getLanguageAtOffset(file, editor.getCaretModel().getOffset()); + for (PostfixTemplateProvider provider : templates.allForLanguage(language)) { + result.addAll(getLookupElementsForProvider(provider, file, editor, offset)); + } + + return result; + } + + @NotNull + private Collection getLookupElementsForProvider( + @NotNull PostfixTemplateProvider provider, + @NotNull PsiFile file, + @NotNull Editor editor, + int offset) { + String key = computeTemplateKeyWithoutContextChecking(file, editor, offset); if (key != null && editor.getCaretModel().getCaretCount() == 1) { Collection result = ContainerUtil.newHashSet(); - Condition isApplicationTemplateFunction = createIsApplicationTemplateFunction(key, file, editor); - for (Map.Entry entry : myTemplates.entrySet()) { - PostfixTemplate postfixTemplate = entry.getValue(); + + Condition isApplicationTemplateFunction = createIsApplicationTemplateFunction(provider, key, file, editor); + for (String postfixKey : provider.getKeys()) { + PostfixTemplate postfixTemplate = provider.get(postfixKey); + assert postfixTemplate != null; if (isApplicationTemplateFunction.value(postfixTemplate)) { - result.add(new PostfixTemplateLookupElement(this, postfixTemplate, entry.getKey(), false)); + result.add(new PostfixTemplateLookupElement(this, postfixTemplate, postfixKey, false)); } } return result; } - return super.getLookupElements(file, editor, offset); + return Collections.emptyList(); } - @NotNull - public Set getAllTemplateKeys() { - return myTemplates.keySet(); - } - @Nullable - public PostfixTemplate getTemplateByKey(@NotNull String key) { - return myTemplates.get(key); - } - - private static void expandTemplate(@NotNull final PostfixTemplate template, - @NotNull final Editor editor, - @NotNull final PsiElement context) { + private static void expandTemplate( + @NotNull final PostfixTemplate template, + @NotNull final Editor editor, + @NotNull final PsiElement context) { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { @@ -218,56 +270,6 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase { }); } - @Contract("null, _, _, _ -> false") - private static boolean isApplicableTemplate(@Nullable PostfixTemplate template, @NotNull String key, @NotNull PsiFile file, @NotNull Editor editor) { - return createIsApplicationTemplateFunction(key, file, editor).value(template); - } - - private static Condition createIsApplicationTemplateFunction(@NotNull String key, @NotNull PsiFile file, @NotNull Editor editor) { - int currentOffset = editor.getCaretModel().getOffset(); - final int newOffset = currentOffset - key.length(); - CharSequence fileContent = editor.getDocument().getCharsSequence(); - - StringBuilder fileContentWithoutKey = new StringBuilder(); - fileContentWithoutKey.append(fileContent.subSequence(0, newOffset)); - fileContentWithoutKey.append(fileContent.subSequence(currentOffset, fileContent.length())); - PsiFile copyFile = copyFile(file, fileContentWithoutKey); - Document copyDocument = copyFile.getViewProvider().getDocument(); - if (copyDocument == null) { - //noinspection unchecked - return Condition.FALSE; - } - - if (isSemicolonNeeded(copyFile, editor)) { - fileContentWithoutKey.insert(newOffset, ';'); - copyFile = copyFile(file, fileContentWithoutKey); - copyDocument = copyFile.getViewProvider().getDocument(); - if (copyDocument == null) { - //noinspection unchecked - return Condition.FALSE; - } - } - - final PsiElement context = CustomTemplateCallback.getContext(copyFile, newOffset > 0 ? newOffset - 1 : newOffset); - final Document finalCopyDocument = copyDocument; - return new Condition() { - @Override - public boolean value(PostfixTemplate template) { - return template != null && template.isEnabled() && template.isApplicable(context, finalCopyDocument, newOffset); - } - }; - } - - @NotNull - private static PsiFile copyFile(@NotNull PsiFile file, @NotNull StringBuilder fileContentWithoutKey) { - final PsiFileFactory psiFileFactory = PsiFileFactory.getInstance(file.getProject()); - PsiFile copy = psiFileFactory.createFileFromText(file.getName(), file.getFileType(), fileContentWithoutKey); - VirtualFile vFile = copy.getVirtualFile(); - if (vFile != null) { - vFile.putUserData(UndoConstants.DONT_RECORD_UNDO, Boolean.TRUE); - } - return copy; - } @NotNull private static PsiElement deleteTemplateKey(@NotNull final PsiFile file, @@ -291,33 +293,58 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase { return CustomTemplateCallback.getContext(file, startOffset > 0 ? startOffset - 1 : startOffset); } - @NotNull - private static PsiElement addSemicolonIfNeeded(@NotNull final Editor editor, - @NotNull final Document document, - @NotNull final PsiElement context, - final int offset) { - ApplicationManager.getApplication().assertIsDispatchThread(); - - final Ref newContext = Ref.create(context); - final PsiFile file = context.getContainingFile(); - if (isSemicolonNeeded(file, editor)) { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() { - public void run() { - document.insertString(offset, ";"); - PsiDocumentManager.getInstance(context.getProject()).commitDocument(document); - newContext.set(CustomTemplateCallback.getContext(file, offset - 1)); - } - }); - } - }); + private static Condition createIsApplicationTemplateFunction( + @NotNull PostfixTemplateProvider provider, + @NotNull String key, + @NotNull PsiFile file, + @NotNull Editor editor) { + int currentOffset = editor.getCaretModel().getOffset(); + final int newOffset = currentOffset - key.length(); + CharSequence fileContent = editor.getDocument().getCharsSequence(); + StringBuilder fileContentWithoutKey = new StringBuilder(); + fileContentWithoutKey.append(fileContent.subSequence(0, newOffset)); + fileContentWithoutKey.append(fileContent.subSequence(currentOffset, fileContent.length())); + PsiFile copyFile = copyFile(file, fileContentWithoutKey); + Document copyDocument = copyFile.getViewProvider().getDocument(); + if (copyDocument == null) { + //noinspection unchecked + return Condition.FALSE; } - return newContext.get(); + + copyFile = provider.preCheck(editor, copyFile, newOffset); + copyDocument = copyFile.getViewProvider().getDocument(); + if (copyDocument == null) { + //noinspection unchecked + return Condition.FALSE; + } + + final PsiElement context = CustomTemplateCallback.getContext(copyFile, newOffset > 0 ? newOffset - 1 : newOffset); + final Document finalCopyDocument = copyDocument; + return new Condition() { + @Override + public boolean value(PostfixTemplate template) { + return template != null && template.isEnabled() && template.isApplicable(context, finalCopyDocument, newOffset); + } + }; } - private static boolean isSemicolonNeeded(@NotNull PsiFile file, @NotNull Editor editor) { - return JavaCompletionContributor.semicolonNeeded(editor, file, CompletionInitializationContext.calcStartOffset(editor)); + + @NotNull + public static PsiFile copyFile(@NotNull PsiFile file, @NotNull StringBuilder fileContentWithoutKey) { + final PsiFileFactory psiFileFactory = PsiFileFactory.getInstance(file.getProject()); + PsiFile copy = psiFileFactory.createFileFromText(file.getName(), file.getFileType(), fileContentWithoutKey); + VirtualFile vFile = copy.getVirtualFile(); + if (vFile != null) { + vFile.putUserData(UndoConstants.DONT_RECORD_UNDO, Boolean.TRUE); + } + return copy; + } + + public static boolean isApplicableTemplate( + @NotNull PostfixTemplateProvider provider, + @NotNull String key, + @NotNull PsiFile file, + @NotNull Editor editor) { + return createIsApplicationTemplateFunction(provider, key, file, editor).value(provider.get(key)); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplate.java index 80a68ab26650..4131dd907d32 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplate.java @@ -18,7 +18,6 @@ package com.intellij.codeInsight.template.postfix.templates; import com.intellij.codeInsight.template.postfix.settings.PostfixTemplatesSettings; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiExpression; import com.intellij.psi.PsiExpressionStatement; @@ -32,9 +31,6 @@ public abstract class PostfixTemplate { @NotNull private final String myDescription; @NotNull private final String myExample; - @NotNull - public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.postfixTemplate"); - protected PostfixTemplate(@NotNull String name, @NotNull String description, @NotNull String example) { this(name, "." + name, description, example); } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateProvider.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateProvider.java new file mode 100644 index 000000000000..b74beba50598 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateProvider.java @@ -0,0 +1,64 @@ +/* + * Copyright 2000-2014 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.template.postfix.templates; + + +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Set; + +public interface PostfixTemplateProvider { + + /** + * Return all templates registered in the provider + */ + @NotNull + Set getTemplates(); + + /** + * Return all keys registered in the provider + */ + @NotNull + Set getKeys(); + + /** + * Return postfix template for key + */ + @Nullable + PostfixTemplate get(@Nullable String key); + + /** + * Check symbol can separate template keys + */ + boolean isTerminalSymbol(char currentChar); + + /** + * Prepare original file content for template expanding + */ + @NotNull + PsiElement preExpand(@NotNull Editor editor, @NotNull PsiElement context, int currentOffset, @NotNull String key); + + /** + * Do some actions with the file content before check applicable. + * Return new copy of file because we should not change original file + */ + @NotNull + PsiFile preCheck(@NotNull Editor editor, @NotNull PsiFile file, int currentOffset); +} diff --git a/resources/src/META-INF/PostfixTemplates.xml b/resources/src/META-INF/PostfixTemplates.xml index 90294e0d619c..2fe4cd381876 100644 --- a/resources/src/META-INF/PostfixTemplates.xml +++ b/resources/src/META-INF/PostfixTemplates.xml @@ -1,41 +1,23 @@ - + + + + - - - - - - - - - - - - - - - - - - - - - - - + - + - + \ No newline at end of file From 7ac3c11ddd40afa217f05333bc3a75422ff2ee70 Mon Sep 17 00:00:00 2001 From: nik Date: Tue, 22 Apr 2014 11:30:11 +0400 Subject: [PATCH 21/34] paste rich-text data: NPE fixed --- .../openapi/editor/richcopy/view/HtmlTransferableData.java | 1 - 1 file changed, 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/openapi/editor/richcopy/view/HtmlTransferableData.java b/platform/lang-impl/src/com/intellij/openapi/editor/richcopy/view/HtmlTransferableData.java index 0d64830b5041..c6224026bc11 100644 --- a/platform/lang-impl/src/com/intellij/openapi/editor/richcopy/view/HtmlTransferableData.java +++ b/platform/lang-impl/src/com/intellij/openapi/editor/richcopy/view/HtmlTransferableData.java @@ -89,7 +89,6 @@ public class HtmlTransferableData extends AbstractSyntaxAwareReaderTransferableD } finally { myResultBuffer = null; - myRawText = null; myColorRegistry = null; myFontNameRegistry = null; myColors.clear(); From cc57b3958dd18742559784cc961856f4d377e61a Mon Sep 17 00:00:00 2001 From: nik Date: Tue, 22 Apr 2014 12:12:48 +0400 Subject: [PATCH 22/34] do not include runtime dependencies to compilation scope (ZD-25927) --- .../com/intellij/compiler/impl/ModuleCompileScope.java | 9 ++------- .../com/intellij/compiler/BaseCompilerTestCase.java | 10 +++++++++- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/ModuleCompileScope.java b/java/compiler/impl/src/com/intellij/compiler/impl/ModuleCompileScope.java index 3be5efc00fb0..b2485e8d8ea8 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/ModuleCompileScope.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/ModuleCompileScope.java @@ -33,6 +33,7 @@ import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.util.CommonProcessors; import org.jetbrains.annotations.NotNull; import java.util.HashMap; @@ -75,13 +76,7 @@ public class ModuleCompileScope extends FileIndexCompileScope { } private void buildScopeModulesSet(Module module) { - myScopeModules.add(module); - final Module[] dependencies = ModuleRootManager.getInstance(module).getDependencies(); - for (Module dependency : dependencies) { - if (!myScopeModules.contains(dependency)) { // may be in case of module circular dependencies - buildScopeModulesSet(dependency); - } - } + ModuleRootManager.getInstance(module).orderEntries().recursively().compileOnly().forEachModule(new CommonProcessors.CollectProcessor(myScopeModules)); } @NotNull diff --git a/java/compiler/impl/testSrc/com/intellij/compiler/BaseCompilerTestCase.java b/java/compiler/impl/testSrc/com/intellij/compiler/BaseCompilerTestCase.java index 40170c9b2605..5900ea2d6097 100644 --- a/java/compiler/impl/testSrc/com/intellij/compiler/BaseCompilerTestCase.java +++ b/java/compiler/impl/testSrc/com/intellij/compiler/BaseCompilerTestCase.java @@ -171,7 +171,15 @@ public abstract class BaseCompilerTestCase extends ModuleTestCase { } protected CompilationLog make(Module... modules) { - return make(getCompilerManager().createModulesCompileScope(modules, false), CompilerFilter.ALL); + return make(false, modules); + } + + protected CompilationLog makeWithDependencies(Module... modules) { + return make(true, modules); + } + + private CompilationLog make(boolean includeDependentModules, Module... modules) { + return make(getCompilerManager().createModulesCompileScope(modules, includeDependentModules), CompilerFilter.ALL); } protected CompilationLog recompile(Module... modules) { From 362b4d74fc0d87b97fd62f5277663d3e4970f7cb Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Tue, 22 Apr 2014 12:26:47 +0400 Subject: [PATCH 23/34] IDEA-123986 New project wizard: remove highlighting from disabled templates --- .../ide/projectWizard/ProjectTemplateList.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTemplateList.java b/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTemplateList.java index 3c6d456b420f..57918b1843d1 100644 --- a/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTemplateList.java +++ b/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTemplateList.java @@ -25,8 +25,8 @@ import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.text.StringUtil; import com.intellij.platform.ProjectTemplate; import com.intellij.platform.templates.ArchivedProjectTemplate; -import com.intellij.ui.*; -import com.intellij.ui.SingleSelectionModel; +import com.intellij.ui.CollectionListModel; +import com.intellij.ui.IdeBorderFactory; import com.intellij.ui.components.JBList; import com.intellij.ui.popup.list.GroupedItemsListRenderer; import com.intellij.util.containers.ContainerUtil; @@ -100,7 +100,6 @@ public class ProjectTemplateList extends JPanel { } }; myList.setCellRenderer(renderer); - myList.setSelectionModel(new SingleSelectionModel()); myList.getSelectionModel().addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { @@ -136,7 +135,9 @@ public class ProjectTemplateList extends JPanel { int index = preserveSelection ? myList.getSelectedIndex() : -1; //noinspection unchecked myList.setModel(new CollectionListModel(list)); - myList.setSelectedIndex(index == -1 ? 0 : index); + if (myList.isEnabled()) { + myList.setSelectedIndex(index == -1 ? 0 : index); + } updateSelection(); } @@ -149,6 +150,12 @@ public class ProjectTemplateList extends JPanel { public void setEnabled(boolean enabled) { super.setEnabled(enabled); myList.setEnabled(enabled); + if (!enabled) { + myList.clearSelection(); + } + else { + myList.setSelectedIndex(0); + } myDescriptionPane.setEnabled(enabled); } From cbb86d488d62f9489916c63b505cf01b5873eaa5 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Tue, 22 Apr 2014 12:50:48 +0400 Subject: [PATCH 24/34] IDEA-124021 Cannot resolve method 'print(int)' with JSP custom tags: comment --- .../codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index 1216b358b869..f386b646f1e4 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -553,6 +553,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh if ((!result.isAccessible() || !result.isStaticsScopeCorrect()) && !HighlightMethodUtil.isDummyConstructorCall(expression, myResolveHelper, list, referenceExpression) && + // this check is for fake expression from JspMethodCallImpl referenceExpression.getParent() == expression) { try { myHolder.add(HighlightMethodUtil.checkAmbiguousMethodCallArguments(referenceExpression, results, list, resolved, result, expression, myResolveHelper)); From cb20ef57f4b655116b7845e9b491e95dc783f538 Mon Sep 17 00:00:00 2001 From: Sergey Simonchik Date: Tue, 22 Apr 2014 13:19:11 +0400 Subject: [PATCH 25/34] Prevent horizontal scrollbar appearance if an error message is too long --- .../packaging/PackagesNotificationPanel.java | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java index 679674523d8f..0795c482f0f2 100644 --- a/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java @@ -22,7 +22,7 @@ import java.util.Map; * @author yole */ public class PackagesNotificationPanel { - private final JEditorPane myEditorPane = new JEditorPane(); + private final JEditorPane myEditorPane = new MyNotificationPane(); private final Project myProject; private final Map myLinkHandlers = new HashMap(); private String myErrorTitle; @@ -91,6 +91,10 @@ public class PackagesNotificationPanel { myLinkHandlers.put(key, handler); } + public void removeAllLinkHandlers() { + myLinkHandlers.clear(); + } + public JComponent getComponent() { return myEditorPane; } @@ -101,7 +105,8 @@ public class PackagesNotificationPanel { private void showContent(String text, final Color background) { myEditorPane.removeAll(); - myEditorPane.setText(UIUtil.toHtml(text)); + String htmlText = text.startsWith("") ? text : UIUtil.toHtml(text); + myEditorPane.setText(htmlText); myEditorPane.setBackground(background); myEditorPane.setVisible(true); myErrorTitle = null; @@ -125,4 +130,15 @@ public class PackagesNotificationPanel { public boolean hasLinkHandler(String key) { return myLinkHandlers.containsKey(key); } + + private static class MyNotificationPane extends JEditorPane { + @Override + public Dimension getPreferredSize() { + // This trick makes text component to carry text over to the next line + // iff the text line width exceeds parent's width + Dimension dimension = super.getPreferredSize(); + dimension.width = 0; + return dimension; + } + } } From 97a6a21c7c3af552ce4c1077464706c3882c5ef0 Mon Sep 17 00:00:00 2001 From: Sergey Simonchik Date: Tue, 22 Apr 2014 13:20:48 +0400 Subject: [PATCH 26/34] possible race condition fixed --- .../packaging/InstalledPackagesPanel.java | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java b/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java index 5f5fecfa8a6e..eb2936a058d5 100644 --- a/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java @@ -44,7 +44,8 @@ public class InstalledPackagesPanel extends JPanel { protected final JBTable myPackagesTable; private DefaultTableModel myPackagesTableModel; - protected PackageManagementService myPackageManagementService; + // can be accessed from any thread + protected volatile PackageManagementService myPackageManagementService; protected final Project myProject; protected final PackagesNotificationPanel myNotificationArea; protected final List> myPathChangedListeners = ContainerUtil.createLockFreeCopyOnWriteList(); @@ -424,7 +425,7 @@ public class InstalledPackagesPanel extends JPanel { final boolean shouldFetchLatestVersionsForOnlyInstalledPackages = shouldFetchLatestVersionsForOnlyInstalledPackages(); if (cache.isEmpty()) { if (!shouldFetchLatestVersionsForOnlyInstalledPackages) { - refreshLatestVersions(); + refreshLatestVersions(packageManagementService); } } UIUtil.invokeLaterIfNeeded(new Runnable() { @@ -514,28 +515,30 @@ public class InstalledPackagesPanel extends JPanel { return false; } - private void refreshLatestVersions() { + private void refreshLatestVersions(@NotNull final PackageManagementService packageManagementService) { final Application application = ApplicationManager.getApplication(); application.executeOnPooledThread(new Runnable() { @Override public void run() { - try { - List packages = myPackageManagementService.reloadAllPackages(); - final Map packageMap = buildNameToPackageMap(packages); - application.invokeLater(new Runnable() { - @Override - public void run() { - for (int i = 0; i != myPackagesTableModel.getRowCount(); ++i) { - final InstalledPackage pyPackage = (InstalledPackage)myPackagesTableModel.getValueAt(i, 0); - final RepoPackage repoPackage = packageMap.get(pyPackage.getName()); - myPackagesTableModel.setValueAt(repoPackage == null ? null : repoPackage.getLatestVersion(), i, 2); + if (packageManagementService == myPackageManagementService) { + try { + List packages = packageManagementService.reloadAllPackages(); + final Map packageMap = buildNameToPackageMap(packages); + application.invokeLater(new Runnable() { + @Override + public void run() { + for (int i = 0; i != myPackagesTableModel.getRowCount(); ++i) { + final InstalledPackage pyPackage = (InstalledPackage)myPackagesTableModel.getValueAt(i, 0); + final RepoPackage repoPackage = packageMap.get(pyPackage.getName()); + myPackagesTableModel.setValueAt(repoPackage == null ? null : repoPackage.getLatestVersion(), i, 2); + } + myPackagesTable.setPaintBusy(false); } - myPackagesTable.setPaintBusy(false); - } - }, ModalityState.stateForComponent(myPackagesTable)); - } - catch (IOException ignored) { - myPackagesTable.setPaintBusy(false); + }, ModalityState.stateForComponent(myPackagesTable)); + } + catch (IOException ignored) { + myPackagesTable.setPaintBusy(false); + } } } }); From 2e517c5603851661baf805654084bc48be9eaeeb Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Tue, 22 Apr 2014 13:26:13 +0400 Subject: [PATCH 27/34] IDEA-112050 vcs: fix ByteBuffer to array conversion --- .../intellij/openapi/vcs/vfs/ContentRevisionVirtualFile.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/vfs/ContentRevisionVirtualFile.java b/platform/vcs-api/src/com/intellij/openapi/vcs/vfs/ContentRevisionVirtualFile.java index 6f5a84881cbd..71626fa32e61 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/vfs/ContentRevisionVirtualFile.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/vfs/ContentRevisionVirtualFile.java @@ -84,8 +84,7 @@ public class ContentRevisionVirtualFile extends AbstractVcsVirtualFile { myModificationStamp++; setRevision(myContentRevision.getRevisionNumber().asString()); - final ByteBuffer byteBuffer = getCharset().encode(content); - myContent = byteBuffer.compact().array(); + myContent = content.getBytes(getCharset()); ApplicationManager.getApplication().runWriteAction(new Runnable() { public void run() { vcsFileSystem.fireContentsChanged(this, ContentRevisionVirtualFile.this, 0); From 147c6475231909249aaeef4fb3796481a1da9574 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Tue, 22 Apr 2014 09:37:29 +0200 Subject: [PATCH 28/34] use ValueType enum instead of plain numbers --- .../src/org/jetbrains/debugger/values/ValueType.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/platform/script-debugger/backend/src/org/jetbrains/debugger/values/ValueType.java b/platform/script-debugger/backend/src/org/jetbrains/debugger/values/ValueType.java index bd24b5b971e8..06da6c1fdc5e 100644 --- a/platform/script-debugger/backend/src/org/jetbrains/debugger/values/ValueType.java +++ b/platform/script-debugger/backend/src/org/jetbrains/debugger/values/ValueType.java @@ -15,14 +15,7 @@ public enum ValueType { ARRAY, NODE, - /** - * undefined type. - */ UNDEFINED, - - /** - * null type. This is a bogus type that doesn't exist in JavaScript. - */ NULL; private static final ValueType[] VALUE_TYPES = ValueType.values(); From 3e576c2cb0aff622ddda9633a8dc611f0b9a3136 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Tue, 22 Apr 2014 15:03:59 +0400 Subject: [PATCH 29/34] fixed EA-55872 - NPE: PyProtectedMemberInspection$Visitor.checkReference --- .../python/inspections/PyProtectedMemberInspection.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java b/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java index e766bc00a7bf..bc46d71e8f78 100644 --- a/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java @@ -81,7 +81,10 @@ public class PyProtectedMemberInspection extends PyInspection { public void visitPyImportElement(PyImportElement node) { final PyStatement statement = node.getContainingImportStatement(); if (!(statement instanceof PyFromImportStatement)) return; - checkReference(node.getImportReferenceExpression(), ((PyFromImportStatement)statement).getImportSource()); + final PyReferenceExpression importReferenceExpression = node.getImportReferenceExpression(); + final PyReferenceExpression importSource = ((PyFromImportStatement)statement).getImportSource(); + if (importReferenceExpression != null && importSource != null) + checkReference(importReferenceExpression, importSource); } @Override @@ -91,7 +94,7 @@ public class PyProtectedMemberInspection extends PyInspection { checkReference(node, qualifier); } - private void checkReference(PyReferenceExpression node, PyExpression qualifier) { + private void checkReference(@NotNull final PyReferenceExpression node, @NotNull final PyExpression qualifier) { if (myTypeEvalContext.getType(qualifier) instanceof PyNamedTupleType) return; final String name = node.getName(); final List quickFixes = new ArrayList(); From c9a1c11f9c2b56914dcfc09ffd738d5e63f8a4b6 Mon Sep 17 00:00:00 2001 From: "Gregory.Shrago" Date: Tue, 22 Apr 2014 14:42:24 +0400 Subject: [PATCH 30/34] apply laf once, fix stale bg, add beforeShown() & preselect laf-default editor scheme --- .../ide/customize/AbstractCustomizeWizardStep.java | 12 ++++++++++-- .../ide/customize/CustomizeUIThemeStepPanel.java | 9 --------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ide/customize/AbstractCustomizeWizardStep.java b/platform/platform-impl/src/com/intellij/ide/customize/AbstractCustomizeWizardStep.java index af703fa4bfef..5b57ca8d4787 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/AbstractCustomizeWizardStep.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/AbstractCustomizeWizardStep.java @@ -46,18 +46,23 @@ public abstract class AbstractCustomizeWizardStep extends JPanel { return anchorButton.isSelected() ? getSelectionBackground() : super.getBackground(); } }; - panel.setOpaque(true); + panel.setOpaque(anchorButton.isSelected()); new ClickListener() { @Override public boolean onClick(@NotNull MouseEvent event, int clickCount) { anchorButton.setSelected(true); - action.run(); return true; } }.installOn(panel); anchorButton.addItemListener(new ItemListener() { + boolean curState = anchorButton.isSelected(); @Override public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED && curState != anchorButton.isSelected()) { + action.run(); + } + curState = anchorButton.isSelected(); + panel.setOpaque(curState); panel.repaint(); } }); @@ -67,4 +72,7 @@ public abstract class AbstractCustomizeWizardStep extends JPanel { Component getDefaultFocusedComponent() { return null; } + + public void beforeShown(boolean forward) { + } } diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java index 84329788785b..91e2625c3a6b 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java @@ -31,8 +31,6 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; import java.util.LinkedHashMap; import java.util.Map; @@ -102,13 +100,6 @@ public class CustomizeUIThemeStepPanel extends AbstractCustomizeWizardStep { }; label.setVerticalAlignment(SwingConstants.TOP); panel.add(label, BorderLayout.CENTER); - radioButton.addItemListener(new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { - if (e.getStateChange() != ItemEvent.SELECTED) return; - applyLaf(lafName, CustomizeUIThemeStepPanel.this); - } - }); group.add(radioButton); buttonsPanel.add(panel); From d62771184d914c7fedba430f07664e8ed967ada9 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Tue, 22 Apr 2014 15:46:17 +0400 Subject: [PATCH 31/34] fixed EA-55749 - AIOOBE: PyStructuredDocstringFormatter.formatDocstring --- .../documentation/PyStructuredDocstringFormatter.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/documentation/PyStructuredDocstringFormatter.java b/python/src/com/jetbrains/python/documentation/PyStructuredDocstringFormatter.java index 964d5b1715dc..b439a54f3012 100644 --- a/python/src/com/jetbrains/python/documentation/PyStructuredDocstringFormatter.java +++ b/python/src/com/jetbrains/python/documentation/PyStructuredDocstringFormatter.java @@ -15,6 +15,7 @@ */ package com.jetbrains.python.documentation; +import com.google.common.collect.Lists; import com.intellij.execution.process.ProcessOutput; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; @@ -50,7 +51,11 @@ public class PyStructuredDocstringFormatter { @Nullable public static List formatDocstring(@NotNull final PsiElement element, @NotNull final String docstring) { Module module = ModuleUtilCore.findModuleForPsiElement(element); - if (module == null) module = ModuleManager.getInstance(element.getProject()).getModules()[0]; + if (module == null) { + final Module[] modules = ModuleManager.getInstance(element.getProject()).getModules(); + if (modules.length == 0) return Lists.newArrayList(); + module = modules[0]; + } final PyDocumentationSettings documentationSettings = PyDocumentationSettings.getInstance(module); final List result = new ArrayList(); From c08f50bfafe089a3509cb5abc8a224fac0f78f14 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Tue, 22 Apr 2014 15:47:08 +0400 Subject: [PATCH 32/34] fixed EA-54835 - NPE: PythonNoseTestConfigurationProducer.isAvailable --- .../testing/nosetest/PythonNoseTestConfigurationProducer.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python/src/com/jetbrains/python/testing/nosetest/PythonNoseTestConfigurationProducer.java b/python/src/com/jetbrains/python/testing/nosetest/PythonNoseTestConfigurationProducer.java index b7dc704ff4e8..ddbd90623080 100644 --- a/python/src/com/jetbrains/python/testing/nosetest/PythonNoseTestConfigurationProducer.java +++ b/python/src/com/jetbrains/python/testing/nosetest/PythonNoseTestConfigurationProducer.java @@ -41,8 +41,7 @@ public class PythonNoseTestConfigurationProducer extends module = modules[0]; } final Sdk sdk = PythonSdkType.findPythonSdk(module); - return (TestRunnerService.getInstance(module).getProjectConfiguration().equals( - PythonTestConfigurationsModel.PYTHONS_NOSETEST_NAME) && sdk != null); + return (PythonTestConfigurationsModel.PYTHONS_NOSETEST_NAME.equals(TestRunnerService.getInstance(module).getProjectConfiguration()) && sdk != null); } @Override From 9c69111454c7bd2dd24dceeb22b3113290c3ef23 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Mon, 21 Apr 2014 16:27:53 +0400 Subject: [PATCH 33/34] [vcs] Reuse the VirtualFile instead of creating FilePathImpl on File Relates to IDEA-119996 --- .../com/intellij/openapi/vcs/changes/ui/TreeModelBuilder.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/TreeModelBuilder.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/TreeModelBuilder.java index ba022cbc282c..48a17aeaa31a 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/TreeModelBuilder.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/TreeModelBuilder.java @@ -504,7 +504,8 @@ public class TreeModelBuilder { ChangesBrowserNode parentNode = myFoldersCache.get(parentPath.getKey()); if (parentNode == null) { - parentNode = ChangesBrowserNode.create(myProject, new FilePathImpl(new File(parentPath.getPath()), true)); + FilePathImpl filePath = parentPath.getVf() == null ? new FilePathImpl(new File(parentPath.getPath()), true) : new FilePathImpl(parentPath.getVf()); + parentNode = ChangesBrowserNode.create(myProject, filePath); ChangesBrowserNode grandPa = getParentNodeFor(parentPath, policy, rootNode); model.insertNodeInto(parentNode, grandPa, grandPa.getChildCount()); myFoldersCache.put(parentPath.getKey(), parentNode); From 5248eb09058228bd71bf79be0ae5ed445b6da476 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Tue, 22 Apr 2014 15:43:06 +0400 Subject: [PATCH 34/34] IDEA-119996 FilePathImpl: avoid VFS refresh from getVirtualFile etc. Actually, a revert of e31663b & ee19cc4 & 57c4367. There is the public refresh() methods for those who needs to refresh. Otherwise getPath(), isDirectory() and getVirtualFile() itself are accessed from too many places and too often, and clients are not aware that it may be costly to call this method frequently. --- .../intellij/openapi/vcs/FilePathImpl.java | 171 +++++++++--------- 1 file changed, 86 insertions(+), 85 deletions(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/FilePathImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/FilePathImpl.java index 516508176fe4..5c8e0abac81f 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/FilePathImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/FilePathImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2012 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. @@ -62,8 +62,7 @@ public class FilePathImpl implements FilePath { } } - @NotNull - private static File fileFromVirtual(VirtualFile virtualParent, final VirtualFile child, @NotNull String name) { + private static File fileFromVirtual(VirtualFile virtualParent, final VirtualFile child, String name) { assert virtualParent != null || child != null; if (virtualParent != null) { return new File(virtualParent.getPath(), name); @@ -72,12 +71,12 @@ public class FilePathImpl implements FilePath { } @Heavy - public FilePathImpl(@NotNull VirtualFile virtualParent, @NotNull String name, final boolean isDirectory) { + public FilePathImpl(@NotNull VirtualFile virtualParent, String name, final boolean isDirectory) { this(virtualParent, name, isDirectory, null, false); } @Heavy - private FilePathImpl(@NotNull VirtualFile virtualParent, @NotNull String name, final boolean isDirectory, final boolean forDeleted) { + private FilePathImpl(@NotNull VirtualFile virtualParent, String name, final boolean isDirectory, final boolean forDeleted) { this(virtualParent, name, isDirectory, null, forDeleted); } @@ -96,15 +95,15 @@ public class FilePathImpl implements FilePath { this(virtualFile.getParent(), virtualFile.getName(), virtualFile.isDirectory(), virtualFile, false); } - @NotNull public FilePath createChild(final String subPath, final boolean isDirectory) { if (StringUtil.isEmptyOrSpaces(subPath)) return this; - VirtualFile virtualFile = getVirtualFile(); - if (virtualFile != null && subPath.indexOf('/') == -1 && subPath.indexOf('\\') == -1) { - return new FilePathImpl(virtualFile, subPath, isDirectory, true); + if (getVirtualFile() != null && subPath.indexOf('/') == -1 && subPath.indexOf('\\') == -1) { + return new FilePathImpl(getVirtualFile(), subPath, isDirectory, true); + } + else { + return new FilePathImpl(new File(getIOFile(), subPath), isDirectory); } - return new FilePathImpl(new File(getIOFile(), subPath), isDirectory); } public int hashCode() { @@ -115,13 +114,13 @@ public class FilePathImpl implements FilePath { if (!(o instanceof FilePath)) { return false; } - if (!isSpecialName(myName) && !isSpecialName(((FilePath)o).getName()) && - !Comparing.equal(myName, ((FilePath)o).getName())) { - return false; + else { + if (! isSpecialName(myName) && ! isSpecialName(((FilePath)o).getName()) && + ! Comparing.equal(myName, ((FilePath)o).getName())) return false; + return myFile.equals(((FilePath)o).getIOFile()); } - return myFile.equals(((FilePath)o).getIOFile()); } - + private static boolean isSpecialName(final String name) { return ".".equals(name) || "..".equals(name); } @@ -129,29 +128,31 @@ public class FilePathImpl implements FilePath { @Override public void refresh() { if (myLocal) { - VirtualFile virtualParent = getVirtualFileParent(); - myVirtualFile = virtualParent == null ? LocalFileSystem.getInstance().findFileByIoFile(myFile) : virtualParent.findChild(myName); + if (myVirtualParent == null) { + myVirtualFile = LocalFileSystem.getInstance().findFileByIoFile(myFile); + } + else { + myVirtualFile = myVirtualParent.findChild(myName); + } } } @Override public void hardRefresh() { - if (myLocal) { - VirtualFile virtualFile = getVirtualFile(); - if (virtualFile == null || !virtualFile.isValid()) { - myVirtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(myFile); - } + if (myLocal && (myVirtualFile == null || ! myVirtualFile.isValid())) { + myVirtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(myFile); } } - @NotNull @Override public String getPath() { - final VirtualFile virtualFile = getVirtualFile(); + final VirtualFile virtualFile = myVirtualFile; if (virtualFile != null && virtualFile.isValid()) { return virtualFile.getPath(); } - return myFile.getPath(); + else { + return myFile.getPath(); + } } public void setIsDirectory(boolean isDirectory) { @@ -160,17 +161,20 @@ public class FilePathImpl implements FilePath { @Override public boolean isDirectory() { - VirtualFile virtualFile = getVirtualFile(); - return virtualFile == null ? myIsDirectory : virtualFile.isDirectory(); + if (myVirtualFile == null) { + return myIsDirectory; + } + else { + return myVirtualFile.isDirectory(); + } } @Override - public boolean isUnder(@NotNull FilePath parent, boolean strict) { - VirtualFile virtualFile = getVirtualFile(); - if (virtualFile != null) { + public boolean isUnder(FilePath parent, boolean strict) { + if (myVirtualFile != null) { final VirtualFile parentFile = parent.getVirtualFile(); if (parentFile != null) { - return VfsUtilCore.isAncestor(parentFile, virtualFile, strict); + return VfsUtilCore.isAncestor(parentFile, myVirtualFile, strict); } } return FileUtil.isAncestor(parent.getIOFile(), getIOFile(), strict); @@ -178,9 +182,8 @@ public class FilePathImpl implements FilePath { @Override public FilePath getParentPath() { - VirtualFile virtualParent = getVirtualFileParent(); - if (virtualParent != null && virtualParent.getParent() != null) { - return new FilePathImpl(virtualParent); + if (myVirtualParent != null && myVirtualParent.isValid() && myVirtualParent.getParent() != null) { + return new FilePathImpl(myVirtualParent); } // can't use File.getParentPath() because the path may not correspond to an actual file on disk, @@ -197,25 +200,19 @@ public class FilePathImpl implements FilePath { @Override @Nullable public VirtualFile getVirtualFile() { - VirtualFile virtualFile = myVirtualFile; - if (virtualFile != null && !virtualFile.isValid()) { - myVirtualFile = virtualFile = null; + if (myVirtualFile != null && !myVirtualFile.isValid()) { + myVirtualFile = null; } - if (virtualFile == null) { - refresh(); - virtualFile = myVirtualFile; - } - return virtualFile; + return myVirtualFile; } @Override @Nullable public VirtualFile getVirtualFileParent() { - VirtualFile virtualParent = myVirtualParent; - if (virtualParent != null && !virtualParent.isValid()) { - myVirtualParent = virtualParent = null; + if (myVirtualParent != null && !myVirtualParent.isValid()) { + myVirtualParent = null; } - return virtualParent; + return myVirtualParent; } @Override @@ -224,7 +221,6 @@ public class FilePathImpl implements FilePath { return myFile; } - @NotNull @Override public String getName() { return myName; @@ -232,21 +228,21 @@ public class FilePathImpl implements FilePath { @Override public String getPresentableUrl() { - VirtualFile virtualFile = getVirtualFile(); - if (virtualFile == null || !virtualFile.isValid()) { + if (myVirtualFile == null || !myVirtualFile.isValid()) { return myFile.getAbsolutePath(); } - return virtualFile.getPresentableUrl(); + else { + return myVirtualFile.getPresentableUrl(); + } } @Override @Nullable public Document getDocument() { - VirtualFile virtualFile = getVirtualFile(); - if (virtualFile == null || virtualFile.getFileType().isBinary()) { + if (myVirtualFile == null || myVirtualFile.getFileType().isBinary()) { return null; } - return FileDocumentManager.getInstance().getDocument(virtualFile); + return FileDocumentManager.getInstance().getDocument(myVirtualFile); } @Override @@ -257,8 +253,7 @@ public class FilePathImpl implements FilePath { @Override public Charset getCharset(Project project) { // try to find existing virtual file - VirtualFile virtualFile = getVirtualFile(); - VirtualFile existing = virtualFile != null && virtualFile.isValid() ? virtualFile : null; + VirtualFile existing = myVirtualFile != null && myVirtualFile.isValid() ? myVirtualFile : null; if (existing == null) { LocalFileSystem lfs = LocalFileSystem.getInstance(); for (File f = myFile; f != null; f = f.getParentFile()) { @@ -283,8 +278,7 @@ public class FilePathImpl implements FilePath { @Override public FileType getFileType() { - VirtualFile virtualFile = getVirtualFile(); - return virtualFile != null ? virtualFile.getFileType() : FileTypeManager.getInstance().getFileTypeByFileName(myFile.getName()); + return myVirtualFile != null ? myVirtualFile.getFileType() : FileTypeManager.getInstance().getFileTypeByFileName(myFile.getName()); } public static FilePathImpl create(VirtualFile file) { @@ -322,52 +316,59 @@ public class FilePathImpl implements FilePath { if (virtualFileParent != null) { return new FilePathImpl(virtualFileParent, selectedFile.getName(), isDirectory, true); } - return new FilePathImpl(selectedFile, isDirectory); + else { + return new FilePathImpl(selectedFile, isDirectory); + } } - public static FilePath createOn(@NotNull String s) { + public static FilePath createOn(String s) { File ioFile = new File(s); final LocalFileSystem localFileSystem = LocalFileSystem.getInstance(); VirtualFile virtualFile = localFileSystem.findFileByIoFile(ioFile); if (virtualFile != null) { return new FilePathImpl(virtualFile); } - VirtualFile virtualFileParent = localFileSystem.findFileByIoFile(ioFile.getParentFile()); - if (virtualFileParent == null) return null; - return new FilePathImpl(virtualFileParent, ioFile.getName(), false); + else { + VirtualFile virtualFileParent = localFileSystem.findFileByIoFile(ioFile.getParentFile()); + if (virtualFileParent != null) { + return new FilePathImpl(virtualFileParent, ioFile.getName(), false); + } + else { + return null; + } + } } - private static final Constructor ourFileStringConstructor; - static { - // avoid filename normalization (IDEADEV-10548) - Constructor constructor = null; // new File(String, int) - try { - constructor = File.class.getDeclaredConstructor(String.class, int.class); - constructor.setAccessible(true); - } - catch (Exception ignored) { - } - ourFileStringConstructor = constructor; - } + private static Constructor ourFileStringConstructor; + private static boolean ourFileStringConstructorInitialized; @NotNull - public static FilePath createNonLocal(@NotNull String path, final boolean directory) { + public static FilePath createNonLocal(String path, final boolean directory) { path = path.replace('/', File.separatorChar); - File file = createIoFile(path); - return new FilePathImpl(file, directory, false); - } - - @NotNull - private static File createIoFile(@NotNull String path) { - if (ourFileStringConstructor != null) { + // avoid filename normalization (IDEADEV-10548) + if (!ourFileStringConstructorInitialized) { + ourFileStringConstructorInitialized = true; try { - return ourFileStringConstructor.newInstance(path, 1); + ourFileStringConstructor = File.class.getDeclaredConstructor(String.class, int.class); + ourFileStringConstructor.setAccessible(true); } catch (Exception ex) { - // reflection call failed, try regular call + ourFileStringConstructor = null; } } - return new File(path); + File file = null; + try { + if (ourFileStringConstructor != null) { + file = ourFileStringConstructor.newInstance(path, 1); + } + } + catch (Exception ex) { + // reflection call failed, try regular call + } + if (file == null) { + file = new File(path); + } + return new FilePathImpl(file, directory, false); } @Override