diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/DefineParamsDefaultValueAction.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/DefineParamsDefaultValueAction.java index 7440f6feb0e8..8374967361d3 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/DefineParamsDefaultValueAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/DefineParamsDefaultValueAction.java @@ -15,24 +15,62 @@ */ package com.intellij.codeInsight.daemon.impl.quickfix; +import com.intellij.codeInsight.FileModificationService; +import com.intellij.codeInsight.generation.ClassMember; +import com.intellij.codeInsight.hint.HintManager; +import com.intellij.codeInsight.intention.LowPriorityAction; +import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; import com.intellij.codeInsight.intention.impl.ParameterClassMember; +import com.intellij.codeInsight.template.Template; +import com.intellij.codeInsight.template.TemplateBuilderImpl; +import com.intellij.codeInsight.template.impl.TextExpression; +import com.intellij.icons.AllIcons; import com.intellij.ide.util.MemberChooser; import com.intellij.lang.java.JavaLanguage; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Iconable; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; +import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; +import com.intellij.refactoring.util.RefactoringUtil; +import com.intellij.util.ArrayUtil; +import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import javax.swing.*; +import java.util.Arrays; +import java.util.HashSet; import java.util.List; /** * User: anna * Date: 8/2/12 */ -public class DefineParamsDefaultValueAction extends DelegateWithDefaultParamValueIntentionAction { +public class DefineParamsDefaultValueAction extends PsiElementBaseIntentionAction implements Iconable, LowPriorityAction { + private static final Logger LOG = Logger.getInstance(DefineParamsDefaultValueAction.class); + + @Override + public boolean startInWriteAction() { + return false; + } + + @NotNull + @Override + public String getFamilyName() { + return "Generate overloaded method with default parameter values"; + } + + @Override + public Icon getIcon(int flags) { + return AllIcons.Actions.RefactoringBulb; + } @Override public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) { @@ -56,20 +94,106 @@ public class DefineParamsDefaultValueAction extends DelegateWithDefaultParamValu return true; } - @Nullable @Override + public void invoke(@NotNull final Project project, final Editor editor, @NotNull PsiElement element) throws IncorrectOperationException { + final PsiParameter[] parameters = getParams(element); + if (parameters == null || parameters.length == 0) return; + final PsiMethod method = (PsiMethod)parameters[0].getDeclarationScope(); + final PsiMethod methodPrototype = generateMethodPrototype(method, parameters); + final PsiClass containingClass = method.getContainingClass(); + if (containingClass == null) return; + final PsiMethod existingMethod = containingClass.findMethodBySignature(methodPrototype, false); + if (existingMethod != null) { + editor.getCaretModel().moveToOffset(existingMethod.getTextOffset()); + HintManager.getInstance().showErrorHint(editor, (existingMethod.isConstructor() ? "Constructor" : "Method") + + " with the chosen signature already exists"); + return; + } + + if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return; + + Runnable runnable = () -> { + final PsiMethod prototype = (PsiMethod)containingClass.addBefore(methodPrototype, method); + RefactoringUtil.fixJavadocsForParams(prototype, new HashSet(Arrays.asList(prototype.getParameterList().getParameters()))); + TemplateBuilderImpl builder = new TemplateBuilderImpl(prototype); + + PsiCodeBlock body = prototype.getBody(); + final String callArgs = + "(" + StringUtil.join(method.getParameterList().getParameters(), psiParameter -> { + if (ArrayUtil.find(parameters, psiParameter) > -1) return "IntelliJIDEARulezzz"; + return psiParameter.getName(); + }, ",") + ");"; + final String methodCall; + if (method.getReturnType() == null) { + methodCall = "this"; + } else if (!PsiType.VOID.equals(method.getReturnType())) { + methodCall = "return " + method.getName(); + } else { + methodCall = method.getName(); + } + LOG.assertTrue(body != null); + body.add(JavaPsiFacade.getElementFactory(project).createStatementFromText(methodCall + callArgs, method)); + body = (PsiCodeBlock)CodeStyleManager.getInstance(project).reformat(body); + final PsiStatement stmt = body.getStatements()[0]; + PsiExpression expr = null; + if (stmt instanceof PsiReturnStatement) { + expr = ((PsiReturnStatement)stmt).getReturnValue(); + } else if (stmt instanceof PsiExpressionStatement) { + expr = ((PsiExpressionStatement)stmt).getExpression(); + } + if (expr instanceof PsiMethodCallExpression) { + PsiMethodCallExpression methodCallExp = (PsiMethodCallExpression)expr; + RangeMarker rangeMarker = editor.getDocument().createRangeMarker(prototype.getTextRange()); + for (PsiParameter parameter : parameters) { + final PsiExpression exprToBeDefault = + methodCallExp.getArgumentList().getExpressions()[method.getParameterList().getParameterIndex(parameter)]; + builder.replaceElement(exprToBeDefault, new TextExpression("")); + } + Template template = builder.buildTemplate(); + editor.getCaretModel().moveToOffset(rangeMarker.getStartOffset()); + + PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument()); + editor.getDocument().deleteString(rangeMarker.getStartOffset(), rangeMarker.getEndOffset()); + + rangeMarker.dispose(); + + CreateFromUsageBaseFix.startTemplate(editor, template, project); + } + }; + if (startInWriteAction()) { + runnable.run(); + } else { + ApplicationManager.getApplication().runWriteAction(runnable); + } + } + + @Nullable protected PsiParameter[] getParams(PsiElement element) { final PsiMethod method = PsiTreeUtil.getParentOfType(element, PsiMethod.class); assert method != null; final PsiParameter[] parameters = method.getParameterList().getParameters(); + if (parameters.length == 1) { + return parameters; + } final ParameterClassMember[] members = new ParameterClassMember[parameters.length]; for (int i = 0; i < members.length; i++) { members[i] = new ParameterClassMember(parameters[i]); } + final PsiParameter selectedParam = PsiTreeUtil.getParentOfType(element, PsiParameter.class); + final int idx = selectedParam != null ? ArrayUtil.find(parameters, selectedParam) : -1; + if (ApplicationManager.getApplication().isUnitTestMode()) { + return idx >= 0 ? new PsiParameter[] {selectedParam} : null; + } final MemberChooser chooser = new MemberChooser(members, false, true, element.getProject()); - chooser.selectElements(members); + if (idx >= 0) { + chooser.selectElements(new ClassMember[] {members[idx]}); + } + else { + chooser.selectElements(members); + } chooser.setTitle("Choose Default Value Parameters"); + chooser.setCopyJavadocVisible(false); if (chooser.showAndGet()) { final List elements = chooser.getSelectedElements(); if (elements != null) { @@ -83,14 +207,34 @@ public class DefineParamsDefaultValueAction extends DelegateWithDefaultParamValu return null; } - @Override - public boolean startInWriteAction() { - return false; - } + private static PsiMethod generateMethodPrototype(PsiMethod method, PsiParameter... params) { + final PsiMethod prototype = (PsiMethod)method.copy(); + final PsiCodeBlock body = prototype.getBody(); + final PsiCodeBlock emptyBody = JavaPsiFacade.getElementFactory(method.getProject()).createMethodFromText("void foo(){}", prototype).getBody(); + assert emptyBody != null; + if (body != null) { + body.replace(emptyBody); + } else { + prototype.getModifierList().setModifierProperty(PsiModifier.ABSTRACT, false); + prototype.addBefore(emptyBody, null); + } - @NotNull - @Override - public String getFamilyName() { - return "Generate overloaded method with default parameter values"; + final PsiClass aClass = method.getContainingClass(); + if (aClass != null && aClass.isInterface() && !method.hasModifierProperty(PsiModifier.STATIC)) { + prototype.getModifierList().setModifierProperty(PsiModifier.DEFAULT, true); + } + + final PsiParameterList parameterList = method.getParameterList(); + Arrays.sort(params, (p1, p2) -> { + final int parameterIndex1 = parameterList.getParameterIndex(p1); + final int parameterIndex2 = parameterList.getParameterIndex(p2); + return parameterIndex1 > parameterIndex2 ? -1 : 1; + }); + + for (PsiParameter param : params) { + final int parameterIndex = parameterList.getParameterIndex(param); + prototype.getParameterList().getParameters()[parameterIndex].delete(); + } + return prototype; } } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/DelegateWithDefaultParamValueIntentionAction.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/DelegateWithDefaultParamValueIntentionAction.java deleted file mode 100644 index d3bbc2bed94d..000000000000 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/DelegateWithDefaultParamValueIntentionAction.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright 2000-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.codeInsight.daemon.impl.quickfix; - -import com.intellij.codeInsight.FileModificationService; -import com.intellij.codeInsight.hint.HintManager; -import com.intellij.codeInsight.intention.LowPriorityAction; -import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; -import com.intellij.codeInsight.template.Template; -import com.intellij.codeInsight.template.TemplateBuilderImpl; -import com.intellij.codeInsight.template.impl.TextExpression; -import com.intellij.icons.AllIcons; -import com.intellij.lang.StdLanguages; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.RangeMarker; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Iconable; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.*; -import com.intellij.psi.codeStyle.CodeStyleManager; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiUtil; -import com.intellij.refactoring.util.RefactoringUtil; -import com.intellij.util.ArrayUtil; -import com.intellij.util.Function; -import com.intellij.util.IncorrectOperationException; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashSet; - -/** - * User: anna - */ -public class DelegateWithDefaultParamValueIntentionAction extends PsiElementBaseIntentionAction implements Iconable, LowPriorityAction { - @Override - public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) { - final PsiParameter parameter = PsiTreeUtil.getParentOfType(element, PsiParameter.class); - if (parameter != null) { - if (!parameter.getLanguage().isKindOf(StdLanguages.JAVA)) return false; - final PsiElement declarationScope = parameter.getDeclarationScope(); - if (declarationScope instanceof PsiMethod) { - final PsiMethod method = (PsiMethod)declarationScope; - final PsiClass containingClass = method.getContainingClass(); - if (containingClass != null && (!containingClass.isInterface() || PsiUtil.isLanguageLevel8OrHigher(method))) { - if (containingClass.findMethodBySignature(generateMethodPrototype(method, parameter), false) != null) { - return false; - } - setText("Generate overloaded " + (method.isConstructor() ? "constructor" : "method") + " with default parameter value"); - return true; - } - } - } - return false; - } - - @Override - public Icon getIcon(int flags) { - return AllIcons.Actions.RefactoringBulb; - } - - private static PsiMethod generateMethodPrototype(PsiMethod method, PsiParameter... params) { - final PsiMethod prototype = (PsiMethod)method.copy(); - final PsiCodeBlock body = prototype.getBody(); - final PsiCodeBlock emptyBody = JavaPsiFacade.getElementFactory(method.getProject()).createMethodFromText("void foo(){}", prototype).getBody(); - assert emptyBody != null; - if (body != null) { - body.replace(emptyBody); - } else { - prototype.getModifierList().setModifierProperty(PsiModifier.ABSTRACT, false); - prototype.addBefore(emptyBody, null); - } - - final PsiClass aClass = method.getContainingClass(); - if (aClass != null && aClass.isInterface() && !method.hasModifierProperty(PsiModifier.STATIC)) { - prototype.getModifierList().setModifierProperty(PsiModifier.DEFAULT, true); - } - - final PsiParameterList parameterList = method.getParameterList(); - Arrays.sort(params, (p1, p2) -> { - final int parameterIndex1 = parameterList.getParameterIndex(p1); - final int parameterIndex2 = parameterList.getParameterIndex(p2); - return parameterIndex1 > parameterIndex2 ? -1 : 1; - }); - - for (PsiParameter param : params) { - final int parameterIndex = parameterList.getParameterIndex(param); - prototype.getParameterList().getParameters()[parameterIndex].delete(); - } - return prototype; - } - - @Override - public void invoke(@NotNull final Project project, final Editor editor, @NotNull PsiElement element) throws IncorrectOperationException { - final PsiParameter[] parameters = getParams(element); - if (parameters == null || parameters.length == 0) return; - final PsiMethod method = (PsiMethod)parameters[0].getDeclarationScope(); - final PsiMethod methodPrototype = generateMethodPrototype(method, parameters); - final PsiMethod existingMethod = method.getContainingClass().findMethodBySignature(methodPrototype, false); - if (existingMethod != null) { - editor.getCaretModel().moveToOffset(existingMethod.getTextOffset()); - HintManager.getInstance().showErrorHint(editor, (existingMethod.isConstructor() ? "Constructor" : "Method") + - " with the chosen signature already exists"); - return; - } - - if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return; - - Runnable runnable = () -> { - - final PsiMethod prototype = (PsiMethod)method.getContainingClass().addBefore(methodPrototype, method); - RefactoringUtil.fixJavadocsForParams(prototype, new HashSet(Arrays.asList(prototype.getParameterList().getParameters()))); - TemplateBuilderImpl builder = new TemplateBuilderImpl(prototype); - - PsiCodeBlock body = prototype.getBody(); - final String callArgs = - "(" + StringUtil.join(method.getParameterList().getParameters(), psiParameter -> { - if (ArrayUtil.find(parameters, psiParameter) > -1) return "IntelliJIDEARulezzz"; - return psiParameter.getName(); - }, ",") + ");"; - final String methodCall; - if (method.getReturnType() == null) { - methodCall = "this"; - } else if (!PsiType.VOID.equals(method.getReturnType())) { - methodCall = "return " + method.getName(); - } else { - methodCall = method.getName(); - } - body.add(JavaPsiFacade.getElementFactory(project).createStatementFromText(methodCall + callArgs, method)); - body = (PsiCodeBlock)CodeStyleManager.getInstance(project).reformat(body); - final PsiStatement stmt = body.getStatements()[0]; - PsiExpression expr = null; - if (stmt instanceof PsiReturnStatement) { - expr = ((PsiReturnStatement)stmt).getReturnValue(); - } else if (stmt instanceof PsiExpressionStatement) { - expr = ((PsiExpressionStatement)stmt).getExpression(); - } - if (expr instanceof PsiMethodCallExpression) { - PsiMethodCallExpression methodCallExp = (PsiMethodCallExpression)expr; - RangeMarker rangeMarker = editor.getDocument().createRangeMarker(prototype.getTextRange()); - for (PsiParameter parameter : parameters) { - final PsiExpression exprToBeDefault = - methodCallExp.getArgumentList().getExpressions()[method.getParameterList().getParameterIndex(parameter)]; - builder.replaceElement(exprToBeDefault, new TextExpression("")); - } - Template template = builder.buildTemplate(); - editor.getCaretModel().moveToOffset(rangeMarker.getStartOffset()); - - PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument()); - editor.getDocument().deleteString(rangeMarker.getStartOffset(), rangeMarker.getEndOffset()); - - rangeMarker.dispose(); - - CreateFromUsageBaseFix.startTemplate(editor, template, project); - } - }; - if (startInWriteAction()) { - runnable.run(); - } else { - ApplicationManager.getApplication().runWriteAction(runnable); - } - } - - @Nullable - protected PsiParameter[] getParams(PsiElement element) { - return new PsiParameter[]{PsiTreeUtil.getParentOfType(element, PsiParameter.class)}; - } - - @NotNull - @Override - public String getFamilyName() { - return "Generate overloaded method with default parameter value"; - } -} diff --git a/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassHandler.java b/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassHandler.java index b263d49ddfdc..6a6146434d54 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassHandler.java @@ -63,7 +63,7 @@ public class ExtractClassHandler implements ElementsHandler { if (cannotRefactorMessage != null) { CommonRefactoringUtil.showErrorHint(project, editor, RefactorJBundle.message("cannot.perform.the.refactoring") + cannotRefactorMessage, - null, getHelpID()); + ExtractClassProcessor.REFACTORING_NAME, getHelpID()); return; } new ExtractClassDialog(containingClass, selectedMember).show(); diff --git a/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassProcessor.java b/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassProcessor.java index 1e92218a183e..b0f0236075f0 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassProcessor.java @@ -66,6 +66,7 @@ import java.util.*; public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor { private static final Logger logger = Logger.getInstance("com.siyeh.rpp.extractclass.ExtractClassProcessor"); + @NonNls public static final String REFACTORING_NAME = "Extract Delegate"; private final PsiClass sourceClass; private final List fields; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/after1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/after1.java index 89683098fc6b..d785f80ad35f 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/after1.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/after1.java @@ -1,4 +1,4 @@ -// "Generate overloaded method with default parameter value" "true" +// "Generate overloaded method with default parameter values" "true" class Test { void foo() { foo(); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterAbstractMethod.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterAbstractMethod.java index e72764fff0dc..768f01176512 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterAbstractMethod.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterAbstractMethod.java @@ -1,4 +1,4 @@ -// "Generate overloaded method with default parameter value" "true" +// "Generate overloaded method with default parameter values" "true" abstract class Test { int foo(boolean... args) { return foo(, args); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterCommentsInBody.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterCommentsInBody.java index 4844902fdf14..0bd44690c430 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterCommentsInBody.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterCommentsInBody.java @@ -1,4 +1,4 @@ -// "Generate overloaded method with default parameter value" "true" +// "Generate overloaded method with default parameter values" "true" class Test { int foo() { return foo(); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterConstructor.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterConstructor.java index b73c48ebd2df..1f3d305ec5b1 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterConstructor.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterConstructor.java @@ -1,4 +1,4 @@ -// "Generate overloaded constructor with default parameter value" "true" +// "Generate overloaded constructor with default parameter values" "true" class Test { Test() { this(); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterExistinMethod.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterExistinMethod.java new file mode 100644 index 000000000000..d1e063f7955a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterExistinMethod.java @@ -0,0 +1,6 @@ +// "Generate overloaded method with default parameter values" "true" +class Test { + void foo(){} + void foo(int ii){ + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterInterface.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterInterface.java index decd3f141818..7a3ddc85e93d 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterInterface.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterInterface.java @@ -1,4 +1,4 @@ -// "Generate overloaded method with default parameter value" "true" +// "Generate overloaded method with default parameter values" "true" interface Test { default void foo() { foo(); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterJavadoc.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterJavadoc.java index 3f15183133d7..697c0e305b6a 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterJavadoc.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterJavadoc.java @@ -1,4 +1,4 @@ -// "Generate overloaded method with default parameter value" "true" +// "Generate overloaded method with default parameter values" "true" class Test { /** */ diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterReturnValue.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterReturnValue.java index 1e89e83a3cb7..971da0d8e6da 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterReturnValue.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterReturnValue.java @@ -1,4 +1,4 @@ -// "Generate overloaded method with default parameter value" "true" +// "Generate overloaded method with default parameter values" "true" class Test { int foo() { return foo(); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterStaticMethodInInterface.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterStaticMethodInInterface.java index 01e1bdab551b..eb84d0998b75 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterStaticMethodInInterface.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterStaticMethodInInterface.java @@ -1,4 +1,4 @@ -// "Generate overloaded method with default parameter value" "true" +// "Generate overloaded method with default parameter values" "true" interface Test { static void foo() { foo(); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterTypeParams.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterTypeParams.java index 57fb3563e0ac..c392c5a86323 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterTypeParams.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterTypeParams.java @@ -1,4 +1,4 @@ -// "Generate overloaded method with default parameter value" "true" +// "Generate overloaded method with default parameter values" "true" class Test { int foo(boolean... args) { return foo(, args); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterVarargs.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterVarargs.java index 68e2eaeec564..d9878cc69b41 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterVarargs.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterVarargs.java @@ -1,4 +1,4 @@ -// "Generate overloaded method with default parameter value" "true" +// "Generate overloaded method with default parameter values" "true" class Test { int foo(boolean... args) { return foo(, args); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/before1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/before1.java index 48895a8251b2..558e6e4c68f6 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/before1.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/before1.java @@ -1,4 +1,4 @@ -// "Generate overloaded method with default parameter value" "true" +// "Generate overloaded method with default parameter values" "true" class Test { void foo(int ii){ } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeAbstractMethod.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeAbstractMethod.java index 320debae10f2..9080288f6fa8 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeAbstractMethod.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeAbstractMethod.java @@ -1,4 +1,4 @@ -// "Generate overloaded method with default parameter value" "true" +// "Generate overloaded method with default parameter values" "true" abstract class Test { abstract int foo(int ii, boolean... args); } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeCommentsInBody.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeCommentsInBody.java index 9a6ad6184854..97357c6b3e0e 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeCommentsInBody.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeCommentsInBody.java @@ -1,4 +1,4 @@ -// "Generate overloaded method with default parameter value" "true" +// "Generate overloaded method with default parameter values" "true" class Test { int foo(int ii){ //comment1 diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeConstructor.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeConstructor.java index 7fe236b65cf4..a8b8be8ba4b8 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeConstructor.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeConstructor.java @@ -1,4 +1,4 @@ -// "Generate overloaded constructor with default parameter value" "true" +// "Generate overloaded constructor with default parameter values" "true" class Test { Test(int ii){} } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeExistinMethod.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeExistinMethod.java index 52fa17401c5d..f8dbe503ccc7 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeExistinMethod.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeExistinMethod.java @@ -1,4 +1,4 @@ -// "Generate overloaded method with default parameter value" "false" +// "Generate overloaded method with default parameter values" "true" class Test { void foo(){} void foo(int ii){ diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeInterface.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeInterface.java index 1f960d28b21d..8217a2c642fe 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeInterface.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeInterface.java @@ -1,4 +1,4 @@ -// "Generate overloaded method with default parameter value" "true" +// "Generate overloaded method with default parameter values" "true" interface Test { void foo(int ii); } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeJavadoc.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeJavadoc.java index 267cdf9f901a..c4906ebcfafb 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeJavadoc.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeJavadoc.java @@ -1,4 +1,4 @@ -// "Generate overloaded method with default parameter value" "true" +// "Generate overloaded method with default parameter values" "true" class Test { /** * @param i diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeReturnValue.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeReturnValue.java index 4df7057f4e31..056d8badf78b 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeReturnValue.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeReturnValue.java @@ -1,4 +1,4 @@ -// "Generate overloaded method with default parameter value" "true" +// "Generate overloaded method with default parameter values" "true" class Test { int foo(int ii){ return 1; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeStaticMethodInInterface.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeStaticMethodInInterface.java index 5ea839e503a6..f2026953d9f3 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeStaticMethodInInterface.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeStaticMethodInInterface.java @@ -1,4 +1,4 @@ -// "Generate overloaded method with default parameter value" "true" +// "Generate overloaded method with default parameter values" "true" interface Test { static void foo(int ii) {} } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeTypeParams.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeTypeParams.java index e55000a24f37..4ab9ec7459fd 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeTypeParams.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeTypeParams.java @@ -1,4 +1,4 @@ -// "Generate overloaded method with default parameter value" "true" +// "Generate overloaded method with default parameter values" "true" class Test { int foo(T ii, boolean... args){ return 1; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeVarargs.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeVarargs.java index 6ddbc5a9069c..7f6628882710 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeVarargs.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/beforeVarargs.java @@ -1,4 +1,4 @@ -// "Generate overloaded method with default parameter value" "true" +// "Generate overloaded method with default parameter values" "true" class Test { int foo(int ii, boolean... args){ return 1; diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/DelegateWithDefaultParamValueTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/DelegateWithDefaultParamValueTest.java index c53bb0085d87..eb9825dd8c70 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/DelegateWithDefaultParamValueTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/DelegateWithDefaultParamValueTest.java @@ -30,8 +30,9 @@ public class DelegateWithDefaultParamValueTest extends LightQuickFixParameterize if (actionShouldBeAvailable) { TemplateState state = TemplateManagerImpl.getTemplateState(getEditor()); - assert state != null; - state.gotoEnd(false); + if (state != null) { + state.gotoEnd(false); + } } } diff --git a/platform/diff-api/src/com/intellij/diff/comparison/ComparisonManager.java b/platform/diff-api/src/com/intellij/diff/comparison/ComparisonManager.java index cd0915a0d435..30f8a8f7d35e 100644 --- a/platform/diff-api/src/com/intellij/diff/comparison/ComparisonManager.java +++ b/platform/diff-api/src/com/intellij/diff/comparison/ComparisonManager.java @@ -17,6 +17,7 @@ package com.intellij.diff.comparison; import com.intellij.diff.fragments.DiffFragment; import com.intellij.diff.fragments.LineFragment; +import com.intellij.diff.fragments.MergeLineFragment; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.progress.ProgressIndicator; import org.jetbrains.annotations.NotNull; @@ -61,6 +62,16 @@ public abstract class ComparisonManager { @NotNull ComparisonPolicy policy, @NotNull ProgressIndicator indicator) throws DiffTooBigException; + /** + * Compare three texts by-line (LEFT - BASE - RIGHT) + */ + @NotNull + public abstract List compareLines(@NotNull CharSequence text1, + @NotNull CharSequence text2, + @NotNull CharSequence text3, + @NotNull ComparisonPolicy policy, + @NotNull ProgressIndicator indicator) throws DiffTooBigException; + /** * Compare two texts by-word */ diff --git a/platform/diff-impl/src/com/intellij/diff/fragments/MergeLineFragment.java b/platform/diff-api/src/com/intellij/diff/fragments/MergeLineFragment.java similarity index 100% rename from platform/diff-impl/src/com/intellij/diff/fragments/MergeLineFragment.java rename to platform/diff-api/src/com/intellij/diff/fragments/MergeLineFragment.java diff --git a/platform/diff-impl/src/com/intellij/diff/fragments/MergeWordFragment.java b/platform/diff-api/src/com/intellij/diff/fragments/MergeWordFragment.java similarity index 100% rename from platform/diff-impl/src/com/intellij/diff/fragments/MergeWordFragment.java rename to platform/diff-api/src/com/intellij/diff/fragments/MergeWordFragment.java diff --git a/platform/diff-impl/src/com/intellij/diff/comparison/ByLine.java b/platform/diff-impl/src/com/intellij/diff/comparison/ByLine.java index f204eafde823..ae75bbaf8f33 100644 --- a/platform/diff-impl/src/com/intellij/diff/comparison/ByLine.java +++ b/platform/diff-impl/src/com/intellij/diff/comparison/ByLine.java @@ -15,13 +15,8 @@ */ package com.intellij.diff.comparison; -import com.intellij.diff.comparison.iterables.DiffIterableUtil.*; +import com.intellij.diff.comparison.iterables.DiffIterableUtil.ExpandChangeBuilder; import com.intellij.diff.comparison.iterables.FairDiffIterable; -import com.intellij.diff.fragments.LineFragment; -import com.intellij.diff.fragments.LineFragmentImpl; -import com.intellij.diff.fragments.MergeLineFragment; -import com.intellij.diff.fragments.MergeLineFragmentImpl; -import com.intellij.diff.util.IntPair; import com.intellij.diff.util.MergeRange; import com.intellij.diff.util.Range; import com.intellij.openapi.progress.ProgressIndicator; @@ -29,68 +24,77 @@ import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.containers.ContainerUtil; +import gnu.trove.Equality; import gnu.trove.TIntArrayList; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; +import static com.intellij.diff.comparison.ComparisonPolicy.IGNORE_WHITESPACES; import static com.intellij.diff.comparison.TrimUtil.trimEnd; import static com.intellij.diff.comparison.TrimUtil.trimStart; -import static com.intellij.diff.comparison.iterables.DiffIterableUtil.*; +import static com.intellij.diff.comparison.iterables.DiffIterableUtil.diff; +import static com.intellij.diff.comparison.iterables.DiffIterableUtil.fair; import static com.intellij.openapi.util.text.StringUtil.isWhiteSpace; public class ByLine { @NotNull - public static List compare(@NotNull CharSequence text1, - @NotNull CharSequence text2, - @NotNull ComparisonPolicy policy, - @NotNull ProgressIndicator indicator) { + public static FairDiffIterable compare(@NotNull List lines1, + @NotNull List lines2, + @NotNull ComparisonPolicy policy, + @NotNull ProgressIndicator indicator) { indicator.checkCanceled(); - - List lines1 = getLines(text1, policy); - List lines2 = getLines(text2, policy); - - FairDiffIterable changes = compareSmart(lines1, lines2, indicator); - changes = optimizeLineChunks(lines1, lines2, changes, indicator); - changes = expandRanges(lines1, lines2, changes, indicator); - return convertIntoFragments(lines1, lines2, changes); + return doCompare(getLines(lines1, policy), getLines(lines2, policy), policy, indicator); } @NotNull - public static List compareTwoStep(@NotNull CharSequence text1, - @NotNull CharSequence text2, - @NotNull ComparisonPolicy policy, - @NotNull ProgressIndicator indicator) { + public static List compare(@NotNull List lines1, + @NotNull List lines2, + @NotNull List lines3, + @NotNull ComparisonPolicy policy, + @NotNull ProgressIndicator indicator) { + indicator.checkCanceled(); + return doCompare(getLines(lines1, policy), getLines(lines2, policy), getLines(lines3, policy), policy, indicator); + } + + // + // Impl + // + + @NotNull + static FairDiffIterable doCompare(@NotNull List lines1, + @NotNull List lines2, + @NotNull ComparisonPolicy policy, + @NotNull ProgressIndicator indicator) { indicator.checkCanceled(); - List lines1 = getLines(text1, policy); - List lines2 = getLines(text2, policy); + if (policy == IGNORE_WHITESPACES) { + FairDiffIterable changes = compareSmart(lines1, lines2, indicator); + changes = optimizeLineChunks(lines1, lines2, changes, indicator); + return correctChangesSecondStepIW(lines1, lines2, changes); + } + else { + List iwLines1 = convertMode(lines1, IGNORE_WHITESPACES); + List iwLines2 = convertMode(lines2, IGNORE_WHITESPACES); - List iwLines1 = convertToIgnoreWhitespace(lines1); - List iwLines2 = convertToIgnoreWhitespace(lines2); - - FairDiffIterable iwChanges = compareSmart(iwLines1, iwLines2, indicator); - iwChanges = optimizeLineChunks(lines1, lines2, iwChanges, indicator); - FairDiffIterable changes = correctChangesSecondStep(lines1, lines2, iwChanges); - return convertIntoFragments(lines1, lines2, changes); + FairDiffIterable iwChanges = compareSmart(iwLines1, iwLines2, indicator); + iwChanges = optimizeLineChunks(lines1, lines2, iwChanges, indicator); + return correctChangesSecondStep(lines1, lines2, iwChanges); + } } @NotNull - public static List compareTwoStep(@NotNull CharSequence text1, - @NotNull CharSequence text2, - @NotNull CharSequence text3, - @NotNull ComparisonPolicy policy, - @NotNull ProgressIndicator indicator) { + static List doCompare(@NotNull List lines1, + @NotNull List lines2, + @NotNull List lines3, + @NotNull ComparisonPolicy policy, + @NotNull ProgressIndicator indicator) { indicator.checkCanceled(); - List lines1 = getLines(text1, policy); - List lines2 = getLines(text2, policy); - List lines3 = getLines(text3, policy); - - List iwLines1 = convertToIgnoreWhitespace(lines1); - List iwLines2 = convertToIgnoreWhitespace(lines2); - List iwLines3 = convertToIgnoreWhitespace(lines3); + List iwLines1 = convertMode(lines1, IGNORE_WHITESPACES); + List iwLines2 = convertMode(lines2, IGNORE_WHITESPACES); + List iwLines3 = convertMode(lines3, IGNORE_WHITESPACES); FairDiffIterable iwChanges1 = compareSmart(iwLines2, iwLines1, indicator); iwChanges1 = optimizeLineChunks(lines2, lines1, iwChanges1, indicator); @@ -100,18 +104,30 @@ public class ByLine { iwChanges2 = optimizeLineChunks(lines2, lines3, iwChanges2, indicator); FairDiffIterable iterable2 = correctChangesSecondStep(lines2, lines3, iwChanges2); - List conflicts = ComparisonMergeUtil.buildFair(iterable1, iterable2, indicator); - return convertIntoFragments(conflicts); + return ComparisonMergeUtil.buildFair(iterable1, iterable2, indicator); } - // - // Impl - // - @NotNull private static FairDiffIterable correctChangesSecondStep(@NotNull final List lines1, @NotNull final List lines2, @NotNull final FairDiffIterable changes) { + return doCorrectChangesSecondStep(lines1, lines2, changes, + Equality.CANONICAL); + } + + @NotNull + private static FairDiffIterable correctChangesSecondStepIW(@NotNull final List lines1, + @NotNull final List lines2, + @NotNull final FairDiffIterable changes) { + return doCorrectChangesSecondStep(lines1, lines2, changes, + (l1, l2) -> StringUtil.equals(l1.getContent(), l2.getContent())); + } + + @NotNull + private static FairDiffIterable doCorrectChangesSecondStep(@NotNull final List lines1, + @NotNull final List lines2, + @NotNull final FairDiffIterable changes, + @NotNull final Equality maximisingEquality) { /* * We want to fix invalid matching here: * @@ -156,7 +172,7 @@ public class ByLine { Line line2 = lines2.get(index2); if (!StringUtil.equalsIgnoreWhitespaces(sample, line1.getContent())) { - if (line1.equals(line2)) { + if (maximisingEquality.equals(line1, line2)) { flush(index1, index2); builder.markEqual(index1, index2); } @@ -197,13 +213,24 @@ public class ByLine { } private void alignExactMatching(TIntArrayList subLines1, TIntArrayList subLines2) { - if (subLines1.size() == subLines2.size()) return; - int n = Math.max(subLines1.size(), subLines2.size()); - if (n > 10) return; // we use brute-force algorithm (C_n_k). This will limit search space by ~250 cases. + boolean skipAligning = n > 10 || // we use brute-force algorithm (C_n_k). This will limit search space by ~250 cases. + subLines1.size() == subLines2.size(); // nothing to do + + if (skipAligning) { + int count = Math.min(subLines1.size(), subLines2.size()); + for (int i = 0; i < count; i++) { + int index1 = subLines1.get(i); + int index2 = subLines2.get(i); + if (lines1.get(index1).equals(lines2.get(index2))) { + builder.markEqual(index1, index2); + } + } + return; + } if (subLines1.size() < subLines2.size()) { - int[] matching = getBestMatchingAlignment(subLines1, subLines2, lines1, lines2); + int[] matching = getBestMatchingAlignment(subLines1, subLines2, lines1, lines2, maximisingEquality); for (int i = 0; i < subLines1.size(); i++) { int index1 = subLines1.get(i); int index2 = subLines2.get(matching[i]); @@ -213,7 +240,7 @@ public class ByLine { } } else { - int[] matching = getBestMatchingAlignment(subLines2, subLines1, lines2, lines1); + int[] matching = getBestMatchingAlignment(subLines2, subLines1, lines2, lines1, maximisingEquality); for (int i = 0; i < subLines2.size(); i++) { int index1 = subLines1.get(matching[i]); int index2 = subLines2.get(i); @@ -232,7 +259,8 @@ public class ByLine { private static int[] getBestMatchingAlignment(@NotNull final TIntArrayList subLines1, @NotNull final TIntArrayList subLines2, @NotNull final List lines1, - @NotNull final List lines2) { + @NotNull final List lines2, + @NotNull final Equality maximisingEquality) { assert subLines1.size() < subLines2.size(); final int size = subLines1.size(); @@ -267,7 +295,7 @@ public class ByLine { for (int i = 0; i < size; i++) { int index1 = subLines1.get(i); int index2 = subLines2.get(comb[i]); - if (lines1.get(index1).equals(lines2.get(index2))) weight++; + if (maximisingEquality.equals(lines1.get(index1), lines2.get(index2))) weight++; } if (weight > bestWeight) { @@ -288,45 +316,6 @@ public class ByLine { return new ChunkOptimizer.LineChunkOptimizer(lines1, lines2, iterable, indicator).build(); } - @NotNull - private static List convertIntoFragments(@NotNull List lines1, - @NotNull List lines2, - @NotNull FairDiffIterable changes) { - List fragments = new ArrayList<>(); - for (Range ch : changes.iterateChanges()) { - IntPair offsets1 = getOffsets(lines1, ch.start1, ch.end1); - IntPair offsets2 = getOffsets(lines2, ch.start2, ch.end2); - - fragments.add(new LineFragmentImpl(ch.start1, ch.end1, ch.start2, ch.end2, - offsets1.val1, offsets1.val2, offsets2.val1, offsets2.val2)); - } - return fragments; - } - - @NotNull - private static List convertIntoFragments(@NotNull List conflicts) { - return ContainerUtil.map(conflicts, ch -> new MergeLineFragmentImpl(ch)); - } - - @NotNull - private static IntPair getOffsets(@NotNull List lines, int startIndex, int endIndex) { - if (startIndex == endIndex) { - int offset; - if (startIndex < lines.size()) { - offset = lines.get(startIndex).getOffset1(); - } - else { - offset = lines.get(lines.size() - 1).getOffset2(); - } - return new IntPair(offset, offset); - } - else { - int offset1 = lines.get(startIndex).getOffset1(); - int offset2 = lines.get(endIndex - 1).getOffset2(); - return new IntPair(offset1, offset2); - } - } - /* * Compare lines in two steps: * - compare ignoring "unimportant" lines @@ -361,90 +350,44 @@ public class ByLine { return Pair.create(bigLines, indexes); } - @NotNull - private static FairDiffIterable expandRanges(@NotNull List lines1, - @NotNull List lines2, - @NotNull FairDiffIterable iterable, - @NotNull ProgressIndicator indicator) { - List changes = new ArrayList<>(); - - for (Range ch : iterable.iterateChanges()) { - Range expanded = TrimUtil.expand(lines1, lines2, ch.start1, ch.start2, ch.end1, ch.end2); - if (!expanded.isEmpty()) changes.add(expanded); - } - - return fair(create(changes, lines1.size(), lines2.size())); - } - // // Lines // @NotNull - private static List getLines(@NotNull CharSequence text, @NotNull ComparisonPolicy policy) { - List lines = new ArrayList<>(); - - int offset = 0; - while (true) { - Line line = createLine(text, offset, policy); - lines.add(line); - offset = line.getOffset2(); - if (!line.hasNewline()) break; - } - - return lines; + private static List getLines(@NotNull List text, @NotNull ComparisonPolicy policy) { + return ContainerUtil.map(text, (line) -> new Line(line, policy)); } @NotNull - private static Line createLine(@NotNull CharSequence text, int offset, @NotNull ComparisonPolicy policy) { - switch (policy) { - case DEFAULT: - return Line.createDefault(text, offset); - case IGNORE_WHITESPACES: - return Line.createIgnore(text, offset); - case TRIM_WHITESPACES: - return Line.createTrim(text, offset); - default: - throw new IllegalArgumentException(policy.name()); - } - } - - @NotNull - private static List convertToIgnoreWhitespace(@NotNull List original) { + private static List convertMode(@NotNull List original, @NotNull ComparisonPolicy policy) { List result = new ArrayList<>(original.size()); - for (Line line : original) { - result.add(Line.createIgnore(line.getOriginalText(), line.getOffset1())); + result.add(new Line(line.getContent(), policy)); } - return result; } - static class Line extends TextChunk { - enum Mode {DEFAULT, TRIM, IGNORE} - - @NotNull private final Mode myMode; + static class Line { + @NotNull private final CharSequence myText; + @NotNull private final ComparisonPolicy myPolicy; private final int myHash; private final int myNonSpaceChars; - private final boolean myNewline; - public Line(@NotNull CharSequence text, int offset1, int offset2, - @NotNull Mode mode, int hash, int nonSpaceChars, boolean newline) { - super(text, offset1, offset2); - myMode = mode; - myHash = hash; - myNonSpaceChars = nonSpaceChars; - myNewline = newline; - } - - public boolean hasNewline() { - return myNewline; + public Line(@NotNull CharSequence text, @NotNull ComparisonPolicy policy) { + myText = text; + myPolicy = policy; + myHash = hashCode(text, policy); + myNonSpaceChars = countNonSpaceChars(text); } @NotNull - @Override public CharSequence getContent() { - return getOriginalText().subSequence(getOffset1(), getOffset2() - (myNewline ? 1 : 0)); + return myText; + } + + public int getNonSpaceChars() { + return myNonSpaceChars; } @Override @@ -453,20 +396,11 @@ public class ByLine { if (o == null || getClass() != o.getClass()) return false; Line line = (Line)o; - assert myMode == line.myMode; + assert myPolicy == line.myPolicy; if (hashCode() != line.hashCode()) return false; - switch (myMode) { - case DEFAULT: - return StringUtil.equals(getContent(), line.getContent()); - case TRIM: - return StringUtil.equalsTrimWhitespaces(getContent(), line.getContent()); - case IGNORE: - return StringUtil.equalsIgnoreWhitespaces(getContent(), line.getContent()); - default: - throw new IllegalArgumentException(myMode.toString()); - } + return equals(getContent(), line.getContent(), myPolicy); } @Override @@ -474,87 +408,47 @@ public class ByLine { return myHash; } - public int getNonSpaceChars() { - return myNonSpaceChars; - } - - public static Line createDefault(@NotNull CharSequence text, int startOffset) { - int len = text.length(); - - int h = 0; + private static int countNonSpaceChars(@NotNull CharSequence text) { int nonSpace = 0; - boolean newline = false; - int offset = startOffset; + int len = text.length(); + int offset = 0; + while (offset < len) { char c = text.charAt(offset); - if (c == '\n') { - offset++; - newline = true; - break; - } - if (!isWhiteSpace(c)) nonSpace++; - h = 31 * h + c; - offset++; - } - - return new Line(text, startOffset, offset, Mode.DEFAULT, h, nonSpace, newline); - } - - public static Line createIgnore(@NotNull CharSequence text, int startOffset) { - int len = text.length(); - - int h = 0; - int nonSpace = 0; - boolean newline = false; - - int offset = startOffset; - while (offset < len) { - char c = text.charAt(offset); - if (c == '\n') { - offset++; - newline = true; - break; - } - if (!isWhiteSpace(c)) { - nonSpace++; - h = 31 * h + c; - } - offset++; - } - - return new Line(text, startOffset, offset, Mode.IGNORE, h, nonSpace, newline); - } - - public static Line createTrim(@NotNull CharSequence text, int startOffset) { - int len = text.length(); - - int nonSpace = 0; - boolean newline = false; - - int offset = startOffset; - while (offset < len) { - char c = text.charAt(offset); - if (c == '\n') { - offset++; - newline = true; - break; - } if (!isWhiteSpace(c)) nonSpace++; offset++; } - int h = calcTrimHash(text, startOffset, offset); - - return new Line(text, startOffset, offset, Mode.TRIM, h, nonSpace, newline); + return nonSpace; } + private static boolean equals(@NotNull CharSequence text1, @NotNull CharSequence text2, @NotNull ComparisonPolicy policy) { + switch (policy) { + case DEFAULT: + return StringUtil.equals(text1, text2); + case TRIM_WHITESPACES: + return StringUtil.equalsTrimWhitespaces(text1, text2); + case IGNORE_WHITESPACES: + return StringUtil.equalsIgnoreWhitespaces(text1, text2); + default: + throw new IllegalArgumentException(policy.toString()); + } + } - private static int calcTrimHash(@NotNull CharSequence text, int offset1, int offset2) { - offset1 = trimStart(text, offset1, offset2); - offset2 = trimEnd(text, offset1, offset2); - - return StringUtil.stringHashCode(text, offset1, offset2); + private static int hashCode(@NotNull CharSequence text, @NotNull ComparisonPolicy policy) { + switch (policy) { + case DEFAULT: + return StringUtil.stringHashCode(text); + case TRIM_WHITESPACES: + int offset1 = trimStart(text, 0, text.length()); + int offset2 = trimEnd(text, offset1, text.length()); + return StringUtil.stringHashCode(text, offset1, offset2); + case IGNORE_WHITESPACES: + return StringUtil.stringHashCodeIgnoreWhitespaces(text); + default: + throw new IllegalArgumentException(policy.name()); + } } } } diff --git a/platform/diff-impl/src/com/intellij/diff/comparison/ByWord.java b/platform/diff-impl/src/com/intellij/diff/comparison/ByWord.java index 7e97f0de823a..eec26d3f1e25 100644 --- a/platform/diff-impl/src/com/intellij/diff/comparison/ByWord.java +++ b/platform/diff-impl/src/com/intellij/diff/comparison/ByWord.java @@ -17,24 +17,23 @@ package com.intellij.diff.comparison; import com.intellij.diff.comparison.LineFragmentSplitter.WordBlock; import com.intellij.diff.comparison.iterables.DiffIterable; -import com.intellij.diff.comparison.iterables.DiffIterableUtil; import com.intellij.diff.comparison.iterables.DiffIterableUtil.*; import com.intellij.diff.comparison.iterables.FairDiffIterable; import com.intellij.diff.fragments.DiffFragment; import com.intellij.diff.fragments.MergeWordFragment; -import com.intellij.diff.fragments.MergeWordFragmentImpl; import com.intellij.diff.util.MergeRange; import com.intellij.diff.util.Range; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.util.Couple; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.containers.ContainerUtil; import com.intellij.util.text.MergingCharSequence; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; +import static com.intellij.diff.comparison.ComparisonManagerImpl.convertIntoDiffFragments; +import static com.intellij.diff.comparison.ComparisonManagerImpl.convertIntoMergeWordFragments; import static com.intellij.diff.comparison.TrimUtil.*; import static com.intellij.diff.comparison.TrimUtil.trim; import static com.intellij.diff.comparison.iterables.DiffIterableUtil.*; @@ -66,7 +65,7 @@ public class ByWord { FairDiffIterable delimitersIterable = matchAdjustmentDelimiters(text1, text2, words1, words2, wordChanges, indicator); DiffIterable iterable = matchAdjustmentWhitespaces(text1, text2, delimitersIterable, policy, indicator); - return convertIntoFragments(iterable); + return convertIntoDiffFragments(iterable); } @NotNull @@ -92,7 +91,7 @@ public class ByWord { List wordConflicts = ComparisonMergeUtil.buildFair(iterable1, iterable2, indicator); List result = matchAdjustmentWhitespaces(text1, text2, text3, wordConflicts, policy, indicator); - return convertIntoFragments(result); + return convertIntoMergeWordFragments(result); } @NotNull @@ -144,7 +143,7 @@ public class ByWord { offsets.start1, offsets.start2, indicator); DiffIterable iterable = matchAdjustmentWhitespaces(subtext1, subtext2, delimitersIterable, policy, indicator); - List fragments = convertIntoFragments(iterable); + List fragments = convertIntoDiffFragments(iterable); int newlines1 = countNewlines(subwords1); int newlines2 = countNewlines(subwords2); @@ -159,16 +158,6 @@ public class ByWord { // Impl // - @NotNull - private static List convertIntoFragments(@NotNull List conflicts) { - return ContainerUtil.map(conflicts, ch -> new MergeWordFragmentImpl(ch)); - } - - @NotNull - private static List convertIntoFragments(@NotNull DiffIterable iterable) { - return DiffIterableUtil.convertIntoFragments(iterable); - } - @NotNull private static FairDiffIterable optimizeWordChunks(@NotNull CharSequence text1, @NotNull CharSequence text2, @@ -863,14 +852,32 @@ public class ByWord { int getOffset2(); } - static class WordChunk extends TextChunk implements InlineChunk { + static class WordChunk implements InlineChunk { + @NotNull private final CharSequence myText; + private final int myOffset1; + private final int myOffset2; private final int myHash; public WordChunk(@NotNull CharSequence text, int offset1, int offset2, int hash) { - super(text, offset1, offset2); + myText = text; + myOffset1 = offset1; + myOffset2 = offset2; myHash = hash; } + @NotNull + public CharSequence getContent() { + return myText.subSequence(myOffset1, myOffset2); + } + + public int getOffset1() { + return myOffset1; + } + + public int getOffset2() { + return myOffset2; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/platform/diff-impl/src/com/intellij/diff/comparison/ComparisonManagerImpl.java b/platform/diff-impl/src/com/intellij/diff/comparison/ComparisonManagerImpl.java index 26aa3fa4ed20..31f67d866a6f 100644 --- a/platform/diff-impl/src/com/intellij/diff/comparison/ComparisonManagerImpl.java +++ b/platform/diff-impl/src/com/intellij/diff/comparison/ComparisonManagerImpl.java @@ -15,10 +15,11 @@ */ package com.intellij.diff.comparison; -import com.intellij.diff.fragments.DiffFragment; -import com.intellij.diff.fragments.DiffFragmentImpl; -import com.intellij.diff.fragments.LineFragment; -import com.intellij.diff.fragments.LineFragmentImpl; +import com.intellij.diff.comparison.iterables.DiffIterable; +import com.intellij.diff.comparison.iterables.FairDiffIterable; +import com.intellij.diff.fragments.*; +import com.intellij.diff.util.IntPair; +import com.intellij.diff.util.MergeRange; import com.intellij.diff.util.Range; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; @@ -33,8 +34,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import static com.intellij.diff.comparison.iterables.DiffIterableUtil.convertIntoFragments; - public class ComparisonManagerImpl extends ComparisonManager { public static final Logger LOG = Logger.getInstance(ComparisonManagerImpl.class); @@ -44,12 +43,24 @@ public class ComparisonManagerImpl extends ComparisonManager { @NotNull CharSequence text2, @NotNull ComparisonPolicy policy, @NotNull ProgressIndicator indicator) throws DiffTooBigException { - if (policy == ComparisonPolicy.IGNORE_WHITESPACES) { - return ByLine.compare(text1, text2, policy, indicator); - } - else { - return ByLine.compareTwoStep(text1, text2, policy, indicator); - } + List lines1 = getLines(text1); + List lines2 = getLines(text2); + FairDiffIterable iterable = ByLine.compare(lines1, lines2, policy, indicator); + return convertIntoLineFragments(lines1, lines2, iterable); + } + + @NotNull + @Override + public List compareLines(@NotNull CharSequence text1, + @NotNull CharSequence text2, + @NotNull CharSequence text3, + @NotNull ComparisonPolicy policy, + @NotNull ProgressIndicator indicator) throws DiffTooBigException { + List lines1 = getLines(text1); + List lines2 = getLines(text2); + List lines3 = getLines(text3); + List ranges = ByLine.compare(lines1, lines2, lines3, policy, indicator); + return convertIntoMergeLineFragments(ranges); } @NotNull @@ -145,13 +156,13 @@ public class ComparisonManagerImpl extends ComparisonManager { @NotNull ComparisonPolicy policy, @NotNull ProgressIndicator indicator) throws DiffTooBigException { if (policy == ComparisonPolicy.IGNORE_WHITESPACES) { - return convertIntoFragments(ByChar.compareIgnoreWhitespaces(text1, text2, indicator)); + return convertIntoDiffFragments(ByChar.compareIgnoreWhitespaces(text1, text2, indicator)); } if (policy == ComparisonPolicy.DEFAULT) { - return convertIntoFragments(ByChar.compareTwoStep(text1, text2, indicator)); + return convertIntoDiffFragments(ByChar.compareTwoStep(text1, text2, indicator)); } LOG.warn(policy.toString() + " is not supported by ByChar comparison"); - return convertIntoFragments(ByChar.compareTwoStep(text1, text2, indicator)); + return convertIntoDiffFragments(ByChar.compareTwoStep(text1, text2, indicator)); } @Override @@ -159,6 +170,63 @@ public class ComparisonManagerImpl extends ComparisonManager { return ComparisonUtil.isEquals(text1, text2, policy); } + // + // Fragments + // + + @NotNull + public static List convertIntoDiffFragments(@NotNull DiffIterable changes) { + final List fragments = new ArrayList<>(); + for (Range ch : changes.iterateChanges()) { + fragments.add(new DiffFragmentImpl(ch.start1, ch.end1, ch.start2, ch.end2)); + } + return fragments; + } + + @NotNull + public static List convertIntoLineFragments(@NotNull List lines1, + @NotNull List lines2, + @NotNull FairDiffIterable changes) { + List fragments = new ArrayList<>(); + for (Range ch : changes.iterateChanges()) { + IntPair offsets1 = getOffsets(lines1, ch.start1, ch.end1); + IntPair offsets2 = getOffsets(lines2, ch.start2, ch.end2); + + fragments.add(new LineFragmentImpl(ch.start1, ch.end1, ch.start2, ch.end2, + offsets1.val1, offsets1.val2, offsets2.val1, offsets2.val2)); + } + return fragments; + } + + @NotNull + private static IntPair getOffsets(@NotNull List lines, int startIndex, int endIndex) { + if (startIndex == endIndex) { + int offset; + if (startIndex < lines.size()) { + offset = lines.get(startIndex).getOffset1(); + } + else { + offset = lines.get(lines.size() - 1).getOffset2(); + } + return new IntPair(offset, offset); + } + else { + int offset1 = lines.get(startIndex).getOffset1(); + int offset2 = lines.get(endIndex - 1).getOffset2(); + return new IntPair(offset1, offset2); + } + } + + @NotNull + public static List convertIntoMergeLineFragments(@NotNull List conflicts) { + return ContainerUtil.map(conflicts, ch -> new MergeLineFragmentImpl(ch.start1, ch.end1, ch.start2, ch.end2, ch.start3, ch.end3)); + } + + @NotNull + public static List convertIntoMergeWordFragments(@NotNull List conflicts) { + return ContainerUtil.map(conflicts, ch -> new MergeWordFragmentImpl(ch.start1, ch.end1, ch.start2, ch.end2, ch.start3, ch.end3)); + } + // // Post process line fragments // @@ -304,4 +372,45 @@ public class ComparisonManagerImpl extends ComparisonManager { int length2 = lineFragment.getEndOffset2() - lineFragment.getStartOffset2(); return Collections.singletonList(new DiffFragmentImpl(0, length1, 0, length2)); } + + @NotNull + private static List getLines(@NotNull CharSequence text) { + List lines = new ArrayList<>(); + + int offset = 0; + while (true) { + int lineEnd = StringUtil.indexOf(text, '\n', offset); + if (lineEnd != -1) { + lines.add(new Line(text, offset, lineEnd, true)); + offset = lineEnd + 1; + } + else { + lines.add(new Line(text, offset, text.length(), false)); + break; + } + } + + return lines; + } + + private static class Line extends CharSequenceSubSequence { + private final int myOffset1; + private final int myOffset2; + private final boolean myNewline; + + public Line(@NotNull CharSequence chars, int offset1, int offset2, boolean newline) { + super(chars, offset1, offset2); + myOffset1 = offset1; + myOffset2 = offset2; + myNewline = newline; + } + + public int getOffset1() { + return myOffset1; + } + + public int getOffset2() { + return myOffset2 + (myNewline ? 1 : 0); + } + } } diff --git a/platform/diff-impl/src/com/intellij/diff/comparison/TextChunk.java b/platform/diff-impl/src/com/intellij/diff/comparison/TextChunk.java deleted file mode 100644 index 8a163f39b653..000000000000 --- a/platform/diff-impl/src/com/intellij/diff/comparison/TextChunk.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2000-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.diff.comparison; - -import org.jetbrains.annotations.NotNull; - -abstract class TextChunk { - @NotNull private final CharSequence myText; - private final int myOffset1; - private final int myOffset2; - - public TextChunk(@NotNull CharSequence text, int offset1, int offset2) { - myText = text; - myOffset1 = offset1; - myOffset2 = offset2; - } - - @Override - public abstract int hashCode(); - - @Override - public abstract boolean equals(Object obj); - - @NotNull - public CharSequence getContent() { - return myText.subSequence(myOffset1, myOffset2); - } - - @NotNull - public CharSequence getOriginalText() { - return myText; - } - - public int getOffset1() { - return myOffset1; - } - - public int getOffset2() { - return myOffset2; - } - - @Override - public String toString() { - return getContent().toString(); - } -} diff --git a/platform/diff-impl/src/com/intellij/diff/comparison/iterables/DiffIterableUtil.java b/platform/diff-impl/src/com/intellij/diff/comparison/iterables/DiffIterableUtil.java index 10a4fc0de250..346e1402e489 100644 --- a/platform/diff-impl/src/com/intellij/diff/comparison/iterables/DiffIterableUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/comparison/iterables/DiffIterableUtil.java @@ -18,7 +18,6 @@ package com.intellij.diff.comparison.iterables; import com.intellij.diff.comparison.DiffTooBigException; import com.intellij.diff.comparison.TrimUtil; import com.intellij.diff.fragments.DiffFragment; -import com.intellij.diff.fragments.DiffFragmentImpl; import com.intellij.diff.util.Range; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.util.Comparing; @@ -88,20 +87,6 @@ public class DiffIterableUtil { return diff(data1, data2, indicator); } - /* - * Compare two arrays, basing on equals() and hashCode() of it's elements - * - * If the input arrays are too big, "everything is changed" can be returned. - */ - @NotNull - public static FairDiffIterable diffSomehow(@NotNull T[] data1, @NotNull T[] data2, @NotNull ProgressIndicator indicator) { - indicator.checkCanceled(); - - // TODO: use ProgressIndicator inside - Diff.Change change = Diff.buildChangesSomehow(data1, data2); - return fair(create(change, data1.length, data2.length)); - } - // // Iterable // @@ -158,15 +143,6 @@ public class DiffIterableUtil { // Misc // - @NotNull - public static List convertIntoFragments(@NotNull DiffIterable changes) { - final List fragments = new ArrayList<>(); - for (Range ch : changes.iterateChanges()) { - fragments.add(new DiffFragmentImpl(ch.start1, ch.end1, ch.start2, ch.end2)); - } - return fragments; - } - @NotNull public static Iterable> iterateAll(@NotNull final DiffIterable iterable) { return () -> new Iterator>() { diff --git a/platform/diff-impl/src/com/intellij/diff/fragments/MergeLineFragmentImpl.java b/platform/diff-impl/src/com/intellij/diff/fragments/MergeLineFragmentImpl.java index eea8b6a2d590..a9d7b118fc3c 100644 --- a/platform/diff-impl/src/com/intellij/diff/fragments/MergeLineFragmentImpl.java +++ b/platform/diff-impl/src/com/intellij/diff/fragments/MergeLineFragmentImpl.java @@ -15,7 +15,6 @@ */ package com.intellij.diff.fragments; -import com.intellij.diff.util.MergeRange; import com.intellij.diff.util.ThreeSide; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -57,14 +56,6 @@ public class MergeLineFragmentImpl implements MergeLineFragment { myInnerFragments = innerFragments; } - public MergeLineFragmentImpl(@NotNull MergeRange range) { - this(range, null); - } - - public MergeLineFragmentImpl(@NotNull MergeRange range, @Nullable List innerFragments) { - this(range.start1, range.end1, range.start2, range.end2, range.start3, range.end3, innerFragments); - } - public MergeLineFragmentImpl(@NotNull MergeLineFragment fragment, @Nullable List fragments) { this(fragment.getStartLine(ThreeSide.LEFT), fragment.getEndLine(ThreeSide.LEFT), fragment.getStartLine(ThreeSide.BASE), fragment.getEndLine(ThreeSide.BASE), diff --git a/platform/diff-impl/src/com/intellij/diff/fragments/MergeWordFragmentImpl.java b/platform/diff-impl/src/com/intellij/diff/fragments/MergeWordFragmentImpl.java index b95a3e316493..b7abb9c333ed 100644 --- a/platform/diff-impl/src/com/intellij/diff/fragments/MergeWordFragmentImpl.java +++ b/platform/diff-impl/src/com/intellij/diff/fragments/MergeWordFragmentImpl.java @@ -41,10 +41,6 @@ public class MergeWordFragmentImpl implements MergeWordFragment { myEndOffset3 = endOffset3; } - public MergeWordFragmentImpl(@NotNull MergeRange range) { - this(range.start1, range.end1, range.start2, range.end2, range.start3, range.end3); - } - @Override public int getStartOffset(@NotNull ThreeSide side) { return side.select(myStartOffset1, myStartOffset2, myStartOffset3); diff --git a/platform/diff-impl/src/com/intellij/diff/merge/TextMergeViewer.java b/platform/diff-impl/src/com/intellij/diff/merge/TextMergeViewer.java index dd6e166e4a16..e5de1b18f040 100644 --- a/platform/diff-impl/src/com/intellij/diff/merge/TextMergeViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/merge/TextMergeViewer.java @@ -18,7 +18,7 @@ package com.intellij.diff.merge; import com.intellij.diff.DiffContext; import com.intellij.diff.FrameDiffTool; import com.intellij.diff.actions.ProxyUndoRedoAction; -import com.intellij.diff.comparison.ByLine; +import com.intellij.diff.comparison.ComparisonManager; import com.intellij.diff.comparison.ComparisonMergeUtil; import com.intellij.diff.comparison.ComparisonPolicy; import com.intellij.diff.comparison.DiffTooBigException; @@ -363,8 +363,9 @@ public class TextMergeViewer implements MergeTool.MergeViewer { return ContainerUtil.map(documents, Document::getImmutableCharSequence); }); - List lineFragments = ByLine.compareTwoStep(sequences.get(0), sequences.get(1), sequences.get(2), - ComparisonPolicy.DEFAULT, indicator); + ComparisonManager manager = ComparisonManager.getInstance(); + List lineFragments = manager.compareLines(sequences.get(0), sequences.get(1), sequences.get(2), + ComparisonPolicy.DEFAULT, indicator); List conflictTypes = ReadAction.compute(() -> { indicator.checkCanceled(); diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java index 3d2325550148..c67f66adcf11 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java @@ -16,7 +16,7 @@ package com.intellij.diff.tools.simple; import com.intellij.diff.DiffContext; -import com.intellij.diff.comparison.ByLine; +import com.intellij.diff.comparison.ComparisonManager; import com.intellij.diff.comparison.ComparisonPolicy; import com.intellij.diff.comparison.DiffTooBigException; import com.intellij.diff.contents.DiffContent; @@ -125,8 +125,10 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewerEx { }); final ComparisonPolicy comparisonPolicy = getIgnorePolicy().getComparisonPolicy(); - List lineFragments = ByLine.compareTwoStep(sequences.get(0), sequences.get(1), sequences.get(2), - comparisonPolicy, indicator); + + ComparisonManager manager = ComparisonManager.getInstance(); + List lineFragments = manager.compareLines(sequences.get(0), sequences.get(1), sequences.get(2), + comparisonPolicy, indicator); List conflictTypes = ReadAction.compute(() -> { indicator.checkCanceled(); diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilAutoTest.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilAutoTest.kt index 36431c682616..4de112cd371b 100644 --- a/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilAutoTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilAutoTest.kt @@ -134,7 +134,7 @@ class ComparisonUtilAutoTest : DiffTestCase() { val sequence2 = text2.charsSequence val sequence3 = text3.charsSequence - val fragments = ByLine.compareTwoStep(sequence1, sequence2, sequence3, policy, INDICATOR) + val fragments = MANAGER.compareLines(sequence1, sequence2, sequence3, policy, INDICATOR) val fineFragments = fragments.map { f -> val chunk1 = DiffUtil.getLinesContent(text1, f.startLine1, f.endLine1) diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/LineComparisonUtilTest.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/LineComparisonUtilTest.kt index 9144618851d3..2eab93eccb42 100644 --- a/platform/diff-impl/tests/com/intellij/diff/comparison/LineComparisonUtilTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/LineComparisonUtilTest.kt @@ -401,6 +401,7 @@ class LineComparisonUtilTest : ComparisonUtilTestBase() { // TODO (" _-------_ _ _ " - " _ _ _ ").trim() (" _-------_ _ _ " - " _ _ _ ").default() (" _ _--_-_------" - " _--_-_ ").trim() + (" _-------_ _ _ " - " _ _ _ ").ignore() testAll() } @@ -416,7 +417,6 @@ class LineComparisonUtilTest : ComparisonUtilTestBase() { lines() { ("====}_==== }_Y_====}" - "====}_Y_====}") (" _------_ _ " - " _ _ ").default() // result after second step correction - (" _ _-_-----" - " _-_ ").ignore() // result looks strange because of 'diff.unimportant.line.char.count' testAll() } } @@ -428,4 +428,59 @@ class LineComparisonUtilTest : ComparisonUtilTestBase() { testDefault() } } + + fun `test ignore whitespace policy applies two-step correction`() { + lines() { + ("1_ _ 1" - " 1") + ("-_-_ " - " ").default() + (" _-_---" - " ").trim() + ("-_-_ " - " ").ignore() + testAll() + } + + lines() { + (" 1_ _1" - " 1") + (" _-_-" - " ").default() + testAll() + } + + lines() { + ("X_ Y_X" - "Y ") + ("-_--_-" - "--").default() + ("-_ _-" - " ").trim() + testAll() + } + } + + fun `test regression - second step correction should be performed if there are no ambigous matchings`() { + lines { + ("}_ }" - " }_}") + ("-_--" - "--_-").default() + (" _ " - " _ ").trim() + testAll() + } + + lines { + (" }_}_ }" - "}_}_}") + ("--_ _--" - "-_ _-").default() + (" _ _ " - " _ _ ").trim() + testAll() + } + + lines() { + ("X_X __Y" - "X__Z") + (" _--__-" - " __-").default() + ("-_ __-" - " __-").trim() + testAll() + } + } + + fun `test regression - second step with too many possible matchings`() { + lines { + (" X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_ X" - "X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X ") + ("--_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _-_-_-_-_-_--" - "-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _--").default() + (" _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _-_-_-_-_--" - " _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ").trim() + testAll() + } + } } diff --git a/platform/icons/src/icon.png b/platform/icons/src/icon.png index 5af892c1ec5f..78b2f6c13262 100644 Binary files a/platform/icons/src/icon.png and b/platform/icons/src/icon.png differ diff --git a/platform/icons/src/windows/close.png b/platform/icons/src/windows/close.png deleted file mode 100644 index 4c593adde24b..000000000000 Binary files a/platform/icons/src/windows/close.png and /dev/null differ diff --git a/platform/icons/src/windows/closeActive.png b/platform/icons/src/windows/closeActive.png new file mode 100644 index 000000000000..9399d0a75cb8 Binary files /dev/null and b/platform/icons/src/windows/closeActive.png differ diff --git a/platform/icons/src/windows/closeActive@2x.png b/platform/icons/src/windows/closeActive@2x.png new file mode 100644 index 000000000000..0d27cde74cd9 Binary files /dev/null and b/platform/icons/src/windows/closeActive@2x.png differ diff --git a/platform/icons/src/windows/closeActive@2x_dark.png b/platform/icons/src/windows/closeActive@2x_dark.png new file mode 100644 index 000000000000..bfad5bb3d0c4 Binary files /dev/null and b/platform/icons/src/windows/closeActive@2x_dark.png differ diff --git a/platform/icons/src/windows/closeActive_dark.png b/platform/icons/src/windows/closeActive_dark.png new file mode 100644 index 000000000000..c7b47d6b1e26 Binary files /dev/null and b/platform/icons/src/windows/closeActive_dark.png differ diff --git a/platform/icons/src/windows/closeHover.png b/platform/icons/src/windows/closeHover.png new file mode 100644 index 000000000000..6b0095161034 Binary files /dev/null and b/platform/icons/src/windows/closeHover.png differ diff --git a/platform/icons/src/windows/closeHover@2x.png b/platform/icons/src/windows/closeHover@2x.png new file mode 100644 index 000000000000..33cb80c35b65 Binary files /dev/null and b/platform/icons/src/windows/closeHover@2x.png differ diff --git a/platform/icons/src/windows/closeInactive.png b/platform/icons/src/windows/closeInactive.png new file mode 100644 index 000000000000..a24938407f83 Binary files /dev/null and b/platform/icons/src/windows/closeInactive.png differ diff --git a/platform/icons/src/windows/closeInactive@2x.png b/platform/icons/src/windows/closeInactive@2x.png new file mode 100644 index 000000000000..4d232434c41b Binary files /dev/null and b/platform/icons/src/windows/closeInactive@2x.png differ diff --git a/platform/icons/src/windows/closeInactive@2x_dark.png b/platform/icons/src/windows/closeInactive@2x_dark.png new file mode 100644 index 000000000000..e5d8f6671fab Binary files /dev/null and b/platform/icons/src/windows/closeInactive@2x_dark.png differ diff --git a/platform/icons/src/windows/closeInactive_dark.png b/platform/icons/src/windows/closeInactive_dark.png new file mode 100644 index 000000000000..0be3f457eba8 Binary files /dev/null and b/platform/icons/src/windows/closeInactive_dark.png differ diff --git a/platform/icons/src/windows/iconify.png b/platform/icons/src/windows/iconify.png deleted file mode 100644 index b2dd7458495e..000000000000 Binary files a/platform/icons/src/windows/iconify.png and /dev/null differ diff --git a/platform/icons/src/windows/maximize.png b/platform/icons/src/windows/maximize.png deleted file mode 100644 index 4dfcc65f4f88..000000000000 Binary files a/platform/icons/src/windows/maximize.png and /dev/null differ diff --git a/platform/icons/src/windows/maximize@2x.png b/platform/icons/src/windows/maximize@2x.png new file mode 100644 index 000000000000..a9dc5c1eaeb3 Binary files /dev/null and b/platform/icons/src/windows/maximize@2x.png differ diff --git a/platform/icons/src/windows/maximize@2x_dark.png b/platform/icons/src/windows/maximize@2x_dark.png new file mode 100644 index 000000000000..c12370c18563 Binary files /dev/null and b/platform/icons/src/windows/maximize@2x_dark.png differ diff --git a/platform/icons/src/windows/maximizeInactive.png b/platform/icons/src/windows/maximizeInactive.png new file mode 100644 index 000000000000..688e4e3fc0b6 Binary files /dev/null and b/platform/icons/src/windows/maximizeInactive.png differ diff --git a/platform/icons/src/windows/maximizeInactive@2x.png b/platform/icons/src/windows/maximizeInactive@2x.png new file mode 100644 index 000000000000..1b8ba51e0556 Binary files /dev/null and b/platform/icons/src/windows/maximizeInactive@2x.png differ diff --git a/platform/icons/src/windows/maximizeInactive@2x_dark.png b/platform/icons/src/windows/maximizeInactive@2x_dark.png new file mode 100644 index 000000000000..a335b14ef756 Binary files /dev/null and b/platform/icons/src/windows/maximizeInactive@2x_dark.png differ diff --git a/platform/icons/src/windows/maximizeInactive_dark.png b/platform/icons/src/windows/maximizeInactive_dark.png new file mode 100644 index 000000000000..23d7af2614e5 Binary files /dev/null and b/platform/icons/src/windows/maximizeInactive_dark.png differ diff --git a/platform/icons/src/windows/maximize_dark.png b/platform/icons/src/windows/maximize_dark.png new file mode 100644 index 000000000000..ff9b20695970 Binary files /dev/null and b/platform/icons/src/windows/maximize_dark.png differ diff --git a/platform/icons/src/windows/minimize.png b/platform/icons/src/windows/minimize.png index dc6b39109161..f21aafd5314b 100644 Binary files a/platform/icons/src/windows/minimize.png and b/platform/icons/src/windows/minimize.png differ diff --git a/platform/icons/src/windows/minimize@2x.png b/platform/icons/src/windows/minimize@2x.png new file mode 100644 index 000000000000..fa32e78f8176 Binary files /dev/null and b/platform/icons/src/windows/minimize@2x.png differ diff --git a/platform/icons/src/windows/minimize@2x_dark.png b/platform/icons/src/windows/minimize@2x_dark.png new file mode 100644 index 000000000000..f463d2d83d41 Binary files /dev/null and b/platform/icons/src/windows/minimize@2x_dark.png differ diff --git a/platform/icons/src/windows/minimizeInactive.png b/platform/icons/src/windows/minimizeInactive.png new file mode 100644 index 000000000000..49be621e23a8 Binary files /dev/null and b/platform/icons/src/windows/minimizeInactive.png differ diff --git a/platform/icons/src/windows/minimizeInactive@2x.png b/platform/icons/src/windows/minimizeInactive@2x.png new file mode 100644 index 000000000000..afe98f8b3a40 Binary files /dev/null and b/platform/icons/src/windows/minimizeInactive@2x.png differ diff --git a/platform/icons/src/windows/minimizeInactive@2x_dark.png b/platform/icons/src/windows/minimizeInactive@2x_dark.png new file mode 100644 index 000000000000..a4fd6cebfdd0 Binary files /dev/null and b/platform/icons/src/windows/minimizeInactive@2x_dark.png differ diff --git a/platform/icons/src/windows/minimizeInactive_dark.png b/platform/icons/src/windows/minimizeInactive_dark.png new file mode 100644 index 000000000000..c5ff31352b07 Binary files /dev/null and b/platform/icons/src/windows/minimizeInactive_dark.png differ diff --git a/platform/icons/src/windows/minimize_dark.png b/platform/icons/src/windows/minimize_dark.png new file mode 100644 index 000000000000..01275e1af39b Binary files /dev/null and b/platform/icons/src/windows/minimize_dark.png differ diff --git a/platform/icons/src/windows/restore.png b/platform/icons/src/windows/restore.png new file mode 100644 index 000000000000..9dcafef6a1a7 Binary files /dev/null and b/platform/icons/src/windows/restore.png differ diff --git a/platform/icons/src/windows/restore@2x.png b/platform/icons/src/windows/restore@2x.png new file mode 100644 index 000000000000..2d1fc2b55664 Binary files /dev/null and b/platform/icons/src/windows/restore@2x.png differ diff --git a/platform/icons/src/windows/restore@2x_dark.png b/platform/icons/src/windows/restore@2x_dark.png new file mode 100644 index 000000000000..0fa65cd5b7c1 Binary files /dev/null and b/platform/icons/src/windows/restore@2x_dark.png differ diff --git a/platform/icons/src/windows/restoreInactive.png b/platform/icons/src/windows/restoreInactive.png new file mode 100644 index 000000000000..6e25f284583d Binary files /dev/null and b/platform/icons/src/windows/restoreInactive.png differ diff --git a/platform/icons/src/windows/restoreInactive@2x.png b/platform/icons/src/windows/restoreInactive@2x.png new file mode 100644 index 000000000000..2faaa2b60268 Binary files /dev/null and b/platform/icons/src/windows/restoreInactive@2x.png differ diff --git a/platform/icons/src/windows/restoreInactive@2x_dark.png b/platform/icons/src/windows/restoreInactive@2x_dark.png new file mode 100644 index 000000000000..056a3a5aebc8 Binary files /dev/null and b/platform/icons/src/windows/restoreInactive@2x_dark.png differ diff --git a/platform/icons/src/windows/restoreInactive_dark.png b/platform/icons/src/windows/restoreInactive_dark.png new file mode 100644 index 000000000000..718897b7bdea Binary files /dev/null and b/platform/icons/src/windows/restoreInactive_dark.png differ diff --git a/platform/icons/src/windows/restore_dark.png b/platform/icons/src/windows/restore_dark.png new file mode 100644 index 000000000000..ae6ffc89101b Binary files /dev/null and b/platform/icons/src/windows/restore_dark.png differ diff --git a/platform/lang-impl/src/com/intellij/codeInspection/InspectionApplication.java b/platform/lang-impl/src/com/intellij/codeInspection/InspectionApplication.java index dc00f6bfb3dd..a6d7c9b2241c 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/InspectionApplication.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/InspectionApplication.java @@ -16,6 +16,7 @@ package com.intellij.codeInspection; import com.intellij.analysis.AnalysisScope; +import com.intellij.codeInsight.daemon.HighlightDisplayKey; import com.intellij.codeInspection.ex.*; import com.intellij.conversion.ConversionListener; import com.intellij.conversion.ConversionService; @@ -255,7 +256,8 @@ public class InspectionApplication { }); final String descriptionsFile = resultsDataPath + File.separatorChar + DESCRIPTIONS + XML_EXTENSION; describeInspections(descriptionsFile, - myRunWithEditorSettings ? null : inspectionProfile.getName()); + myRunWithEditorSettings ? null : inspectionProfile.getName(), + (InspectionProfile)inspectionProfile); inspectionsResults.add(new File(descriptionsFile)); // convert report if (reportConverter != null) { @@ -446,8 +448,8 @@ public class InspectionApplication { } } - private static void describeInspections(@NonNls String myOutputPath, final String name) throws IOException { - final InspectionToolWrapper[] toolWrappers = InspectionProfileImpl.getDefaultProfile().getInspectionTools(null); + private static void describeInspections(@NonNls String myOutputPath, final String name, final InspectionProfile profile) throws IOException { + final InspectionToolWrapper[] toolWrappers = profile.getInspectionTools(null); final Map> map = new HashMap>(); for (InspectionToolWrapper toolWrapper : toolWrappers) { final String groupName = toolWrapper.getGroupDisplayName(); @@ -472,14 +474,17 @@ public class InspectionApplication { final Set entries = map.get(groupName); for (InspectionToolWrapper toolWrapper : entries) { xmlWriter.startNode("inspection"); - xmlWriter.addAttribute("shortName", toolWrapper.getShortName()); + final String shortName = toolWrapper.getShortName(); + xmlWriter.addAttribute("shortName", shortName); xmlWriter.addAttribute("displayName", toolWrapper.getDisplayName()); + final boolean toolEnabled = profile.isToolEnabled(HighlightDisplayKey.find(shortName)); + xmlWriter.addAttribute("enabled", Boolean.toString(toolEnabled)); final String description = toolWrapper.loadDescription(); if (description != null) { xmlWriter.setValue(description); } else { - LOG.error(toolWrapper.getShortName() + " descriptionUrl==" + toolWrapper); + LOG.error(shortName + " descriptionUrl==" + toolWrapper); } xmlWriter.endNode(); } diff --git a/platform/lang-impl/src/com/intellij/ide/scopeView/ScopePaneSelectInTarget.java b/platform/lang-impl/src/com/intellij/ide/scopeView/ScopePaneSelectInTarget.java index 78d612f36939..99d00a895cd4 100644 --- a/platform/lang-impl/src/com/intellij/ide/scopeView/ScopePaneSelectInTarget.java +++ b/platform/lang-impl/src/com/intellij/ide/scopeView/ScopePaneSelectInTarget.java @@ -51,7 +51,8 @@ public class ScopePaneSelectInTarget extends ProjectViewSelectInTarget { } @Nullable - private NamedScope getContainingScope(PsiFile file) { + private NamedScope getContainingScope(@Nullable PsiFile file) { + if (file == null) return null; NamedScopesHolder scopesHolder = DependencyValidationManager.getInstance(myProject); for (NamedScope scope : ScopeViewPane.getShownScopes(myProject)) { PackageSet packageSet = scope.getValue(); diff --git a/platform/lang-impl/src/com/intellij/util/indexing/ChangeTrackingValueContainer.java b/platform/lang-impl/src/com/intellij/util/indexing/ChangeTrackingValueContainer.java index 096e77b84ceb..b2a358ebbcf4 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/ChangeTrackingValueContainer.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/ChangeTrackingValueContainer.java @@ -127,7 +127,9 @@ class ChangeTrackingValueContainer extends UpdatableValueContainer newMerged = ((ChangeTrackingValueContainer)fromDisk).getMergedData().copy(); } - if ((myAdded != null || myInvalidated != null) && newMerged.size() > ValueContainerImpl.NUMBER_OF_VALUES_THRESHOLD) { + if ((myAdded != null || myInvalidated != null) && + (newMerged.size() > ValueContainerImpl.NUMBER_OF_VALUES_THRESHOLD || + (myAdded != null && myAdded.size() > ValueContainerImpl.NUMBER_OF_VALUES_THRESHOLD))) { // Calculate file ids that have Value mapped to avoid O(NumberOfValuesInMerged) during removal fileId2ValueMapping = new FileId2ValueMapping(newMerged); } diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/darcula_windows.properties b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/darcula_windows.properties index e633a34e62ee..e28788497193 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/darcula_windows.properties +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/darcula_windows.properties @@ -9,7 +9,7 @@ InternalFrameUI=com.intellij.ide.ui.laf.darcula.ui.DarculaInternalFrameUI InternalFrame.border=com.intellij.ide.ui.laf.darcula.ui.DarculaInternalBorder RootPaneUI=com.intellij.ide.ui.laf.darcula.ui.DarculaRootPaneUI -InternalFrame.closeIcon=AllIcons.Windows.Close -InternalFrame.iconifyIcon=AllIcons.Windows.Iconify -InternalFrame.maximizeIcon=AllIcons.Windows.Maximize -InternalFrame.minimizeIcon=AllIcons.Windows.Minimize +InternalFrame.closeIcon=AllIcons.Windows.CloseInactive +InternalFrame.iconifyIcon=AllIcons.Windows.MinimizeInactive +InternalFrame.maximizeIcon=AllIcons.Windows.MaximizeInactive +InternalFrame.minimizeIcon=AllIcons.Windows.RestoreInactive diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaTitlePane.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaTitlePane.java index 7b86a9c29738..2c91e3b7e546 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaTitlePane.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaTitlePane.java @@ -15,14 +15,18 @@ */ package com.intellij.ide.ui.laf.darcula.ui; +import com.intellij.icons.AllIcons; import com.intellij.ide.DataManager; import com.intellij.openapi.actionSystem.ex.ActionManagerEx; -import com.intellij.openapi.ui.GraphicsConfig; import com.intellij.openapi.wm.impl.IdeMenuBar; import com.intellij.openapi.wm.impl.IdeRootPane; import com.intellij.ui.Gray; import com.intellij.ui.JBColor; -import com.intellij.util.ui.*; +import com.intellij.util.IconUtil; +import com.intellij.util.ui.ImageUtil; +import com.intellij.util.ui.JBDimension; +import com.intellij.util.ui.JBUI; +import com.intellij.util.ui.UIUtil; import org.imgscalr.Scalr; import sun.swing.SwingUtilities2; @@ -293,7 +297,7 @@ public class DarculaTitlePane extends JComponent { menu.add(myCloseAction); } - private static JButton createButton(String accessibleName, Icon icon, Action action) { + private static JButton createButton(String accessibleName, Icon icon, Icon hoverIcon, Action action, Color hoverBg) { JButton button = new JButton() { boolean mouseOverButton = false; { @@ -314,11 +318,11 @@ public class DarculaTitlePane extends JComponent { } @Override protected void paintComponent(Graphics g) { - final Window window = SwingUtilities.windowForComponent(this); - float alpha = window.isActive() && mouseOverButton ? 1f : 0.5f; - final GraphicsConfig config = GraphicsUtil.paintWithAlpha(g, alpha); - getIcon().paintIcon(this, g, 0, 0); - config.restore(); + if (mouseOverButton) { + g.setColor(hoverBg); + g.fillRect(0, 0, getWidth(), getHeight()); + } + IconUtil.paintInCenterOf(this, g, mouseOverButton ? hoverIcon : icon); } }; button.setFocusPainted(false); @@ -334,14 +338,14 @@ public class DarculaTitlePane extends JComponent { } private void createButtons() { - myCloseButton = createButton("Close", UIManager.getIcon("InternalFrame.closeIcon"), myCloseAction); + myCloseButton = createButton("Close", AllIcons.Windows.CloseActive, AllIcons.Windows.CloseHover, myCloseAction, Color.red); if (getWindowDecorationStyle() == JRootPane.FRAME) { myMaximizeIcon = UIManager.getIcon("InternalFrame.maximizeIcon"); myMinimizeIcon = UIManager.getIcon("InternalFrame.minimizeIcon"); - myIconifyButton = createButton("Iconify", UIManager.getIcon("InternalFrame.iconifyIcon"), myIconifyAction); - myToggleButton = createButton("Maximize", myMaximizeIcon, myRestoreAction); + myIconifyButton = createButton("Iconify", AllIcons.Windows.MinimizeInactive, AllIcons.Windows.Minimize, myIconifyAction, new Color(0x55585A)); + myToggleButton = createButton("Maximize", AllIcons.Windows.MaximizeInactive, AllIcons.Windows.MaximizeInactive, myRestoreAction, new Color(0x55585A)); } } @@ -541,14 +545,14 @@ public class DarculaTitlePane extends JComponent { } - int w = width; - int h = height; - h--; + //int w = width; + //int h = height; + //h--; g.setColor(UIManager.getColor("MenuBar.darcula.borderColor")); - g.drawLine(0, h, w, h); - h--; - g.setColor(UIManager.getColor("MenuBar.darcula.borderShadowColor")); - g.drawLine(0, h, w, h); + //g.drawLine(0, h, w, h); + //h--; + //g.setColor(UIManager.getColor("MenuBar.darcula.borderShadowColor")); + g.drawLine(0, getHeight()-1, getWidth(), getHeight()-1); } private class CloseAction extends AbstractAction { @@ -604,7 +608,8 @@ public class DarculaTitlePane extends JComponent { } if (mySystemIcon != null) { - g.drawImage(mySystemIcon, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, null); + final int offset = (getHeight() - mySystemIcon.getHeight(null)) / 2; + g.drawImage(mySystemIcon, offset, offset, null); } else { Icon icon = UIManager.getIcon("InternalFrame.icon"); @@ -653,7 +658,7 @@ public class DarculaTitlePane extends JComponent { iconHeight = IMAGE_HEIGHT; } - return Math.max(Math.max(fontHeight, iconHeight), JBUI.scale(myIdeMenu == null ? 28 : 36)); + return Math.max(Math.max(fontHeight, iconHeight), JBUI.scale(31)); } public void layoutContainer(Container c) { @@ -661,17 +666,17 @@ public class DarculaTitlePane extends JComponent { int h = getHeight(); int x; int spacing; - int buttonHeight; - int buttonWidth; + int buttonHeight = JBUI.scale(29); + int buttonWidth = JBUI.scale(45); - if (myCloseButton != null && myCloseButton.getIcon() != null) { - buttonHeight = myCloseButton.getIcon().getIconHeight(); - buttonWidth = myCloseButton.getIcon().getIconWidth(); - } - else { - buttonHeight = IMAGE_HEIGHT; - buttonWidth = IMAGE_WIDTH; - } + //if (myCloseButton != null && myCloseButton.getIcon() != null) { + // buttonHeight = myCloseButton.getIcon().getIconHeight(); + // buttonWidth = myCloseButton.getIcon().getIconWidth(); + //} + //else { + // buttonHeight = IMAGE_HEIGHT; + // buttonWidth = IMAGE_WIDTH; + //} spacing = 5; x = spacing; @@ -679,33 +684,33 @@ public class DarculaTitlePane extends JComponent { myMenuBar.setBounds(x, (h - buttonHeight) / 2, buttonWidth, buttonHeight); } + int systemIconSize = mySystemIcon == null ? JBUI.scale(16) : mySystemIcon.getWidth(null); + + x = buttonHeight - systemIconSize + systemIconSize + systemIconSize/2; // offset + width + offset, where offset is (H - iconHeight) / 2 if (myIdeMenu != null) { final Dimension size = myIdeMenu.getPreferredSize(); - x += spacing + (myMenuBar != null ? buttonWidth : 0); - myIdeMenu.setBounds(x, (h - size.height) / 2, size.width, size.height); } x = w; - spacing = 8; - x += -spacing - buttonWidth; + spacing = 0; + x -= spacing + buttonWidth; if (myCloseButton != null) { myCloseButton.setBounds(x, (h - buttonHeight) / 2, buttonWidth, buttonHeight); } - if (getWindowDecorationStyle() == JRootPane.FRAME) { if (Toolkit.getDefaultToolkit().isFrameStateSupported( Frame.MAXIMIZED_BOTH)) { if (myToggleButton.getParent() != null) { //spacing = 10; - x += -spacing - buttonWidth; + x -= spacing + buttonWidth; myToggleButton.setBounds(x, (h - buttonHeight) / 2, buttonWidth, buttonHeight); } } if (myIconifyButton != null && myIconifyButton.getParent() != null) { - x += -spacing - buttonWidth; + x -= spacing + buttonWidth; myIconifyButton.setBounds(x, (h - buttonHeight) / 2, buttonWidth, buttonHeight); } } @@ -757,7 +762,7 @@ public class DarculaTitlePane extends JComponent { } else if (icons.size() == 1) { mySystemIcon = icons.get(0); } else { - final JBDimension size = JBUI.size(32); + final JBDimension size = JBUI.size(16); final Image image = icons.get(0); mySystemIcon = Scalr.resize(ImageUtil.toBufferedImage(image), Scalr.Method.ULTRA_QUALITY, size.width, size.height); } diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java index 0cc2487e0846..1ec346b95475 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java @@ -314,6 +314,13 @@ public class ProjectManagerImpl extends ProjectManagerEx implements Disposable { } } + for (Project p : myOpenProjects) { + if (ProjectUtil.isSameProject(project.getBasePath(), p)) { + ProjectUtil.focusProjectWindow(p, false); + return false; + } + } + if (!addToOpened(project)) { return false; } diff --git a/platform/util/src/com/intellij/icons/AllIcons.java b/platform/util/src/com/intellij/icons/AllIcons.java index 17f75185e7b5..06e71b0bc314 100644 --- a/platform/util/src/com/intellij/icons/AllIcons.java +++ b/platform/util/src/com/intellij/icons/AllIcons.java @@ -1267,10 +1267,14 @@ public class AllIcons { } public static class Windows { - public static final Icon Close = IconLoader.getIcon("/windows/close.png"); // 16x16 - public static final Icon Iconify = IconLoader.getIcon("/windows/iconify.png"); // 16x16 - public static final Icon Maximize = IconLoader.getIcon("/windows/maximize.png"); // 16x16 + public static final Icon CloseActive = IconLoader.getIcon("/windows/closeActive.png"); // 16x16 + public static final Icon CloseHover = IconLoader.getIcon("/windows/closeHover.png"); // 16x16 + public static final Icon CloseInactive = IconLoader.getIcon("/windows/closeInactive.png"); // 16x16 + public static final Icon MaximizeInactive = IconLoader.getIcon("/windows/maximizeInactive.png"); // 16x16 public static final Icon Minimize = IconLoader.getIcon("/windows/minimize.png"); // 16x16 + public static final Icon MinimizeInactive = IconLoader.getIcon("/windows/minimizeInactive.png"); // 16x16 + public static final Icon Restore = IconLoader.getIcon("/windows/restore.png"); // 16x16 + public static final Icon RestoreInactive = IconLoader.getIcon("/windows/restoreInactive.png"); // 16x16 public static class Shadow { public static final Icon Bottom = IconLoader.getIcon("/windows/shadow/bottom.png"); // 1x8 diff --git a/platform/util/src/com/intellij/util/diff/Diff.java b/platform/util/src/com/intellij/util/diff/Diff.java index 7cf36e31e5e3..627fc2db358c 100644 --- a/platform/util/src/com/intellij/util/diff/Diff.java +++ b/platform/util/src/com/intellij/util/diff/Diff.java @@ -35,22 +35,6 @@ import java.util.BitSet; public class Diff { private static final Logger LOG = Logger.getInstance("#com.intellij.util.diff.Diff"); - @Nullable - public static Change buildChangesSomehow(@NotNull T[] objects1, @NotNull T[] objects2) { - try { - return buildChanges(objects1, objects2); - } - catch (FilesTooBigForDiffException e) { - final int startShift = getStartShift(objects1, objects2); - final int endCut = getEndCut(objects1, objects2, startShift); - - int trimmedLength1 = objects1.length - startShift - endCut; - int trimmedLength2 = objects2.length - startShift - endCut; - - return new Change(startShift, startShift, trimmedLength1, trimmedLength2, null); - } - } - @Nullable public static Change buildChanges(@NotNull CharSequence before, @NotNull CharSequence after) throws FilesTooBigForDiffException { final String[] strings1 = LineTokenizer.tokenize(before, false); diff --git a/platform/vcs-impl/src/com/intellij/diff/Block.java b/platform/vcs-impl/src/com/intellij/diff/Block.java index 5bff2d3acbf0..1e333cf119a4 100644 --- a/platform/vcs-impl/src/com/intellij/diff/Block.java +++ b/platform/vcs-impl/src/com/intellij/diff/Block.java @@ -15,6 +15,9 @@ */ package com.intellij.diff; +import com.intellij.diff.comparison.ByLine; +import com.intellij.diff.comparison.ComparisonPolicy; +import com.intellij.diff.comparison.DiffTooBigException; import com.intellij.diff.comparison.iterables.DiffIterableUtil; import com.intellij.diff.comparison.iterables.FairDiffIterable; import com.intellij.diff.util.Range; @@ -64,39 +67,50 @@ public class Block { int end = -1; int shift = 0; - FairDiffIterable iterable = DiffIterableUtil.diffSomehow(prevContent, mySource, DumbProgressIndicator.INSTANCE); - for (Pair pair : DiffIterableUtil.iterateAll(iterable)) { - Boolean equals = pair.second; - Range range = pair.first; - if (!equals) { - if (Math.max(myStart, range.start2) < Math.min(myEnd, range.end2)) { - // ranges intersect - if (range.start2 <= myStart) start = range.start1; - if (range.end2 > myEnd) end = range.end1; - } - if (range.start2 > myStart) { - if (start == -1) start = myStart - shift; - if (end == -1 && range.start2 >= myEnd) end = myEnd - shift; - } + try { + FairDiffIterable iterable = ByLine.compare(Arrays.asList(prevContent), Arrays.asList(mySource), + ComparisonPolicy.IGNORE_WHITESPACES, DumbProgressIndicator.INSTANCE); - shift += (range.end2 - range.start2) - (range.end1 - range.start1); - } - else { - // intern strings, reducing memory usage - int count = range.end1 - range.start1; - for (int i = 0; i < count; i++) { - prevContent[range.start1 + i] = mySource[range.start2 + i]; + for (Pair pair : DiffIterableUtil.iterateAll(iterable)) { + Boolean equals = pair.second; + Range range = pair.first; + if (!equals) { + if (Math.max(myStart, range.start2) < Math.min(myEnd, range.end2)) { + // ranges intersect + if (range.start2 <= myStart) start = range.start1; + if (range.end2 > myEnd) end = range.end1; + } + if (range.start2 > myStart) { + if (start == -1) start = myStart - shift; + if (end == -1 && range.start2 >= myEnd) end = myEnd - shift; + } + + shift += (range.end2 - range.start2) - (range.end1 - range.start1); + } + else { + // intern strings, reducing memory usage + int count = range.end1 - range.start1; + for (int i = 0; i < count; i++) { + int prevIndex = range.start1 + i; + int sourceIndex = range.start2 + i; + if (prevContent[prevIndex].equals(mySource[sourceIndex])) { + prevContent[prevIndex] = mySource[sourceIndex]; + } + } } } - } - if (start == -1) start = myStart - shift; - if (end == -1) end = myEnd - shift; + if (start == -1) start = myStart - shift; + if (end == -1) end = myEnd - shift; - if (start < 0 || end > prevContent.length || end < start) { - LOG.error("Invalid block range: [" + start + ", " + end + "); length - " + prevContent.length); - } + if (start < 0 || end > prevContent.length || end < start) { + LOG.error("Invalid block range: [" + start + ", " + end + "); length - " + prevContent.length); + } - return new Block(prevContent, start, end); + return new Block(prevContent, start, end); + } + catch (DiffTooBigException e) { + return new Block(prevContent, 0, 0); + } } @NotNull diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RangesBuilder.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RangesBuilder.java index 56d7ca2449ee..3f12ec58f470 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RangesBuilder.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RangesBuilder.java @@ -15,17 +15,22 @@ */ package com.intellij.openapi.vcs.ex; +import com.intellij.diff.comparison.ByLine; +import com.intellij.diff.comparison.ComparisonPolicy; +import com.intellij.diff.comparison.TrimUtil; +import com.intellij.diff.comparison.iterables.DiffIterableUtil; +import com.intellij.diff.comparison.iterables.FairDiffIterable; import com.intellij.diff.util.DiffUtil; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.ArrayUtil; -import com.intellij.util.diff.Diff; +import com.intellij.openapi.progress.DumbProgressIndicator; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.vcs.ex.Range.InnerRange; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.diff.FilesTooBigForDiffException; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; -import java.util.Collections; import java.util.List; public class RangesBuilder { @@ -45,131 +50,166 @@ public class RangesBuilder { @NotNull public static List createRanges(@NotNull List current, @NotNull List vcs, - int shift, + int currentShift, int vcsShift, boolean innerWhitespaceChanges) throws FilesTooBigForDiffException { - Diff.Change ch = Diff.buildChanges(ArrayUtil.toStringArray(vcs), ArrayUtil.toStringArray(current)); + if (innerWhitespaceChanges) { + return createRangesSmart(current, vcs, currentShift, vcsShift); + } + else { + return createRangesSimple(current, vcs, currentShift, vcsShift); + } + } + + @NotNull + private static List createRangesSimple(@NotNull List current, + @NotNull List vcs, + int currentShift, + int vcsShift) throws FilesTooBigForDiffException { + FairDiffIterable iterable = ByLine.compare(vcs, current, ComparisonPolicy.DEFAULT, DumbProgressIndicator.INSTANCE); List result = new ArrayList(); - while (ch != null) { - if (innerWhitespaceChanges) { - result.add(createOnSmart(ch, shift, vcsShift, current, vcs)); - } - else { - result.add(createOn(ch, shift, vcsShift)); - } - ch = ch.link; + for (com.intellij.diff.util.Range range : iterable.iterateChanges()) { + int vcsLine1 = vcsShift + range.start1; + int vcsLine2 = vcsShift + range.end1; + int currentLine1 = currentShift + range.start2; + int currentLine2 = currentShift + range.end2; + + result.add(new Range(currentLine1, currentLine2, vcsLine1, vcsLine2)); } return result; } - private static Range createOn(@NotNull Diff.Change change, int shift, int vcsShift) { - int offset1 = shift + change.line1; - int offset2 = offset1 + change.inserted; + @NotNull + private static List createRangesSmart(@NotNull List current, + @NotNull List vcs, + int shift, + int vcsShift) throws FilesTooBigForDiffException { + FairDiffIterable iwIterable = ByLine.compare(vcs, current, ComparisonPolicy.IGNORE_WHITESPACES, DumbProgressIndicator.INSTANCE); - int uOffset1 = vcsShift + change.line0; - int uOffset2 = uOffset1 + change.deleted; + RangeBuilder rangeBuilder = new RangeBuilder(current, vcs, shift, vcsShift); - return new Range(offset1, offset2, uOffset1, uOffset2); - } + for (Pair pair : DiffIterableUtil.iterateAll(iwIterable)) { + com.intellij.diff.util.Range range = pair.first; + Boolean equals = pair.second; - private static Range createOnSmart(@NotNull Diff.Change change, - int shift, - int vcsShift, - @NotNull List current, - @NotNull List vcs) throws FilesTooBigForDiffException { - byte type = getChangeType(change); + if (equals) { + int count = range.end1 - range.start1; + for (int i = 0; i < count; i++) { + int vcsIndex = range.start1 + i; + int currentIndex = range.start2 + i; + String vcsLine = vcs.get(vcsIndex); + String currentLine = current.get(currentIndex); - int offset1 = shift + change.line1; - int offset2 = offset1 + change.inserted; - - int uOffset1 = vcsShift + change.line0; - int uOffset2 = uOffset1 + change.deleted; - - if (type != Range.MODIFIED) { - return new Range(offset1, offset2, uOffset1, uOffset2, Collections.singletonList(new Range.InnerRange(offset1, offset2, type))); - } - - LineWrapper[] lines1 = new LineWrapper[change.deleted]; - LineWrapper[] lines2 = new LineWrapper[change.inserted]; - for (int i = 0; i < change.deleted; i++) { - lines1[i] = new LineWrapper(vcs.get(i + change.line0)); - } - for (int i = 0; i < change.inserted; i++) { - lines2[i] = new LineWrapper(current.get(i + change.line1)); - } - - Diff.Change ch = Diff.buildChanges(lines1, lines2); - - List inner = new ArrayList(); - - int last0 = 0; - int last1 = 0; - while (ch != null) { - if (ch.line0 != last0 && ch.line1 != last1) { - byte innerType = Range.EQUAL; - int innerStart = shift + change.line1 + last1; - int innerEnd = shift + change.line1 + ch.line1; - inner.add(new Range.InnerRange(innerStart, innerEnd, innerType)); + if (vcsLine.equals(currentLine)) { + rangeBuilder.flushChange(); + } + else { + rangeBuilder.markChangedWhitespaces(vcsIndex, currentIndex); + } + } + } + else { + rangeBuilder.markChanged(range.start1, range.end1, range.start2, range.end2); } - - byte innerType = getChangeType(ch); - int innerStart = shift + change.line1 + ch.line1; - int innerEnd = innerStart + ch.inserted; - inner.add(new Range.InnerRange(innerStart, innerEnd, innerType)); - - last0 = ch.line0 + ch.deleted; - last1 = ch.line1 + ch.inserted; - - ch = ch.link; - } - if (change.deleted != last0 && change.inserted != last1) { - byte innerType = Range.EQUAL; - int innerStart = shift + change.line1 + last1; - int innerEnd = shift + change.line1 + change.inserted; - inner.add(new Range.InnerRange(innerStart, innerEnd, innerType)); } - return new Range(offset1, offset2, uOffset1, uOffset2, inner); + return rangeBuilder.finish(); } - private static byte getChangeType(@NotNull Diff.Change change) { - if ((change.deleted > 0) && (change.inserted > 0)) return Range.MODIFIED; - if ((change.deleted > 0)) return Range.DELETED; - if ((change.inserted > 0)) return Range.INSERTED; - LOG.error("Unknown change type"); - return Range.EQUAL; - } + private static class RangeBuilder { + @NotNull private final List myCurrent; + @NotNull private final List myVcs; + private final int myCurrentShift; + private final int myVcsShift; - private static class LineWrapper { - @NotNull private final String myLine; - private final int myHash; + @NotNull private final List myRanges = new ArrayList<>(); - public LineWrapper(@NotNull String line) { - myLine = line; - myHash = StringUtil.stringHashCodeIgnoreWhitespaces(line); + private com.intellij.diff.util.Range change; + private ArrayList innerRanges; + + public RangeBuilder(@NotNull List current, + @NotNull List vcs, + int currentShift, + int vcsShift) { + myCurrent = current; + myVcs = vcs; + myCurrentShift = currentShift; + myVcsShift = vcsShift; + } + + public void flushChange() { + if (change == null) return; + + for (InnerRange range : innerRanges) { + range.shift(myCurrentShift); + } + innerRanges.trimToSize(); + + change = TrimUtil.expand(myVcs, myCurrent, change.start1, change.start2, change.end1, change.end2); + + int currentLine1 = myCurrentShift + change.start2; + int currentLine2 = myCurrentShift + change.end2; + int vcsLine1 = myVcsShift + change.start1; + int vcsLine2 = myVcsShift + change.end1; + myRanges.add(new Range(currentLine1, currentLine2, vcsLine1, vcsLine2, innerRanges)); + + change = null; + innerRanges = null; + } + + public void markChangedWhitespaces(int vcsIndex, int currentIndex) { + appendChangedLine(vcsIndex, vcsIndex + 1, currentIndex, currentIndex + 1); + appendInnerEquals(vcsIndex, vcsIndex + 1, currentIndex, currentIndex + 1); + } + + public void markChanged(int vcsStart, int vcsEnd, int currentStart, int currentEnd) { + appendChangedLine(vcsStart, vcsEnd, currentStart, currentEnd); + appendInnerChange(vcsStart, vcsEnd, currentStart, currentEnd); } @NotNull - public String getLine() { - return myLine; + public List finish() { + flushChange(); + return myRanges; } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - LineWrapper wrapper = (LineWrapper)o; - - if (myHash != wrapper.myHash) return false; - - return StringUtil.equalsIgnoreWhitespaces(myLine, wrapper.myLine); + private void appendChangedLine(int vcsStart, int vcsEnd, int currentStart, int currentEnd) { + if (change == null) { + change = new com.intellij.diff.util.Range(vcsStart, vcsEnd, currentStart, currentEnd); + innerRanges = new ArrayList<>(); + } + else { + assert vcsStart == change.end1; + assert currentStart == change.end2; + change = new com.intellij.diff.util.Range(change.start1, vcsEnd, change.start2, currentEnd); + } } - @Override - public int hashCode() { - return myHash; + private void appendInnerChange(int vcsStart, int vcsEnd, int currentStart, int currentEnd) { + byte type = getChangeType(vcsStart, vcsEnd, currentStart, currentEnd); + innerRanges.add(new InnerRange(currentStart, currentEnd, type)); + } + + private void appendInnerEquals(int vcsStart, int vcsEnd, int currentStart, int currentEnd) { + InnerRange last = ContainerUtil.getLastItem(innerRanges); + if (last == null || last.getType() != Range.EQUAL) { + innerRanges.add(new InnerRange(currentStart, currentEnd, Range.EQUAL)); + } + else { + assert currentStart == last.getLine2(); + innerRanges.set(innerRanges.size() - 1, new InnerRange(last.getLine1(), currentEnd, Range.EQUAL)); + } } } + + private static byte getChangeType(int vcsStart, int vcsEnd, int currentStart, int currentEnd) { + int deleted = vcsEnd - vcsStart; + int inserted = currentEnd - currentStart; + if (deleted > 0 && inserted > 0) return Range.MODIFIED; + if (deleted > 0) return Range.DELETED; + if (inserted > 0) return Range.INSERTED; + LOG.error("Unknown change type"); + return Range.EQUAL; + } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/VcsSelectionHistoryDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/VcsSelectionHistoryDialog.java index 6f9cb5d3c0f8..891108da0b8e 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/VcsSelectionHistoryDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/VcsSelectionHistoryDialog.java @@ -228,7 +228,8 @@ public class VcsSelectionHistoryDialog extends FrameWrapper implements DataProvi @Override protected void notifyError(@NotNull VcsException e) { SwingUtilities.invokeLater(() -> { - if (!VcsSelectionHistoryDialog.this.getFrame().isShowing()) return; + VcsSelectionHistoryDialog dialog = VcsSelectionHistoryDialog.this; + if (dialog.isDisposed() || !dialog.getFrame().isShowing()) return; PopupUtil.showBalloonForComponent(mySplitter, canNoLoadMessage(e), MessageType.ERROR, true, myProject); }); } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/statistics/VcsLogRepoSizeCollector.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/statistics/VcsLogRepoSizeCollector.java index 5fb354443773..37c41b6033d4 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/statistics/VcsLogRepoSizeCollector.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/statistics/VcsLogRepoSizeCollector.java @@ -59,7 +59,7 @@ public class VcsLogRepoSizeCollector extends AbstractApplicationUsagesCollector asList(0, 1, 100, 1000, 10 * 1000, 100 * 1000, 500 * 1000))); for (VcsKey vcs : groupedRoots.keySet()) { usages.add(StatisticsUtilKt.getCountingUsage("data." + vcs.getName().toLowerCase() + ".root.count", groupedRoots.get(vcs).size(), - asList(0, 1, 2, 5, 8, 15, 30, 50, 100))); + asList(0, 1, 2, 5, 8, 15, 30, 50, 100, 500, 1000))); } return usages; } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/synthetic/GrTraitMethod.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/synthetic/GrTraitMethod.java index 19f30aab7a27..af4225639c66 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/synthetic/GrTraitMethod.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/impl/synthetic/GrTraitMethod.java @@ -25,6 +25,7 @@ public class GrTraitMethod extends LightMethod implements PsiMirrorElement { @NotNull PsiMethod method, @NotNull PsiSubstitutor substitutor) { super(containingClass, method, substitutor); + setNavigationElement(method); } @Override diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/Gr23HighlightingTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/Gr23HighlightingTest.groovy index c0e2bc27e838..64d5b720afac 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/Gr23HighlightingTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/highlighting/Gr23HighlightingTest.groovy @@ -19,6 +19,7 @@ import com.intellij.codeInspection.InspectionProfileEntry import com.intellij.ide.highlighter.JavaFileType import com.intellij.testFramework.LightProjectDescriptor import org.jetbrains.plugins.groovy.GroovyLightProjectDescriptor +import org.jetbrains.plugins.groovy.codeInspection.GroovyUnusedDeclarationInspection import org.jetbrains.plugins.groovy.codeInspection.assignment.GroovyAssignabilityCheckInspection import org.jetbrains.plugins.groovy.codeInspection.untypedUnresolvedAccess.GrUnresolvedAccessInspection @@ -460,4 +461,16 @@ class C implements T { ''' myFixture.testHighlighting false, false, false } + + void 'test trait method usages'() { + testHighlighting '''\ +trait T { + def getFoo() {} +} + +class A implements T {} + +new A().foo +''', GroovyUnusedDeclarationInspection + } } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java index 5ab441fd84bf..edd3a7f240ac 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java @@ -18,7 +18,6 @@ import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileEvent; import com.intellij.psi.PsiDirectory; import com.intellij.util.DocumentUtil; import com.intellij.util.Function; @@ -31,7 +30,10 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; public class CCUtils { public static final String ANSWER_EXTENSION_DOTTED = ".answer."; @@ -144,11 +146,8 @@ public class CCUtils { return generatedRoot.get(); } - /** - * @param requestor {@link VirtualFileEvent#getRequestor} - */ @Nullable - public static VirtualFile generateFolder(@NotNull Project project, @NotNull Module module, @Nullable Object requestor, String name) { + public static VirtualFile generateFolder(@NotNull Project project, @NotNull Module module, String name) { VirtualFile generatedRoot = getGeneratedFilesFolder(project, module); if (generatedRoot == null) { return null; @@ -159,9 +158,9 @@ public class CCUtils { ApplicationManager.getApplication().runWriteAction(() -> { try { if (folder.get() != null) { - folder.get().delete(requestor); + folder.get().delete(null); } - folder.set(generatedRoot.createChildDirectory(requestor, name)); + folder.set(generatedRoot.createChildDirectory(null, name)); } catch (IOException e) { LOG.info("Failed to generate folder " + name, e); diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAddAnswerPlaceholder.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAddAnswerPlaceholder.java index 41275862f6f0..0643eaa8cf0c 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAddAnswerPlaceholder.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAddAnswerPlaceholder.java @@ -26,7 +26,7 @@ import java.util.List; public class CCAddAnswerPlaceholder extends CCAnswerPlaceholderAction { public CCAddAnswerPlaceholder() { - super("Add/Delete Answer Placeholder", "Add/Delete answer placeholder", null); + super("Add/Delete Answer Placeholder", "Add/Delete answer placeholder"); } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAnswerPlaceholderAction.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAnswerPlaceholderAction.java index 4a0479403094..ba3db4d16fb1 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAnswerPlaceholderAction.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCAnswerPlaceholderAction.java @@ -14,12 +14,10 @@ import com.jetbrains.edu.learning.courseFormat.TaskFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.swing.*; - abstract public class CCAnswerPlaceholderAction extends DumbAwareAction { - protected CCAnswerPlaceholderAction(@Nullable String text, @Nullable String description, @Nullable Icon icon) { - super(text, description, icon); + protected CCAnswerPlaceholderAction(@Nullable String text, @Nullable String description) { + super(text, description, null); } @Nullable diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java index 8425c71b0483..50e2336444f5 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateCourseArchive.java @@ -77,7 +77,7 @@ public class CCCreateCourseArchive extends DumbAwareAction { final Course course = StudyTaskManager.getInstance(project).getCourse(); if (course == null) return; final VirtualFile baseDir = project.getBaseDir(); - VirtualFile archiveFolder = CCUtils.generateFolder(project, module, null, zipName); + VirtualFile archiveFolder = CCUtils.generateFolder(project, module, zipName); if (archiveFolder == null) { return; } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCDeleteAllAnswerPlaceholdersAction.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCDeleteAllAnswerPlaceholdersAction.java index 0916e1b678f4..7c10fba712b4 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCDeleteAllAnswerPlaceholdersAction.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCDeleteAllAnswerPlaceholdersAction.java @@ -107,7 +107,7 @@ public class CCDeleteAllAnswerPlaceholdersAction extends DumbAwareAction { private static class ClearPlaceholders implements UndoableAction { private final List myPlaceholders; private final Editor myEditor; - TaskFile myTaskFile; + private final TaskFile myTaskFile; public ClearPlaceholders(TaskFile taskFile, List placeholders, Editor editor) { myTaskFile = taskFile; diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCEditAnswerPlaceholder.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCEditAnswerPlaceholder.java index 622ddacf1cac..72a33a1e0261 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCEditAnswerPlaceholder.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCEditAnswerPlaceholder.java @@ -12,7 +12,7 @@ import org.jetbrains.annotations.NotNull; public class CCEditAnswerPlaceholder extends CCAnswerPlaceholderAction { public CCEditAnswerPlaceholder() { - super("Edit Answer Placeholder", "Edit answer placeholder", null); + super("Edit Answer Placeholder", "Edit answer placeholder"); } @Override diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCLessonMoveHandlerDelegate.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCLessonMoveHandlerDelegate.java index d9d6f5adc3df..15da6057e0c7 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCLessonMoveHandlerDelegate.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCLessonMoveHandlerDelegate.java @@ -21,7 +21,6 @@ import com.intellij.util.Function; import com.jetbrains.edu.coursecreator.CCUtils; import com.jetbrains.edu.coursecreator.ui.CCMoveStudyItemDialog; import com.jetbrains.edu.learning.StudyTaskManager; -import com.jetbrains.edu.coursecreator.CCUtils; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.Course; diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCTaskMoveHandlerDelegate.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCTaskMoveHandlerDelegate.java index e69a1eeb8523..b37e0093c0b4 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCTaskMoveHandlerDelegate.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/handlers/CCTaskMoveHandlerDelegate.java @@ -17,7 +17,6 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; import com.intellij.refactoring.move.MoveCallback; import com.intellij.refactoring.move.MoveHandlerDelegate; -import com.intellij.util.Function; import com.jetbrains.edu.coursecreator.CCUtils; import com.jetbrains.edu.coursecreator.ui.CCMoveStudyItemDialog; import com.jetbrains.edu.learning.StudyTaskManager; @@ -25,7 +24,6 @@ import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; -import com.jetbrains.edu.learning.courseFormat.StudyItem; import com.jetbrains.edu.learning.courseFormat.Task; import org.jetbrains.annotations.Nullable; @@ -91,6 +89,9 @@ public class CCTaskMoveHandlerDelegate extends MoveHandlerDelegate { final Course course = StudyTaskManager.getInstance(project).getCourse(); final PsiDirectory sourceDirectory = (PsiDirectory)elements[0]; + if (course == null) { + return; + } final Task taskToMove = EduUtils.getTask(sourceDirectory, course); if (taskToMove == null) { return; diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/projectView/CCTreeStructureProvider.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/projectView/CCTreeStructureProvider.java index 3ba35d688992..39d0ab89145b 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/projectView/CCTreeStructureProvider.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/projectView/CCTreeStructureProvider.java @@ -12,7 +12,6 @@ import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.projectView.StudyTreeStructureProvider; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; import java.util.Collection; public class CCTreeStructureProvider extends StudyTreeStructureProvider { @@ -24,7 +23,7 @@ public class CCTreeStructureProvider extends StudyTreeStructureProvider { if (!needModify(parent)) { return children; } - Collection modifiedChildren = new ArrayList(super.modify(parent, children, settings)); + Collection modifiedChildren = super.modify(parent, children, settings); for (AbstractTreeNode node : children) { Project project = node.getProject(); @@ -41,7 +40,8 @@ public class CCTreeStructureProvider extends StudyTreeStructureProvider { if (virtualFile == null) { continue; } - if (StudyUtils.getTaskFile(project, virtualFile) == null && !StudyUtils.isTaskDescriptionFile(virtualFile.getName())) { + if (project != null && StudyUtils.getTaskFile(project, virtualFile) == null + && !StudyUtils.isTaskDescriptionFile(virtualFile.getName())) { modifiedChildren.add(new CCStudentInvisibleFileNode(project, ((PsiFileNode)node).getValue(), settings)); } } diff --git a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateStudyItemPanel.java b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateStudyItemPanel.java index fd4854ffb359..572ba0806508 100644 --- a/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateStudyItemPanel.java +++ b/python/educational-core/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateStudyItemPanel.java @@ -8,7 +8,7 @@ public class CCCreateStudyItemPanel extends JPanel { private JPanel myPanel; private JTextField myNameField; private CCItemPositionPanel myPositionalPanel; - private String myThresholdName; + private final String myThresholdName; public CCCreateStudyItemPanel(String itemName, String thresholdName, int thresholdIndex) { myThresholdName = thresholdName; diff --git a/python/educational-core/student/resources/META-INF/plugin.xml b/python/educational-core/student/resources/META-INF/plugin.xml index d950bc814f9b..aafd06787e4e 100644 --- a/python/educational-core/student/resources/META-INF/plugin.xml +++ b/python/educational-core/student/resources/META-INF/plugin.xml @@ -89,7 +89,6 @@ displayName="Education"/> - diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java index 44cf72f11f58..cfb8faa2bbb0 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyProjectComponent.java @@ -25,15 +25,14 @@ import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowAnchor; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.util.containers.hash.HashMap; +import com.jetbrains.edu.learning.actions.StudyActionWithShortcut; import com.jetbrains.edu.learning.core.EduNames; import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseFormat.Lesson; import com.jetbrains.edu.learning.courseFormat.Task; import com.jetbrains.edu.learning.courseFormat.TaskFile; -import com.jetbrains.edu.learning.actions.*; import com.jetbrains.edu.learning.editor.StudyEditorFactoryListener; -import com.jetbrains.edu.learning.ui.StudyProgressToolWindowFactory; import com.jetbrains.edu.learning.ui.StudyToolWindow; import com.jetbrains.edu.learning.ui.StudyToolWindowFactory; import javafx.application.Platform; @@ -93,13 +92,9 @@ public class StudyProjectComponent implements ProjectComponent { final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject); registerToolWindows(toolWindowManager); final ToolWindow studyToolWindow = toolWindowManager.getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW); - final ToolWindow progressToolWindow = toolWindowManager.getToolWindow(StudyProgressToolWindowFactory.ID); if (studyToolWindow != null) { studyToolWindow.show(null); - } - if (progressToolWindow != null) { StudyUtils.initToolWindows(myProject); - progressToolWindow.show(null); } } } @@ -130,10 +125,6 @@ public class StudyProjectComponent implements ProjectComponent { if (toolWindow == null) { toolWindowManager.registerToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW, true, ToolWindowAnchor.RIGHT, myProject, true); } - ToolWindow progressToolWindow = toolWindowManager.getToolWindow(StudyProgressToolWindowFactory.ID); - if (progressToolWindow == null) { - toolWindowManager.registerToolWindow(StudyProgressToolWindowFactory.ID, true, ToolWindowAnchor.LEFT, myProject, true, true); - } } private void updateCourse() { @@ -221,10 +212,6 @@ public class StudyProjectComponent implements ProjectComponent { if (toolWindow != null) { toolWindow.getContentManager().removeAllContents(false); } - final ToolWindow progressToolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(StudyProgressToolWindowFactory.ID); - if (progressToolWindow != null) { - progressToolWindow.getContentManager().removeAllContents(false); - } KeymapManagerEx keymapManager = KeymapManagerEx.getInstanceEx(); for (Keymap keymap : keymapManager.getAllKeymaps()) { List> pairs = myDeletedShortcuts.get(keymap); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java index b85cad3d58bf..69542b448629 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/StudyUtils.java @@ -42,8 +42,8 @@ import com.intellij.psi.PsiFile; import com.intellij.ui.JBColor; import com.intellij.ui.awt.RelativePoint; import com.intellij.ui.content.Content; -import com.intellij.util.TimeoutUtil; import com.intellij.util.ObjectUtils; +import com.intellij.util.TimeoutUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.text.MarkdownUtil; import com.intellij.util.ui.UIUtil; @@ -56,7 +56,6 @@ import com.jetbrains.edu.learning.core.EduUtils; import com.jetbrains.edu.learning.courseFormat.*; import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; import com.jetbrains.edu.learning.editor.StudyEditor; -import com.jetbrains.edu.learning.ui.StudyProgressToolWindowFactory; import com.jetbrains.edu.learning.ui.StudyToolWindow; import com.jetbrains.edu.learning.ui.StudyToolWindowFactory; import com.petebevin.markdown.MarkdownProcessor; @@ -165,10 +164,17 @@ public class StudyUtils { } public static void updateToolWindows(@NotNull final Project project) { - updateStudyToolWindow(project); - - final ToolWindowManager windowManager = ToolWindowManager.getInstance(project); - createProgressToolWindowContent(project, windowManager); + final StudyToolWindow studyToolWindow = getStudyToolWindow(project); + if (studyToolWindow != null) { + String taskText = getTaskText(project); + if (taskText != null) { + studyToolWindow.setTaskText(taskText, null, project); + } + else { + LOG.warn("Task text is null"); + } + studyToolWindow.updateCourseProgress(project); + } } public static void initToolWindows(@NotNull final Project project) { @@ -177,15 +183,8 @@ public class StudyUtils { StudyToolWindowFactory factory = new StudyToolWindowFactory(); factory.createToolWindowContent(project, windowManager.getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW)); - createProgressToolWindowContent(project, windowManager); } - private static void createProgressToolWindowContent(@NotNull Project project, ToolWindowManager windowManager) { - windowManager.getToolWindow(StudyProgressToolWindowFactory.ID).getContentManager().removeAllContents(false); - StudyProgressToolWindowFactory windowFactory = new StudyProgressToolWindowFactory(); - windowFactory.createToolWindowContent(project, windowManager.getToolWindow(StudyProgressToolWindowFactory.ID)); - } - @Nullable public static StudyToolWindow getStudyToolWindow(@NotNull final Project project) { ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW); @@ -558,19 +557,6 @@ public class StudyUtils { return taskFile != null ? taskFile.getTask() : null; } - public static void updateStudyToolWindow(Project project) { - final StudyToolWindow studyToolWindow = getStudyToolWindow(project); - if (studyToolWindow != null) { - String taskText = getTaskText(project); - if (taskText != null) { - studyToolWindow.setTaskText(taskText, null, project); - } - else { - LOG.warn("Task text is null"); - } - } - } - public static boolean isStudyProject(@NotNull Project project) { return StudyTaskManager.getInstance(project).getCourse() != null; } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java index 04d692333e01..39ef983b5800 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java @@ -21,7 +21,7 @@ import javax.swing.*; public abstract class StudyCheckAction extends StudyActionWithShortcut { public static final String SHORTCUT = "ctrl alt pressed ENTER"; - protected Ref myCheckInProgress = new Ref<>(false); + protected final Ref myCheckInProgress = new Ref<>(false); public StudyCheckAction() { super("Check Task (" + KeymapUtil.getShortcutText(new KeyboardShortcut(KeyStroke.getKeyStroke(SHORTCUT), null)) + ")", "Check current task", InteractiveLearningIcons.CheckTask); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyNextWindowAction.java b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyNextWindowAction.java index 01fd8dcbceec..17769a988282 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyNextWindowAction.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/actions/StudyNextWindowAction.java @@ -26,7 +26,7 @@ public class StudyNextWindowAction extends StudyWindowNavigationAction { List windows = window.getTaskFile().getAnswerPlaceholders(); if (StudyUtils.indexIsValid(index, windows)) { int newIndex = index + 1; - return newIndex == windows.size() ? windows.get(0) : windows.get(newIndex); + return windows.get(newIndex == windows.size() ? 0 : newIndex); } return null; } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java index fd98e1690139..0a9d5ca85394 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckTask.java @@ -24,6 +24,7 @@ import com.jetbrains.edu.learning.courseFormat.StudyStatus; import com.jetbrains.edu.learning.courseFormat.Task; import com.jetbrains.edu.learning.stepic.EduAdaptiveStepicConnector; import com.jetbrains.edu.learning.stepic.EduStepicConnector; +import com.jetbrains.edu.learning.stepic.StepicUser; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -36,9 +37,10 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun protected final VirtualFile myTaskDir; protected final StudyTaskManager myTaskManger; private final StudyStatus myStatusBeforeCheck; - private Ref myCheckInProcess; + private final Ref myCheckInProcess; private final Process myTestProcess; private final String myCommandLine; + private final String FAILED_CHECK_LAUNCH = "Failed to launch checking"; public StudyCheckTask(Project project, StudyState studyState, Ref checkInProcess, Process testProcess, String commandLine) { super(project, "Checking Task"); @@ -118,7 +120,7 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun String stderr = output.getStderr(); if (!stderr.isEmpty()) { ApplicationManager.getApplication().invokeLater(() -> - StudyCheckUtils.showTestResultPopUp("Failed to launch checking", + StudyCheckUtils.showTestResultPopUp(FAILED_CHECK_LAUNCH, MessageType.WARNING.getPopupBackground(), myProject)); //log error output of tests @@ -149,7 +151,7 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun runAfterTaskCheckedActions(); } else { - ApplicationManager.getApplication().invokeLater(() -> StudyCheckUtils.showTestResultPopUp("Failed to launch checking", + ApplicationManager.getApplication().invokeLater(() -> StudyCheckUtils.showTestResultPopUp(FAILED_CHECK_LAUNCH, MessageType.WARNING .getPopupBackground(), myProject)); @@ -214,8 +216,10 @@ public class StudyCheckTask extends com.intellij.openapi.progress.Task.Backgroun protected void postAttemptToStepic(@NotNull StudyTestsOutputParser.TestsOutput testsOutput) { final StudyTaskManager studySettings = StudyTaskManager.getInstance(myProject); - final String login = studySettings.getUser().getEmail(); - final String password = StringUtil.isEmptyOrSpaces(login) ? "" : studySettings.getUser().getPassword(); + final StepicUser user = studySettings.getUser(); + if (user == null) return; + final String login = user.getEmail(); + final String password = StringUtil.isEmptyOrSpaces(login) ? "" : user.getPassword(); EduStepicConnector.postAttempt(myTask, testsOutput.isSuccess(), login, password); } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java index a14e89fa6c73..73812b9be5bc 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudyCheckUtils.java @@ -185,7 +185,7 @@ public class StudyCheckUtils { if (virtualFile == null) { continue; } - EduUtils.flushWindows(taskFile, virtualFile, true); + EduUtils.flushWindows(taskFile, virtualFile); } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java index 9621efbd7d39..e67c2b62b227 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/checker/StudySmartChecker.java @@ -62,7 +62,7 @@ public class StudySmartChecker { String text = usersDocument.getText(new TextRange(userStart, userEnd)); windowDocument.replaceString(start, end, text); ApplicationManager.getApplication().runWriteAction(() -> documentManager.saveDocument(windowDocument)); - VirtualFile fileWindows = EduUtils.flushWindows(windowTaskFile, windowCopy, true); + VirtualFile fileWindows = EduUtils.flushWindows(windowTaskFile, windowCopy); Process smartTestProcess = testRunner.createCheckProcess(project, windowCopy.getPath()); final CapturingProcessHandler handler = new CapturingProcessHandler(smartTestProcess, null, windowCopy.getPath()); final ProcessOutput output = handler.runProcess(); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduNames.java b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduNames.java index a9537e24d6c5..cf6d4fd21c2e 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduNames.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduNames.java @@ -41,11 +41,11 @@ public class EduNames { public static final String SANDBOX_DIR = "Sandbox"; public static final String COURSE_META_FILE = "course.json"; public static final String STUDY = "Study"; - public static String PYCHARM_ADDITIONAL = "PyCharm additional materials"; + public static final String PYCHARM_ADDITIONAL = "PyCharm additional materials"; public static final String PYCHARM = "PyCharm"; - public static String PLACEHOLDER = "Answer Placeholder"; - public static String SRC = "src"; + public static final String PLACEHOLDER = "Answer Placeholder"; + public static final String SRC = "src"; private EduNames() { } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduUtils.java b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduUtils.java index e6e520fca01c..edad8e41cacd 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduUtils.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/core/EduUtils.java @@ -31,7 +31,7 @@ public class EduUtils { } private static final Logger LOG = Logger.getInstance(EduUtils.class.getName()); - public static Comparator INDEX_COMPARATOR = (o1, o2) -> o1.getIndex() - o2.getIndex(); + public static final Comparator INDEX_COMPARATOR = (o1, o2) -> o1.getIndex() - o2.getIndex(); public static void enableAction(@NotNull final AnActionEvent event, boolean isEnable) { final Presentation presentation = event.getPresentation(); @@ -64,8 +64,7 @@ public class EduUtils { @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") @Nullable - public static VirtualFile flushWindows(@NotNull final TaskFile taskFile, @NotNull final VirtualFile file, - boolean useLength) { + public static VirtualFile flushWindows(@NotNull final TaskFile taskFile, @NotNull final VirtualFile file) { final VirtualFile taskDir = file.getParent(); VirtualFile fileWindows = null; final Document document = FileDocumentManager.getInstance().getDocument(file); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Course.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Course.java index 6b60f47f6faa..a5b88246230c 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Course.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/Course.java @@ -75,7 +75,7 @@ public class Course { } public static String getAuthorsString(@NotNull List authors) { - return StringUtil.join(authors, author -> author.getName(), ", "); + return StringUtil.join(authors, StepicUser::getName, ", "); } public void setAuthors(String[] authors) { diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/TaskFile.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/TaskFile.java index 88037f08679c..1223f43f3d10 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/TaskFile.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseFormat/TaskFile.java @@ -98,6 +98,8 @@ public class TaskFile { answerPlaceholderCopy.setPossibleAnswer(answerPlaceholder.getPossibleAnswer()); answerPlaceholderCopy.setIndex(answerPlaceholder.getIndex()); answerPlaceholderCopy.setHint(answerPlaceholder.getHint()); + final AnswerPlaceholder.MyInitialState state = answerPlaceholder.getInitialState(); + answerPlaceholderCopy.setInitialState(new AnswerPlaceholder.MyInitialState(state.getOffset(), state.getLength())); answerPlaceholdersCopy.add(answerPlaceholderCopy); } target.name = source.name; diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java b/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java index 6e031380977b..58687fc2391e 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/courseGeneration/StudyGenerator.java @@ -116,15 +116,15 @@ public class StudyGenerator { baseDir.createChildDirectory(project, EduNames.SANDBOX_DIR); File[] files = resourceRoot.listFiles( (dir, name) -> !name.contains(EduNames.LESSON) && !name.equals(EduNames.COURSE_META_FILE) && !name.equals(EduNames.HINTS)); - for (File file : files) { - File dir = new File(baseDir.getPath(), file.getName()); - if (file.isDirectory()) { - FileUtil.copyDir(file, dir); - continue; + if (files != null) { + for (File file : files) { + File dir = new File(baseDir.getPath(), file.getName()); + if (file.isDirectory()) { + FileUtil.copyDir(file, dir); + continue; + } + FileUtil.copy(file, dir); } - - FileUtil.copy(file, dir); - } } catch (IOException e) { diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/projectView/StudyDirectoryNode.java b/python/educational-core/student/src/com/jetbrains/edu/learning/projectView/StudyDirectoryNode.java index c9821a704112..5439bce17d2e 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/projectView/StudyDirectoryNode.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/projectView/StudyDirectoryNode.java @@ -142,7 +142,7 @@ public class StudyDirectoryNode extends PsiDirectoryNode { @Override public void navigate(boolean requestFocus) { final String myValueName = myValue.getName(); - if (myValueName != null && myValueName.contains(EduNames.TASK)) { + if (myValueName.contains(EduNames.TASK)) { TaskFile taskFile = null; VirtualFile virtualFile = null; for (PsiElement child : myValue.getChildren()) { @@ -186,7 +186,7 @@ public class StudyDirectoryNode extends PsiDirectoryNode { @Override public boolean expandOnDoubleClick() { final String myValueName = myValue.getName(); - if (myValueName!= null && myValueName.contains(EduNames.TASK)) { + if (myValueName.contains(EduNames.TASK)) { return false; } return super.expandOnDoubleClick(); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/settings/StudyConfigurable.java b/python/educational-core/student/src/com/jetbrains/edu/learning/settings/StudyConfigurable.java index ba47bee2c62a..7b45780e26f8 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/settings/StudyConfigurable.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/settings/StudyConfigurable.java @@ -27,7 +27,7 @@ import java.util.List; public class StudyConfigurable extends CompositeConfigurable { public static final String ID = "com.jetbrains.edu.learning.stepic.EduConfigurable"; - private JPanel myMainPanel; + private final JPanel myMainPanel; public StudyConfigurable() { myMainPanel = new JPanel(new VerticalFlowLayout()); @@ -59,11 +59,6 @@ public class StudyConfigurable extends CompositeConfigurable createConfigurables() { return ConfigurableWrapper.createConfigurables(StudyOptionsProviderEP.EP_NAME); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CourseInfo.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CourseInfo.java index 004b313054a1..b29140ba494c 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CourseInfo.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/CourseInfo.java @@ -12,7 +12,7 @@ import java.util.List; * and when project is being created */ public class CourseInfo { - public static CourseInfo INVALID_COURSE = new CourseInfo(); + public static final CourseInfo INVALID_COURSE = new CourseInfo(); @SerializedName("title") private String myName; int id; diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java index 18225c548598..d867774a22c3 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduAdaptiveStepicConnector.java @@ -403,7 +403,7 @@ public class EduAdaptiveStepicConnector { public static Pair checkTask(@NotNull final Project project, @NotNull final Task task) { int attemptId = -1; try { - attemptId = getAttemptId(project, task, EduStepicNames.ATTEMPTS); + attemptId = getAttemptId(project, task); } catch (IOException e) { LOG.warn(e.getMessage()); @@ -519,10 +519,10 @@ public class EduAdaptiveStepicConnector { return null; } - private static int getAttemptId(@NotNull final Project project, @NotNull Task task, @NotNull final String attempts) throws IOException { + private static int getAttemptId(@NotNull final Project project, @NotNull Task task) throws IOException { final StepicWrappers.AttemptToPostWrapper attemptWrapper = new StepicWrappers.AttemptToPostWrapper(task.getStepicId()); - final HttpPost post = new HttpPost(EduStepicNames.STEPIC_API_URL + attempts); + final HttpPost post = new HttpPost(EduStepicNames.STEPIC_API_URL + EduStepicNames.ATTEMPTS); post.setEntity(new StringEntity(new Gson().toJson(attemptWrapper))); final CloseableHttpClient client = getHttpClient(project); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicNames.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicNames.java index 5ece7fe02284..7710f5e392c5 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicNames.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/EduStepicNames.java @@ -22,5 +22,5 @@ public class EduStepicNames { public static final String STEPS = "/steps"; public static final String SECTIONS = "/sections/"; public static final String ENROLLMENTS = "/enrollments"; - public static String STEPIC_SIGN_IN_LINK = "https://stepic.org/accounts/signup/?next=/users/16516293/learn"; + public static final String STEPIC_SIGN_IN_LINK = "https://stepic.org/accounts/signup/?next=/users/16516293/learn"; } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicAdaptiveReactionsPanel.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicAdaptiveReactionsPanel.java index 04a52dec8e7d..24db1b1575aa 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicAdaptiveReactionsPanel.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicAdaptiveReactionsPanel.java @@ -38,8 +38,8 @@ public class StepicAdaptiveReactionsPanel extends JPanel { setLayout(new GridBagLayout()); setBackground(UIUtil.getTextFieldBackground()); - myHardPanel = new ReactionButtonPanel(HARD_REACTION, HARD_LABEL_TOOLTIP, SOLVED_TASK_TOOLTIP, TOO_HARD_REACTION); - myBoringPanel = new ReactionButtonPanel(BORING_REACTION, BORING_LABEL_TOOLTIP, SOLVED_TASK_TOOLTIP, TOO_BORING_REACTION); + myHardPanel = new ReactionButtonPanel(HARD_REACTION, HARD_LABEL_TOOLTIP, TOO_HARD_REACTION); + myBoringPanel = new ReactionButtonPanel(BORING_REACTION, BORING_LABEL_TOOLTIP, TOO_BORING_REACTION); addFileListener(); final GridBagConstraints c = new GridBagConstraints(); @@ -99,7 +99,6 @@ public class StepicAdaptiveReactionsPanel extends JPanel { public ReactionButtonPanel(@NotNull final String text, @NotNull final String enabledTooltip, - @NotNull final String disabledTooltip, int reaction) { com.jetbrains.edu.learning.courseFormat.Task task = StudyUtils.getTaskFromSelectedEditor(myProject); final boolean isEnabled = task != null && task.getStatus() != StudyStatus.Solved; @@ -108,7 +107,7 @@ public class StepicAdaptiveReactionsPanel extends JPanel { myButtonPanel = new JPanel(); myButtonPanel.setLayout(new BoxLayout(myButtonPanel, BoxLayout.PAGE_AXIS)); - myButtonPanel.setToolTipText(isEnabled ? enabledTooltip : disabledTooltip); + myButtonPanel.setToolTipText(isEnabled ? enabledTooltip : SOLVED_TASK_TOOLTIP); myButtonPanel.add(Box.createVerticalStrut(5)); myButtonPanel.add(myLabel); myButtonPanel.add(Box.createVerticalStrut(5)); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicStudyOptions.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicStudyOptions.java index ae823206ddf4..9b48578db82a 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicStudyOptions.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicStudyOptions.java @@ -108,9 +108,10 @@ public class StepicStudyOptions implements StudyOptionsProvider { Project project = StudyUtils.getStudyProject(); if (project != null) { final StepicUser user = StudyTaskManager.getInstance(project).getUser(); - setLogin(user.getEmail()); - setPassword(DEFAULT_PASSWORD_TEXT); - + if (user != null) { + setLogin(user.getEmail()); + setPassword(DEFAULT_PASSWORD_TEXT); + } resetCredentialsModification(); } else { diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java index 83363d59b082..c5820eaf95cf 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/stepic/StepicWrappers.java @@ -61,15 +61,12 @@ public class StepicWrappers { for (final Map.Entry entry : task.getTaskFiles().entrySet()) { final TaskFile taskFile = new TaskFile(); TaskFile.copy(entry.getValue(), taskFile); - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - final VirtualFile taskDir = task.getTaskDir(project); - assert taskDir != null; - VirtualFile ideaDir = project.getBaseDir().findChild(".idea"); - assert ideaDir != null; - EduUtils.createStudentFileFromAnswer(project, ideaDir, taskDir, entry.getKey(), taskFile); - } + ApplicationManager.getApplication().runWriteAction(() -> { + final VirtualFile taskDir = task.getTaskDir(project); + assert taskDir != null; + VirtualFile ideaDir = project.getBaseDir().findChild(".idea"); + assert ideaDir != null; + EduUtils.createStudentFileFromAnswer(project, ideaDir, taskDir, entry.getKey(), taskFile); }); taskFile.name = entry.getKey(); @@ -98,11 +95,8 @@ public class StepicWrappers { private static void setTests(@NotNull final Task task, @NotNull final StepOptions source, @NotNull final Project project) { final Map testsText = task.getTestsText(); if (testsText.isEmpty()) { - ApplicationManager.getApplication().runReadAction(new Runnable() { - @Override - public void run() { - source.test = Collections.singletonList(new TestFileWrapper(EduNames.TESTS_FILE, task.getTestsText(project))); - } + ApplicationManager.getApplication().runReadAction(() -> { + source.test = Collections.singletonList(new TestFileWrapper(EduNames.TESTS_FILE, task.getTestsText(project))); }); } else { diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/twitter/StudyTwitterUtils.java b/python/educational-core/student/src/com/jetbrains/edu/learning/twitter/StudyTwitterUtils.java index 1192e7bea612..8c6d8668066d 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/twitter/StudyTwitterUtils.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/twitter/StudyTwitterUtils.java @@ -242,7 +242,7 @@ public class StudyTwitterUtils { * Dialog wrapper class with DoNotAsl option for asking user to tweet. * */ private static class TwitterDialogWrapper extends DialogWrapper { - private StudyTwitterUtils.TwitterDialogPanel myPanel; + private final StudyTwitterUtils.TwitterDialogPanel myPanel; TwitterDialogWrapper(@Nullable Project project, @NotNull StudyTwitterUtils.TwitterDialogPanel panel, DoNotAskOption doNotAskOption) { super(project); diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyProgressBar.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyProgressBar.java index c21aef13acb7..6effcc2df65c 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyProgressBar.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyProgressBar.java @@ -20,7 +20,7 @@ public class StudyProgressBar extends JComponent implements DumbAware { private final int myHeight; private final int myIndent; private double myFraction = 0.0; - private static Color myColor = JBColor.GREEN; + private static final Color myColor = JBColor.GREEN; public StudyProgressBar(double fraction, int height, int indent) { myFraction = fraction; @@ -100,4 +100,8 @@ public class StudyProgressBar extends JComponent implements DumbAware { dimension.height = myHeight + 10; return dimension; } + + public void setFraction(double fraction) { + myFraction = fraction; + } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyProgressToolWindowFactory.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyProgressToolWindowFactory.java deleted file mode 100644 index d57ad24c70f8..000000000000 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyProgressToolWindowFactory.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.jetbrains.edu.learning.ui; - -import com.intellij.openapi.project.DumbAware; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.wm.ToolWindow; -import com.intellij.openapi.wm.ToolWindowFactory; -import com.intellij.ui.content.Content; -import com.intellij.ui.content.ContentFactory; -import com.intellij.util.ui.UIUtil; -import com.jetbrains.edu.learning.core.EduNames; -import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.Lesson; -import com.jetbrains.edu.learning.courseFormat.StudyStatus; -import com.jetbrains.edu.learning.courseFormat.Task; -import com.jetbrains.edu.learning.StudyTaskManager; -import icons.InteractiveLearningIcons; -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; -import java.awt.*; -import java.util.List; - -public class StudyProgressToolWindowFactory implements ToolWindowFactory, DumbAware { - public static final String ID = "Course Progress"; - - - @Override - public void createToolWindowContent(@NotNull final Project project, @NotNull final ToolWindow toolWindow) { - toolWindow.setIcon(InteractiveLearningIcons.CourseProgress); - JPanel contentPanel = new JPanel(); - StudyTaskManager taskManager = StudyTaskManager.getInstance(project); - if (taskManager.getCourse() != null) { - contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.PAGE_AXIS)); - contentPanel.add(Box.createRigidArea(new Dimension(10, 0))); - - Course course = taskManager.getCourse(); - if (course == null) { - return; - } - int taskNum = 0; - int taskSolved = 0; - List lessons = course.getLessons(); - for (Lesson lesson : lessons) { - if (lesson.getName().equals(EduNames.PYCHARM_ADDITIONAL)) continue; - taskNum += lesson.getTaskList().size(); - taskSolved += getSolvedTasks(lesson); - } - String completedTasks = String.format("%d of %d tasks completed", taskSolved, taskNum); - - double percent = (taskSolved * 100.0) / taskNum; - contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); - StudyProgressBar studyProgressBar = new StudyProgressBar(percent / 100, 40, 10); - contentPanel.add(studyProgressBar); - contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); - addStatistics(completedTasks, contentPanel); - contentPanel.setPreferredSize(new Dimension(100, 50)); - ContentFactory contentFactory = ContentFactory.SERVICE.getInstance(); - Content content = contentFactory.createContent(contentPanel, "", false); - contentPanel.setMinimumSize(new Dimension(300, 100)); - toolWindow.getContentManager().addContent(content); - } - } - - private static void addStatistics(String statistics, JPanel contentPanel) { - String labelText = UIUtil.toHtml(statistics, 5); - contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); - JLabel statisticLabel = new JLabel(labelText); - contentPanel.add(statisticLabel); - } - - private static int getSolvedTasks(@NotNull final Lesson lesson) { - int solved = 0; - for (Task task : lesson.getTaskList()) { - if (task.getStatus() == StudyStatus.Solved) { - solved += 1; - } - } - return solved; - } -} diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java index d47ef138d308..85f1511f9bf8 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java @@ -30,16 +30,18 @@ import com.intellij.openapi.ui.SimpleToolWindowPanel; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.JBCardLayout; +import com.intellij.ui.JBColor; import com.intellij.ui.OnePixelSplitter; import com.intellij.util.ui.JBUI; import com.jetbrains.edu.learning.*; -import com.jetbrains.edu.learning.courseFormat.Course; -import com.jetbrains.edu.learning.courseFormat.TaskFile; +import com.jetbrains.edu.learning.core.EduNames; +import com.jetbrains.edu.learning.courseFormat.*; import com.jetbrains.edu.learning.stepic.StepicAdaptiveReactionsPanel; import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; +import java.util.List; import java.util.Map; public abstract class StudyToolWindow extends SimpleToolWindowPanel implements DataProvider, Disposable { @@ -50,6 +52,8 @@ public abstract class StudyToolWindow extends SimpleToolWindowPanel implements D private final JBCardLayout myCardLayout; private final JPanel myContentPanel; private final OnePixelSplitter mySplitPane; + private JLabel myStatisticLabel; + private StudyProgressBar myStudyProgressBar; public StudyToolWindow() { super(true, true); @@ -72,6 +76,7 @@ public abstract class StudyToolWindow extends SimpleToolWindowPanel implements D } JComponent taskInfoPanel = createTaskInfoPanel(project); panel.add(taskInfoPanel, BorderLayout.CENTER); + panel.add(createCourseProgress(project), BorderLayout.SOUTH); myContentPanel.add(TASK_INFO_ID, panel); mySplitPane.setFirstComponent(myContentPanel); addAdditionalPanels(project); @@ -231,6 +236,53 @@ public abstract class StudyToolWindow extends SimpleToolWindowPanel implements D WebBrowserManager.getInstance().setShowBrowserHover(true); mySplitPane.setFirstComponent(myContentPanel); StudyTaskManager.getInstance(project).setToolWindowMode(StudyToolWindowMode.TEXT); - StudyUtils.updateStudyToolWindow(project); + StudyUtils.updateToolWindows(project); + } + + private JPanel createCourseProgress(@NotNull final Project project) { + JPanel contentPanel = new JPanel(); + contentPanel.setBackground(JBColor.WHITE); + contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.PAGE_AXIS)); + contentPanel.add(Box.createRigidArea(new Dimension(10, 0))); + contentPanel.add(Box.createRigidArea(new Dimension(0, 10))); + myStudyProgressBar = new StudyProgressBar(0, 20, 10); + + myStatisticLabel = new JLabel("", SwingConstants.LEFT); + contentPanel.add(myStatisticLabel); + contentPanel.add(myStudyProgressBar); + + contentPanel.setPreferredSize(new Dimension(100, 60)); + contentPanel.setMinimumSize(new Dimension(300, 40)); + updateCourseProgress(project); + return contentPanel; + } + + public void updateCourseProgress(@NotNull final Project project) { + final Course course = StudyTaskManager.getInstance(project).getCourse(); + if (course != null) { + int taskNum = 0; + int taskSolved = 0; + List lessons = course.getLessons(); + for (Lesson lesson : lessons) { + if (lesson.getName().equals(EduNames.PYCHARM_ADDITIONAL)) continue; + taskNum += lesson.getTaskList().size(); + taskSolved += getSolvedTasks(lesson); + } + String completedTasks = String.format("%d of %d tasks completed", taskSolved, taskNum); + double percent = (taskSolved * 100.0) / taskNum; + + myStatisticLabel.setText(completedTasks); + myStudyProgressBar.setFraction(percent / 100); + } + } + + private static int getSolvedTasks(@NotNull final Lesson lesson) { + int solved = 0; + for (Task task : lesson.getTaskList()) { + if (task.getStatus() == StudyStatus.Solved) { + solved += 1; + } + } + return solved; } } diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java index 4530e004504d..2cbb4cff7f99 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java @@ -7,7 +7,6 @@ import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowFactory; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentManager; -import com.jetbrains.edu.learning.StudyProjectComponent; import com.jetbrains.edu.learning.StudyTaskManager; import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.courseFormat.Course; diff --git a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java index 20d11cb4bc97..1e63ad532000 100644 --- a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java +++ b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java @@ -165,6 +165,7 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator imp return generator -> { final List enrolledCoursesIds = myGenerator.getEnrolledCoursesIds(); final CourseInfo course = (CourseInfo)mySettingsPanel.getCoursesComboBox().getSelectedItem(); + if (course == null) return true; if (course.isAdaptive() && !enrolledCoursesIds.contains(course.getId())) { ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> { ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); diff --git a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyInstructionPainter.java b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyInstructionPainter.java index c8dc443d26f6..0496db550dbb 100644 --- a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyInstructionPainter.java +++ b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyInstructionPainter.java @@ -5,7 +5,6 @@ import com.intellij.openapi.fileEditor.impl.EditorEmptyTextPainter; import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.util.ui.UIUtil; import com.jetbrains.edu.learning.actions.*; -import com.jetbrains.edu.learning.ui.StudyProgressToolWindowFactory; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -23,6 +22,5 @@ public class PyStudyInstructionPainter extends EditorEmptyTextPainter { appendAction(painter, "Reset current task file", getActionShortcutText(StudyRefreshTaskFileAction.ACTION_ID)); appendAction(painter, "Check task", getActionShortcutText(PyStudyCheckAction.ACTION_ID)); appendAction(painter, "Get hint for the answer placeholder", getActionShortcutText(StudyShowHintAction.ACTION_ID)); - appendLine(painter, "To see your progress open the '" + StudyProgressToolWindowFactory.ID + "' panel"); } } \ No newline at end of file diff --git a/python/helpers/pycharm/tcunittest.py b/python/helpers/pycharm/tcunittest.py index 185c12898a1e..1418bfa487ed 100644 --- a/python/helpers/pycharm/tcunittest.py +++ b/python/helpers/pycharm/tcunittest.py @@ -211,6 +211,8 @@ class TeamcityTestResult(TestResult): def __getDuration(self, test): start = getattr(test, "startTime", datetime.datetime.now()) + assert isinstance(start, datetime.datetime), \ + "You testcase has property named 'startTime' (value {0}). Please, rename it".format(start) d = datetime.datetime.now() - start duration = d.microseconds / 1000 + d.seconds * 1000 + d.days * 86400000 return duration diff --git a/python/helpers/pycharm_generator_utils/module_redeclarator.py b/python/helpers/pycharm_generator_utils/module_redeclarator.py index a79a91ed4dd3..fd34c3ad7c37 100644 --- a/python/helpers/pycharm_generator_utils/module_redeclarator.py +++ b/python/helpers/pycharm_generator_utils/module_redeclarator.py @@ -684,7 +684,9 @@ class ModuleRedeclarator(object): try: item = getattr(p_class, item_name) # let getters do the magic except AttributeError: - item = field_source[item_name] # have it raw + item = field_source.get(item_name) # have it raw + if item is None: + continue except Exception: continue if is_callable(item) and not isinstance(item, type): diff --git a/python/ide/src/com/jetbrains/python/PythonSdkChooserCombo.java b/python/ide/src/com/jetbrains/python/PythonSdkChooserCombo.java index 7c9335ba62bd..032bda6b3741 100644 --- a/python/ide/src/com/jetbrains/python/PythonSdkChooserCombo.java +++ b/python/ide/src/com/jetbrains/python/PythonSdkChooserCombo.java @@ -20,16 +20,15 @@ import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel; +import com.intellij.openapi.ui.ComboBox; import com.intellij.openapi.util.Condition; import com.intellij.ui.CollectionComboBoxModel; import com.intellij.ui.ComboboxWithBrowseButton; -import com.intellij.util.NullableConsumer; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.configuration.PyConfigurableInterpreterList; import com.jetbrains.python.sdk.PySdkListCellRenderer; import com.jetbrains.python.sdk.PySdkService; import com.jetbrains.python.sdk.PythonSdkDetailsStep; -import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.event.ActionEvent; @@ -41,17 +40,12 @@ import java.util.List; */ public class PythonSdkChooserCombo extends ComboboxWithBrowseButton { private final List myChangedListeners = ContainerUtil.createLockFreeCopyOnWriteList(); - private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.PythonSdkChooserCombo"); + private static final Logger LOG = Logger.getInstance(PythonSdkChooserCombo.class); @SuppressWarnings("unchecked") public PythonSdkChooserCombo(final Project project, List sdks, final Condition acceptableSdkCondition) { - Sdk initialSelection = null; - for (Sdk sdk : sdks) { - if (acceptableSdkCondition.value(sdk)) { - initialSelection = sdk; - break; - } - } + super(new ComboBox<>()); + final Sdk initialSelection = ContainerUtil.find(sdks, acceptableSdkCondition); final JComboBox comboBox = getComboBox(); comboBox.setModel(new CollectionComboBoxModel(sdks, initialSelection)); comboBox.setRenderer(new PySdkListCellRenderer(true)); @@ -78,6 +72,9 @@ public class PythonSdkChooserCombo extends ComboboxWithBrowseButton { private void showOptions(final Project project) { final PyConfigurableInterpreterList interpreterList = PyConfigurableInterpreterList.getInstance(project); final Sdk[] sdks = interpreterList.getModel().getSdks(); + //noinspection unchecked + final JComboBox comboBox = getComboBox(); + final Sdk oldSelectedSdk = (Sdk)comboBox.getSelectedItem(); PythonSdkDetailsStep.show(project, sdks, null, this, getButton().getLocationOnScreen(), sdk -> { if (sdk == null) return; final PySdkService sdkService = PySdkService.getInstance(); @@ -93,8 +90,10 @@ public class PythonSdkChooserCombo extends ComboboxWithBrowseButton { LOG.error("Error adding new python interpreter " + e.getMessage()); } } - //noinspection unchecked - getComboBox().setModel(new CollectionComboBoxModel(interpreterList.getAllPythonSdks(), sdk)); + final List committedSdks = interpreterList.getAllPythonSdks(); + final Sdk copiedSdk = interpreterList.getModel().findSdk(sdk.getName()); + comboBox.setModel(new CollectionComboBoxModel<>(committedSdks, oldSelectedSdk)); + comboBox.setSelectedItem(copiedSdk); }, true); } diff --git a/python/ide/src/com/jetbrains/python/newProject/actions/ProjectSpecificSettingsStep.java b/python/ide/src/com/jetbrains/python/newProject/actions/ProjectSpecificSettingsStep.java index 5a34a82f377b..1bdc5359402e 100644 --- a/python/ide/src/com/jetbrains/python/newProject/actions/ProjectSpecificSettingsStep.java +++ b/python/ide/src/com/jetbrains/python/newProject/actions/ProjectSpecificSettingsStep.java @@ -15,7 +15,6 @@ */ package com.jetbrains.python.newProject.actions; -import com.intellij.execution.ExecutionException; import com.intellij.facet.ui.ValidationResult; import com.intellij.ide.util.projectWizard.ProjectSettingsStepBase; import com.intellij.ide.util.projectWizard.WebProjectTemplate; @@ -29,15 +28,16 @@ import com.intellij.platform.DirectoryProjectGenerator; import com.intellij.ui.DocumentAdapter; import com.intellij.ui.HideableDecorator; import com.intellij.util.NullableConsumer; +import com.intellij.util.PathUtil; import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.update.UiNotifyConnector; import com.jetbrains.python.PythonSdkChooserCombo; import com.jetbrains.python.configuration.PyConfigurableInterpreterList; import com.jetbrains.python.configuration.VirtualEnvProjectFilter; import com.jetbrains.python.newProject.PyFrameworkProjectGenerator; import com.jetbrains.python.newProject.PythonProjectGenerator; -import com.jetbrains.python.packaging.PyPackageManager; +import com.jetbrains.python.packaging.PyPackage; import com.jetbrains.python.packaging.PyPackageUtil; -import com.jetbrains.python.sdk.PySdkUtil; import com.jetbrains.python.sdk.PythonSdkType; import icons.PythonIcons; import org.jetbrains.annotations.NotNull; @@ -48,9 +48,7 @@ import javax.swing.event.DocumentEvent; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.io.File; +import java.awt.event.ItemEvent; import java.util.ArrayList; import java.util.List; @@ -119,20 +117,28 @@ public class ProjectSpecificSettingsStep extends ProjectSettingsStepBase impleme protected void registerValidators() { super.registerValidators(); if (myProjectGenerator instanceof PythonProjectGenerator && !((PythonProjectGenerator)myProjectGenerator).hideInterpreter()) { - mySdkCombo.getComboBox().addPropertyChangeListener(new PropertyChangeListener() { + myLocationField.getTextField().getDocument().addDocumentListener(new DocumentAdapter() { @Override - public void propertyChange(PropertyChangeEvent event) { + protected void textChanged(DocumentEvent e) { + final String path = myLocationField.getText().trim(); + ((PythonProjectGenerator)myProjectGenerator).locationChanged(PathUtil.getFileName(path)); + } + }); + + mySdkCombo.getComboBox().addItemListener(e -> { + if (e.getStateChange() == ItemEvent.SELECTED) { checkValid(); } }); - final ActionListener listener = new ActionListener() { - @Override - public void actionPerformed(ActionEvent event) { - checkValid(); - } - }; - mySdkCombo.getComboBox().addActionListener(listener); - mySdkCombo.addActionListener(listener); + UiNotifyConnector.doWhenFirstShown(mySdkCombo, this::checkValid); + } + } + + @Override + protected void initGeneratorListeners() { + super.initGeneratorListeners(); + if (myProjectGenerator instanceof PythonProjectGenerator) { + ((PythonProjectGenerator)myProjectGenerator).addSettingsStateListener(this::checkValid); } } @@ -169,14 +175,11 @@ public class ProjectSpecificSettingsStep extends ProjectSettingsStepBase impleme if (PyPackageUtil.packageManagementEnabled(sdk)) { warningList.add(frameworkName + " will be installed on the selected interpreter"); myInstallFramework = true; - final PyPackageManager packageManager = PyPackageManager.getInstance(sdk); - boolean hasManagement = false; - try { - hasManagement = packageManager.hasManagement(PySdkUtil.isRemote(sdk)); + final List packages = PyPackageUtil.refreshAndGetPackagesModally(sdk); + if (packages == null) { + return false; } - catch (ExecutionException ignored) { - } - if (!hasManagement) { + if (!PyPackageUtil.hasManagement(packages)) { warningList.add("Python packaging tools and " + warningList); } } else { @@ -282,33 +285,4 @@ public class ProjectSpecificSettingsStep extends ProjectSettingsStepBase impleme return LabeledComponent.create(mySdkCombo, "Interpreter", BorderLayout.WEST); } - - @Override - protected void initGeneratorListeners() { - super.initGeneratorListeners(); - if (myProjectGenerator instanceof PythonProjectGenerator) { - ((PythonProjectGenerator)myProjectGenerator).addSettingsStateListener(new PythonProjectGenerator.SettingsListener() { - @Override - public void stateChanged() { - checkValid(); - } - }); - - myErrorLabel.addMouseListener(((PythonProjectGenerator)myProjectGenerator).getErrorLabelMouseListener()); - } - myLocationField.getTextField().getDocument().addDocumentListener(new DocumentAdapter() { - @Override - protected void textChanged(DocumentEvent e) { - if (myProjectGenerator instanceof PythonProjectGenerator) { - String path = myLocationField.getText().trim(); - path = StringUtil.trimEnd(path, File.separator); - int ind = path.lastIndexOf(File.separator); - if (ind != -1) { - String projectName = path.substring(ind + 1, path.length()); - ((PythonProjectGenerator)myProjectGenerator).locationChanged(projectName); - } - } - } - }); - } } diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/configuration/IpnbConnectionManager.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/configuration/IpnbConnectionManager.java index 27be6ff10510..7bba8e8e03a8 100644 --- a/python/ipnb/src/org/jetbrains/plugins/ipnb/configuration/IpnbConnectionManager.java +++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/configuration/IpnbConnectionManager.java @@ -33,6 +33,7 @@ import com.intellij.util.ui.UIUtil; import com.jetbrains.python.PythonHelper; import com.jetbrains.python.packaging.PyPackage; import com.jetbrains.python.packaging.PyPackageManager; +import com.jetbrains.python.packaging.PyPackageUtil; import com.jetbrains.python.sdk.PythonSdkType; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -269,15 +270,12 @@ public final class IpnbConnectionManager implements ProjectComponent { showWarning(fileEditor, "Please check Python Interpreter in Settings->Python Interpreter", null); return false; } - try { - final PyPackage ipythonPackage = PyPackageManager.getInstance(sdk).findPackage("ipython", false); - final PyPackage jupyterPackage = PyPackageManager.getInstance(sdk).findPackage("jupyter", false); - if (ipythonPackage == null && jupyterPackage == null) { - showWarning(fileEditor, "Add Jupyter to the interpreter of the current project.", null); - return false; - } - } - catch (ExecutionException ignored) { + final List packages = PyPackageManager.getInstance(sdk).getPackages(); + final PyPackage ipythonPackage = packages != null ? PyPackageUtil.findPackage(packages, "ipython") : null; + final PyPackage jupyterPackage = packages != null ? PyPackageUtil.findPackage(packages, "jupyter") : null; + if (ipythonPackage == null && jupyterPackage == null) { + showWarning(fileEditor, "Add Jupyter to the interpreter of the current project.", null); + return false; } String url = showDialogUrl(initUrl); diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/format/IpnbParser.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/format/IpnbParser.java index e9bc33718e75..4d19bea816a4 100644 --- a/python/ipnb/src/org/jetbrains/plugins/ipnb/format/IpnbParser.java +++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/format/IpnbParser.java @@ -4,7 +4,6 @@ import com.google.common.collect.Lists; import com.google.gson.*; import com.google.gson.annotations.SerializedName; import com.google.gson.stream.JsonWriter; -import com.intellij.execution.ExecutionException; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.module.Module; @@ -16,7 +15,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.text.VersionComparatorUtil; import com.jetbrains.python.packaging.PyPackage; -import com.jetbrains.python.packaging.PyPackageManager; +import com.jetbrains.python.packaging.PyPackageUtil; import com.jetbrains.python.sdk.PythonSdkType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -79,14 +78,12 @@ public class IpnbParser { if (module != null) { final Sdk sdk = PythonSdkType.findPythonSdk(module); if (sdk != null) { - try { - final PyPackage ipython = PyPackageManager.getInstance(sdk).findPackage("ipython", true); - final PyPackage jupyter = PyPackageManager.getInstance(sdk).findPackage("jupyter", true); - if (jupyter == null && ipython != null && VersionComparatorUtil.compare(ipython.getVersion(), "3.0") <= 0) { - return false; - } - } - catch (ExecutionException ignored) { + // It should be called first before IpnbConnectionManager#startIpythonServer() + final List packages = PyPackageUtil.refreshAndGetPackagesModally(sdk); + final PyPackage ipython = packages != null ? PyPackageUtil.findPackage(packages, "ipython") : null; + final PyPackage jupyter = packages != null ? PyPackageUtil.findPackage(packages, "jupyter") : null; + if (jupyter == null && ipython != null && VersionComparatorUtil.compare(ipython.getVersion(), "3.0") <= 0) { + return false; } } } diff --git a/python/openapi/src/com/jetbrains/python/packaging/PyPackageManager.java b/python/openapi/src/com/jetbrains/python/packaging/PyPackageManager.java index c8b8dd4ab633..a500532496eb 100644 --- a/python/openapi/src/com/jetbrains/python/packaging/PyPackageManager.java +++ b/python/openapi/src/com/jetbrains/python/packaging/PyPackageManager.java @@ -31,19 +31,16 @@ import java.util.Set; public abstract class PyPackageManager { public static final Key RUNNING_PACKAGING_TASKS = Key.create("PyPackageRequirementsInspection.RunningPackagingTasks"); - public static final String SETUPTOOLS = "setuptools"; - public static final String PIP = "pip"; - public static final String DISTRIBUTE = "distribute"; - public static final String USE_USER_SITE = "--user"; - public static PyPackageManager getInstance(Sdk sdk) { + @NotNull + public static PyPackageManager getInstance(@NotNull Sdk sdk) { return PyPackageManagers.getInstance().forSdk(sdk); } public abstract void installManagement() throws ExecutionException; - public abstract boolean hasManagement(boolean cachedOnly) throws ExecutionException; + public abstract boolean hasManagement() throws ExecutionException; public abstract void install(@NotNull String requirementString) throws ExecutionException; @@ -57,26 +54,14 @@ public abstract class PyPackageManager { public abstract String createVirtualEnv(@NotNull String destinationDir, boolean useGlobalSite) throws ExecutionException; @Nullable - public abstract List getPackages(boolean cachedOnly) throws ExecutionException; + public abstract List getPackages(); - /** - * @param cachedOnly only search through cached packages. Cache may be empty just after project opened. - * warning: non-cache access may be slow on remote interpreters. - * Use {@link #findPackage(String)}: this method uses cache on remote interpreters and skips - * in local - */ - @Nullable - public abstract PyPackage findPackage(@NotNull String name, boolean cachedOnly) throws ExecutionException; - - /** - * Like {@link #findPackage(String, boolean)} but controls cache access based on intepreter remote/local type - */ - @Nullable - public abstract PyPackage findPackage(@NotNull String name) throws ExecutionException; + @NotNull + public abstract List refreshAndGetPackages(boolean alwaysRefresh) throws ExecutionException; @Nullable public abstract List getRequirements(@NotNull Module module); - @Nullable + @NotNull public abstract Set getDependents(@NotNull PyPackage pkg) throws ExecutionException; } diff --git a/python/openapi/src/com/jetbrains/python/packaging/PyPackageManagers.java b/python/openapi/src/com/jetbrains/python/packaging/PyPackageManagers.java index 83fccdcea6cf..6729ff51ffd4 100644 --- a/python/openapi/src/com/jetbrains/python/packaging/PyPackageManagers.java +++ b/python/openapi/src/com/jetbrains/python/packaging/PyPackageManagers.java @@ -32,7 +32,7 @@ public abstract class PyPackageManagers { } @NotNull - public abstract PyPackageManager forSdk(Sdk sdk); + public abstract PyPackageManager forSdk(@NotNull Sdk sdk); public abstract PackageManagementService getManagementService(Project project, Sdk sdk); diff --git a/python/openapi/src/com/jetbrains/python/templateLanguages/PyTemplatesUtil.java b/python/openapi/src/com/jetbrains/python/templateLanguages/PyTemplatesUtil.java deleted file mode 100644 index ce08ddfd80dc..000000000000 --- a/python/openapi/src/com/jetbrains/python/templateLanguages/PyTemplatesUtil.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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.jetbrains.python.templateLanguages; - -import com.intellij.execution.ExecutionException; -import com.intellij.facet.ui.ValidationResult; -import com.intellij.lang.Language; -import com.intellij.openapi.projectRoots.Sdk; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.FileViewProvider; -import com.intellij.psi.PsiElement; -import com.intellij.psi.templateLanguages.TemplateLanguageFileViewProvider; -import com.jetbrains.python.packaging.PyPackage; -import com.jetbrains.python.packaging.PyPackageManager; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class PyTemplatesUtil { - private PyTemplatesUtil() { - } - - public static ValidationResult checkInstalled(@Nullable final Sdk sdk, @NotNull final TemplateLanguagePanel templatesPanel, - @NotNull final String prefix) { - if (sdk == null) return ValidationResult.OK; - String templateBinding = null; - @NonNls String language = templatesPanel.getTemplateLanguage(); - if (language != null) { - String framework = StringUtil.trimEnd(prefix, '_'); - framework = StringUtil.trimEnd(framework, '-'); - String postfix = language.toLowerCase(); - if (framework.equals(postfix)) return ValidationResult.OK; - if (language.equals(TemplatesService.JINJA2)) { - postfix = "jinja"; - } - templateBinding = prefix + postfix; - } - final PyPackageManager packageManager = PyPackageManager.getInstance(sdk); - if (templateBinding != null) { - if (TemplatesService.ALL_TEMPLATE_BINDINGS.contains(templateBinding)) { - try { - final PyPackage installedPackage = packageManager.findPackage(templateBinding, false); - if (installedPackage == null) { - return new ValidationResult(templateBinding + " will be installed on the selected interpreter"); - } - } - catch (ExecutionException ignored) { - } - } - } - if (language != null) { - try { - final PyPackage installedPackage = packageManager.findPackage(language, false); - if (installedPackage == null) { - return new ValidationResult(language + " will be installed on the selected interpreter"); - } - } - catch (ExecutionException ignored) { - } - } - return null; - } - - /** - * Fetches template data language if file has {@link TemplateLanguageFileViewProvider} - * - * @param psiElement element to get lang for - * @param expectedProvider only fetch language if provider has certain type. Pass null for any type. - * @return template data language - */ - @Nullable - public static Language getTemplateDataLanguage(@Nullable final PsiElement psiElement, - @Nullable final Class expectedProvider) { - if (psiElement == null) { - return null; - } - - final FileViewProvider provider = psiElement.getContainingFile().getViewProvider(); - if (provider instanceof TemplateLanguageFileViewProvider) { - if (expectedProvider == null || expectedProvider.isInstance(provider)) { - return (((TemplateLanguageFileViewProvider)provider).getTemplateDataLanguage()); - } - } - - return psiElement.getLanguage(); - } -} - diff --git a/python/openapi/src/com/jetbrains/python/templateLanguages/PythonTemplateIndentOptionsProvider.java b/python/openapi/src/com/jetbrains/python/templateLanguages/PythonTemplateIndentOptionsProvider.java deleted file mode 100644 index cb8663544b77..000000000000 --- a/python/openapi/src/com/jetbrains/python/templateLanguages/PythonTemplateIndentOptionsProvider.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2000-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.jetbrains.python.templateLanguages; - -import com.intellij.lang.Language; -import com.intellij.psi.PsiFile; -import com.intellij.psi.codeStyle.CodeStyleSettings; -import com.intellij.psi.codeStyle.CommonCodeStyleSettings.IndentOptions; -import com.intellij.psi.codeStyle.FileIndentOptionsProvider; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * Injects indents for Python templates. - * In Python we have template langs, but we should use indent from underlying language (like html) - * because templ. language never works standalone: it is always emebedded in some language - * - * @author Ilya.Kazakevich - */ -public class PythonTemplateIndentOptionsProvider extends FileIndentOptionsProvider { - @Nullable - @Override - public final IndentOptions getIndentOptions(@NotNull final CodeStyleSettings settings, - @NotNull final PsiFile file) { - final Language language = file.getLanguage(); - if (!(language instanceof PythonTemplateLanguage)) { - return null; // We only care about python template files - } - - // This template language has no settings, lets use parent language then - final Language templateDataLanguage = PyTemplatesUtil.getTemplateDataLanguage(file, null); - if (templateDataLanguage == null) { - return null; // No template data language - } - return settings.getIndentOptions(templateDataLanguage.getAssociatedFileType()); - } -} diff --git a/python/openapi/src/com/jetbrains/python/templateLanguages/TemplatesService.java b/python/openapi/src/com/jetbrains/python/templateLanguages/TemplatesService.java index b8eb026b8674..a0be24a26434 100644 --- a/python/openapi/src/com/jetbrains/python/templateLanguages/TemplatesService.java +++ b/python/openapi/src/com/jetbrains/python/templateLanguages/TemplatesService.java @@ -19,9 +19,9 @@ import com.intellij.lang.Language; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleServiceManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.ContainerUtil; -import com.jetbrains.python.packaging.PyPackageManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -111,7 +111,7 @@ public abstract class TemplatesService { public abstract void setTemplateFileTypes(List fileTypes); public abstract void generateTemplates(@NotNull final TemplateSettingsHolder settings, VirtualFile baseDir); - public abstract void installTemplateEngine(@NotNull final TemplateSettingsHolder settings, @NotNull final PyPackageManager packageManager, + public abstract void installTemplateEngine(@NotNull final TemplateSettingsHolder settings, @NotNull final Sdk sdk, @NotNull final Project project, @NotNull final String prefix); public abstract void addLanguageSelectedListener(Runnable listener); diff --git a/python/pluginJava/com/jetbrains/python/psi/impl/PyJavaImportCandidateProvider.java b/python/pluginJava/com/jetbrains/python/psi/impl/PyJavaImportCandidateProvider.java index a8673fb642c4..90a667299d84 100644 --- a/python/pluginJava/com/jetbrains/python/psi/impl/PyJavaImportCandidateProvider.java +++ b/python/pluginJava/com/jetbrains/python/psi/impl/PyJavaImportCandidateProvider.java @@ -41,7 +41,11 @@ public class PyJavaImportCandidateProvider implements PyImportCandidateProvider PsiShortNamesCache cache = PsiShortNamesCache.getInstance(project); final PsiClass[] classesByName = cache.getClassesByName(name, scope); for (PsiClass psiClass : classesByName) { - final QualifiedName packageQName = QualifiedName.fromDottedString(psiClass.getQualifiedName()).removeLastComponent(); + final String qualifiedName = psiClass.getQualifiedName(); + if (qualifiedName == null) { + continue; + } + final QualifiedName packageQName = QualifiedName.fromDottedString(qualifiedName).removeLastComponent(); quickFix.addImport(psiClass, psiClass.getContainingFile(), packageQName); } } diff --git a/python/python-community-configure/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java b/python/python-community-configure/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java index d15ae055eaec..41be0e419608 100644 --- a/python/python-community-configure/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java +++ b/python/python-community-configure/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java @@ -51,8 +51,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -195,24 +194,16 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable { myInitialSdkSet = myProjectSdksModel.getProjectSdks().keySet(); myProjectSdksModel.addListener(mySdkModelListener); - mySdkCombo.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { + mySdkCombo.addItemListener(e -> { + if (e.getStateChange() == ItemEvent.SELECTED) { final Sdk selectedSdk = (Sdk)mySdkCombo.getSelectedItem(); - myPackagesPanel.updatePackages(selectedSdk != null ? - PyPackageManagers.getInstance().getManagementService(myProject, selectedSdk) : null); + final PyPackageManagers packageManagers = PyPackageManagers.getInstance(); + myPackagesPanel.updatePackages(selectedSdk != null ? packageManagers.getManagementService(myProject, selectedSdk) : null); myPackagesPanel.updateNotifications(selectedSdk); } }); myAddSdkCallback = new SdkAddedCallback(); - myDetailsButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - showDetails(); - } - } - ); - + myDetailsButton.addActionListener(e -> showDetails()); } private void showDetails() { @@ -295,8 +286,6 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable { else { mySdkCombo.getModel().setSelectedItem(selectedSdk == null ? null : myProjectSdksModel.findSdk(selectedSdk.getName())); } - myPackagesPanel.updatePackages(selectedSdk != null ? PyPackageManagers.getInstance().getManagementService(myProject, selectedSdk) : null); - myPackagesPanel.updateNotifications(selectedSdk); } private void rehighlightVersionSpecific(@Nullable final Sdk newSdk, @Nullable final Sdk prevSdk) { diff --git a/python/python-rest/src/com/jetbrains/rest/RestPythonUtil.java b/python/python-rest/src/com/jetbrains/rest/RestPythonUtil.java index e7e2eae2b928..b3353c6ab7e2 100644 --- a/python/python-rest/src/com/jetbrains/rest/RestPythonUtil.java +++ b/python/python-rest/src/com/jetbrains/rest/RestPythonUtil.java @@ -15,7 +15,6 @@ */ package com.jetbrains.rest; -import com.intellij.execution.ExecutionException; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.LangDataKeys; @@ -26,8 +25,11 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.jetbrains.python.packaging.PyPackage; import com.jetbrains.python.packaging.PyPackageManager; +import com.jetbrains.python.packaging.PyPackageUtil; import com.jetbrains.python.sdk.PythonSdkType; +import java.util.List; + /** * User : catherine */ @@ -45,15 +47,11 @@ public class RestPythonUtil { module = modules.length == 0 ? null : modules [0]; } if (module != null) { - Sdk sdk = PythonSdkType.findPythonSdk(module); + final Sdk sdk = PythonSdkType.findPythonSdk(module); if (sdk != null) { - PyPackageManager manager = PyPackageManager.getInstance(sdk); - try { - final PyPackage sphinx = manager.findPackage("Sphinx", false); - presentation.setEnabled(sphinx != null); - } - catch (ExecutionException ignored) { - } + final List packages = PyPackageManager.getInstance(sdk).getPackages(); + final PyPackage sphinx = packages != null ? PyPackageUtil.findPackage(packages, "Sphinx") : null; + presentation.setEnabled(sphinx != null); } } } diff --git a/python/python-rest/src/com/jetbrains/rest/run/sphinx/SphinxRunConfiguration.java b/python/python-rest/src/com/jetbrains/rest/run/sphinx/SphinxRunConfiguration.java index f2e90d02079e..26311708c5ce 100644 --- a/python/python-rest/src/com/jetbrains/rest/run/sphinx/SphinxRunConfiguration.java +++ b/python/python-rest/src/com/jetbrains/rest/run/sphinx/SphinxRunConfiguration.java @@ -25,10 +25,13 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.jetbrains.python.packaging.PyPackage; import com.jetbrains.python.packaging.PyPackageManager; +import com.jetbrains.python.packaging.PyPackageUtil; import com.jetbrains.rest.run.RestConfigurationEditor; import com.jetbrains.rest.run.RestRunConfiguration; import org.jetbrains.annotations.NotNull; +import java.util.List; + /** * User : catherine */ @@ -42,14 +45,13 @@ public class SphinxRunConfiguration extends RestRunConfiguration { protected SettingsEditor createConfigurationEditor() { final SphinxTasksModel model = new SphinxTasksModel(); if (!model.contains("pdf") && getSdk() != null) { - try { - final PyPackage rst2pdf = PyPackageManager.getInstance(getSdk()).findPackage("rst2pdf"); + final List packages = PyPackageManager.getInstance(getSdk()).getPackages(); + if (packages != null) { + final PyPackage rst2pdf = PyPackageUtil.findPackage(packages,"rst2pdf"); if (rst2pdf != null) { model.add(13, "pdf"); } } - catch (ExecutionException ignored) { - } } RestConfigurationEditor editor = new RestConfigurationEditor(getProject(), this, model); diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index ee67f7d0e69a..d1e327049b9a 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -889,7 +889,8 @@ sdk.gen.reloading=Reloading generated skeletons... sdk.gen.reading.versions.file=Reading versions file... sdk.gen.notify.converting.old.skels=Converting old skeletons sdk.gen.notify.converting.text=Skeletons of binary modules seem to be from an older version.
These will be fully re-generated, which will take some time, but will happen only once.
Next time you open the project, only skeletons of new or updated binary modules will be re-generated. -sdk.gen.updating.skeletons=Updating Skeletons +sdk.gen.updating.interpreter=Updating Python Interpreter +sdk.scanning.installed.packages=Scanning Installed Packages sdk.gen.stubs.for.binary.modules=Generate stubs for binary module {0} # Active SDK configurable and related dialogs @@ -1009,4 +1010,4 @@ formatter.dictionary.literals=Dictionary literals smartKeys.insert.backslash.in.statement.on.enter=Insert backslash when pressing Enter inside a statement smartKeys.insert.self.in.method=Insert 'self' when defining a method -smartKeys.insert.type.placeholder.in.docstring.stub=Insert type placeholders in the documentation comment stub \ No newline at end of file +smartKeys.insert.type.placeholder.in.docstring.stub=Insert type placeholders in the documentation comment stub diff --git a/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java b/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java index 67d0ef5e71da..c36f84fb3b77 100644 --- a/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java @@ -39,7 +39,6 @@ import com.jetbrains.python.packaging.*; import com.jetbrains.python.packaging.ui.PyChooseRequirementsDialog; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyPsiUtils; -import com.jetbrains.python.sdk.PySdkUtil; import com.jetbrains.python.sdk.PythonSdkType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -158,7 +157,7 @@ public class PyPackageRequirementsInspection extends PyInspection { return; } } - if (PyPackageManager.SETUPTOOLS.equals(packageName)) { + if (PyPackageUtil.SETUPTOOLS.equals(packageName)) { return; } final Module module = ModuleUtilCore.findModuleForPsiElement(packageReferenceExpression); @@ -211,13 +210,7 @@ public class PyPackageRequirementsInspection extends PyInspection { return Collections.emptySet(); } final Set results = new HashSet(requirements); - final List packages; - try { - packages = PyPackageManager.getInstance(sdk).getPackages(PySdkUtil.isRemote(sdk)); - } - catch (ExecutionException e) { - return null; - } + final List packages = PyPackageManager.getInstance(sdk).getPackages(); if (packages == null) return null; for (PyRequirement req : requirements) { final PyPackage pkg = req.match(packages); @@ -237,15 +230,10 @@ public class PyPackageRequirementsInspection extends PyInspection { final PyPackageManager manager = PyPackageManager.getInstance(sdk); List requirements = manager.getRequirements(module); if (requirements != null) { - final List packages; - try { - packages = manager.getPackages(PySdkUtil.isRemote(sdk)); - } - catch (ExecutionException e) { - LOG.warn(e); + final List packages = manager.getPackages(); + if (packages == null) { return null; } - if (packages == null) return null; final List unsatisfied = new ArrayList(); for (PyRequirement req : requirements) { if (!ignoredPackages.contains(req.getName()) && req.match(packages) == null) { @@ -297,13 +285,11 @@ public class PyPackageRequirementsInspection extends PyInspection { public void applyFix(@NotNull final Project project, @NotNull ProblemDescriptor descriptor) { boolean installManagement = false; final PyPackageManager manager = PyPackageManager.getInstance(mySdk); - boolean hasManagement = false; - try { - hasManagement = manager.hasManagement(false); + final List packages = manager.getPackages(); + if (packages == null) { + return; } - catch (ExecutionException ignored) { - } - if (!hasManagement) { + if (PyPackageUtil.hasManagement(packages)) { final int result = Messages.showYesNoDialog(project, "Python packaging tools are required for installing packages. Do you want to " + "install 'pip' and 'setuptools' for your interpreter?", diff --git a/python/src/com/jetbrains/python/packaging/PyCondaPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyCondaPackageManagerImpl.java index ac6135ac7135..12b0f622468b 100644 --- a/python/src/com/jetbrains/python/packaging/PyCondaPackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyCondaPackageManagerImpl.java @@ -49,9 +49,8 @@ public class PyCondaPackageManagerImpl extends PyPackageManagerImpl { public void installManagement() throws ExecutionException { } - @Override - public boolean hasManagement(boolean cachedOnly) throws ExecutionException { + public boolean hasManagement() throws ExecutionException { final Sdk sdk = getSdk(); return isCondaVEnv(sdk); } @@ -126,10 +125,10 @@ public class PyCondaPackageManagerImpl extends PyPackageManagerImpl { @NotNull @Override - protected List getPackages() throws ExecutionException { + protected List collectPackages() throws ExecutionException { final ProcessOutput output = getCondaOutput("list", Lists.newArrayList("-e")); final Set packages = Sets.newConcurrentHashSet(parseCondaToolOutput(output.getStdout())); - packages.addAll(super.getPackages()); + packages.addAll(super.collectPackages()); return Lists.newArrayList(packages); } diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java index f504c905450e..fce9cffa7961 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java @@ -41,7 +41,6 @@ import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.net.HttpConfigurable; import com.jetbrains.python.PythonHelpersLocator; import com.jetbrains.python.psi.LanguageLevel; -import com.jetbrains.python.sdk.PySdkUtil; import com.jetbrains.python.sdk.PythonEnvUtil; import com.jetbrains.python.sdk.PythonSdkType; import org.jetbrains.annotations.NotNull; @@ -50,22 +49,22 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; /** * @author vlan */ public class PyPackageManagerImpl extends PyPackageManager { // Python 2.4-2.5 compatible versions - public static final String SETUPTOOLS_PRE_26_VERSION = "1.4.2"; - public static final String PIP_PRE_26_VERSION = "1.1"; - public static final String VIRTUALENV_PRE_26_VERSION = "1.7.2"; + private static final String SETUPTOOLS_PRE_26_VERSION = "1.4.2"; + private static final String PIP_PRE_26_VERSION = "1.1"; + private static final String VIRTUALENV_PRE_26_VERSION = "1.7.2"; - public static final String SETUPTOOLS_VERSION = "18.1"; - public static final String PIP_VERSION = "7.1.0"; - public static final String VIRTUALENV_VERSION = "13.1.0"; + private static final String SETUPTOOLS_VERSION = "18.1"; + private static final String PIP_VERSION = "7.1.0"; + private static final String VIRTUALENV_VERSION = "13.1.0"; - public static final int OK = 0; - public static final int ERROR_NO_SETUPTOOLS = 3; + private static final int ERROR_NO_SETUPTOOLS = 3; private static final Logger LOG = Logger.getInstance(PyPackageManagerImpl.class); @@ -74,13 +73,12 @@ public class PyPackageManagerImpl extends PyPackageManager { private static final String BUILD_DIR_OPTION = "--build-dir"; - public static final String INSTALL = "install"; - public static final String UNINSTALL = "uninstall"; - public static final String UNTAR = "untar"; + private static final String INSTALL = "install"; + private static final String UNINSTALL = "uninstall"; + private static final String UNTAR = "untar"; - private final Object myCacheLock = new Object(); - private List myPackagesCache = null; - private ExecutionException myExceptionCache = null; + @Nullable private volatile List myPackagesCache = null; + private final AtomicBoolean myUpdatingCache = new AtomicBoolean(false); @NotNull final private Sdk mySdk; @@ -95,7 +93,6 @@ public class PyPackageManagerImpl extends PyPackageManager { VfsUtil.markDirtyAndRefresh(true, true, true, files); }); PythonSdkType.getInstance().setupSdkPaths(sdk); - clearCaches(); }); } @@ -103,24 +100,26 @@ public class PyPackageManagerImpl extends PyPackageManager { public void installManagement() throws ExecutionException { final Sdk sdk = getSdk(); final boolean pre26 = PythonSdkType.getLanguageLevelForSdk(sdk).isOlderThan(LanguageLevel.PYTHON26); - if (!hasSetuptools(false)) { - final String name = SETUPTOOLS + "-" + (pre26 ? SETUPTOOLS_PRE_26_VERSION : SETUPTOOLS_VERSION); + if (!refreshAndCheckForSetuptools()) { + final String name = PyPackageUtil.SETUPTOOLS + "-" + (pre26 ? SETUPTOOLS_PRE_26_VERSION : SETUPTOOLS_VERSION); installManagement(name); } - if (!hasPackage(PIP, false)) { - final String name = PIP + "-" + (pre26 ? PIP_PRE_26_VERSION : PIP_VERSION); + if (PyPackageUtil.findPackage(refreshAndGetPackages(false), PyPackageUtil.PIP) == null) { + final String name = PyPackageUtil.PIP + "-" + (pre26 ? PIP_PRE_26_VERSION : PIP_VERSION); installManagement(name); } } @Override - public boolean hasManagement(boolean cachedOnly) throws ExecutionException { - return hasSetuptools(cachedOnly) && hasPackage(PIP, cachedOnly); + public boolean hasManagement() throws ExecutionException { + return refreshAndCheckForSetuptools() && PyPackageUtil.findPackage(refreshAndGetPackages(false), PyPackageUtil.PIP) != null; } - private boolean hasSetuptools(boolean cachedOnly) throws ExecutionException { + private boolean refreshAndCheckForSetuptools() throws ExecutionException { try { - return hasPackage(SETUPTOOLS, cachedOnly) || hasPackage(DISTRIBUTE, cachedOnly); + final List packages = refreshAndGetPackages(false); + return PyPackageUtil.findPackage(packages, PyPackageUtil.SETUPTOOLS) != null || + PyPackageUtil.findPackage(packages, PyPackageUtil.DISTRIBUTE) != null; } catch (PyExecutionException e) { if (e.getExitCode() == ERROR_NO_SETUPTOOLS) { @@ -137,7 +136,6 @@ public class PyPackageManagerImpl extends PyPackageManager { getPythonProcessResult(fileName, Collections.singletonList(INSTALL), true, true, dirName + name); } finally { - clearCaches(); FileUtil.delete(new File(dirName)); } } @@ -154,10 +152,6 @@ public class PyPackageManagerImpl extends PyPackageManager { return dirName; } - private boolean hasPackage(@NotNull String name, boolean cachedOnly) throws ExecutionException { - return findPackage(name, cachedOnly) != null; - } - PyPackageManagerImpl(@NotNull final Sdk sdk) { mySdk = sdk; subscribeToLocalChanges(); @@ -177,12 +171,12 @@ public class PyPackageManagerImpl extends PyPackageManager { @Override public void install(@NotNull String requirementString) throws ExecutionException { installManagement(); - install(Collections.singletonList(PyRequirement.fromLine(requirementString)), Collections.emptyList()); + install(Collections.singletonList(PyRequirement.fromLine(requirementString)), Collections.emptyList()); } @Override public void install(@NotNull List requirements, @NotNull List extraArgs) throws ExecutionException { - final List args = new ArrayList(); + final List args = new ArrayList<>(); args.add(INSTALL); final File buildDir; try { @@ -195,7 +189,7 @@ public class PyPackageManagerImpl extends PyPackageManager { args.addAll(Arrays.asList(BUILD_DIR_OPTION, buildDir.getAbsolutePath())); } - boolean useUserSite = extraArgs.contains(USE_USER_SITE); + final boolean useUserSite = extraArgs.contains(USE_USER_SITE); final String proxyString = getProxyString(); if (proxyString != null) { @@ -210,7 +204,7 @@ public class PyPackageManagerImpl extends PyPackageManager { getHelperResult(PACKAGING_TOOL, args, !useUserSite, true, null); } catch (PyExecutionException e) { - final List simplifiedArgs = new ArrayList(); + final List simplifiedArgs = new ArrayList<>(); simplifiedArgs.add("install"); if (proxyString != null) { simplifiedArgs.add("--proxy"); @@ -223,14 +217,15 @@ public class PyPackageManagerImpl extends PyPackageManager { throw new PyExecutionException(e.getMessage(), "pip", simplifiedArgs, e.getStdout(), e.getStderr(), e.getExitCode(), e.getFixes()); } finally { - LOG.debug("Packages cache is about to be cleared because these requirements were installed: " + requirements); - clearCaches(); + LOG.debug("Packages cache is about to be refreshed because these requirements were installed: " + requirements); + refreshPackagesSynchronously(); FileUtil.delete(buildDir); } } + @Override public void uninstall(@NotNull List packages) throws ExecutionException { - final List args = new ArrayList(); + final List args = new ArrayList<>(); try { args.add(UNINSTALL); boolean canModify = true; @@ -249,60 +244,31 @@ public class PyPackageManagerImpl extends PyPackageManager { throw new PyExecutionException(e.getMessage(), "pip", args, e.getStdout(), e.getStderr(), e.getExitCode(), e.getFixes()); } finally { - LOG.debug("Packages cache is about to be cleared because these packages were uninstalled: " + packages); - clearCaches(); + LOG.debug("Packages cache is about to be refreshed because these packages were uninstalled: " + packages); + refreshPackagesSynchronously(); } } + @Nullable - public List getPackages(boolean cachedOnly) throws ExecutionException { - synchronized (myCacheLock) { - if (myPackagesCache != null) { - return new ArrayList(myPackagesCache); - } - if (myExceptionCache != null) { - throw myExceptionCache; - } - if (cachedOnly) { - return null; - } - } - try { - final List packages = getPackages(); - if (LOG.isDebugEnabled()) { - LOG.debug("Packages installed in " + mySdk.getName() + ": " + packages); - } - synchronized (myCacheLock) { - myPackagesCache = packages; - return new ArrayList(myPackagesCache); - } - } - catch (ExecutionException e) { - synchronized (myCacheLock) { - myExceptionCache = e; - } - throw e; - } + @Override + public List getPackages() { + final List packages = myPackagesCache; + return packages != null ? Collections.unmodifiableList(packages) : null; } - //@NotNull - //public String fetchLatestVersion(InstalledPackage pkg) throws ExecutionException { - // final ArrayList arguments = Lists.newArrayList("latestVersion", pkg.getName()); - // arguments.addAll(PyPackageService.getInstance().additionalRepositories); - // return getHelperResult(PACKAGING_TOOL, arguments, false, false, null); - //} - @NotNull - protected List getPackages() throws ExecutionException { + protected List collectPackages() throws ExecutionException { final String output; try { + LOG.debug("Collecting installed packages for the SDK " + mySdk.getName(), new Throwable()); output = getHelperResult(PACKAGING_TOOL, Collections.singletonList("list"), false, false, null); } catch (final ProcessNotCreatedException ex) { if (ApplicationManager.getApplication().isUnitTestMode()) { LOG.info("Not-env unit test mode, will return mock packages"); - return Lists.newArrayList(new PyPackage(PIP, PIP_VERSION, null, Collections.emptyList()), - new PyPackage(SETUPTOOLS, SETUPTOOLS_VERSION, null, Collections.emptyList())); + return Lists.newArrayList(new PyPackage(PyPackageUtil.PIP, PIP_VERSION, null, Collections.emptyList()), + new PyPackage(PyPackageUtil.SETUPTOOLS, SETUPTOOLS_VERSION, null, Collections.emptyList())); } else { throw ex; @@ -312,47 +278,26 @@ public class PyPackageManagerImpl extends PyPackageManager { return parsePackagingToolOutput(output); } - @Nullable + @Override + @NotNull public Set getDependents(@NotNull PyPackage pkg) throws ExecutionException { - final List packages = getPackages(false); - if (packages != null) { - final Set dependents = new HashSet(); - for (PyPackage p : packages) { - final List requirements = p.getRequirements(); - for (PyRequirement requirement : requirements) { - if (requirement.getName().equals(pkg.getName())) { - dependents.add(p); - } - } - } - return dependents; - } - return null; - } - - @Override - @Nullable - public PyPackage findPackage(@NotNull String name, boolean cachedOnly) throws ExecutionException { - final List packages = getPackages(cachedOnly); - if (packages != null) { - for (PyPackage pkg : packages) { - if (name.equalsIgnoreCase(pkg.getName())) { - return pkg; + final List packages = refreshAndGetPackages(false); + final Set dependents = new HashSet<>(); + for (PyPackage p : packages) { + final List requirements = p.getRequirements(); + for (PyRequirement requirement : requirements) { + if (requirement.getName().equals(pkg.getName())) { + dependents.add(p); } } } - return null; + return dependents; } - @Nullable @Override - public final PyPackage findPackage(@NotNull final String name) throws ExecutionException { - return findPackage(name, PySdkUtil.isRemote(mySdk)); - } - @NotNull public String createVirtualEnv(@NotNull String destinationDir, boolean useGlobalSite) throws ExecutionException { - final List args = new ArrayList(); + final List args = new ArrayList<>(); final Sdk sdk = getSdk(); final LanguageLevel languageLevel = PythonSdkType.getLanguageLevelForSdk(sdk); final boolean usePyVenv = languageLevel.isAtLeast(LanguageLevel.PYTHON33); @@ -398,6 +343,7 @@ public class PyPackageManagerImpl extends PyPackageManager { return path; } + @Override @Nullable public List getRequirements(@NotNull Module module) { return Optional @@ -405,12 +351,30 @@ public class PyPackageManagerImpl extends PyPackageManager { .orElseGet(() -> PyPackageUtil.findSetupPyRequires(module)); } - protected void clearCaches() { - synchronized (myCacheLock) { - myPackagesCache = null; - myExceptionCache = null; - LOG.debug("Packages cache is cleared"); + + // public List refreshAndGetPackagesIfNotInProgress(boolean alwaysRefresh) throws ExecutionException + + @Override + @NotNull + public List refreshAndGetPackages(boolean alwaysRefresh) throws ExecutionException { + final List currentPackages = myPackagesCache; + if (alwaysRefresh || currentPackages == null) { + try { + final List packages = collectPackages(); + LOG.debug("Packages installed in " + mySdk.getName() + ": " + packages); + myPackagesCache = packages; + return Collections.unmodifiableList(packages); + } + catch (ExecutionException e) { + myPackagesCache = Collections.emptyList(); + throw e; + } } + return Collections.unmodifiableList(currentPackages); + } + + private void refreshPackagesSynchronously() { + PyPackageUtil.updatePackagesSynchronouslyWithGuard(this, myUpdatingCache); } @Nullable @@ -468,7 +432,7 @@ public class PyPackageManagerImpl extends PyPackageManager { if (workingDir == null) { workingDir = new File(homePath).getParent(); } - final List cmdline = new ArrayList(); + final List cmdline = new ArrayList<>(); cmdline.add(homePath); cmdline.add(helperPath); cmdline.addAll(args); @@ -478,11 +442,11 @@ public class PyPackageManagerImpl extends PyPackageManager { final boolean useSudo = !canCreate && !SystemInfo.isWindows && askForSudo; try { - final Map environment = new HashMap(System.getenv()); + final Map environment = new HashMap<>(System.getenv()); PythonEnvUtil.setPythonUnbuffered(environment); PythonEnvUtil.setPythonDontWriteBytecode(environment); - GeneralCommandLine commandLine = new GeneralCommandLine(cmdline).withWorkDirectory(workingDir).withEnvironment(environment); - Process process; + final GeneralCommandLine commandLine = new GeneralCommandLine(cmdline).withWorkDirectory(workingDir).withEnvironment(environment); + final Process process; if (useSudo) { process = ExecUtil.sudo(commandLine, "Please enter your password to make changes in system packages: "); } @@ -535,16 +499,16 @@ public class PyPackageManagerImpl extends PyPackageManager { @NotNull private static List parsePackagingToolOutput(@NotNull String s) throws ExecutionException { final String[] lines = StringUtil.splitByLines(s); - final List packages = new ArrayList(); + final List packages = new ArrayList<>(); for (String line : lines) { final List fields = StringUtil.split(line, "\t"); if (fields.size() < 3) { - throw new PyExecutionException("Invalid output format", PACKAGING_TOOL, Collections.emptyList()); + throw new PyExecutionException("Invalid output format", PACKAGING_TOOL, Collections.emptyList()); } final String name = fields.get(0); final String version = fields.get(1); final String location = fields.get(2); - final List requirements = new ArrayList(); + final List requirements = new ArrayList<>(); if (fields.size() >= 4) { final String requiresLine = fields.get(3); final String requiresSpec = StringUtil.join(StringUtil.split(requiresLine, ":"), "\n"); @@ -567,8 +531,8 @@ public class PyPackageManagerImpl extends PyPackageManager { if (file != null) { for (VirtualFile root : roots) { if (VfsUtilCore.isAncestor(root, file, false)) { - LOG.debug("Clearing packages cache on SDK change"); - clearCaches(); + LOG.debug("Refreshing packages cache on SDK change"); + ApplicationManager.getApplication().executeOnPooledThread(PyPackageManagerImpl.this::refreshPackagesSynchronously); return; } } diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagersImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagersImpl.java index 11d61778f85a..6f0a8cee52c2 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagersImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagersImpl.java @@ -29,10 +29,7 @@ import com.jetbrains.python.sdk.flavors.PythonSdkFlavor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * @author yole @@ -98,16 +95,16 @@ public class PyPackageManagersImpl extends PyPackageManagers { throw new ExecutionException(getErrorMessage()); } + @Override + public boolean hasManagement() throws ExecutionException { + return false; + } + @NotNull private String getErrorMessage() { return "Invalid interpreter \"" + myName + "\" version: " + myLanguageLevel.toString() + " type: " + myFlavor.getName(); } - @Override - public boolean hasManagement(boolean cachedOnly) throws ExecutionException { - throw new ExecutionException(getErrorMessage()); - } - @Override public void install(@NotNull String requirementString) throws ExecutionException { throw new ExecutionException(getErrorMessage()); @@ -135,19 +132,13 @@ public class PyPackageManagersImpl extends PyPackageManagers { @Nullable @Override - public List getPackages(boolean cachedOnly) throws ExecutionException { - throw new ExecutionException(getErrorMessage()); + public List getPackages() { + return null; } - @Nullable + @NotNull @Override - public PyPackage findPackage(@NotNull String name, boolean cachedOnly) throws ExecutionException { - throw new ExecutionException(getErrorMessage()); - } - - @Nullable - @Override - public PyPackage findPackage(@NotNull String name) throws ExecutionException { + public List refreshAndGetPackages(boolean alwaysRefresh) throws ExecutionException { throw new ExecutionException(getErrorMessage()); } @@ -157,7 +148,7 @@ public class PyPackageManagersImpl extends PyPackageManagers { return null; } - @Nullable + @NotNull @Override public Set getDependents(@NotNull PyPackage pkg) throws ExecutionException { throw new ExecutionException(getErrorMessage()); diff --git a/python/src/com/jetbrains/python/packaging/PyPackageUtil.java b/python/src/com/jetbrains/python/packaging/PyPackageUtil.java index 6ce2c625d814..b1e2ffe071d5 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageUtil.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageUtil.java @@ -17,6 +17,9 @@ package com.jetbrains.python.packaging; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.execution.ExecutionException; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; @@ -32,6 +35,7 @@ import com.intellij.openapi.vfs.VirtualFileVisitor; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; +import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; import com.jetbrains.python.packaging.setupPy.SetupTaskIntrospector; @@ -48,11 +52,16 @@ import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; +import java.util.concurrent.atomic.AtomicBoolean; /** * @author vlan */ public class PyPackageUtil { + public static final String SETUPTOOLS = "setuptools"; + public static final String PIP = "pip"; + public static final String DISTRIBUTE = "distribute"; + private static final Logger LOG = Logger.getInstance(PyPackageUtil.class); @NotNull private static final String REQUIRES = "requires"; @@ -270,6 +279,66 @@ public class PyPackageUtil { }.withSshContribution(true).withVagrantContribution(true).withWebDeploymentContribution(true).check(sdk); } + @Nullable + public static List refreshAndGetPackagesModally(@NotNull Sdk sdk) { + final Ref> packagesRef = Ref.create(); + LOG.debug("Showing modal progress for collecting installed packages", new Throwable()); + PyUtil.runWithProgress(null, PyBundle.message("sdk.scanning.installed.packages"), true, false, indicator -> { + indicator.setIndeterminate(true); + try { + packagesRef.set(PyPackageManager.getInstance(sdk).refreshAndGetPackages(false)); + } + catch (ExecutionException e) { + LOG.warn(e); + } + }); + return packagesRef.get(); + } + + /** + * Run unconditional update of the list of packages installed in SDK. Normally only one such of updates should run at time. + * This behavior in enforced by the parameter isUpdating. + * + * @param manager package manager for SDK + * @param isUpdating flag indicating whether another refresh is already running + * @return whether packages were refreshed successfully, e.g. this update wasn't cancelled because of another refresh in progress + */ + public static boolean updatePackagesSynchronouslyWithGuard(@NotNull PyPackageManager manager, @NotNull AtomicBoolean isUpdating) { + assert !ApplicationManager.getApplication().isDispatchThread(); + if (!isUpdating.compareAndSet(false, true)) { + return false; + } + try { + if (manager instanceof PyPackageManagerImpl) { + LOG.info("Refreshing installed packages for SDK " + ((PyPackageManagerImpl)manager).getSdk().getHomePath()); + } + manager.refreshAndGetPackages(true); + } + catch (ExecutionException e) { + LOG.warn(e); + } + finally { + isUpdating.set(false); + } + return true; + } + + + @Nullable + public static PyPackage findPackage(@NotNull List packages, @NotNull String name) { + for (PyPackage pkg : packages) { + if (name.equalsIgnoreCase(pkg.getName())) { + return pkg; + } + } + return null; + } + + public static boolean hasManagement(@NotNull List packages) { + return (findPackage(packages, SETUPTOOLS) != null || findPackage(packages, DISTRIBUTE) != null) || + findPackage(packages, PIP) != null; + } + @Nullable public static List getRequirementsFromTxt(@NotNull Module module) { final VirtualFile requirementsTxt = findRequirementsTxt(module); diff --git a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java index 2fe8f70bfead..158dcfceed99 100644 --- a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java @@ -214,7 +214,6 @@ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl { try { manager.runVagrant(myVagrantFolder, myMachineName); PythonSdkType.getInstance().setupSdkPaths(sdk); - clearCaches(); } catch (ExecutionException e) { throw new RuntimeException(e); diff --git a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java index f0a5a850751a..f84a7eb24b51 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java @@ -93,7 +93,7 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel { application.executeOnPooledThread(() -> { PyExecutionException exception = null; try { - myHasManagement = PyPackageManager.getInstance(selectedSdk).hasManagement(false); + myHasManagement = PyPackageManager.getInstance(selectedSdk).hasManagement(); if (!myHasManagement) { throw new PyExecutionException("Python packaging tools not found", "pip", Collections.emptyList(), "", "", 0, ImmutableList.of(new PyInstallPackageManagementFix())); @@ -156,9 +156,9 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel { } } final String name = pkg.getName(); - if (PyPackageManager.PIP.equals(name) || - PyPackageManager.SETUPTOOLS.equals(name) || - PyPackageManager.DISTRIBUTE.equals(name) || + if (PyPackageUtil.PIP.equals(name) || + PyPackageUtil.SETUPTOOLS.equals(name) || + PyPackageUtil.DISTRIBUTE.equals(name) || PyCondaPackageManagerImpl.PYTHON.equals(name)) { return false; } diff --git a/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java b/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java index 4096539aef49..78332f1fdba4 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java @@ -15,6 +15,7 @@ */ package com.jetbrains.python.packaging.ui; +import com.google.common.collect.Lists; import com.intellij.execution.ExecutionException; import com.intellij.execution.RunCanceledByUserException; import com.intellij.openapi.project.Project; @@ -139,17 +140,17 @@ public class PyPackageManagementService extends PackageManagementServiceEx { @Override public Collection getInstalledPackages() throws IOException { - List packages; + + final PyPackageManager manager = PyPackageManager.getInstance(mySdk); + final List packages; try { - packages = PyPackageManager.getInstance(mySdk).getPackages(false); - if (packages != null) { - Collections.sort(packages, (pkg1, pkg2) -> pkg1.getName().compareTo(pkg2.getName())); - } + packages = Lists.newArrayList(manager.refreshAndGetPackages(true)); } catch (ExecutionException e) { throw new IOException(e); } - return packages != null ? new ArrayList(packages) : new ArrayList(); + Collections.sort(packages, (pkg1, pkg2) -> pkg1.getName().compareTo(pkg2.getName())); + return new ArrayList(packages); } @Override diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java index 9fdc17b2ebb1..bdf133eed108 100644 --- a/python/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/src/com/jetbrains/python/psi/PyUtil.java @@ -28,6 +28,7 @@ import com.intellij.injected.editor.VirtualFileWindow; import com.intellij.lang.ASTFactory; import com.intellij.lang.ASTNode; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorFactory; @@ -38,6 +39,9 @@ import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.module.ModuleUtilCore; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.ModuleRootManager; @@ -81,15 +85,11 @@ import com.jetbrains.python.refactoring.classes.PyDependenciesComparator; import com.jetbrains.python.refactoring.classes.extractSuperclass.PyExtractSuperclassHelper; import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo; import com.jetbrains.python.sdk.PythonSdkType; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.*; import javax.swing.*; import java.awt.*; import java.io.File; -import java.io.FileFilter; import java.io.IOException; import java.util.*; import java.util.List; @@ -858,6 +858,28 @@ public class PyUtil { return result; } + public static void runWithProgress(@Nullable Project project, @Nls(capitalization = Nls.Capitalization.Title) @NotNull String title, + boolean modal, boolean canBeCancelled, @NotNull final Consumer function) { + ApplicationManager.getApplication().invokeAndWait(() -> { + if (modal) { + ProgressManager.getInstance().run(new Task.Modal(project, title, canBeCancelled) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + function.consume(indicator); + } + }); + } + else { + ProgressManager.getInstance().run(new Task.Backgroundable(project, title, canBeCancelled) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + function.consume(indicator); + } + }); + } + }, ModalityState.current()); + } + /** * Executes code only if
_PYCHARM_VERBOSE_MODE
is set in env (which should be done for debug purposes only) * @param runnable code to call diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkUpdater.java b/python/src/com/jetbrains/python/sdk/PythonSdkUpdater.java index 7baf341b46de..7e38682fcf4f 100644 --- a/python/src/com/jetbrains/python/sdk/PythonSdkUpdater.java +++ b/python/src/com/jetbrains/python/sdk/PythonSdkUpdater.java @@ -18,6 +18,7 @@ package com.jetbrains.python.sdk; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Sets; +import com.intellij.execution.ExecutionException; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; @@ -44,6 +45,7 @@ import com.intellij.util.concurrency.BlockingSet; import com.intellij.util.concurrency.EdtExecutorService; import com.jetbrains.python.PyBundle; import com.jetbrains.python.codeInsight.userSkeletons.PyUserSkeletonsUtil; +import com.jetbrains.python.packaging.PyPackageManager; import com.jetbrains.python.psi.PyUtil; import com.jetbrains.python.remote.PyCredentialsContribution; import com.jetbrains.python.remote.PyRemoteSdkAdditionalDataBase; @@ -138,7 +140,7 @@ public class PythonSdkUpdater implements StartupActivity { } ourScheduledToRefresh.remove(homePath); } - ProgressManager.getInstance().run(new Task.Backgroundable(project, PyBundle.message("sdk.gen.updating.skeletons"), false) { + ProgressManager.getInstance().run(new Task.Backgroundable(project, PyBundle.message("sdk.gen.updating.interpreter"), false) { @Override public void run(@NotNull ProgressIndicator indicator) { final Project project1 = getProject(); @@ -152,8 +154,19 @@ public class PythonSdkUpdater implements StartupActivity { LOG.error("For refreshing skeletons of remote SDK, either project or owner component must be specified"); } LOG.info("Performing background update of skeletons for SDK " + sdk12.getHomePath()); + indicator.setText("Updating skeletons..."); PySkeletonRefresher.refreshSkeletonsOfSdk(project1, ownerComponent, skeletonsPath, sdk12); updateRemoteSdkPaths(sdk12); + indicator.setIndeterminate(true); + indicator.setText("Scanning installed packages..."); + indicator.setText2(""); + LOG.info("Performing background scan of packages for SDK " + sdk12.getHomePath()); + try { + PyPackageManager.getInstance(sdk12).refreshAndGetPackages(true); + } + catch (ExecutionException e) { + LOG.error(e); + } } catch (InvalidSdkException e) { if (PythonSdkType.isVagrant(sdk12) diff --git a/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java b/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java index a3600e260b3b..bc5cd1f8f936 100644 --- a/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java +++ b/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java @@ -43,7 +43,6 @@ import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.buildout.BuildoutFacet; import com.jetbrains.python.codeInsight.userSkeletons.PyUserSkeletonsUtil; -import com.jetbrains.python.packaging.PyPackageManager; import com.jetbrains.python.psi.resolve.PythonSdkPathCache; import com.jetbrains.python.remote.PythonRemoteInterpreterManager; import com.jetbrains.python.sdk.InvalidSdkException; @@ -345,15 +344,6 @@ public class PySkeletonRefresher { indicate(PyBundle.message("sdk.gen.cleaning.$0", readablePath)); cleanUpSkeletons(skeletonsDir); } - if (PySdkUtil.isRemote(mySdk)) { - try { - // Force loading packages - PyPackageManager.getInstance(mySdk).getPackages(false); - } - catch (ExecutionException e) { - // ignore - already logged - } - } if ((builtinsUpdated || PySdkUtil.isRemote(mySdk)) && myProject != null) { ApplicationManager.getApplication().invokeLater(() -> DaemonCodeAnalyzer.getInstance(myProject).restart(), myProject.getDisposed()); diff --git a/python/src/com/jetbrains/python/testing/PyIntegratedToolsProjectConfigurator.java b/python/src/com/jetbrains/python/testing/PyIntegratedToolsProjectConfigurator.java index 3ad43f6d6c86..8cf525865f0f 100644 --- a/python/src/com/jetbrains/python/testing/PyIntegratedToolsProjectConfigurator.java +++ b/python/src/com/jetbrains/python/testing/PyIntegratedToolsProjectConfigurator.java @@ -38,6 +38,7 @@ import com.jetbrains.python.PythonModuleTypeBase; import com.jetbrains.python.documentation.PyDocumentationSettings; import com.jetbrains.python.documentation.docstrings.DocStringFormat; import com.jetbrains.python.documentation.docstrings.DocStringUtil; +import com.jetbrains.python.packaging.PyPackage; import com.jetbrains.python.packaging.PyPackageUtil; import com.jetbrains.python.psi.*; import com.jetbrains.python.sdk.PythonSdkType; @@ -114,17 +115,20 @@ public class PyIntegratedToolsProjectConfigurator implements DirectoryProjectCon //check if installed in sdk final Sdk sdk = PythonSdkType.findPythonSdk(module); if (sdk != null && sdk.getSdkType() instanceof PythonSdkType) { - final Boolean nose = VFSTestFrameworkListener.isTestFrameworkInstalled(sdk, PyNames.NOSE_TEST); - final Boolean pytest = VFSTestFrameworkListener.isTestFrameworkInstalled(sdk, PyNames.PY_TEST); - final Boolean attest = VFSTestFrameworkListener.isTestFrameworkInstalled(sdk, PyNames.AT_TEST); - if (nose != null && nose) - testRunner = PythonTestConfigurationsModel.PYTHONS_NOSETEST_NAME; - else if (pytest != null && pytest) - testRunner = PythonTestConfigurationsModel.PY_TEST_NAME; - else if (attest != null && attest) - testRunner = PythonTestConfigurationsModel.PYTHONS_ATTEST_NAME; - if (!testRunner.isEmpty()) { - LOG.debug("Test runner '" + testRunner + "' was detected from SDK " + sdk); + final List packages = PyPackageUtil.refreshAndGetPackagesModally(sdk); + if (packages != null) { + final boolean nose = PyPackageUtil.findPackage(packages, PyNames.NOSE_TEST) != null; + final boolean pytest = PyPackageUtil.findPackage(packages, PyNames.PY_TEST) != null; + final boolean attest = PyPackageUtil.findPackage(packages, PyNames.AT_TEST) != null; + if (nose) + testRunner = PythonTestConfigurationsModel.PYTHONS_NOSETEST_NAME; + else if (pytest) + testRunner = PythonTestConfigurationsModel.PY_TEST_NAME; + else if (attest) + testRunner = PythonTestConfigurationsModel.PYTHONS_ATTEST_NAME; + if (!testRunner.isEmpty()) { + LOG.debug("Test runner '" + testRunner + "' was detected from SDK " + sdk); + } } } } diff --git a/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java b/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java index 6dd8eb7b839a..d193dc2aabc9 100644 --- a/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java +++ b/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java @@ -15,7 +15,6 @@ */ package com.jetbrains.python.testing; -import com.intellij.execution.ExecutionException; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.projectRoots.Sdk; @@ -30,22 +29,25 @@ import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent; import com.intellij.openapi.vfs.newvfs.events.VFileEvent; import com.intellij.util.Alarm; import com.intellij.util.messages.MessageBus; -import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.update.MergingUpdateQueue; import com.intellij.util.ui.update.Update; import com.jetbrains.python.PyNames; +import com.jetbrains.python.packaging.PyPackage; import com.jetbrains.python.packaging.PyPackageManager; +import com.jetbrains.python.packaging.PyPackageUtil; import com.jetbrains.python.sdk.PySdkUtil; import com.jetbrains.python.sdk.PythonSdkType; import org.jetbrains.annotations.NotNull; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; /** * User: catherine */ public class VFSTestFrameworkListener { private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.testing.VFSTestFrameworkListener"); + private final AtomicBoolean myIsUpdating = new AtomicBoolean(false); private final MergingUpdateQueue myQueue; private final PyTestFrameworkService myService; @@ -112,17 +114,18 @@ public class VFSTestFrameworkListener { /** * @return null if we can't be sure */ - public static Boolean isTestFrameworkInstalled(Sdk sdk, String testPackageName) { + public Boolean isTestFrameworkInstalled(Sdk sdk, String testPackageName) { if (sdk == null || StringUtil.isEmptyOrSpaces(sdk.getHomePath())) { LOG.info("Searching test runner in empty sdk"); return null; } - final PyPackageManager packageManager = PyPackageManager.getInstance(sdk); - try { - return packageManager.findPackage(testPackageName, false) != null; - } - catch (ExecutionException e) { - LOG.info("Can't load package list " + e.getMessage()); + final PyPackageManager manager = PyPackageManager.getInstance(sdk); + final boolean refreshed = PyPackageUtil.updatePackagesSynchronouslyWithGuard(manager, myIsUpdating); + if (refreshed) { + final List packages = manager.getPackages(); + if (packages != null) { + return PyPackageUtil.findPackage(packages, testPackageName) != null; + } } return null; } diff --git a/python/src/com/jetbrains/python/testing/pytest/PyTestConfigurationProducer.java b/python/src/com/jetbrains/python/testing/pytest/PyTestConfigurationProducer.java index 2249516da492..69fa292538c8 100644 --- a/python/src/com/jetbrains/python/testing/pytest/PyTestConfigurationProducer.java +++ b/python/src/com/jetbrains/python/testing/pytest/PyTestConfigurationProducer.java @@ -15,7 +15,6 @@ */ package com.jetbrains.python.testing.pytest; -import com.intellij.execution.ExecutionException; import com.intellij.execution.Location; import com.intellij.execution.actions.ConfigurationContext; import com.intellij.openapi.module.Module; @@ -31,6 +30,7 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.webcore.packaging.PackageVersionComparator; import com.jetbrains.python.packaging.PyPackage; import com.jetbrains.python.packaging.PyPackageManager; +import com.jetbrains.python.packaging.PyPackageUtil; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyFile; import com.jetbrains.python.psi.PyFunction; @@ -115,17 +115,12 @@ public class PyTestConfigurationProducer extends PythonTestConfigurationProducer if (pyFunction != null) { keywords = pyFunction.getName(); if (pyClass != null) { - final PyPackageManager packageManager = PyPackageManager.getInstance(sdk); - try { - final PyPackage pytestPackage = packageManager.findPackage("pytest", false); - if (pytestPackage != null && PackageVersionComparator.VERSION_COMPARATOR.compare(pytestPackage.getVersion(), "2.3.3") >= 0) { - keywords = pyClass.getName() + " and " + keywords; - } - else { - keywords = pyClass.getName() + "." + keywords; - } + final List packages = PyPackageManager.getInstance(sdk).getPackages(); + final PyPackage pytestPackage = packages != null ? PyPackageUtil.findPackage(packages, "pytest") : null; + if (pytestPackage != null && PackageVersionComparator.VERSION_COMPARATOR.compare(pytestPackage.getVersion(), "2.3.3") >= 0) { + keywords = pyClass.getName() + " and " + keywords; } - catch (ExecutionException e) { + else { keywords = pyClass.getName() + "." + keywords; } } diff --git a/python/testSrc/com/jetbrains/env/PyEnvTestCase.java b/python/testSrc/com/jetbrains/env/PyEnvTestCase.java index 3e0d55d15fb4..01587acba9c2 100644 --- a/python/testSrc/com/jetbrains/env/PyEnvTestCase.java +++ b/python/testSrc/com/jetbrains/env/PyEnvTestCase.java @@ -19,6 +19,7 @@ import com.intellij.util.ui.UIUtil; import com.jetbrains.TestEnv; import com.jetbrains.python.packaging.PyPackage; import com.jetbrains.python.packaging.PyPackageManager; +import com.jetbrains.python.packaging.PyPackageUtil; import org.hamcrest.Matchers; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -113,7 +114,7 @@ public abstract class PyEnvTestCase { @Nullable public static PyPackage getInstalledDjango(@NotNull final Sdk sdk) throws ExecutionException { - return PyPackageManager.getInstance(sdk).findPackage("django", false); + return PyPackageUtil.findPackage(PyPackageManager.getInstance(sdk).refreshAndGetPackages(false), "django"); } public static String norm(String testDataPath) { diff --git a/python/testSrc/com/jetbrains/env/python/PyPackagingTest.java b/python/testSrc/com/jetbrains/env/python/PyPackagingTest.java index e89d9033bf8e..a7d127c5a325 100644 --- a/python/testSrc/com/jetbrains/env/python/PyPackagingTest.java +++ b/python/testSrc/com/jetbrains/env/python/PyPackagingTest.java @@ -52,7 +52,7 @@ public class PyPackagingTest extends PyEnvTestCase { final Sdk sdk = createTempSdk(sdkHome, SdkCreationType.EMPTY_SDK); List packages = null; try { - packages = PyPackageManager.getInstance(sdk).getPackages(false); + packages = PyPackageManager.getInstance(sdk).refreshAndGetPackages(false); } catch (ExecutionException ignored) { } @@ -82,7 +82,7 @@ public class PyPackagingTest extends PyEnvTestCase { assertNotNull(venvSdk); assertTrue(PythonSdkType.isVirtualEnv(venvSdk)); assertInstanceOf(PythonSdkFlavor.getPlatformIndependentFlavor(venvSdk.getHomePath()), VirtualEnvSdkFlavor.class); - final List packages = PyPackageManager.getInstance(venvSdk).getPackages(false); + final List packages = PyPackageManager.getInstance(venvSdk).refreshAndGetPackages(false); final PyPackage setuptools = findPackage("setuptools", packages); assertNotNull(setuptools); assertEquals("setuptools", setuptools.getName()); @@ -114,11 +114,11 @@ public class PyPackagingTest extends PyEnvTestCase { final Sdk venvSdk = createTempSdk(venvSdkHome, SdkCreationType.EMPTY_SDK); assertNotNull(venvSdk); final PyPackageManager manager = PyPackageManager.getInstance(venvSdk); - final List packages1 = manager.getPackages(false); + final List packages1 = manager.refreshAndGetPackages(false); // TODO: Install Markdown from a local file manager.install(list(PyRequirement.fromLine("Markdown<2.2"), new PyRequirement("httplib2")), Collections.emptyList()); - final List packages2 = manager.getPackages(false); + final List packages2 = manager.refreshAndGetPackages(false); final PyPackage markdown2 = findPackage("Markdown", packages2); assertNotNull(markdown2); assertTrue(markdown2.isInstalled()); @@ -126,7 +126,7 @@ public class PyPackagingTest extends PyEnvTestCase { assertNotNull(pip1); assertEquals("pip", pip1.getName()); manager.uninstall(list(pip1)); - final List packages3 = manager.getPackages(false); + final List packages3 = manager.refreshAndGetPackages(false); final PyPackage pip2 = findPackage("pip", packages3); assertNull(pip2); } diff --git a/python/testSrc/com/jetbrains/python/inspections/PyPackageRequirementsInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyPackageRequirementsInspectionTest.java index 00d5eee2330e..554c34a904cf 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyPackageRequirementsInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyPackageRequirementsInspectionTest.java @@ -15,14 +15,25 @@ */ package com.jetbrains.python.inspections; +import com.intellij.openapi.projectRoots.Sdk; import com.jetbrains.python.fixtures.PyTestCase; +import com.jetbrains.python.packaging.PyPackageManager; import com.jetbrains.python.psi.LanguageLevel; +import com.jetbrains.python.sdk.PythonSdkType; import org.jetbrains.annotations.NotNull; /** * @author vlan */ public class PyPackageRequirementsInspectionTest extends PyTestCase { + @Override + public void setUp() throws Exception { + super.setUp(); + final Sdk sdk = PythonSdkType.findPythonSdk(myFixture.getModule()); + assertNotNull(sdk); + PyPackageManager.getInstance(sdk).refreshAndGetPackages(true); + } + public void testPartiallySatisfiedRequirementsTxt() { doTest("test1.py"); } diff --git a/resources-en/src/intentionDescriptions/DelegateWithDefaultParamValueIntentionAction/after.java.template b/resources-en/src/intentionDescriptions/DelegateWithDefaultParamValueIntentionAction/after.java.template deleted file mode 100644 index 8b1a0e1d4fe8..000000000000 --- a/resources-en/src/intentionDescriptions/DelegateWithDefaultParamValueIntentionAction/after.java.template +++ /dev/null @@ -1,9 +0,0 @@ -class Test { - void foo(int a) { - foo(a, |); - } - - void foo(int a, int b) { - //do smth - } -} \ No newline at end of file diff --git a/resources-en/src/intentionDescriptions/DelegateWithDefaultParamValueIntentionAction/before.java.template b/resources-en/src/intentionDescriptions/DelegateWithDefaultParamValueIntentionAction/before.java.template deleted file mode 100644 index 0ecad2088cdf..000000000000 --- a/resources-en/src/intentionDescriptions/DelegateWithDefaultParamValueIntentionAction/before.java.template +++ /dev/null @@ -1,5 +0,0 @@ -class Test { - void foo(int a, int b) { - //do smth - } -} \ No newline at end of file diff --git a/resources-en/src/intentionDescriptions/DelegateWithDefaultParamValueIntentionAction/description.html b/resources-en/src/intentionDescriptions/DelegateWithDefaultParamValueIntentionAction/description.html deleted file mode 100644 index 821c26642038..000000000000 --- a/resources-en/src/intentionDescriptions/DelegateWithDefaultParamValueIntentionAction/description.html +++ /dev/null @@ -1,5 +0,0 @@ - - -Generates an overloaded method which delegates to the current one setting the selected parameter to the specified default value. - - diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index 241fb3149571..29201b0565b2 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -960,10 +960,6 @@ com.intellij.codeInsight.intention.impl.AddOverrideAnnotationAction Java/Annotations - - com.intellij.codeInsight.daemon.impl.quickfix.DelegateWithDefaultParamValueIntentionAction - Java/Declaration - com.intellij.codeInsight.daemon.impl.quickfix.DefineParamsDefaultValueAction Java/Declaration