diff --git a/.idea/libraries/pty4j.xml b/.idea/libraries/pty4j.xml index 55f81956ee96..bf8da8019521 100644 --- a/.idea/libraries/pty4j.xml +++ b/.idea/libraries/pty4j.xml @@ -1,11 +1,11 @@ - + - + diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactManagerImpl.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactManagerImpl.java index 490bb8a9666c..dffdbaba8aa1 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactManagerImpl.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/ArtifactManagerImpl.java @@ -16,9 +16,7 @@ package com.intellij.packaging.impl.artifacts; import com.intellij.compiler.server.BuildManager; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.Result; -import com.intellij.openapi.application.WriteAction; +import com.intellij.openapi.application.*; import com.intellij.openapi.components.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.ProjectLoadingErrorsNotifier; @@ -193,9 +191,18 @@ public class ArtifactManagerImpl extends ArtifactManager implements ProjectCompo @Override public void loadState(ArtifactManagerState managerState) { - final List artifacts = new ArrayList(); - for (ArtifactState state : managerState.getArtifacts()) { - artifacts.add(loadArtifact(state)); + List artifactStates = managerState.getArtifacts(); + final List artifacts = new ArrayList(artifactStates.size()); + if (!artifactStates.isEmpty()) { + AccessToken token = ReadAction.start(); + try { + for (ArtifactState state : artifactStates) { + artifacts.add(loadArtifact(state)); + } + } + finally { + token.finish(); + } } if (myLoaded) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java index 26dbb4456d8f..e9f9d49eb3e5 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java @@ -49,7 +49,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState { private final DfaValueFactory myFactory; private final List myEqClasses; - // dfa value id -> indices in myEqClasses list of the classes which contain the id (or negated or wrapped) + // dfa value id -> indices in myEqClasses list of the classes which contain the id (or wrapped) private final TIntObjectHashMap myIdToEqClassesIndices; private final Stack myStack; private final TLongHashSet myDistinctClasses; @@ -321,7 +321,6 @@ public class DfaMemoryStateImpl implements DfaMemoryState { } private void removeFromMap(int id, int index) { - if (id < 0) return; id = unwrap(myFactory.getValue(id)).getID(); int[] classes = myIdToEqClassesIndices.get(id); if (classes != null) { @@ -487,11 +486,6 @@ public class DfaMemoryStateImpl implements DfaMemoryState { } EqClass newClass = new EqClass(c1); - for (int i = 0; i < newClass.size(); i++) { - int c = newClass.get(i); - removeFromMap(c, c1Index); - addToMap(c, c1Index); - } myEqClasses.set(c1Index, newClass); for (int i = 0; i < c2.size(); i++) { diff --git a/java/java-analysis-impl/src/com/intellij/refactoring/util/duplicates/DuplicatesFinder.java b/java/java-analysis-impl/src/com/intellij/refactoring/util/duplicates/DuplicatesFinder.java index cc65ad2184c5..c34982debacf 100644 --- a/java/java-analysis-impl/src/com/intellij/refactoring/util/duplicates/DuplicatesFinder.java +++ b/java/java-analysis-impl/src/com/intellij/refactoring/util/duplicates/DuplicatesFinder.java @@ -376,10 +376,13 @@ public class DuplicatesFinder { final PsiClass patternClass = RefactoringChangeUtil.getThisClass(pattern); final PsiClass candidateClass = RefactoringChangeUtil.getThisClass(candidate); if (resolveResult1 == resolveResult2 && - resolveResult1 instanceof PsiMember && - !InheritanceUtil.isInheritorOrSelf(candidateClass, patternClass, true) && - InheritanceUtil.isInheritorOrSelf(candidateClass, ((PsiMember)resolveResult1).getContainingClass(), true)) { - return false; + resolveResult1 instanceof PsiMember) { + final PsiClass containingClass = ((PsiMember)resolveResult1).getContainingClass(); + if (!InheritanceUtil.isInheritorOrSelf(candidateClass, patternClass, true) && + InheritanceUtil.isInheritorOrSelf(candidateClass, containingClass, true) && + InheritanceUtil.isInheritorOrSelf(patternClass, containingClass, true)) { + return false; + } } } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/actions/AddImportAction.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/actions/AddImportAction.java index f01b5b7d08bd..b9bb43c0abcc 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/actions/AddImportAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/actions/AddImportAction.java @@ -42,12 +42,14 @@ import com.intellij.psi.PsiReference; import com.intellij.psi.statistics.JavaStatisticsManager; import com.intellij.psi.statistics.StatisticsManager; import com.intellij.ui.popup.list.ListPopupImpl; +import com.intellij.ui.popup.list.PopupListElementRenderer; import com.intellij.util.IncorrectOperationException; import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import javax.swing.*; +import java.awt.*; import java.util.ArrayList; import java.util.List; @@ -161,7 +163,18 @@ public class AddImportAction implements QuestionAction { ListPopupImpl popup = new ListPopupImpl(step) { @Override protected ListCellRenderer getListElementRenderer() { - return new DefaultPsiElementCellRenderer(); + final PopupListElementRenderer baseRenderer = (PopupListElementRenderer)super.getListElementRenderer(); + final DefaultPsiElementCellRenderer psiRenderer = new DefaultPsiElementCellRenderer(); + return new ListCellRenderer() { + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + JPanel panel = new JPanel(new BorderLayout()); + baseRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + panel.add(baseRenderer.getNextStepLabel(), BorderLayout.EAST); + panel.add(psiRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus)); + return panel; + } + }; } }; popup.showInBestPositionFor(myEditor); 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 02e4c3afcc69..7440f6feb0e8 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 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,10 +17,12 @@ package com.intellij.codeInsight.daemon.impl.quickfix; import com.intellij.codeInsight.intention.impl.ParameterClassMember; import com.intellij.ide.util.MemberChooser; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiMethod; -import com.intellij.psi.PsiParameter; +import com.intellij.lang.java.JavaLanguage; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -32,24 +34,42 @@ import java.util.List; */ public class DefineParamsDefaultValueAction extends DelegateWithDefaultParamValueIntentionAction { - @NotNull @Override - public String getText() { - return "Define params default value"; + public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) { + if (!JavaLanguage.INSTANCE.equals(element.getLanguage())) { + return false; + } + final PsiElement parent = PsiTreeUtil.getParentOfType(element, PsiMethod.class, PsiCodeBlock.class); + if (!(parent instanceof PsiMethod)) { + return false; + } + final PsiMethod method = (PsiMethod)parent; + final PsiParameterList parameterList = method.getParameterList(); + if (parameterList.getParametersCount() == 0) { + return false; + } + final PsiClass containingClass = method.getContainingClass(); + if (containingClass == null || (containingClass.isInterface() && !PsiUtil.isLanguageLevel8OrHigher(method))) { + return false; + } + setText("Generate overloaded " + (method.isConstructor() ? "constructor" : "method") + " with default parameter values"); + return true; } @Nullable @Override protected PsiParameter[] getParams(PsiElement element) { final PsiMethod method = PsiTreeUtil.getParentOfType(element, PsiMethod.class); + assert method != null; final PsiParameter[] parameters = method.getParameterList().getParameters(); final ParameterClassMember[] members = new ParameterClassMember[parameters.length]; for (int i = 0; i < members.length; i++) { members[i] = new ParameterClassMember(parameters[i]); } - final MemberChooser chooser = new MemberChooser(members, false, true, element.getProject()); + final MemberChooser chooser = + new MemberChooser(members, false, true, element.getProject()); chooser.selectElements(members); - chooser.setTitle("Choose " + (method.isConstructor() ? "Constructor" : "Method") + " Parameters"); + chooser.setTitle("Choose Default Value Parameters"); if (chooser.showAndGet()) { final List elements = chooser.getSelectedElements(); if (elements != null) { @@ -67,4 +87,10 @@ public class DefineParamsDefaultValueAction extends DelegateWithDefaultParamValu public boolean startInWriteAction() { return false; } + + @NotNull + @Override + public String getFamilyName() { + return "Generate overloaded method with default parameter values"; + } } 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 index 856ffee4ef5d..45655508b787 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,6 +33,7 @@ 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; @@ -57,8 +58,12 @@ public class DelegateWithDefaultParamValueIntentionAction extends PsiElementBase if (declarationScope instanceof PsiMethod) { final PsiMethod method = (PsiMethod)declarationScope; final PsiClass containingClass = method.getContainingClass(); - if (containingClass != null && !containingClass.isInterface()) { - return containingClass.findMethodBySignature(generateMethodPrototype(method, parameter), false) == null; + if (containingClass != null && (!containingClass.isInterface() || PsiUtil.isLanguageLevel7OrHigher(method))) { + if (containingClass.findMethodBySignature(generateMethodPrototype(method, parameter), false) != null) { + return false; + } + setText("Generate overloaded " + (method.isConstructor() ? "constructor" : "method") + " with default parameter value"); + return true; } } } @@ -81,6 +86,11 @@ public class DelegateWithDefaultParamValueIntentionAction extends PsiElementBase prototype.getModifierList().setModifierProperty(PsiModifier.ABSTRACT, false); prototype.addBefore(emptyBody, null); } + final PsiClass aClass = method.getContainingClass(); + if (aClass != null && aClass.isInterface()) { + prototype.getModifierList().setModifierProperty(PsiModifier.DEFAULT, true); + } + for (int i = params.length - 1; i >= 0; i--) { PsiParameter param = params[i]; final int parameterIndex = method.getParameterList().getParameterIndex(param); @@ -98,7 +108,8 @@ public class DelegateWithDefaultParamValueIntentionAction extends PsiElementBase final PsiMethod existingMethod = method.getContainingClass().findMethodBySignature(methodPrototype, false); if (existingMethod != null) { editor.getCaretModel().moveToOffset(existingMethod.getTextOffset()); - HintManager.getInstance().showErrorHint(editor, "Method with the chosen signature already exist"); + HintManager.getInstance().showErrorHint(editor, (existingMethod.isConstructor() ? "Constructor" : "Method") + + " with the chosen signature already exists"); return; } @@ -170,15 +181,9 @@ public class DelegateWithDefaultParamValueIntentionAction extends PsiElementBase return new PsiParameter[]{PsiTreeUtil.getParentOfType(element, PsiParameter.class)}; } - @NotNull - @Override - public String getText() { - return "Generate delegated method with default parameter value"; - } - @NotNull @Override public String getFamilyName() { - return getText(); + return "Generate overloaded method with default parameter value"; } } diff --git a/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/JavaSmartEnterProcessor.java b/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/JavaSmartEnterProcessor.java index a5b28cb663bd..7915984fa253 100644 --- a/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/JavaSmartEnterProcessor.java +++ b/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/JavaSmartEnterProcessor.java @@ -189,13 +189,7 @@ public class JavaSmartEnterProcessor extends SmartEnterProcessor { return; } PsiElement parent = atCaret.getParent(); - if (parent instanceof PsiCodeBlock) { - final PsiCodeBlock block = (PsiCodeBlock) parent; - if (block.getStatements().length > 0 && block.getStatements()[0] == atCaret) { - atCaret = block; - } - } - else if (parent instanceof PsiForStatement) { + if (parent instanceof PsiForStatement) { atCaret = parent; } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ForeachPostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ForeachPostfixTemplate.java index 72404efa582f..7daeb5e3e1ca 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ForeachPostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ForeachPostfixTemplate.java @@ -28,8 +28,8 @@ import static com.intellij.codeInsight.template.postfix.util.JavaPostfixTemplate import static com.intellij.codeInsight.template.postfix.util.JavaPostfixTemplatesUtils.selectorTopmost; public class ForeachPostfixTemplate extends StringBasedPostfixTemplate { - public ForeachPostfixTemplate() { - super("for", "for (T item : expr)", selectorTopmost(IS_ITERABLE_OR_ARRAY)); + public ForeachPostfixTemplate(String name) { + super(name, "for (T item : expr)", selectorTopmost(IS_ITERABLE_OR_ARRAY)); } @Override diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/JavaPostfixTemplateProvider.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/JavaPostfixTemplateProvider.java index 7f21ad2bfcca..dbfe90d7a27f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/JavaPostfixTemplateProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/JavaPostfixTemplateProvider.java @@ -42,7 +42,8 @@ public class JavaPostfixTemplateProvider implements PostfixTemplateProvider { new ElseStatementPostfixTemplate(), new ForAscendingPostfixTemplate(), new ForDescendingPostfixTemplate(), - new ForeachPostfixTemplate(), + new ForeachPostfixTemplate("iter"), + new ForeachPostfixTemplate("for"), new FormatPostfixTemplate(), new IfStatementPostfixTemplate(), new InstanceofExpressionPostfixTemplate(), diff --git a/java/java-tests/testData/codeInsight/completeStatement/AddMissingParen_after.java b/java/java-tests/testData/codeInsight/completeStatement/AddMissingParen_after.java index 3cf8eb4381a0..37db1a8c03f9 100644 --- a/java/java-tests/testData/codeInsight/completeStatement/AddMissingParen_after.java +++ b/java/java-tests/testData/codeInsight/completeStatement/AddMissingParen_after.java @@ -1,8 +1,7 @@ class Foo { abstract void a(); - { - a(); + a(); } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completeStatement/AddMissingSemicolon_after.java b/java/java-tests/testData/codeInsight/completeStatement/AddMissingSemicolon_after.java index 3cf8eb4381a0..37db1a8c03f9 100644 --- a/java/java-tests/testData/codeInsight/completeStatement/AddMissingSemicolon_after.java +++ b/java/java-tests/testData/codeInsight/completeStatement/AddMissingSemicolon_after.java @@ -1,8 +1,7 @@ class Foo { abstract void a(); - { - a(); + a(); } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completeStatement/IncompleteElseIf_after.java b/java/java-tests/testData/codeInsight/completeStatement/IncompleteElseIf_after.java index eb6478527b9c..5950c16978b5 100644 --- a/java/java-tests/testData/codeInsight/completeStatement/IncompleteElseIf_after.java +++ b/java/java-tests/testData/codeInsight/completeStatement/IncompleteElseIf_after.java @@ -1,6 +1,5 @@ class Foo { boolean a; - { if (a) { } else { diff --git a/java/java-tests/testData/codeInsight/completeStatement/NoBlockReformat.java b/java/java-tests/testData/codeInsight/completeStatement/NoBlockReformat.java new file mode 100644 index 000000000000..cea4b2e627c4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completeStatement/NoBlockReformat.java @@ -0,0 +1,9 @@ +public class Test { + public void foo(int x) { + int a = 2 + + System.out.println(""); + System.out.println(""); + System.out.println(""); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completeStatement/NoBlockReformat_after.java b/java/java-tests/testData/codeInsight/completeStatement/NoBlockReformat_after.java new file mode 100644 index 000000000000..12eca8780923 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completeStatement/NoBlockReformat_after.java @@ -0,0 +1,9 @@ +public class Test { + public void foo(int x) { + int a = 2; + + System.out.println(""); + System.out.println(""); + System.out.println(""); + } +} \ No newline at end of file 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 f37bae2df539..89683098fc6b 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 delegated method with default parameter value" "true" +// "Generate overloaded method with default parameter value" "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 0ee8f0b4b26c..e72764fff0dc 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 delegated method with default parameter value" "true" +// "Generate overloaded method with default parameter value" "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 a9044760f7bb..4844902fdf14 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 delegated method with default parameter value" "true" +// "Generate overloaded method with default parameter value" "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 2ba4de2c87e2..b73c48ebd2df 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 delegated method with default parameter value" "true" +// "Generate overloaded constructor with default parameter value" "true" class Test { Test() { this(); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterInterface.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterInterface.java new file mode 100644 index 000000000000..decd3f141818 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/delegateWithDefaultValue/afterInterface.java @@ -0,0 +1,8 @@ +// "Generate overloaded method with default parameter value" "true" +interface Test { + default void foo() { + foo(); + } + + void foo(int ii); +} \ No newline at end of file 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 8637aaf12ea1..3f15183133d7 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 delegated method with default parameter value" "true" +// "Generate overloaded method with default parameter value" "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 39c8186309be..1e89e83a3cb7 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 delegated method with default parameter value" "true" +// "Generate overloaded method with default parameter value" "true" class Test { int foo() { return 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 76d6ebeb4c0b..57fb3563e0ac 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 delegated method with default parameter value" "true" +// "Generate overloaded method with default parameter value" "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 f1645794d65b..68e2eaeec564 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 delegated method with default parameter value" "true" +// "Generate overloaded method with default parameter value" "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 22b77602b869..48895a8251b2 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 delegated method with default parameter value" "true" +// "Generate overloaded method with default parameter value" "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 e298bc77f4a6..320debae10f2 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 delegated method with default parameter value" "true" +// "Generate overloaded method with default parameter value" "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 bdd6fc83b075..9a6ad6184854 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 delegated method with default parameter value" "true" +// "Generate overloaded method with default parameter value" "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 0d75c3918ed8..7fe236b65cf4 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 delegated method with default parameter value" "true" +// "Generate overloaded constructor with default parameter value" "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 0d5ff1b0782f..52fa17401c5d 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 delegated method with default parameter value" "false" +// "Generate overloaded method with default parameter value" "false" 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 a18fdba97688..1f960d28b21d 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 delegated method with default parameter value" "false" +// "Generate overloaded method with default parameter value" "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 76acc03eb3d5..267cdf9f901a 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 delegated method with default parameter value" "true" +// "Generate overloaded method with default parameter value" "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 f77d174ad440..4df7057f4e31 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 delegated method with default parameter value" "true" +// "Generate overloaded method with default parameter value" "true" class Test { int foo(int ii){ return 1; 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 19e77d4724f4..e55000a24f37 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 delegated method with default parameter value" "true" +// "Generate overloaded method with default parameter value" "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 865e86d13fd4..6ddbc5a9069c 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 delegated method with default parameter value" "true" +// "Generate overloaded method with default parameter value" "true" class Test { int foo(int ii, boolean... args){ return 1; diff --git a/java/java-tests/testData/compileServer/incremental/generics/changeExtends.log b/java/java-tests/testData/compileServer/incremental/generics/changeExtends.log index 0cba040f482a..34c270510a8b 100644 --- a/java/java-tests/testData/compileServer/incremental/generics/changeExtends.log +++ b/java/java-tests/testData/compileServer/incremental/generics/changeExtends.log @@ -1,8 +1,10 @@ Cleaning output files: out/production/ChangeExtends/Super.class +out/production/ChangeExtends/Super2.class End of files Compiling files: src/Super.java +src/Super2.java End of files Cleaning output files: out/production/ChangeExtends/Client.class @@ -13,4 +15,4 @@ Compiling files: src/Client.java src/Client2.java src/Client3.java -End of files +End of files \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/generics/changeExtends/Super2.java.new b/java/java-tests/testData/compileServer/incremental/generics/changeExtends/Super2.java.new new file mode 100644 index 000000000000..55dd08a7fffb --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/generics/changeExtends/Super2.java.new @@ -0,0 +1,3 @@ +class Super2 { + char s = 's'; +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/generics/changeExtends/src/Client4.java b/java/java-tests/testData/compileServer/incremental/generics/changeExtends/src/Client4.java new file mode 100644 index 000000000000..9c68d9aab48c --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/generics/changeExtends/src/Client4.java @@ -0,0 +1,2 @@ +class Client4 extends Hyper{ +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/generics/changeExtends/src/Super2.java b/java/java-tests/testData/compileServer/incremental/generics/changeExtends/src/Super2.java new file mode 100644 index 000000000000..0045f0cb5ca6 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/generics/changeExtends/src/Super2.java @@ -0,0 +1,3 @@ +class Super2 extends Hyper { + char s = 's'; +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethod/DuplicatesFromAnonymous.java b/java/java-tests/testData/refactoring/extractMethod/DuplicatesFromAnonymous.java new file mode 100644 index 000000000000..bc0df8f53d7d --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/DuplicatesFromAnonymous.java @@ -0,0 +1,16 @@ +import java.util.*; + +class Main { + + private final Map> myScheduledUpdates = null; + + void foo() { + new Runnable() { + @Override + public void run() { + myScheduledUpdates.keySet().toArray(new Object[myScheduledUpdates.keySet().size()]); + } + }; + myScheduledUpdates.keySet().toArray(new Object[myScheduledUpdates.keySet().size()]); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethod/DuplicatesFromAnonymous_after.java b/java/java-tests/testData/refactoring/extractMethod/DuplicatesFromAnonymous_after.java new file mode 100644 index 000000000000..e6de04ac5a6c --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/DuplicatesFromAnonymous_after.java @@ -0,0 +1,20 @@ +import java.util.*; + +class Main { + + private final Map> myScheduledUpdates = null; + + void foo() { + new Runnable() { + @Override + public void run() { + newMethod(); + } + }; + newMethod(); + } + + private Object[] newMethod() { + return myScheduledUpdates.keySet().toArray(new Object[myScheduledUpdates.keySet().size()]); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/CompleteStatementTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/CompleteStatementTest.java index 5741767274c8..d0a25d0bc7b2 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/CompleteStatementTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/CompleteStatementTest.java @@ -67,6 +67,8 @@ public class CompleteStatementTest extends EditorActionTestCase { } } + public void testNoBlockReformat() { doTest(); } + public void testCompleteCatchWithExpression() throws Exception { doTest(); } public void testCompleteCatchBody() throws Exception { doTest(); } diff --git a/java/java-tests/testSrc/com/intellij/psi/impl/smartPointers/SmartPsiElementPointersTest.java b/java/java-tests/testSrc/com/intellij/psi/impl/smartPointers/SmartPsiElementPointersTest.java index e653f3f26147..9eaae9dbfa58 100644 --- a/java/java-tests/testSrc/com/intellij/psi/impl/smartPointers/SmartPsiElementPointersTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/impl/smartPointers/SmartPsiElementPointersTest.java @@ -28,8 +28,6 @@ import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.event.DocumentListener; import com.intellij.openapi.editor.event.EditorEventMulticaster; import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.fileEditor.FileEditor; -import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileTypes.PlainTextFileType; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.util.Segment; @@ -672,35 +670,6 @@ public class SmartPsiElementPointersTest extends CodeInsightTestCase { assertNotNull(node); } - public void testSmartPointersForOpenFilesAreFastened() { - PsiJavaFile file = (PsiJavaFile)myJavaFacade.findClass("AClass", GlobalSearchScope.allScope(getProject())).getContainingFile(); - - SmartPointerManagerImpl manager = (SmartPointerManagerImpl)SmartPointerManager.getInstance(myProject); - VirtualFile virtualFile = file.getVirtualFile(); - - assertFalse(manager.areBeltsFastened(virtualFile)); - FileEditor[] editors = FileEditorManager.getInstance(myProject).openFile(virtualFile, true); - assertTrue(editors.length != 0); - - assertTrue(manager.areBeltsFastened(virtualFile)); - - FileEditorManager.getInstance(myProject).closeFile(virtualFile); - assertFalse(manager.areBeltsFastened(virtualFile)); - - SmartPsiElementPointer pointer = manager.createSmartPsiElementPointer(file.getClasses()[0]); - - assertFalse(manager.areBeltsFastened(virtualFile)); - editors = FileEditorManager.getInstance(myProject).openFile(virtualFile, true); - assertTrue(editors.length != 0); - - assertTrue(manager.areBeltsFastened(virtualFile)); - - FileEditorManager.getInstance(myProject).closeFile(virtualFile); - assertFalse(manager.areBeltsFastened(virtualFile)); - - assertEquals(file.getClasses()[0], pointer.getElement()); // retain pointer from gc - } - public void testLargeFileWithManyChanges() { configureByText(PlainTextFileType.INSTANCE, StringUtil.repeat("foo foo \n", 50000)); final TextRange range = TextRange.from(10, 10); diff --git a/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodTest.java b/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodTest.java index 47a038460eb7..ba1d6b03ed9f 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodTest.java @@ -225,6 +225,10 @@ public class ExtractMethodTest extends LightCodeInsightTestCase { doDuplicatesTest(); } + public void testDuplicatesFromAnonymous() throws Exception { + doDuplicatesTest(); + } + public void testCodeDuplicatesWithContinueNoReturn() throws Exception { doDuplicatesTest(); } diff --git a/java/testFramework/src/com/intellij/compiler/CompilerTestUtil.java b/java/testFramework/src/com/intellij/compiler/CompilerTestUtil.java index 73613644878f..60c06b99de2c 100644 --- a/java/testFramework/src/com/intellij/compiler/CompilerTestUtil.java +++ b/java/testFramework/src/com/intellij/compiler/CompilerTestUtil.java @@ -79,23 +79,23 @@ public class CompilerTestUtil { } public static void enableExternalCompiler() { + ApplicationManagerEx.getApplicationEx().doNotSave(false); + final JavaAwareProjectJdkTableImpl table = JavaAwareProjectJdkTableImpl.getInstanceEx(); new WriteAction() { @Override protected void run(@NotNull final Result result) { - ApplicationManagerEx.getApplicationEx().doNotSave(false); - JavaAwareProjectJdkTableImpl table = JavaAwareProjectJdkTableImpl.getInstanceEx(); table.addJdk(table.getInternalJdk()); } }.execute(); } public static void disableExternalCompiler(final Project project) { + ApplicationManagerEx.getApplicationEx().doNotSave(true); + final JavaAwareProjectJdkTableImpl table = JavaAwareProjectJdkTableImpl.getInstanceEx(); new WriteAction() { @Override protected void run(@NotNull final Result result) { - ApplicationManagerEx.getApplicationEx().doNotSave(true); Module[] modules = ModuleManager.getInstance(project).getModules(); - JavaAwareProjectJdkTableImpl table = JavaAwareProjectJdkTableImpl.getInstanceEx(); Sdk internalJdk = table.getInternalJdk(); List modulesToRestore = new ArrayList(); for (Module module : modules) { diff --git a/java/testFramework/src/com/intellij/testFramework/CompilerTester.java b/java/testFramework/src/com/intellij/testFramework/CompilerTester.java index 09f5532c4b1d..ab85a3fabbee 100644 --- a/java/testFramework/src/com/intellij/testFramework/CompilerTester.java +++ b/java/testFramework/src/com/intellij/testFramework/CompilerTester.java @@ -69,12 +69,12 @@ public class CompilerTester { myMainOutput = new TempDirTestFixtureImpl(); myMainOutput.setUp(); + CompilerTestUtil.enableExternalCompiler(); new WriteCommandAction(getProject()) { @Override protected void run(@NotNull Result result) throws Throwable { //noinspection ConstantConditions CompilerProjectExtension.getInstance(getProject()).setCompilerOutputUrl(myMainOutput.findOrCreateDir("out").getUrl()); - CompilerTestUtil.enableExternalCompiler(); for (Module module : myModules) { ModuleRootModificationUtil.setModuleSdk(module, JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk()); } diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassfileAnalyzer.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassfileAnalyzer.java index 2b11ac6bd546..c0d00b8b058c 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassfileAnalyzer.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassfileAnalyzer.java @@ -217,61 +217,21 @@ class ClassfileAnalyzer { } } - private final SignatureVisitor mySignatureCrawler = new SignatureVisitor(Opcodes.ASM5) { - public void visitFormalTypeParameter(String name) { - } - + private final SignatureVisitor mySignatureCrawler = new BaseSignatureVisitor() { public SignatureVisitor visitClassBound() { - return this; + return mySignatureWithGenericBoundUsageCrawler; } public SignatureVisitor visitInterfaceBound() { - return this; - } - - public SignatureVisitor visitSuperclass() { - return this; - } - - public SignatureVisitor visitInterface() { - return this; - } - - public SignatureVisitor visitParameterType() { - return this; - } - - public SignatureVisitor visitReturnType() { - return this; - } - - public SignatureVisitor visitExceptionType() { - return this; - } - - public void visitBaseType(char descriptor) { - } - - public void visitTypeVariable(String name) { - } - - public SignatureVisitor visitArrayType() { - return this; - } - - public void visitInnerClassType(String name) { - } - - public void visitTypeArgument() { + return mySignatureWithGenericBoundUsageCrawler; } public SignatureVisitor visitTypeArgument(char wildcard) { - return this; - } - - public void visitEnd() { + return mySignatureWithGenericBoundUsageCrawler; } + }; + private final SignatureVisitor mySignatureWithGenericBoundUsageCrawler = new BaseSignatureVisitor() { public void visitClassType(String name) { final int className = myContext.get(name); myUsages.add(UsageRepr.createClassUsage(myContext, className)); @@ -653,6 +613,72 @@ class ClassfileAnalyzer { myLocalClassFlag.set(true); } } + + private class BaseSignatureVisitor extends SignatureVisitor { + + public BaseSignatureVisitor() { + super(Opcodes.ASM5); + } + + public void visitFormalTypeParameter(String name) { + } + + public SignatureVisitor visitClassBound() { + return this; + } + + public SignatureVisitor visitInterfaceBound() { + return this; + } + + public SignatureVisitor visitSuperclass() { + return this; + } + + public SignatureVisitor visitInterface() { + return this; + } + + public SignatureVisitor visitParameterType() { + return this; + } + + public SignatureVisitor visitReturnType() { + return this; + } + + public SignatureVisitor visitExceptionType() { + return this; + } + + public void visitBaseType(char descriptor) { + } + + public void visitTypeVariable(String name) { + } + + public SignatureVisitor visitArrayType() { + return this; + } + + public void visitInnerClassType(String name) { + } + + public void visitTypeArgument() { + } + + public SignatureVisitor visitTypeArgument(char wildcard) { + return this; + } + + public void visitEnd() { + } + + public void visitClassType(String name) { + final int className = myContext.get(name); + myUsages.add(UsageRepr.createClassUsage(myContext, className)); + } + } } public Pair> analyze(final int fileName, final ClassReader cr) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java index dd87660bcd6b..583406b372d2 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java @@ -716,6 +716,28 @@ public class Mappings { public abstract boolean checkResidence(final int residence); } + public class FileFilterConstraint extends UsageConstraint { + @NotNull + private final DependentFilesFilter myFilter; + + public FileFilterConstraint(@NotNull DependentFilesFilter filter) { + myFilter = filter; + } + + public boolean checkResidence(int residence) { + final Collection fNames = myClassToSourceFile.get(residence); + if (fNames == null || fNames.isEmpty()) { + return true; + } + for (File fName : fNames) { + if (myFilter.accept(fName)) { + return true; + } + } + return false; + } + } + public class PackageConstraint extends UsageConstraint { public final String packageName; @@ -1132,6 +1154,11 @@ public class Mappings { debug("Class is annotation, skipping method analysis"); return; } + + assert myFuture != null; + assert myPresent != null; + assert myAffectedFiles != null; + Ref oldItRef = null; for (final MethodRepr m : added) { debug("Method: ", m.name); @@ -1256,6 +1283,10 @@ public class Mappings { if (removed.isEmpty()) { return; } + assert myFuture != null; + assert myAffectedFiles != null; + assert myCompiledFiles != null; + debug("Processing removed methods:"); for (final MethodRepr m : removed) { debug("Method ", m.name); @@ -1362,6 +1393,10 @@ public class Mappings { return; } debug("Processing changed methods:"); + + assert myFuture != null; + assert myAffectedFiles != null; + for (final Pair mr : changed) { final MethodRepr m = mr.first; final MethodRepr.Diff d = (MethodRepr.Diff)mr.second; @@ -1473,6 +1508,11 @@ public class Mappings { } debug("Processing added fields"); + assert myFuture != null; + assert myPresent != null; + assert myCompiledFiles != null; + assert myAffectedFiles != null; + for (final FieldRepr f : added) { debug("Field: ", f.name); @@ -1535,23 +1575,23 @@ public class Mappings { // nothing } else { - Util.UsageConstraint constaint; + Util.UsageConstraint constraint; if ((ff.isProtected() && f.isPublic()) || (f.isProtected() && ff.isPublic()) || (ff.isPackageLocal() && f.isProtected())) { - constaint = myFuture.new NegationConstraint(myFuture.new InheritanceConstraint(cc.name)); + constraint = myFuture.new NegationConstraint(myFuture.new InheritanceConstraint(cc.name)); } else if (ff.isPublic() && ff.isPackageLocal()) { - constaint = myFuture.new NegationConstraint(myFuture.new PackageConstraint(cc.getPackageName())); + constraint = myFuture.new NegationConstraint(myFuture.new PackageConstraint(cc.getPackageName())); } else { - constaint = + constraint = myFuture.new IntersectionConstraint(myFuture.new NegationConstraint(myFuture.new InheritanceConstraint(cc.name)), myFuture.new NegationConstraint( myFuture.new PackageConstraint(cc.getPackageName()))); } for (final UsageRepr.Usage usage : localUsages) { - state.myUsageConstraints.put(usage, constaint); + state.myUsageConstraints.put(usage, constraint); } } @@ -1569,6 +1609,8 @@ public class Mappings { if (removed.isEmpty()) { return true; } + assert myFuture != null; + debug("Processing removed fields:"); for (final FieldRepr f : removed) { @@ -1577,6 +1619,7 @@ public class Mappings { if (!f.isPrivate() && (f.access & DESPERATE_MASK) == DESPERATE_MASK && f.hasValue()) { debug("Field had value and was (non-private) final static => a switch to non-incremental mode requested"); if (myConstantSearch != null) { + assert myDelayedWorks != null; myDelayedWorks.addConstantWork(it.name, f, true, false); } else { @@ -1601,6 +1644,7 @@ public class Mappings { return true; } debug("Processing changed fields:"); + assert myFuture != null; for (final Pair f : changed) { final Difference d = f.second; @@ -1619,6 +1663,7 @@ public class Mappings { if (harmful || valueChanged || becameLessAccessible) { debug("Inline field changed it's access or value => a switch to non-incremental mode requested"); if (myConstantSearch != null) { + assert myDelayedWorks != null; myDelayedWorks.addConstantWork(it.name, field, false, accessChanged); } else { @@ -1687,6 +1732,10 @@ public class Mappings { final Collection> changedClasses = state.myClassDiff.changed(); if (!changedClasses.isEmpty()) { debug("Processing changed classes:"); + assert myFuture != null; + assert myPresent != null; + + final Util.FileFilterConstraint fileFilterConstraint = myFilter != null? myPresent.new FileFilterConstraint(myFilter) : null; for (final Pair changed : changedClasses) { final ClassRepr changedClass = changed.first; @@ -1753,7 +1802,11 @@ public class Mappings { @Override public boolean execute(int className) { debug("Affecting usages in generic type parameter bounds of class: ", className); - state.myAffectedUsages.add(UsageRepr.createClassAsGenericBoundUsage(myContext, className)); + final UsageRepr.Usage usage = UsageRepr.createClassAsGenericBoundUsage(myContext, className); + state.myAffectedUsages.add(usage); + if (fileFilterConstraint != null) { + state.myUsageConstraints.put(usage, fileFilterConstraint); + } final TIntHashSet depClasses = myClassToClassDependency.get(className); if (depClasses != null) { @@ -1869,6 +1922,9 @@ public class Mappings { if (removed.isEmpty()) { return; } + assert myPresent != null; + assert myDelta.myChangedFiles != null; + myDelta.myChangedFiles.add(fileName); debug("Processing removed classes:"); @@ -1896,6 +1952,9 @@ public class Mappings { if (!myEasyMode && myFilter != null) { // checking if this newly added class duplicates already existing one + assert myCompiledFiles != null; + assert myAffectedFiles != null; + for (ClassRepr c : addedClasses) { if (!c.isLocal() && !c.isAnonymous() && isEmpty(c.getOuterClassName())) { final Set candidates = new THashSet(FileUtil.FILE_HASHING_STRATEGY); @@ -1976,6 +2035,8 @@ public class Mappings { } private void affectCorrespondingSourceFiles(TIntHashSet toAffect) { + assert myAffectedFiles != null; + toAffect.forEach(new TIntProcedure() { @Override public boolean execute(int depClass) { @@ -1995,6 +2056,8 @@ public class Mappings { private void calculateAffectedFiles(final DiffState state) { debug("Checking dependent classes:"); + assert myAffectedFiles != null; + assert myCompiledFiles != null; state.myDependants.forEach(new TIntProcedure() { @Override @@ -2106,6 +2169,8 @@ public class Mappings { if (myEasyMode) { return false; } + assert myAffectedFiles != null; + assert myDelayedWorks != null; final Collection removed = myDelta.myRemovedFiles; if (removed != null) { @@ -2117,6 +2182,7 @@ public class Mappings { } finally { if (myFilesToCompile != null) { + assert myDelta.myChangedFiles != null; // if some class is associated with several sources, // some of them may not have been compiled in this round, so such files should be considered unchanged myDelta.myChangedFiles.retainAll(myFilesToCompile); @@ -2463,7 +2529,9 @@ public class Mappings { } for (final String s : staticImports) { int i = s.length() - 1; - for (; s.charAt(i) != '.'; i--) ; + while (s.charAt(i) != '.') { + i--; + } final String anImport = s.substring(0, i); if (!anImport.endsWith("*")) { allImports.add(anImport); // filter out wildcard imports diff --git a/lib/libpty/win/win10_x86/libwinpty.dll b/lib/libpty/win/win10_x86/libwinpty.dll new file mode 100644 index 000000000000..e1644b8ee80d Binary files /dev/null and b/lib/libpty/win/win10_x86/libwinpty.dll differ diff --git a/lib/libpty/win/win10_x86/winpty-agent.exe b/lib/libpty/win/win10_x86/winpty-agent.exe new file mode 100644 index 000000000000..c818ddf39149 Binary files /dev/null and b/lib/libpty/win/win10_x86/winpty-agent.exe differ diff --git a/lib/libpty/win/win10_x86_64/cyglaunch.exe b/lib/libpty/win/win10_x86_64/cyglaunch.exe new file mode 100644 index 000000000000..8e8a1c843e15 Binary files /dev/null and b/lib/libpty/win/win10_x86_64/cyglaunch.exe differ diff --git a/lib/libpty/win/win10_x86_64/libwinpty.dll b/lib/libpty/win/win10_x86_64/libwinpty.dll new file mode 100644 index 000000000000..ada906c40611 Binary files /dev/null and b/lib/libpty/win/win10_x86_64/libwinpty.dll differ diff --git a/lib/libpty/win/win10_x86_64/winpty-agent.exe b/lib/libpty/win/win10_x86_64/winpty-agent.exe new file mode 100644 index 000000000000..3f0fa6dc9250 Binary files /dev/null and b/lib/libpty/win/win10_x86_64/winpty-agent.exe differ diff --git a/lib/pty4j-0.4.17.jar b/lib/pty4j-0.4.19.jar similarity index 84% rename from lib/pty4j-0.4.17.jar rename to lib/pty4j-0.4.19.jar index 8ec523de3343..7b1bdcaa191a 100644 Binary files a/lib/pty4j-0.4.17.jar and b/lib/pty4j-0.4.19.jar differ diff --git a/lib/required_for_dist.txt b/lib/required_for_dist.txt index e061ec282a91..40c93b595f4a 100644 --- a/lib/required_for_dist.txt +++ b/lib/required_for_dist.txt @@ -55,7 +55,7 @@ oromatcher.jar picocontainer.jar protobuf-2.5.0.jar proxy-vole_20131209.jar -pty4j-0.4.17.jar +pty4j-0.4.19.jar purejavacomm.jar resolver.jar rhino-js-1_7R4.jar diff --git a/lib/src/pty4j-0.4.17-src.jar b/lib/src/pty4j-0.4.19-src.jar similarity index 91% rename from lib/src/pty4j-0.4.17-src.jar rename to lib/src/pty4j-0.4.19-src.jar index a89c650301ee..58124381fab3 100644 Binary files a/lib/src/pty4j-0.4.17-src.jar and b/lib/src/pty4j-0.4.19-src.jar differ diff --git a/platform/configuration-store-impl/src/BaseFileConfigurableStoreImpl.kt b/platform/configuration-store-impl/src/BaseFileConfigurableStoreImpl.kt index 4dac9820e73c..b45082f19b70 100644 --- a/platform/configuration-store-impl/src/BaseFileConfigurableStoreImpl.kt +++ b/platform/configuration-store-impl/src/BaseFileConfigurableStoreImpl.kt @@ -19,6 +19,7 @@ import com.intellij.openapi.components.PathMacroManager import com.intellij.openapi.components.PathMacroSubstitutor import com.intellij.openapi.components.impl.stores.StorageData import org.jdom.Element +import org.jetbrains.annotations.NotNull import kotlin.properties.Delegates abstract class BaseFileConfigurableStoreImpl(protected val pathMacroManager: PathMacroManager) : ComponentStoreImpl() { @@ -39,7 +40,7 @@ open class ProjectStorageData : StorageData { private var version = CURRENT_FORMAT_VERSION - constructor(rootElementName: String) : super(rootElementName) { + constructor() : super() { } protected constructor(storageData: ProjectStorageData) : super(storageData) { @@ -51,11 +52,8 @@ open class ProjectStorageData : StorageData { version = rootElement.getAttributeValue(VERSION_OPTION)?.toInt() ?: CURRENT_FORMAT_VERSION } - override fun save(newLiveStates: Map): Element { - var root = super.save(newLiveStates) - if (root == null) { - root = Element(myRootElementName) - } + override fun save(newLiveStates: Map, rootElementName: String): Element { + val root = super.save(newLiveStates, rootElementName) ?: Element(rootElementName) writeOptions(root) return root } diff --git a/platform/configuration-store-impl/src/DefaultProjectStoreImpl.kt b/platform/configuration-store-impl/src/DefaultProjectStoreImpl.kt index ee55c711c0fc..de3fe586b195 100644 --- a/platform/configuration-store-impl/src/DefaultProjectStoreImpl.kt +++ b/platform/configuration-store-impl/src/DefaultProjectStoreImpl.kt @@ -17,13 +17,15 @@ package com.intellij.configurationStore import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.* -import com.intellij.openapi.components.impl.stores.FileBasedStorage +import com.intellij.openapi.components.impl.stores.FileStorage import com.intellij.openapi.components.impl.stores.StateStorageManager +import com.intellij.openapi.components.impl.stores.StorageData import com.intellij.openapi.components.impl.stores.StreamProvider import com.intellij.openapi.project.Project import com.intellij.openapi.project.impl.ProjectImpl import com.intellij.openapi.util.Couple import com.intellij.util.containers.ContainerUtil +import org.jdom.Element import java.io.File import kotlin.properties.Delegates @@ -38,6 +40,25 @@ class DefaultProjectStoreImpl(override val project: ProjectImpl, private val pat private val storage by Delegates.lazy { DefaultProjectStorage(File(ApplicationManager.getApplication().stateStore.getStateStorageManager().expandMacros(FILE_SPEC)), FILE_SPEC, pathMacroManager) } + private class DefaultProjectStorage(file: File, fileSpec: String, pathMacroManager: PathMacroManager) : FileBasedStorage(file, fileSpec, "defaultProject", pathMacroManager.createTrackingSubstitutor(), RoamingType.DISABLED) { + override public fun loadLocalData(): Element? { + val element = super.loadLocalData() ?: return null + try { + return element.getChild("component").getChild("defaultProject") + } + catch (e: NullPointerException) { + LOG.warn("Cannot read default project") + return null + } + } + + override fun createSaveSession(storageData: StorageData) = object : FileBasedStorage.FileSaveSession(storageData, this) { + override fun saveLocally(element: Element?) { + super.saveLocally(Element("application").addContent(Element("component").setAttribute("name", "ProjectManager").addContent(element))) + } + } + } + private val storageManager = object : StateStorageManager { override fun rename(path: String, newName: String) { } @@ -48,7 +69,7 @@ class DefaultProjectStoreImpl(override val project: ProjectImpl, private val pat override fun getStateStorage(fileSpec: String, roamingType: RoamingType) = storage - override fun getCachedFileStateStorages(changed: Collection, deleted: Collection): Couple> = Couple(emptyList(), emptyList()) + override fun getCachedFileStateStorages(changed: Collection, deleted: Collection): Couple> = Couple(emptyList(), emptyList()) override fun startExternalization(): StateStorageManager.ExternalizationSession? { val externalizationSession = storage.startExternalization() diff --git a/platform/configuration-store-impl/src/ModuleFileData.kt b/platform/configuration-store-impl/src/ModuleFileData.kt index 6a5a74e48b10..0eafe980b971 100644 --- a/platform/configuration-store-impl/src/ModuleFileData.kt +++ b/platform/configuration-store-impl/src/ModuleFileData.kt @@ -29,7 +29,7 @@ class ModuleFileData : ProjectStorageData, OptionManager { override fun isDirty() = dirty - public constructor(rootElementName: String) : super(rootElementName) { + public constructor() : super() { options = TreeMap() } diff --git a/platform/configuration-store-impl/src/ModuleStateStorageManager.kt b/platform/configuration-store-impl/src/ModuleStateStorageManager.kt index 7938aede9c78..2b5e5563bcf3 100644 --- a/platform/configuration-store-impl/src/ModuleStateStorageManager.kt +++ b/platform/configuration-store-impl/src/ModuleStateStorageManager.kt @@ -20,6 +20,7 @@ import com.intellij.openapi.components.StateStorage import com.intellij.openapi.components.StateStorageOperation import com.intellij.openapi.components.StoragePathMacros import com.intellij.openapi.components.TrackingPathMacroSubstitutor +import com.intellij.openapi.components.impl.stores.StorageData import com.intellij.openapi.module.Module import com.intellij.openapi.module.impl.ModuleEx import com.intellij.openapi.module.impl.ModuleManagerImpl @@ -29,14 +30,14 @@ import com.intellij.util.PathUtilRt import com.intellij.util.containers.ContainerUtil class ModuleStateStorageManager(macroSubstitutor: TrackingPathMacroSubstitutor, module: Module) : StateStorageManagerImpl("module", macroSubstitutor, module) { - override fun createStorageData(fileSpec: String) = ModuleFileData(rootTagName) + override fun createStorageData(fileSpec: String) = ModuleFileData() override fun startExternalization() = MyStateStorageManagerExternalizationSession(this) private class MyStateStorageManagerExternalizationSession(storageManager: StateStorageManagerImpl) : StateStorageManagerImpl.StateStorageManagerExternalizationSession(storageManager) { override fun createSaveSessions(): List { val storage = ContainerUtil.getFirstItem(storageManager.getCachedFileStorages(listOf(StoragePathMacros.MODULE_FILE))) - if (storage != null && storage.getStorageData().isDirty()) { + if (storage != null && (storage.getStorageData() as StorageData).isDirty()) { // force XmlElementStorageSaveSession creation getExternalizationSession(storage) } diff --git a/platform/configuration-store-impl/src/ProjectStateStorageManager.kt b/platform/configuration-store-impl/src/ProjectStateStorageManager.kt index dcb6814139fd..7aaea45ede0a 100644 --- a/platform/configuration-store-impl/src/ProjectStateStorageManager.kt +++ b/platform/configuration-store-impl/src/ProjectStateStorageManager.kt @@ -22,7 +22,7 @@ import com.intellij.openapi.components.TrackingPathMacroSubstitutor import com.intellij.openapi.project.impl.ProjectImpl class ProjectStateStorageManager(macroSubstitutor: TrackingPathMacroSubstitutor, private val project: ProjectImpl) : StateStorageManagerImpl("project", macroSubstitutor, project) { - override fun createStorageData(fileSpec: String) = ProjectStorageData(rootTagName) + override fun createStorageData(fileSpec: String) = ProjectStorageData() override fun getOldStorageSpec(component: Any, componentName: String, operation: StateStorageOperation): String? { val workspace = project.isWorkspaceComponent(component.javaClass) diff --git a/platform/configuration-store-impl/src/ProjectStoreImpl.kt b/platform/configuration-store-impl/src/ProjectStoreImpl.kt index 4b028c73f10e..11fb7cd0c0a5 100644 --- a/platform/configuration-store-impl/src/ProjectStoreImpl.kt +++ b/platform/configuration-store-impl/src/ProjectStoreImpl.kt @@ -23,7 +23,6 @@ import com.intellij.openapi.application.ReadAction import com.intellij.openapi.application.invokeAndWaitIfNeed import com.intellij.openapi.components.* import com.intellij.openapi.components.StateStorage.SaveSession -import com.intellij.openapi.components.impl.stores.FileBasedStorage import com.intellij.openapi.components.impl.stores.IComponentStore import com.intellij.openapi.components.impl.stores.IProjectStore import com.intellij.openapi.fileTypes.FileTypeManager @@ -148,7 +147,7 @@ open class ProjectStoreImpl(override val project: ProjectImpl, pathMacroManager: return baseDir.getName().replace(":", "") } else { - var temp = PathUtilRt.getFileName(getProjectFileStorage().getFilePath()) + var temp = PathUtilRt.getFileName(getProjectFilePath()) val fileType = FileTypeManager.getInstance().getFileTypeByFileName(temp) if (fileType is ProjectFileType) { temp = temp.substring(0, temp.length() - fileType.getDefaultExtension().length() - 1) @@ -175,16 +174,13 @@ open class ProjectStoreImpl(override val project: ProjectImpl, pathMacroManager: override fun getProjectFile() = getProjectFileStorage().getVirtualFile() - override fun getProjectFilePath() = getProjectFileStorage().getFilePath() + override fun getProjectFilePath() = storageManager.expandMacros(StoragePathMacros.PROJECT_FILE) private fun getProjectFileStorage() = storageManager.getStateStorage(StoragePathMacros.PROJECT_FILE, RoamingType.PER_USER) as FileBasedStorage - override fun getWorkspaceFile() = workspaceStorage?.getVirtualFile() + override fun getWorkspaceFile() = (storageManager.getStateStorage(StoragePathMacros.WORKSPACE_FILE, RoamingType.DISABLED) as FileBasedStorage?)?.getVirtualFile() - override fun getWorkspaceFilePath() = workspaceStorage?.getFilePath() - - private val workspaceStorage: FileBasedStorage? - get() = storageManager.getStateStorage(StoragePathMacros.WORKSPACE_FILE, RoamingType.DISABLED) as FileBasedStorage? + override fun getWorkspaceFilePath() = storageManager.expandMacros(StoragePathMacros.WORKSPACE_FILE) override fun loadProjectFromTemplate(defaultProject: Project) { defaultProject.save() diff --git a/platform/configuration-store-impl/src/StateStorageManagerImpl.kt b/platform/configuration-store-impl/src/StateStorageManagerImpl.kt index 945e7f3ae16f..49286cfe0e37 100644 --- a/platform/configuration-store-impl/src/StateStorageManagerImpl.kt +++ b/platform/configuration-store-impl/src/StateStorageManagerImpl.kt @@ -46,7 +46,7 @@ import kotlin.reflect.jvm.java /** * If componentManager not specified, storage will not add file tracker (see VirtualFileTracker) */ -open class StateStorageManagerImpl(protected val rootTagName: String, +open class StateStorageManagerImpl(private val rootTagName: String, private val pathMacroSubstitutor: TrackingPathMacroSubstitutor? = null, val componentManager: ComponentManager? = null, private val virtualFileTracker: StorageVirtualFileTracker? = StateStorageManagerImpl.createDefaultVirtualTracker(componentManager) ) : StateStorageManager { @@ -155,7 +155,7 @@ open class StateStorageManagerImpl(protected val rootTagName: String, override final fun getCachedFileStateStorages(changed: MutableCollection, deleted: MutableCollection) = storageLock.withLock { Couple.of(getCachedFileStorages(changed), getCachedFileStorages(deleted)) } - fun getCachedFileStorages(fileSpecs: Collection): Collection { + fun getCachedFileStorages(fileSpecs: Collection): Collection { if (fileSpecs.isEmpty()) { return emptyList() } @@ -185,13 +185,7 @@ open class StateStorageManagerImpl(protected val rootTagName: String, val file = File(filePath) if (isUseVfsListener == ThreeState.UNSURE) { - if (streamProvider != null && streamProvider!!.enabled) { - isUseVfsListener = ThreeState.NO - } - else { - isUseVfsListener = ThreeState.YES -// addVfsChangesListener(componentManager!!) - } + isUseVfsListener = ThreeState.fromBoolean(streamProvider == null || !streamProvider!!.enabled) } //noinspection deprecation @@ -207,22 +201,25 @@ open class StateStorageManagerImpl(protected val rootTagName: String, val effectiveRoamingType = if (roamingType == RoamingType.PER_USER && fileSpec == StoragePathMacros.WORKSPACE_FILE) RoamingType.DISABLED else roamingType val storage = MyFileStorage(this, file, fileSpec, rootTagName, effectiveRoamingType, getMacroSubstitutor(fileSpec), streamProvider) - virtualFileTracker?.put(filePath.normalizePath(), storage) + if (isUseVfsListener == ThreeState.YES) { + virtualFileTracker?.put(filePath.normalizePath(), storage) + } return storage } private class MyDirectoryStorage(override val storageManager: StateStorageManagerImpl, file: File, splitter: StateSplitter) : DirectoryBasedStorage(storageManager.pathMacroSubstitutor, file, splitter), StorageVirtualFileTracker.TrackedStorage private class MyFileStorage(override val storageManager: StateStorageManagerImpl, - file: File, - fileSpec: String, - rootElementName: String, - roamingType: RoamingType? = null, - pathMacroManager: TrackingPathMacroSubstitutor? = null, - streamProvider: StreamProvider? = null) : FileBasedStorage(file, fileSpec, roamingType, pathMacroManager, rootElementName, streamProvider), StorageVirtualFileTracker.TrackedStorage { - override fun createStorageData() = storageManager.createStorageData(myFileSpec) + file: File, + fileSpec: String, + rootElementName: String, + roamingType: RoamingType, + pathMacroManager: TrackingPathMacroSubstitutor? = null, + provider: StreamProvider? = null) : FileBasedStorage(file, fileSpec, rootElementName, pathMacroManager, roamingType, provider), StorageVirtualFileTracker.TrackedStorage { + override val isUseXmlProlog: Boolean + get() = storageManager.isUseXmlProlog - override fun isUseXmlProlog() = storageManager.isUseXmlProlog + override fun createStorageData() = storageManager.createStorageData(fileSpec) } private fun String.normalizePath(): String { @@ -272,7 +269,7 @@ open class StateStorageManagerImpl(protected val rootTagName: String, protected open fun getMacroSubstitutor(fileSpec: String): TrackingPathMacroSubstitutor? = pathMacroSubstitutor - protected open fun createStorageData(fileSpec: String): StorageData = StorageData(rootTagName) + protected open fun createStorageData(fileSpec: String): StorageData = StorageData() override final fun expandMacros(path: String): String { // replacement can contains $ (php tests), so, this check must be performed before expand diff --git a/platform/configuration-store-impl/src/StorageVirtualFileTracker.kt b/platform/configuration-store-impl/src/StorageVirtualFileTracker.kt index 2edb9ac11edf..7b6e811e2081 100644 --- a/platform/configuration-store-impl/src/StorageVirtualFileTracker.kt +++ b/platform/configuration-store-impl/src/StorageVirtualFileTracker.kt @@ -2,7 +2,6 @@ package com.intellij.configurationStore import com.intellij.openapi.components.StateStorage import com.intellij.openapi.components.impl.stores.DirectoryBasedStorage -import com.intellij.openapi.components.impl.stores.FileBasedStorage import com.intellij.openapi.components.impl.stores.StateStorageManager import com.intellij.openapi.components.impl.stores.StorageUtil import com.intellij.openapi.util.text.StringUtilRt diff --git a/platform/configuration-store-impl/src/com/intellij/configurationStore/DefaultProjectStorage.java b/platform/configuration-store-impl/src/com/intellij/configurationStore/DefaultProjectStorage.java deleted file mode 100644 index 11b877f959a4..000000000000 --- a/platform/configuration-store-impl/src/com/intellij/configurationStore/DefaultProjectStorage.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.intellij.configurationStore; - -import com.intellij.openapi.components.PathMacroManager; -import com.intellij.openapi.components.RoamingType; -import com.intellij.openapi.components.impl.stores.FileBasedStorage; -import com.intellij.openapi.components.impl.stores.StorageData; -import org.jdom.Element; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.io.IOException; - -public class DefaultProjectStorage extends FileBasedStorage { - public DefaultProjectStorage(@NotNull File file, @NotNull String fileSpec, PathMacroManager pathMacroManager) { - super(file, fileSpec, RoamingType.DISABLED, pathMacroManager.createTrackingSubstitutor(), "defaultProject", null); - } - - @Nullable - @Override - protected Element loadLocalData() { - Element element = super.loadLocalData(); - if (element == null) { - return null; - } - - try { - return element.getChild("component").getChild("defaultProject"); - } - catch (NullPointerException e) { - LOG.warn("Cannot read default project"); - return null; - } - } - - @NotNull - @Override - protected XmlElementStorageSaveSession createSaveSession(@NotNull StorageData storageData) { - return new FileSaveSession(storageData) { - @Override - protected void doSave(@Nullable Element element) throws IOException { - super.doSave(new Element("application").addContent(new Element("component").setAttribute("name", "ProjectManager").addContent(element))); - } - }; - } -} \ No newline at end of file diff --git a/platform/configuration-store-impl/src/com/intellij/configurationStore/DefaultStorageAnnotation.java b/platform/configuration-store-impl/src/com/intellij/configurationStore/DefaultStorageAnnotation.java index b9b89f475341..c06bad4fc0ad 100644 --- a/platform/configuration-store-impl/src/com/intellij/configurationStore/DefaultStorageAnnotation.java +++ b/platform/configuration-store-impl/src/com/intellij/configurationStore/DefaultStorageAnnotation.java @@ -12,11 +12,6 @@ public final class DefaultStorageAnnotation implements Storage { return "___Default___"; } - @Override - public boolean isDefault() { - return true; - } - @Override public String file() { return StoragePathMacros.PROJECT_FILE; diff --git a/platform/configuration-store-impl/src/com/intellij/configurationStore/FileBasedStorage.kt b/platform/configuration-store-impl/src/com/intellij/configurationStore/FileBasedStorage.kt new file mode 100644 index 000000000000..dabbc0ffc2c4 --- /dev/null +++ b/platform/configuration-store-impl/src/com/intellij/configurationStore/FileBasedStorage.kt @@ -0,0 +1,143 @@ +/* + * 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.configurationStore + +import com.intellij.notification.Notification +import com.intellij.notification.NotificationType +import com.intellij.notification.Notifications +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.components.RoamingType +import com.intellij.openapi.components.StoragePathMacros +import com.intellij.openapi.components.TrackingPathMacroSubstitutor +import com.intellij.openapi.components.impl.stores.* +import com.intellij.openapi.util.JDOMUtil +import com.intellij.openapi.util.io.FileUtilRt +import com.intellij.openapi.util.io.systemIndependentPath +import com.intellij.openapi.util.text.StringUtil +import com.intellij.openapi.vfs.CharsetToolkit +import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.util.LineSeparator +import org.jdom.Element +import org.jdom.JDOMException + +import java.io.File +import java.io.IOException +import java.nio.ByteBuffer +import java.nio.CharBuffer + +open class FileBasedStorage(private volatile var file: File, + fileSpec: String, + rootElementName: String, + pathMacroManager: TrackingPathMacroSubstitutor? = null, + roamingType: RoamingType? = null, + provider: StreamProvider? = null) : XmlElementStorage(fileSpec, rootElementName, pathMacroManager, roamingType, provider), FileStorage { + private volatile var cachedVirtualFile: VirtualFile? = null + private var lineSeparator: LineSeparator? = null + private var blockSavingTheContent = false + + init { + if (ApplicationManager.getApplication().isUnitTestMode() && file.getPath().startsWith('$')) { + throw AssertionError("It seems like some macros were not expanded for path: $file") + } + } + + protected open val isUseXmlProlog: Boolean = false + + // we never set io file to null + override fun setFile(virtualFile: VirtualFile?, ioFileIfChanged: File?) { + cachedVirtualFile = virtualFile + if (ioFileIfChanged != null) { + file = ioFileIfChanged + } + } + + override fun createSaveSession(storageData: StorageData) = FileSaveSession(storageData, this) + + protected open class FileSaveSession(storageData: StorageData, storage: FileBasedStorage) : XmlElementStorage.XmlElementStorageSaveSession(storageData, storage) { + override fun save() { + if (!storage.blockSavingTheContent) { + super.save() + } + } + + override fun saveLocally(element: Element?) { + if (storage.lineSeparator == null) { + storage.lineSeparator = if (storage.isUseXmlProlog) LineSeparator.LF else LineSeparator.getSystemLineSeparator() + } + + val virtualFile = storage.getVirtualFile() + if (element == null) { + StorageUtil.deleteFile(storage.file, this, virtualFile) + storage.cachedVirtualFile = null + } + else { + storage.cachedVirtualFile = StorageUtil.writeFile(storage.file, this, virtualFile, element, if (storage.isUseXmlProlog) storage.lineSeparator!! else LineSeparator.LF, storage.isUseXmlProlog) + } + } + } + + override fun getVirtualFile(): VirtualFile? { + var result = cachedVirtualFile + if (result == null) { + result = LocalFileSystem.getInstance().findFileByIoFile(file) + cachedVirtualFile = result + } + return cachedVirtualFile + } + + override fun getFile() = file + + override fun loadLocalData(): Element? { + blockSavingTheContent = false + try { + val file = getVirtualFile() + if (file == null || file.isDirectory() || !file.isValid()) { + if (LOG.isDebugEnabled()) { + LOG.debug("Document was not loaded for $fileSpec file is ${if (file == null) "null" else "directory"}") + } + } + else if (file.getLength() == 0L) { + processReadException(null) + } + else { + val charBuffer = CharsetToolkit.UTF8_CHARSET.decode(ByteBuffer.wrap(file.contentsToByteArray())) + lineSeparator = StorageUtil.detectLineSeparators(charBuffer, if (isUseXmlProlog) null else LineSeparator.LF) + return JDOMUtil.loadDocument(charBuffer).detachRootElement() + } + } + catch (e: JDOMException) { + processReadException(e) + } + catch (e: IOException) { + processReadException(e) + } + return null + } + + private fun processReadException(e: Exception?) { + val contentTruncated = e == null + blockSavingTheContent = !contentTruncated && (StorageUtil.isProjectOrModuleFile(fileSpec) || fileSpec == StoragePathMacros.WORKSPACE_FILE) + if (!ApplicationManager.getApplication().isUnitTestMode() && !ApplicationManager.getApplication().isHeadlessEnvironment()) { + if (e != null) { + LOG.info(e) + } + Notification(Notifications.SYSTEM_MESSAGES_GROUP_ID, "Load Settings", "Cannot load settings from file '$file': ${if (contentTruncated) "content truncated" else e!!.getMessage()}\n${if (blockSavingTheContent) "Please correct the file content" else "File content will be recreated"}", NotificationType.WARNING).notify(null) + } + } + + override fun toString() = file.systemIndependentPath +} diff --git a/platform/configuration-store-impl/src/com/intellij/configurationStore/XmlElementStorage.kt b/platform/configuration-store-impl/src/com/intellij/configurationStore/XmlElementStorage.kt new file mode 100644 index 000000000000..e9d014936447 --- /dev/null +++ b/platform/configuration-store-impl/src/com/intellij/configurationStore/XmlElementStorage.kt @@ -0,0 +1,202 @@ +/* + * 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.configurationStore + +import com.intellij.openapi.components.RoamingType +import com.intellij.openapi.components.StateStorage +import com.intellij.openapi.components.TrackingPathMacroSubstitutor +import com.intellij.openapi.components.impl.stores.* +import com.intellij.openapi.util.JDOMUtil +import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream +import com.intellij.util.LineSeparator +import com.intellij.util.containers.ContainerUtil +import gnu.trove.THashMap +import org.jdom.Element +import org.jdom.JDOMException + +import java.io.IOException + +abstract class XmlElementStorage protected constructor(protected val fileSpec: String, + protected val rootElementName: String, + protected val pathMacroSubstitutor: TrackingPathMacroSubstitutor?, + roamingType: RoamingType?, + provider: StreamProvider?) : StateStorageBase() { + protected val roamingType: RoamingType = roamingType ?: RoamingType.PER_USER + private val provider: StreamProvider? = if (provider == null || roamingType == RoamingType.DISABLED || !provider.isApplicable(fileSpec, this.roamingType)) null else provider + + protected abstract fun loadLocalData(): Element? + + override fun getStateAndArchive(storageData: StorageData, component: Any, componentName: String) = storageData.getStateAndArchive(componentName) + + override fun loadData(): StorageData { + val storageData = createStorageData() + val element: Element? + // we don't use local data if has stream provider + if (provider != null && provider.enabled) { + try { + element = loadDataFromProvider() + if (element != null) { + storageData.loadState(element) + } + } + catch (e: Exception) { + LOG.error(e) + element = null + } + } + else { + element = loadLocalData() + } + + if (element != null) { + storageData.loadState(element) + } + return storageData + } + + throws(IOException::class, JDOMException::class) + private fun loadDataFromProvider() = JDOMUtil.load(provider!!.loadContent(fileSpec, roamingType)) + + private fun StorageData.loadState(element: Element) { + load(element, pathMacroSubstitutor, true) + } + + protected open fun createStorageData(): StorageData = StorageData() + + fun setDefaultState(element: Element) { + element.setName(rootElementName) + val storageData = createStorageData() + storageData.loadState(element) + storageDataRef.set(storageData) + } + + override fun startExternalization() = if (checkIsSavingDisabled()) null else createSaveSession(getStorageData()) + + protected abstract fun createSaveSession(storageData: StorageData): StateStorage.ExternalizationSession + + override fun analyzeExternalChangesAndUpdateIfNeed(componentNames: MutableSet) { + val oldData = storageDataRef.get() + val newData = getStorageData(true) + if (oldData == null) { + if (LOG.isDebugEnabled()) { + LOG.debug("analyzeExternalChangesAndUpdateIfNeed: old data null, load new for ${toString()}") + } + componentNames.addAll(newData.getComponentNames()) + } + else { + val changedComponentNames = oldData.getChangedComponentNames(newData, pathMacroSubstitutor) + if (LOG.isDebugEnabled()) { + LOG.debug("analyzeExternalChangesAndUpdateIfNeed: changedComponentNames $changedComponentNames for ${toString()}") + } + if (!ContainerUtil.isEmpty(changedComponentNames)) { + componentNames.addAll(changedComponentNames) + } + } + } + + private fun setStorageData(oldStorageData: StorageData, newStorageData: StorageData?) { + if (oldStorageData !== newStorageData && storageDataRef.getAndSet(newStorageData) !== oldStorageData) { + LOG.warn("Old storage data is not equal to current, new storage data was set anyway") + } + } + + abstract class XmlElementStorageSaveSession(private val originalStorageData: StorageData, protected val storage: T) : SaveSessionBase() { + private var copiedStorageData: StorageData? = null + + private val newLiveStates = THashMap() + + override fun createSaveSession() = if (storage.checkIsSavingDisabled() || (copiedStorageData == null && !originalStorageData.isDirty())) null else this + + override fun setSerializedState(component: Any, componentName: String, element: Element?) { + if (copiedStorageData == null) { + copiedStorageData = StorageData.setStateAndCloneIfNeed(componentName, element, originalStorageData, newLiveStates) + } + else { + copiedStorageData!!.setState(componentName, element, newLiveStates) + } + } + + override fun save() { + var storageData = copiedStorageData + if (storageData == null) { + storageData = originalStorageData + if (!storageData.isDirty()) { + LOG.warn("Copied storage data must be not null because original storage data is not dirty") + } + } + + var element = storageData.save(newLiveStates, storage.rootElementName) + if (element == null || JDOMUtil.isEmpty(element)) { + element = null + } + else if (storage.pathMacroSubstitutor != null) { + try { + storage.pathMacroSubstitutor.collapsePaths(element) + } + finally { + storage.pathMacroSubstitutor.reset() + } + } + + val provider = storage.provider + if (provider != null && provider.enabled) { + if (element == null) { + provider.delete(storage.fileSpec, storage.roamingType) + } + else { + // we should use standard line-separator (\n) - stream provider can share file content on any OS + val content = StorageUtil.writeToBytes(element, "\n") + provider.saveContent(storage.fileSpec, content.getInternalBuffer(), content.size(), storage.roamingType) + } + } + else { + saveLocally(element) + } + storage.setStorageData(originalStorageData, storageData) + } + + throws(IOException::class) + protected abstract fun saveLocally(element: Element?) + } + + public fun updatedFromStreamProvider(changedComponentNames: MutableSet, deleted: Boolean) { + if (roamingType == RoamingType.DISABLED) { + // storage roaming was changed to DISABLED, but settings repository has old state + return + } + + try { + val newElement = if (deleted) null else loadDataFromProvider() + val storageData = storageDataRef.get() + if (newElement == null) { + // if data was loaded, mark as changed all loaded components + if (storageData != null) { + changedComponentNames.addAll(storageData.getComponentNames()) + setStorageData(storageData, null) + } + } + else if (storageData != null) { + val newStorageData = createStorageData() + newStorageData.loadState(newElement) + changedComponentNames.addAll(storageData.getChangedComponentNames(newStorageData, pathMacroSubstitutor)) + setStorageData(storageData, newStorageData) + } + } + catch (e: Throwable) { + LOG.error(e) + } + } +} diff --git a/platform/configuration-store-impl/testSrc/MockStreamProvider.kt b/platform/configuration-store-impl/testSrc/MockStreamProvider.kt index 4e813445c43f..d383107dd0fc 100644 --- a/platform/configuration-store-impl/testSrc/MockStreamProvider.kt +++ b/platform/configuration-store-impl/testSrc/MockStreamProvider.kt @@ -9,8 +9,6 @@ import java.io.FileInputStream import java.io.InputStream class MockStreamProvider(private val myBaseDir: File) : StreamProvider { - override fun isApplicable(fileSpec: String, roamingType: RoamingType) = roamingType === RoamingType.PER_USER - override fun saveContent(fileSpec: String, content: ByteArray, size: Int, roamingType: RoamingType) { FileUtil.writeToFile(File(myBaseDir, fileSpec), content, 0, size) } diff --git a/platform/configuration-store-impl/testSrc/ModuleStoreTest.kt b/platform/configuration-store-impl/testSrc/ModuleStoreTest.kt index 17e7acceb4c0..4e285ffd2c18 100644 --- a/platform/configuration-store-impl/testSrc/ModuleStoreTest.kt +++ b/platform/configuration-store-impl/testSrc/ModuleStoreTest.kt @@ -7,7 +7,6 @@ import com.intellij.openapi.application.invokeAndWaitIfNeed import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.components.RoamingType import com.intellij.openapi.components.StoragePathMacros -import com.intellij.openapi.components.impl.stores.FileBasedStorage import com.intellij.openapi.components.impl.stores.StoreUtil import com.intellij.openapi.components.stateStore import com.intellij.openapi.module.ModifiableModuleModel diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ProjectStoreBaseTestCase.java b/platform/configuration-store-impl/testSrc/com/intellij/configurationStore/ProjectStoreBaseTestCase.java similarity index 69% rename from platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ProjectStoreBaseTestCase.java rename to platform/configuration-store-impl/testSrc/com/intellij/configurationStore/ProjectStoreBaseTestCase.java index 3e9d4bd380bf..60b27370ba14 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ProjectStoreBaseTestCase.java +++ b/platform/configuration-store-impl/testSrc/com/intellij/configurationStore/ProjectStoreBaseTestCase.java @@ -13,15 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.openapi.components.impl; +package com.intellij.configurationStore; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ex.ProjectManagerEx; -import com.intellij.openapi.util.JDOMBuilder; -import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.testFramework.PlatformTestCase; @@ -35,23 +33,16 @@ public abstract class ProjectStoreBaseTestCase extends PlatformTestCase { } protected byte[] getIprFileContent() throws UnsupportedEncodingException { - final String iprContent = JDOMUtil.writeDocument( - JDOMBuilder.document(JDOMBuilder.tag("project", - JDOMBuilder.attr("version", "4"), - JDOMBuilder.tag("component", JDOMBuilder.attr("name", "TestIprComponent"), - JDOMBuilder.tag("option", JDOMBuilder.attr("name", "VALUE"), JDOMBuilder.attr("value", "true"))) - )), - "\n"); - return iprContent.getBytes(CharsetToolkit.UTF8); + return ("" + + "" + + " " + + " " + + "").getBytes(CharsetToolkit.UTF8); } - @State( - name = "TestIprComponent", - storages = { - @Storage(file = "$PROJECT_FILE$") - } - ) - public static class TestIprComponent implements PersistentStateComponent { + @State(name = "TestComponent", storages = @Storage(file = "$PROJECT_FILE$")) + public static class TestComponent implements PersistentStateComponent { ProjectStoreBaseTestCase.DataBean myState; @Override diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ProjectStoreImplIdeaDirTest.java b/platform/configuration-store-impl/testSrc/com/intellij/configurationStore/ProjectStoreImplIdeaDirTest.java similarity index 84% rename from platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ProjectStoreImplIdeaDirTest.java rename to platform/configuration-store-impl/testSrc/com/intellij/configurationStore/ProjectStoreImplIdeaDirTest.java index c408ec21e794..f583a302f00d 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ProjectStoreImplIdeaDirTest.java +++ b/platform/configuration-store-impl/testSrc/com/intellij/configurationStore/ProjectStoreImplIdeaDirTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.openapi.components.impl; +package com.intellij.configurationStore; import com.intellij.openapi.components.ComponentsPackage; import com.intellij.openapi.project.Project; @@ -26,7 +26,7 @@ import java.io.IOException; public class ProjectStoreImplIdeaDirTest extends ProjectStoreBaseTestCase { @Override protected File getIprFile() throws IOException { - final File projectDir = FileUtil.createTempDirectory(getTestName(true), "project"); + File projectDir = FileUtil.createTempDirectory(getTestName(true), "project"); File ideaDir = new File(projectDir, Project.DIRECTORY_STORE_FOLDER); assertTrue(ideaDir.mkdir() || ideaDir.isDirectory()); File iprFile = new File(ideaDir, "misc.xml"); @@ -39,8 +39,8 @@ public class ProjectStoreImplIdeaDirTest extends ProjectStoreBaseTestCase { public void testLoadFromDirectoryStorage() throws Exception { ((ProjectEx)myProject).setOptimiseTestLoadSpeed(false); - final TestIprComponent testIprComponent = new TestIprComponent(); - ComponentsPackage.getStateStore(myProject).initComponent(testIprComponent, false); - assertNotNull(testIprComponent.myState); + TestComponent testComponent = new TestComponent(); + ComponentsPackage.getStateStore(myProject).initComponent(testComponent, false); + assertNotNull(testComponent.myState); } } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ProjectStoreImplIprFileTest.java b/platform/configuration-store-impl/testSrc/com/intellij/configurationStore/ProjectStoreImplIprFileTest.java similarity index 87% rename from platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ProjectStoreImplIprFileTest.java rename to platform/configuration-store-impl/testSrc/com/intellij/configurationStore/ProjectStoreImplIprFileTest.java index 1a732c2fab3a..2de8713e3958 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ProjectStoreImplIprFileTest.java +++ b/platform/configuration-store-impl/testSrc/com/intellij/configurationStore/ProjectStoreImplIprFileTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.openapi.components.impl; +package com.intellij.configurationStore; import com.intellij.openapi.components.ComponentsPackage; import com.intellij.openapi.project.ex.ProjectEx; @@ -34,8 +34,8 @@ public class ProjectStoreImplIprFileTest extends ProjectStoreBaseTestCase { ((ProjectEx)myProject).setOptimiseTestLoadSpeed(false); - final TestIprComponent testIprComponent = new TestIprComponent(); - ComponentsPackage.getStateStore(myProject).initComponent(testIprComponent, false); - assertNotNull(testIprComponent.myState); + final TestComponent testComponent = new TestComponent(); + ComponentsPackage.getStateStore(myProject).initComponent(testComponent, false); + assertNotNull(testComponent.myState); } } diff --git a/platform/configuration-store-impl/testSrc/com/intellij/configurationStore/XmlElementStorageTest.kt b/platform/configuration-store-impl/testSrc/com/intellij/configurationStore/XmlElementStorageTest.kt new file mode 100644 index 000000000000..1c027bc43b2b --- /dev/null +++ b/platform/configuration-store-impl/testSrc/com/intellij/configurationStore/XmlElementStorageTest.kt @@ -0,0 +1,65 @@ +/* + * 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.configurationStore + +import com.intellij.openapi.Disposable +import com.intellij.openapi.components.impl.stores.StorageData +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.util.JDOMBuilder.attr +import com.intellij.openapi.util.JDOMBuilder.tag +import com.intellij.testFramework.LightPlatformTestCase +import junit.framework.TestCase +import org.jdom.Element + +class XmlElementStorageTest : LightPlatformTestCase() { + public fun testGetStateSucceeded() { + val storage = MyXmlElementStorage(tag("root", tag("component", attr("name", "test"), tag("foo")))) + val state = storage.getState(this, "test", javaClass(), null) + TestCase.assertNotNull(state) + TestCase.assertEquals("component", state.getName()) + TestCase.assertNotNull(state.getChild("foo")) + } + + public fun testGetStateNotSucceeded() { + val storage = MyXmlElementStorage(tag("root")) + val state = storage.getState(this, "test", javaClass(), null) + TestCase.assertNull(state) + } + + public fun testSetStateOverridesOldState() { + val storage = MyXmlElementStorage(tag("root", tag("component", attr("name", "test"), tag("foo")))) + val newState = tag("component", attr("name", "test"), tag("bar")) + val externalizationSession = storage.startExternalization()!! + externalizationSession.setState(this, "test", newState, null) + externalizationSession.createSaveSession()!!.save() + TestCase.assertNotNull(storage.mySavedElement) + TestCase.assertNotNull(storage.mySavedElement!!.getChild("component").getChild("bar")) + TestCase.assertNull(storage.mySavedElement!!.getChild("component").getChild("foo")) + } + + + private class MyXmlElementStorage(private val myElement: Element) : XmlElementStorage("", "root", null, null, null) { + var mySavedElement: Element? = null + + override fun loadLocalData() = myElement + + override fun createSaveSession(storageData: StorageData) = object : XmlElementStorage.XmlElementStorageSaveSession(storageData, this) { + override fun saveLocally(element: Element?) { + mySavedElement = element?.clone() + } + } + } +} diff --git a/platform/core-api/src/com/intellij/psi/SyntaxTraverser.java b/platform/core-api/src/com/intellij/psi/SyntaxTraverser.java index 8f8d30ce712d..9aa092e1c392 100644 --- a/platform/core-api/src/com/intellij/psi/SyntaxTraverser.java +++ b/platform/core-api/src/com/intellij/psi/SyntaxTraverser.java @@ -18,7 +18,7 @@ import java.util.LinkedList; /** * @author gregsh */ -public class SyntaxTraverser extends FilteredTraverserBase> implements Iterable, UserDataHolder { +public class SyntaxTraverser extends FilteredTraverserBase> implements UserDataHolder { @NotNull public static ApiEx psiApi() { diff --git a/platform/core-impl/src/com/intellij/core/MockDocumentCommitProcessor.java b/platform/core-impl/src/com/intellij/core/MockDocumentCommitProcessor.java index 9a7ac2c4243b..e08fc7b9b1f0 100644 --- a/platform/core-impl/src/com/intellij/core/MockDocumentCommitProcessor.java +++ b/platform/core-impl/src/com/intellij/core/MockDocumentCommitProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ */ package com.intellij.core; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.Project; import com.intellij.psi.impl.DocumentCommitProcessor; @@ -30,6 +31,9 @@ class MockDocumentCommitProcessor extends DocumentCommitProcessor { } @Override - public void commitAsynchronously(@NotNull Project project, @NotNull Document document, @NonNls @NotNull Object reason) { + public void commitAsynchronously(@NotNull Project project, + @NotNull Document document, + @NonNls @NotNull Object reason, + @NotNull ModalityState currentModalityState) { } } diff --git a/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java b/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java index 9e9e4f60bbb8..630ea0b22119 100644 --- a/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java +++ b/platform/core-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java @@ -1660,7 +1660,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { if (child instanceof StartMarker && ((StartMarker)child).myDoneMarker.myCollapse) { int lastIndex = ((StartMarker)child).myDoneMarker.myLexemeIndex; - insertLeaf(child.getTokenType(), marker.myBuilder, child.myLexemeIndex, lastIndex); + insertLeaf(child.getTokenType(), marker.myBuilder, child.myLexemeIndex, lastIndex, true); } else { ensureCapacity(); @@ -1710,17 +1710,25 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { private int insertLeaves(int curToken, int lastIdx, PsiBuilderImpl builder) { lastIdx = Math.min(lastIdx, builder.myLexemeCount); while (curToken < lastIdx) { - insertLeaf(builder.myLexTypes[curToken], builder, curToken, curToken + 1); + insertLeaf(builder.myLexTypes[curToken], builder, curToken, curToken + 1, false); curToken++; } return curToken; } - private void insertLeaf(@NotNull IElementType type, @NotNull PsiBuilderImpl builder, int startLexemeIndex, int endLexemeIndex) { + private void insertLeaf(@NotNull IElementType type, + @NotNull PsiBuilderImpl builder, + int startLexemeIndex, + int endLexemeIndex, + boolean forceInsertion) { final int start = builder.myLexStarts[startLexemeIndex]; final int end = builder.myLexStarts[endLexemeIndex]; - if (start > end || start == end && !(type instanceof ILeafElementType)) return; + /** Corresponding code for heavy tree is located in {@link com.intellij.lang.impl.PsiBuilderImpl#insertLeaves} + * and is applied only to plain lexemes */ + if (start > end || !forceInsertion && start == end && !(type instanceof ILeafElementType)) { + return; + } final Token lexeme; if (type instanceof ILightLazyParseableElementType) { diff --git a/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java b/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java index 6aee3794da28..a92586c8e110 100644 --- a/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java +++ b/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java @@ -804,6 +804,12 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { return myText; } + // Breaks encapsulation, yet required for current zero-latency typing implementation. + // TODO Should be removed when we implement typing without starting write actions. + @NotNull + public ImmutableText getImmutableText() { + return myText; + } @Override public void addDocumentListener(@NotNull DocumentListener listener) { diff --git a/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitProcessor.java b/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitProcessor.java index de29abbbb4f1..49450a51519d 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitProcessor.java +++ b/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitProcessor.java @@ -17,6 +17,7 @@ package com.intellij.psi.impl; import com.intellij.lang.ASTNode; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Attachment; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; @@ -51,28 +52,34 @@ public abstract class DocumentCommitProcessor { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.DocumentCommitThread"); public abstract void commitSynchronously(@NotNull Document document, @NotNull Project project); - public abstract void commitAsynchronously(@NotNull final Project project, @NotNull final Document document, @NonNls @NotNull Object reason); + public abstract void commitAsynchronously(@NotNull final Project project, + @NotNull final Document document, + @NonNls @NotNull Object reason, + @NotNull ModalityState currentModalityState); protected static class CommitTask { - @NotNull public final Document document; - @NotNull public final Project project; + @NotNull final Document document; + @NotNull final Project project; // when queued it's not started // when dequeued it's started // when failed it's canceled - @NotNull public final ProgressIndicator indicator; // progress to commit this doc under. - @NotNull public final Object reason; + @NotNull final ProgressIndicator indicator; // progress to commit this doc under. + @NotNull final Object reason; + @NotNull final ModalityState myCreationModalityState; private final CharSequence myLastCommittedText; public boolean removed; // task marked as removed, should be ignored. public CommitTask(@NotNull Document document, @NotNull Project project, @NotNull ProgressIndicator indicator, - @NotNull Object reason) { + @NotNull Object reason, + @NotNull ModalityState currentModalityState) { this.document = document; this.project = project; this.indicator = indicator; this.reason = reason; + myCreationModalityState = currentModalityState; myLastCommittedText = PsiDocumentManager.getInstance(project).getLastCommittedText(document); } diff --git a/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitThread.java b/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitThread.java index 004ed153776d..c753bf72ccb3 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitThread.java +++ b/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitThread.java @@ -157,24 +157,30 @@ public class DocumentCommitThread extends DocumentCommitProcessor implements Run } @Override - public void commitAsynchronously(@NotNull final Project project, @NotNull final Document document, @NonNls @NotNull Object reason) { - queueCommit(project, document, reason); + public void commitAsynchronously(@NotNull final Project project, + @NotNull final Document document, + @NonNls @NotNull Object reason, + @NotNull ModalityState currentModalityState) { + queueCommit(project, document, reason, currentModalityState); } - void queueCommit(@NotNull final Project project, @NotNull final Document document, @NonNls @NotNull Object reason) { + void queueCommit(@NotNull final Project project, + @NotNull final Document document, + @NonNls @NotNull Object reason, + @NotNull ModalityState currentModalityState) { assert !isDisposed : "already disposed"; if (!project.isInitialized()) return; PsiFile psiFile = PsiDocumentManager.getInstance(project).getCachedPsiFile(document); if (psiFile == null) return; - doQueue(project, document, reason); + doQueue(project, document, reason, currentModalityState); } - private void doQueue(@NotNull Project project, @NotNull Document document, @NotNull Object reason) { + private void doQueue(@NotNull Project project, @NotNull Document document, @NotNull Object reason, @NotNull ModalityState currentModalityState) { synchronized (documentsToCommit) { ProgressIndicator indicator = createProgressIndicator(); - CommitTask newTask = new CommitTask(document, project, indicator, reason); + CommitTask newTask = new CommitTask(document, project, indicator, reason, currentModalityState); markRemovedFromDocsToCommit(newTask); markRemovedCurrentTask(newTask); @@ -358,7 +364,7 @@ public class DocumentCommitThread extends DocumentCommitProcessor implements Run if (success) { assert !myApplication.isDispatchThread(); - myApplication.invokeLater(finishRunnable, ModalityState.NON_MODAL); + myApplication.invokeLater(finishRunnable, task.myCreationModalityState); log("Invoked later finishRunnable", task, false, finishRunnable, indicator); } } @@ -379,7 +385,7 @@ public class DocumentCommitThread extends DocumentCommitProcessor implements Run synchronized (documentsToCommit) { if (!success && !task.removed) { // sync commit has not intervened // reset status for queue back successfully - doQueue(project, document, "re-added on failure"); + doQueue(project, document, "re-added on failure", task.myCreationModalityState); } currentTask = null; // do not cancel, it's being invokeLatered } @@ -402,7 +408,7 @@ public class DocumentCommitThread extends DocumentCommitProcessor implements Run } ProgressIndicator indicator = createProgressIndicator(); - CommitTask task = new CommitTask(document, project, indicator, "Sync commit"); + CommitTask task = new CommitTask(document, project, indicator, "Sync commit", ModalityState.any()); synchronized (documentsToCommit) { markRemovedFromDocsToCommit(task); markRemovedCurrentTask(task); @@ -529,7 +535,7 @@ public class DocumentCommitThread extends DocumentCommitProcessor implements Run } if (!success) { // add document back to the queue - queueCommit(project, document, "Re-added back"); + queueCommit(project, document, "Re-added back", task.myCreationModalityState); } } }; diff --git a/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java b/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java index 619d8137771a..f71e1cea8983 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java +++ b/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java @@ -42,7 +42,6 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.impl.smartPointers.SmartPointerManagerImpl; import com.intellij.psi.impl.source.PsiFileImpl; -import com.intellij.psi.impl.source.text.BlockSupportImpl; import com.intellij.psi.text.BlockSupport; import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.*; @@ -716,7 +715,7 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen commitDocument(document); } else if (!((DocumentEx)document).isInBulkUpdate() && myPerformBackgroundCommit) { - myDocumentCommitProcessor.commitAsynchronously(myProject, document, event); + myDocumentCommitProcessor.commitAsynchronously(myProject, document, event, ApplicationManager.getApplication().getCurrentModalityState()); } } else { diff --git a/platform/core-impl/src/com/intellij/psi/impl/PsiManagerImpl.java b/platform/core-impl/src/com/intellij/psi/impl/PsiManagerImpl.java index 37148091656e..2c304dec8f5a 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/PsiManagerImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/PsiManagerImpl.java @@ -374,6 +374,10 @@ public class PsiManagerImpl extends PsiManagerEx { myTreeChangePreprocessors.add(preprocessor); } + public void removeTreeChangePreprocessor(@NotNull PsiTreeChangePreprocessor preprocessor) { + myTreeChangePreprocessors.remove(preprocessor); + } + private void fireEvent(@NotNull PsiTreeChangeEventImpl event) { boolean isRealTreeChange = event.getCode() != PsiTreeChangeEventImpl.PsiEventType.PROPERTY_CHANGED && event.getCode() != PsiTreeChangeEventImpl.PsiEventType.BEFORE_PROPERTY_CHANGE; diff --git a/platform/core-impl/src/com/intellij/psi/impl/PsiModificationTrackerImpl.java b/platform/core-impl/src/com/intellij/psi/impl/PsiModificationTrackerImpl.java index f4d79f7370fd..3ab8b34f4311 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/PsiModificationTrackerImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/PsiModificationTrackerImpl.java @@ -77,7 +77,8 @@ public class PsiModificationTrackerImpl implements PsiModificationTracker, PsiTr @Override public void treeChanged(@NotNull PsiTreeChangeEventImpl event) { myModificationCount.getAndIncrement(); - if (event.getParent() instanceof PsiDirectory) { + if (event.getParent() instanceof PsiDirectory + || event.getOldParent() instanceof PsiDirectory /* move events */) { myOutOfCodeBlockModificationCount.getAndIncrement(); } diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfo.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfo.java index 0a7bdd6f12a5..2c6d23fd1727 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfo.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfo.java @@ -18,7 +18,6 @@ package com.intellij.psi.impl.smartPointers; import com.intellij.lang.LanguageUtil; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.util.*; import com.intellij.psi.PsiAnchor; import com.intellij.psi.PsiElement; @@ -110,11 +109,11 @@ class AnchorElementInfo extends SelfElementInfo { } @Override - public void fastenBelt(int offset, RangeMarker[] cachedRangeMarker) { + public void fastenBelt() { if (getStubId() != -1) { switchToTree(); } - super.fastenBelt(offset, cachedRangeMarker); + super.fastenBelt(); } private void switchToTree() { diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/ClsElementInfo.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/ClsElementInfo.java index adb5206dc6e6..4d947cb83f3e 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/ClsElementInfo.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/ClsElementInfo.java @@ -15,8 +15,6 @@ */ package com.intellij.psi.impl.smartPointers; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Segment; @@ -29,28 +27,13 @@ import org.jetbrains.annotations.NotNull; /** * User: cdr */ -public class ClsElementInfo implements SmartPointerElementInfo { +public class ClsElementInfo extends SmartPointerElementInfo { private final PsiAnchor.StubIndexReference myStubIndexReference; public ClsElementInfo(@NotNull PsiAnchor.StubIndexReference stubReference) { myStubIndexReference = stubReference; } - @Override - public Document getDocumentToSynchronize() { - return null; - } - - // before change - @Override - public void fastenBelt(int offset, RangeMarker[] cachedRangeMarker) { - } - - // after change - @Override - public void unfastenBelt(int offset) { - } - @Override public PsiElement restoreElement() { return myStubIndexReference.retrieve(); @@ -85,11 +68,6 @@ public class ClsElementInfo implements SmartPointerElementInfo { return myStubIndexReference.getProject(); } - @Override - public void cleanup() { - - } - @Override public PsiFile restoreFile() { return myStubIndexReference.getFile(); diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/FileElementInfo.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/FileElementInfo.java index 5f99cb3c4df8..e7da9a06dc73 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/FileElementInfo.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/FileElementInfo.java @@ -16,8 +16,6 @@ package com.intellij.psi.impl.smartPointers; import com.intellij.lang.Language; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Segment; @@ -30,7 +28,7 @@ import org.jetbrains.annotations.NotNull; /** * User: cdr */ -class FileElementInfo implements SmartPointerElementInfo { +class FileElementInfo extends SmartPointerElementInfo { protected final VirtualFile myVirtualFile; protected final Project myProject; protected final Language myLanguage; @@ -48,19 +46,6 @@ class FileElementInfo implements SmartPointerElementInfo { myLanguage = lang; } - @Override - public Document getDocumentToSynchronize() { - return null; - } - - @Override - public void fastenBelt(int offset, RangeMarker[] cachedRangeMarker) { - } - - @Override - public void unfastenBelt(int offset) { - } - @Override public PsiElement restoreElement() { return SelfElementInfo.restoreFileFromVirtual(myVirtualFile, myProject, myLanguage); @@ -105,8 +90,4 @@ class FileElementInfo implements SmartPointerElementInfo { return myProject; } - @Override - public void cleanup() { - - } } diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/HardElementInfo.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/HardElementInfo.java index 8bbf00ed563d..395375e750d4 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/HardElementInfo.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/HardElementInfo.java @@ -15,8 +15,6 @@ */ package com.intellij.psi.impl.smartPointers; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Segment; @@ -29,7 +27,7 @@ import org.jetbrains.annotations.NotNull; /** * User: cdr */ -class HardElementInfo implements SmartPointerElementInfo { +class HardElementInfo extends SmartPointerElementInfo { @NotNull private final PsiElement myElement; @NotNull @@ -40,19 +38,6 @@ class HardElementInfo implements SmartPointerElementInfo { myProject = project; } - @Override - public Document getDocumentToSynchronize() { - return null; - } - - @Override - public void fastenBelt(int offset, RangeMarker[] cachedRangeMarker) { - } - - @Override - public void unfastenBelt(int offset) { - } - @Override public PsiElement restoreElement() { return myElement; @@ -89,8 +74,4 @@ class HardElementInfo implements SmartPointerElementInfo { return myProject; } - @Override - public void cleanup() { - - } } diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/InjectedSelfElementInfo.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/InjectedSelfElementInfo.java index e16ec5ab5ff7..8c2091c71062 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/InjectedSelfElementInfo.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/InjectedSelfElementInfo.java @@ -20,7 +20,6 @@ import com.intellij.injected.editor.VirtualFileWindow; import com.intellij.lang.Language; import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.ProperTextRange; @@ -39,7 +38,7 @@ import java.util.List; /** * User: cdr */ -class InjectedSelfElementInfo implements SmartPointerElementInfo { +class InjectedSelfElementInfo extends SmartPointerElementInfo { private final SmartPsiFileRange myInjectedFileRangeInHostFile; private final Class anchorClass; private final Language anchorLanguage; @@ -193,16 +192,6 @@ class InjectedSelfElementInfo implements SmartPointerElementInfo { return ((SmartPsiElementPointerImpl)myHostContext).getElementInfo().getDocumentToSynchronize(); } - @Override - public void fastenBelt(int offset, RangeMarker[] cachedRangeMarkers) { - - } - - @Override - public void unfastenBelt(int offset) { - - } - @Override public int elementHashCode() { return ((SmartPsiElementPointerImpl)myHostContext).getElementInfo().elementHashCode(); diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java index 66dcee8daec7..1bb7c8c76f4e 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java @@ -18,7 +18,6 @@ package com.intellij.psi.impl.smartPointers; import com.intellij.lang.Language; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.impl.FrozenDocument; import com.intellij.openapi.editor.impl.ManualRangeMarker; @@ -38,7 +37,7 @@ import java.util.Set; /** * User: cdr */ -public class SelfElementInfo implements SmartPointerElementInfo { +public class SelfElementInfo extends SmartPointerElementInfo { private final VirtualFile myVirtualFile; private final Class myType; private final Project myProject; @@ -81,7 +80,7 @@ public class SelfElementInfo implements SmartPointerElementInfo { // before change @Override - public void fastenBelt(int offset, @Nullable RangeMarker[] cachedRangeMarkers) { + public void fastenBelt() { if (myRangeMarker != null) return; // already tracks changes if (myPsiRange == null) return; // invalid @@ -96,11 +95,6 @@ public class SelfElementInfo implements SmartPointerElementInfo { setRange(myPsiRange, document); } - // after change - @Override - public void unfastenBelt(int offset) { - } - @Override public PsiElement restoreElement() { Segment segment = getPsiRange(); diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerElementInfo.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerElementInfo.java index e0bf6b2eb10c..c975a59a959c 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerElementInfo.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerElementInfo.java @@ -17,7 +17,6 @@ package com.intellij.psi.impl.smartPointers; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Segment; import com.intellij.openapi.vfs.VirtualFile; @@ -26,27 +25,30 @@ import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -interface SmartPointerElementInfo { +abstract class SmartPointerElementInfo { @Nullable - Document getDocumentToSynchronize(); + public Document getDocumentToSynchronize() { + return null; + } - void fastenBelt(int offset, @Nullable RangeMarker[] cachedRangeMarkers); - - void unfastenBelt(int offset); + public void fastenBelt() { + } @Nullable - PsiElement restoreElement(); + public abstract PsiElement restoreElement(); - PsiFile restoreFile(); + public abstract PsiFile restoreFile(); - int elementHashCode(); // must be immutable - boolean pointsToTheSameElementAs(@NotNull SmartPointerElementInfo other); + public abstract int elementHashCode(); // must be immutable + public abstract boolean pointsToTheSameElementAs(@NotNull SmartPointerElementInfo other); - VirtualFile getVirtualFile(); + public abstract VirtualFile getVirtualFile(); @Nullable - Segment getRange(); - @NotNull Project getProject(); + public abstract Segment getRange(); + @NotNull + public abstract Project getProject(); - void cleanup(); + public void cleanup() { + } } diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerEx.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerEx.java index df0ca95ae7d0..160f6f064356 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerEx.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerEx.java @@ -15,15 +15,13 @@ */ package com.intellij.psi.impl.smartPointers; -import com.intellij.openapi.editor.RangeMarker; import com.intellij.psi.PsiElement; import com.intellij.psi.SmartPsiElementPointer; import org.jetbrains.annotations.Nullable; interface SmartPointerEx extends SmartPsiElementPointer { - void fastenBelt(int offset, @Nullable RangeMarker[] cachedRangeMarkers); - - void unfastenBelt(int offset); + void fastenBelt(); + @Nullable PsiElement getCachedElement(); } diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java index be4e9bf53e89..c5501035c154 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java @@ -16,12 +16,9 @@ package com.intellij.psi.impl.smartPointers; -import com.intellij.injected.editor.DocumentWindow; -import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.impl.FrozenDocument; import com.intellij.openapi.editor.impl.ManualRangeMarker; @@ -34,7 +31,6 @@ import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.impl.PsiDocumentManagerBase; -import com.intellij.psi.impl.PsiManagerEx; import com.intellij.psi.util.PsiUtilCore; import com.intellij.reference.SoftReference; import com.intellij.util.ArrayUtil; @@ -65,12 +61,10 @@ public class SmartPointerManagerImpl extends SmartPointerManager { private final Project myProject; private final Key POINTERS_KEY; - private final Key POINTERS_ARE_FASTENED_KEY; public SmartPointerManagerImpl(Project project) { myProject = project; POINTERS_KEY = Key.create("SMART_POINTERS for "+project); - POINTERS_ARE_FASTENED_KEY = Key.create("SMART_POINTERS_ARE_FASTENED for "+project); } private static void processQueue() { @@ -89,50 +83,22 @@ public class SmartPointerManagerImpl extends SmartPointerManager { } } - public void fastenBelts(@NotNull VirtualFile file, int offset, @Nullable RangeMarker[] cachedRangeMarkers) { + public void fastenBelts(@NotNull VirtualFile file) { ApplicationManager.getApplication().assertIsDispatchThread(); processQueue(); synchronized (lock) { - if (getAndFasten(file)) return; FilePointersList pointers = getPointers(file); if (pointers != null && !pointers.isEmpty()) { for (PointerReference ref : pointers.references) { SmartPointerEx pointer = SoftReference.dereference(ref); if (pointer != null) { - pointer.fastenBelt(offset, cachedRangeMarkers); + pointer.fastenBelt(); } } } } } - public void unfastenBelts(@NotNull VirtualFile file, int offset) { - ApplicationManager.getApplication().assertIsDispatchThread(); - processQueue(); - synchronized (lock) { - if (!getAndUnfasten(file)) return; - FilePointersList pointers = getPointers(file); - if (pointers != null && !pointers.isEmpty()) { - for (PointerReference ref : pointers.references) { - SmartPointerEx pointer = SoftReference.dereference(ref); - if (pointer != null) { - pointer.unfastenBelt(offset); - } - } - } - - PsiFile psiFile = ((PsiManagerEx)PsiManager.getInstance(myProject)).getFileManager().getCachedPsiFile(file); - if (psiFile != null) { - PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(myProject); - for (DocumentWindow injectedDoc : InjectedLanguageManager.getInstance(myProject).getCachedInjectedDocuments(psiFile)) { - PsiFile injectedFile = psiDocumentManager.getPsiFile(injectedDoc); - if (injectedFile == null) continue; - unfastenBelts(injectedFile.getViewProvider().getVirtualFile(), 0); - } - } - } - } - private static final Key> CACHED_SMART_POINTER_KEY = Key.create("CACHED_SMART_POINTER_KEY"); @Override @NotNull @@ -158,11 +124,7 @@ public class SmartPointerManagerImpl extends SmartPointerManager { element.putUserData(CACHED_SMART_POINTER_KEY, new SoftReference(pointer)); } else { - containingFile = containingFile == null ? element.getContainingFile() : containingFile; synchronized (lock) { - if (containingFile != null && areBeltsFastened(containingFile.getViewProvider().getVirtualFile())) { - pointer.fastenBelt(0, null); - } if (pointer instanceof SmartPsiElementPointerImpl) { ((SmartPsiElementPointerImpl)pointer).incrementAndGetReferenceCount(1); } @@ -273,20 +235,6 @@ public class SmartPointerManagerImpl extends SmartPointerManager { } } - private boolean getAndFasten(@NotNull VirtualFile file) { - boolean fastened = areBeltsFastened(file); - file.putUserData(POINTERS_ARE_FASTENED_KEY, Boolean.TRUE); - return fastened; - } - private boolean getAndUnfasten(@NotNull VirtualFile file) { - boolean fastened = areBeltsFastened(file); - file.putUserData(POINTERS_ARE_FASTENED_KEY, null); - return fastened; - } - boolean areBeltsFastened(@NotNull VirtualFile file) { - return file.getUserData(POINTERS_ARE_FASTENED_KEY) == Boolean.TRUE; - } - @Override public boolean pointToTheSameElement(@NotNull SmartPsiElementPointer pointer1, @NotNull SmartPsiElementPointer pointer2) { return SmartPsiElementPointerImpl.pointsToTheSameElementAs(pointer1, pointer2); diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java index 2ea0695a08f0..b516188dce6a 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java @@ -20,7 +20,6 @@ import com.intellij.lang.LanguageUtil; import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; @@ -178,13 +177,8 @@ class SmartPsiElementPointerImpl implements SmartPointerEx } @Override - public void unfastenBelt(int offset) { - myElementInfo.unfastenBelt(offset); - } - - @Override - public void fastenBelt(int offset, @Nullable RangeMarker[] cachedRangeMarkers) { - myElementInfo.fastenBelt(offset, cachedRangeMarkers); + public void fastenBelt() { + myElementInfo.fastenBelt(); } @NotNull diff --git a/platform/duplicates-analysis/src/com/intellij/dupLocator/index/DuplicatesIndex.java b/platform/duplicates-analysis/src/com/intellij/dupLocator/index/DuplicatesIndex.java index cf0219e4e7b4..cead2d1bd109 100644 --- a/platform/duplicates-analysis/src/com/intellij/dupLocator/index/DuplicatesIndex.java +++ b/platform/duplicates-analysis/src/com/intellij/dupLocator/index/DuplicatesIndex.java @@ -56,7 +56,7 @@ public class DuplicatesIndex extends FileBasedIndexExtension NAME = ID.create("DuplicatesIndex"); - private static final int myBaseVersion = 20; + private static final int myBaseVersion = 21; private final FileBasedIndex.InputFilter myInputFilter = new FileBasedIndex.InputFilter() { @Override diff --git a/platform/editor-ui-api/src/com/intellij/ide/ui/ColorBlindness.java b/platform/editor-ui-api/src/com/intellij/ide/ui/ColorBlindness.java new file mode 100644 index 000000000000..bf8ac42bcd9b --- /dev/null +++ b/platform/editor-ui-api/src/com/intellij/ide/ui/ColorBlindness.java @@ -0,0 +1,67 @@ +/* + * 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.ide.ui; + +/** + * @author Sergey.Malenkov + */ +public enum ColorBlindness { + /** + * Lacking the long-wavelength sensitive retinal cones, + * those with this condition are unable to distinguish + * between colors in the green–yellow–red section of the spectrum. + * Protanopes are more likely to confuse + *
    + *
  • black with many shades of red;
  • + *
  • dark brown with dark green, dark orange and dark red;
  • + *
  • some blues with some reds, purples and dark pinks;
  • + *
  • mid-greens with some oranges.
  • + *
+ */ + protanopia, + /** + * Lacking the medium-wavelength sensitive retinal cones, + * those with this condition are unable to distinguish + * between colors in the green–yellow–red section of the spectrum. + * Deuteranopes are more likely to confuse + *
    + *
  • mid-reds with mid-greens;
  • + *
  • blue-greens with grey and mid-pinks;
  • + *
  • bright greens with yellows;
  • + *
  • pale pinks with light grey;
  • + *
  • mid-reds with mid-brown
  • + *
  • light blues with lilac.
  • + *
+ */ + deuteranopia, + /** + * Lacking the short-wavelength sensitive retinal cones, + * those affected see short-wavelength colors (blue, indigo and a spectral violet) + * greenish and drastically dimmed, some of these colors even as black. + * Tritanopes are more likely to confuse + *
    + *
  • light blues with greys;
  • + *
  • dark purples with black;
  • + *
  • mid-greens with blues;
  • + *
  • oranges with reds.
  • + *
+ */ + tritanopia, + /** + * Total color blindness is defined as the inability to see color. + */ + achromatopsia +} diff --git a/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java b/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java index 6c922b21772e..8a3eefdffbff 100644 --- a/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java +++ b/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java @@ -101,6 +101,7 @@ public class UISettings extends SimpleModificationTracker implements PersistentS public boolean ANTIALIASING_IN_EDITOR = true; public boolean ANTIALIASING_IN_IDE = ANTIALIASING_IN_EDITOR; public LCDRenderingScope LCD_RENDERING_SCOPE = UIUtil.isRetina() ? LCDRenderingScope.OFF : LCDRenderingScope.IDE; + public ColorBlindness COLOR_BLINDNESS; public boolean USE_LCD_RENDERING_IN_EDITOR = true; public boolean MOVE_MOUSE_ON_DEFAULT_BUTTON = false; public boolean ENABLE_ALPHA_MODE = false; diff --git a/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java b/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java index d3ca96632a7f..47d503a8b1f6 100644 --- a/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java +++ b/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java @@ -19,6 +19,7 @@ */ package com.intellij.openapi.editor.colors.impl; +import com.intellij.ide.ui.ColorBlindness; import com.intellij.ide.ui.UISettings; import com.intellij.openapi.editor.HighlighterColors; import com.intellij.openapi.editor.colors.*; @@ -70,6 +71,10 @@ public abstract class AbstractColorsScheme implements EditorColorsScheme { // version influences XML format and triggers migration private int myVersion = CURR_VERSION; + /** + * The version from the original file. + */ + private int myOriginalVersion = CURR_VERSION; protected Map myColorsMap = ContainerUtilRt.newHashMap(); protected Map myAttributesMap = ContainerUtilRt.newHashMap(); @@ -267,8 +272,9 @@ public abstract class AbstractColorsScheme implements EditorColorsScheme { } public void readExternal(Element parentNode) { - String blindness = Registry.stringValue("color.blindness"); // TODO: get blindness - myValueReader.setAttribute(blindness); + UISettings settings = UISettings.getInstance(); + ColorBlindness blindness = settings == null ? null : settings.COLOR_BLINDNESS; + myValueReader.setAttribute(blindness == null ? null : blindness.name()); if (SCHEME_ELEMENT.equals(parentNode.getName())) { readScheme(parentNode); } @@ -294,6 +300,7 @@ public abstract class AbstractColorsScheme implements EditorColorsScheme { } myVersion = readVersion; + myOriginalVersion = readVersion; String isDefaultScheme = node.getAttributeValue(DEFAULT_SCHEME_ATTR); boolean isDefault = isDefaultScheme != null && Boolean.parseBoolean(isDefaultScheme); if (!isDefault) { @@ -357,6 +364,33 @@ public abstract class AbstractColorsScheme implements EditorColorsScheme { } } + /** + * The method is called for the scheme when it is fully loaded including additional text attributes from providers. + */ + public void upgradeSchemeFromPreviousVersion() { + setUndefinedAttributesAsInheritedForVersion142(); + } + + + /** + * Defines empty attributes with fallback (inheritance) enabled for all the attributes explicitly defined in the parent scheme since + * previously undefined attributes were treated as inherited, not taken from the parent scheme. + */ + private void setUndefinedAttributesAsInheritedForVersion142() { + if (myOriginalVersion >= 142 || myParentScheme == null) return; + if (myParentScheme instanceof AbstractColorsScheme) { + for (TextAttributesKey key : ((AbstractColorsScheme)myParentScheme).myAttributesMap.keySet()) { + TextAttributes parentAttributes = ((AbstractColorsScheme)myParentScheme).getDirectlyDefinedAttributes(key); + if (key.getFallbackAttributeKey() != null && + parentAttributes != null && + !parentAttributes.isFallbackEnabled() && + !myAttributesMap.containsKey(key)) { + myAttributesMap.put(key, new TextAttributes()); + } + } + } + } + @Deprecated @SuppressWarnings("unused") public static final Map DEFAULT_ERROR_STRIPE_COLOR = new THashMap(); diff --git a/platform/editor-ui-ex/src/com/intellij/openapi/editor/ex/util/LexerEditorHighlighter.java b/platform/editor-ui-ex/src/com/intellij/openapi/editor/ex/util/LexerEditorHighlighter.java index 84119ad2f0ec..c578af5efb69 100644 --- a/platform/editor-ui-ex/src/com/intellij/openapi/editor/ex/util/LexerEditorHighlighter.java +++ b/platform/editor-ui-ex/src/com/intellij/openapi/editor/ex/util/LexerEditorHighlighter.java @@ -28,6 +28,7 @@ import com.intellij.openapi.editor.ex.PrioritizedDocumentListener; import com.intellij.openapi.editor.highlighter.EditorHighlighter; import com.intellij.openapi.editor.highlighter.HighlighterClient; import com.intellij.openapi.editor.highlighter.HighlighterIterator; +import com.intellij.openapi.editor.impl.DocumentImpl; import com.intellij.openapi.editor.impl.EditorDocumentPriorities; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.fileTypes.PlainSyntaxHighlighter; @@ -37,6 +38,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.psi.tree.IElementType; import com.intellij.util.ArrayUtil; +import com.intellij.util.text.ImmutableText; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -351,6 +353,44 @@ public class LexerEditorHighlighter implements EditorHighlighter, PrioritizedDoc return attrs; } + // Called to determine visual attributes of inserted character prior to starting a write action. + // TODO Should be removed when we implement typing without starting write actions. + public TextAttributes getAttributes(DocumentImpl document, int offset, char c) { + final int segmentIndex; + try { + segmentIndex = mySegments.findSegmentIndex(offset) - 2; + } + catch (IndexOutOfBoundsException ex) { + throw new IndexOutOfBoundsException(ex.getMessage() + " Lexer: " + myLexer); + } + int startIndex = Math.max(0, segmentIndex); + + int data; + do { + data = mySegments.getSegmentData(startIndex); + if (isInitialState(data)|| startIndex == 0) break; + startIndex--; + } + while (true); + + int startOffset = mySegments.getSegmentStart(startIndex); + + ImmutableText newText = document.getImmutableText().insert(offset, Character.toString(c)); + + myLexer.start(newText, startOffset, newText.length(), myInitialState); + + IElementType tokenType = null; + while (myLexer.getTokenType() != null) { + if (myLexer.getTokenEnd() >= offset + 1) { + tokenType = myLexer.getTokenType(); + break; + } + myLexer.advance(); + } + + return getAttributes(tokenType); + } + protected TextAttributes convertAttributes(@NotNull TextAttributesKey[] keys) { TextAttributes attrs = myScheme.getAttributes(HighlighterColors.TEXT); for (TextAttributesKey key : keys) { diff --git a/platform/lang-api/src/com/intellij/execution/Executor.java b/platform/lang-api/src/com/intellij/execution/Executor.java index e803e85b6bcc..3275cbd51125 100644 --- a/platform/lang-api/src/com/intellij/execution/Executor.java +++ b/platform/lang-api/src/com/intellij/execution/Executor.java @@ -25,7 +25,7 @@ import javax.swing.*; /** * Describes a specific way of executing any possible run configuration. The three default executors provided by the IntelliJ Platform - * by default are Run, Debug and (in IntelliJ IDEA Ultimate and certain platform-based IDEs) Run with Coverage. Each executor gets its + * by default are Run, Debug and Run with Coverage. Each executor gets its * own toolbar button, which starts the selected run configuration using this executor, and its own context menu item for starting * a configuration using this executor. * diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/LanguageCodeStyleSettingsProvider.java b/platform/lang-api/src/com/intellij/psi/codeStyle/LanguageCodeStyleSettingsProvider.java index 95f2f3bec0eb..0942308dec2b 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/LanguageCodeStyleSettingsProvider.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/LanguageCodeStyleSettingsProvider.java @@ -17,20 +17,16 @@ package com.intellij.psi.codeStyle; import com.intellij.application.options.IndentOptionsEditor; import com.intellij.lang.Language; -import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiFile; -import com.intellij.util.ArrayUtil; import com.intellij.util.containers.HashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Arrays; -import java.util.List; import java.util.Set; /** @@ -189,6 +185,7 @@ public abstract class LanguageCodeStyleSettingsProvider { return null; } + @SuppressWarnings("unused") public static DisplayPriority getDisplayPriority(Language language) { LanguageCodeStyleSettingsProvider langProvider = forLanguage(language); if (langProvider == null) return DisplayPriority.LANGUAGE_SETTINGS; diff --git a/platform/lang-impl/lang-impl.iml b/platform/lang-impl/lang-impl.iml index c2c7a543e97a..5bc3dcc67da9 100644 --- a/platform/lang-impl/lang-impl.iml +++ b/platform/lang-impl/lang-impl.iml @@ -6,7 +6,6 @@ - diff --git a/platform/lang-impl/resources/extensions/startup/readme.txt b/platform/lang-impl/resources/extensions/startup/readme.txt deleted file mode 100644 index 621ed6fec72a..000000000000 --- a/platform/lang-impl/resources/extensions/startup/readme.txt +++ /dev/null @@ -1,14 +0,0 @@ -Scripts contained in this directory are executed on IDE startup. - -## Bindings -* 'IDE' binding from the IDE Scripting Console is available -* script output is redirected to the IDE's log - -## Execution -* each script is executed exactly once after a project's initialization is complete -* scripts are executed in alphabetical order -* a script engine for a script is looked up by script's extension - -## Script Languages -Enabling scripting in your favourite scripting language is easy: -put your language runtime, JSR-223, and required libraries to %IDE_HOME%/lib/ \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/SmartPointerEditorTracker.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/SmartPointerEditorTracker.java index fcaaf35293df..8066ee86d669 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/SmartPointerEditorTracker.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/SmartPointerEditorTracker.java @@ -60,20 +60,12 @@ public class SmartPointerEditorTracker extends AbstractProjectComponent implemen boolean isRelevant = virtualFile != null && isRelevant(virtualFile); if (!isBulk && isRelevant && shouldNotifySmartPointers(virtualFile)) { - mySmartPointerManager.fastenBelts(virtualFile, event.getOffset(), null); + mySmartPointerManager.fastenBelts(virtualFile); } } @Override public void documentChanged(DocumentEvent event) { - final Document document = event.getDocument(); - VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document); - boolean isBulk = document instanceof DocumentEx && ((DocumentEx)document).isInBulkUpdate(); - - boolean isRelevant = virtualFile != null && isRelevant(virtualFile); - if (!isBulk && isRelevant && shouldNotifySmartPointers(virtualFile)) { - mySmartPointerManager.unfastenBelts(virtualFile, event.getOffset()); - } } @Override @@ -86,22 +78,12 @@ public class SmartPointerEditorTracker extends AbstractProjectComponent implemen final VirtualFile virtualFile = psiFile.getVirtualFile(); // materialize all range markers and do not let them to be collected to improve responsiveness if (virtualFile != null) { - mySmartPointerManager.fastenBelts(virtualFile, 0, null); + mySmartPointerManager.fastenBelts(virtualFile); } } @Override public void editorReleased(@NotNull EditorFactoryEvent event) { - final Editor editor = event.getEditor(); - if (editor.getProject() != null && editor.getProject() != myProject || myProject.isDisposed()) return; - final PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(editor.getDocument()); - if (psiFile == null) return; - final VirtualFile virtualFile = psiFile.getVirtualFile(); - // allow range markers in smart pointers to be collected - // beware there maybe other editors still open for that file - if (virtualFile != null && virtualFile.isValid() && shouldNotifySmartPointers(virtualFile)) { - mySmartPointerManager.unfastenBelts(virtualFile, 0); - } } private boolean shouldNotifySmartPointers(@NotNull VirtualFile virtualFile) { @@ -116,16 +98,12 @@ public class SmartPointerEditorTracker extends AbstractProjectComponent implemen public void updateStarted(@NotNull Document document) { final VirtualFile virtualFile = getVirtualFile(document); if (virtualFile != null && isRelevant(virtualFile) && shouldNotifySmartPointers(virtualFile)) { - mySmartPointerManager.fastenBelts(virtualFile, 0, null); + mySmartPointerManager.fastenBelts(virtualFile); } } @Override public void updateFinished(@NotNull Document document) { - final VirtualFile virtualFile = getVirtualFile(document); - if (virtualFile != null && isRelevant(virtualFile) && shouldNotifySmartPointers(virtualFile)) { - mySmartPointerManager.unfastenBelts(virtualFile, 0); - } } private static VirtualFile getVirtualFile(@NotNull Document document) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java index 4d9741996d4a..ead031227ea7 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java @@ -712,6 +712,7 @@ public class DocumentationManager extends DockablePopupManager() { @Override @@ -745,6 +748,7 @@ public class DocumentationManager extends DockablePopupManager caretOffsetRef, @NotNull final Ref caretAdvance, - @NotNull final DataContext dataContext, final EditorActionHandler originalHandler) { - Document document = editor.getDocument(); - CharSequence text = document.getCharsSequence(); - Project project = file.getProject(); - int caretOffset = caretOffsetRef.get().intValue(); - int unmatchedLBracesNumber = getUnmatchedLBracesNumberBefore(editor, caretOffset, file.getFileType()); - if (!CodeInsightSettings.getInstance().INSERT_BRACE_ON_ENTER || unmatchedLBracesNumber <= 0) { + public Result preprocessEnter(@NotNull final PsiFile file, + @NotNull final Editor editor, + @NotNull final Ref caretOffsetRef, + @NotNull final Ref caretAdvance, + @NotNull final DataContext dataContext, + final EditorActionHandler originalHandler) { + + int caretOffset = caretOffsetRef.get(); + if (!isApplicable(file, caretOffset)) { return Result.Continue; } - - int offset = CharArrayUtil.shiftForward(text, caretOffset, " \t"); - if (offset < document.getTextLength()) { - char c = text.charAt(offset); - if (c != ')' && c != ']' && c != ';' && c != ',' && c != '%' && c != '<' && c != '?') { - offset = calculateOffsetToInsertClosingBrace(file, text, offset); - //offset = CharArrayUtil.shiftForwardUntil(text, caretOffset, "\n"); + + int maxRBraceCount = getMaxRBraceCount(file, editor, caretOffset); + if (maxRBraceCount > 0) { + insertRBraces(file, editor, + caretOffset, + getRBraceOffset(file, editor, caretOffset), + adjustRBraceCountForPosition(editor, caretOffset, maxRBraceCount)); + return Result.DefaultForceIndent; + } + return Result.Continue; + } + + /** + * Checks that the text context is in responsibility of the handler. + * + * @param file target PSI file + * @param caretOffset target caret offset + * @return true, if handler is in charge + */ + public boolean isApplicable(@NotNull PsiFile file, int caretOffset) { + return true; + } + + /** + * Calculates the maximum number of '}' that can be inserted by handler. + * Can return 0 or less in custom implementation to skip '}' insertion in the preprocessEnter call + * and switch to default implementation. + * + * @param file target PSI file + * @param editor target editor + * @param caretOffset target caret offset + * @return maximum number of '}' that can be inserted by handler, 0 or less to switch to default implementation + */ + protected int getMaxRBraceCount(@NotNull final PsiFile file, @NotNull final Editor editor, int caretOffset) { + if (!CodeInsightSettings.getInstance().INSERT_BRACE_ON_ENTER) { + return 0; + } + return Math.max(0, getUnmatchedLBracesNumberBefore(editor, caretOffset, file.getFileType())); + } + + /** + * Calculates the precise number of '}' that have to be inserted by handler. + * + * @param editor target editor + * @param caretOffset target caret offset + * @param maxRBraceCount the maximum number of '}' for insert at position, it always positive + * @return number of '}' that has to be inserted by handler, it has to positive + */ + protected int adjustRBraceCountForPosition(@NotNull final Editor editor, int caretOffset, int maxRBraceCount) { + assert maxRBraceCount > 0; + + CharSequence text = editor.getDocument().getCharsSequence(); + int bracesToInsert = 0; + for (int i = caretOffset - 1; i >= 0 && bracesToInsert < maxRBraceCount; --i) { + final char c = text.charAt(i); + if (c == '{') { + ++bracesToInsert; + } + else if (isStopChar(c)) { + break; } } - offset = Math.min(offset, document.getTextLength()); + return Math.max(bracesToInsert, 1); + } + /** + * Checks the character before the inserted '}' to reduce the count of inserted '}'. + * The number of inserted '}' will increase for each found '{'. + * + * @param c character to check + * @return true, to stop back iteration + */ + protected boolean isStopChar(char c) { + return " \n\t".indexOf(c) < 0; + } + + /** + * Calculates the position for insertion of one or more '}'. + * + * @param file target PSI file + * @param editor target editor + * @param caretOffset target caret offset + * @return the position between caretOffset and the end of file + */ + protected int getRBraceOffset(@NotNull final PsiFile file, @NotNull final Editor editor, int caretOffset) { + CharSequence text = editor.getDocument().getCharsSequence(); + int offset = CharArrayUtil.shiftForward(text, caretOffset, " \t"); + final int fileLength = text.length(); + if (offset < fileLength && ")];,%rBracesCount of '}' at the rBracesInsertOffset position and formats the code block. + * + * @param file target PSI file + * @param editor target editor + * @param caretOffset target caret offset + * @param rBracesInsertOffset target position to insert + * @param rBracesCount count of '}' to insert + */ + protected void insertRBraces(@NotNull PsiFile file, + @NotNull Editor editor, + int caretOffset, + int rBracesInsertOffset, + int rBracesCount) { + final Document document = editor.getDocument(); + insertRBracesAtPosition(document, caretOffset, rBracesInsertOffset, rBracesCount); + formatCodeFragmentBetweenBraces(file, document, caretOffset, rBracesInsertOffset, rBracesCount); + } + + /** + * Inserts the rBracesCount of '}' at the rBracesInsertOffset position. + * + * @param document target document + * @param caretOffset target caret offset + * @param rBracesInsertOffset target position to insert + * @param rBracesCount count of '}' to insert + */ + protected void insertRBracesAtPosition(Document document, int caretOffset, int rBracesInsertOffset, int rBracesCount) { + document.insertString(rBracesInsertOffset, "\n" + StringUtil.repeatSymbol('}', rBracesCount)); // We need to adjust indents of the text that will be moved, hence, need to insert preliminary line feed. // Example: // if (test1()) { @@ -85,43 +200,47 @@ public class EnterAfterUnmatchedBraceHandler extends EnterHandlerDelegateAdapter // That is formatted incorrectly because line feed between 'else' and 'if' is not inserted yet (whole 'if' block is indent anchor // to 'if' code block('{}')). So, we insert temporary line feed between 'if' and 'else', correct indent and remove that temporary // line feed. - int bracesToInsert = 0; - outer: - for (int i = caretOffset - 1; unmatchedLBracesNumber > 0 && i >= 0 && bracesToInsert < unmatchedLBracesNumber; i--) { - char c = text.charAt(i); - switch (c) { - case ' ': - case '\n': - case '\t': - continue; - case '{': bracesToInsert++; break; - default: break outer; - } - } - bracesToInsert = Math.max(bracesToInsert, 1); - document.insertString(offset, "\n" + StringUtil.repeatSymbol('}', bracesToInsert)); document.insertString(caretOffset, "\n"); - PsiDocumentManager.getInstance(project).commitDocument(document); + } + + /** + * Formats the code block between caret and inserted braces. + * + * @param file target PSI file + * @param document target document + * @param caretOffset target caret offset + * @param rBracesInsertOffset target position to insert + * @param rBracesCount count of '}' to insert + */ + protected void formatCodeFragmentBetweenBraces(@NotNull PsiFile file, + @NotNull Document document, + int caretOffset, + int rBracesInsertOffset, + int rBracesCount) { + Project project = file.getProject(); long stamp = document.getModificationStamp(); boolean closingBraceIndentAdjusted; try { - CodeStyleManager.getInstance(project).adjustLineIndent(file, new TextRange(caretOffset, offset + 2)); + PsiDocumentManager.getInstance(project).commitDocument(document); + CodeStyleManager.getInstance(project).adjustLineIndent(file, new TextRange(caretOffset, rBracesInsertOffset + 2)); } catch (IncorrectOperationException e) { LOG.error(e); } finally { closingBraceIndentAdjusted = stamp != document.getModificationStamp(); + // do you remember that we insert the '\n'? here we take it back! document.deleteString(caretOffset, caretOffset + 1); } // There is a possible case that formatter was unable to adjust line indent for the closing brace (that is the case for plain text // document for example). Hence, we're trying to do the manually. if (!closingBraceIndentAdjusted) { - int line = document.getLineNumber(offset); + int line = document.getLineNumber(rBracesInsertOffset); StringBuilder buffer = new StringBuilder(); int start = document.getLineStartOffset(line); int end = document.getLineEndOffset(line); + final CharSequence text = document.getCharsSequence(); for (int i = start; i < end; i++) { char c = text.charAt(i); if (c != ' ' && c != '\t') { @@ -132,18 +251,16 @@ public class EnterAfterUnmatchedBraceHandler extends EnterHandlerDelegateAdapter } } if (buffer.length() > 0) { - document.insertString(offset + 1, buffer); + document.insertString(rBracesInsertOffset + 1, buffer); } } - - return Result.DefaultForceIndent; } /** * Current handler inserts closing curly brace (right brace) if necessary. There is a possible case that it should be located * more than one line forward. *

- * Example + * Example *

    *     if (test1()) {
    *     } else { if (test2()) {
@@ -165,17 +282,17 @@ public class EnterAfterUnmatchedBraceHandler extends EnterHandlerDelegateAdapter
    * 

* In essence it inspects PSI structure and finds PSE elements with the max length that starts at caret offset. End offset * of that element is used as an insertion point. - * - * @param file target PSI file - * @param text text from the given file - * @param offset target offset where line feed will be inserted - * @return offset to use for inserting closing brace + * + * @param file target PSI file + * @param text text from the given file + * @param offset target offset where line feed will be inserted + * @return pair of (element, offset). The element is the '}' owner, if applicable; the offset is the position for inserting closing brace */ - protected int calculateOffsetToInsertClosingBrace(PsiFile file, CharSequence text, final int offset) { + protected Pair calculateOffsetToInsertClosingBrace(@NotNull PsiFile file, @NotNull CharSequence text, final int offset) { PsiElement element = PsiUtilCore.getElementAtOffset(file, offset); ASTNode node = element.getNode(); if (node != null && node.getElementType() == TokenType.WHITE_SPACE) { - return CharArrayUtil.shiftForwardUntil(text, offset, "\n"); + return Pair.create(null, CharArrayUtil.shiftForwardUntil(text, offset, "\n")); } for (PsiElement parent = element.getParent(); parent != null; parent = parent.getParent()) { ASTNode parentNode = parent.getNode(); @@ -185,26 +302,24 @@ public class EnterAfterUnmatchedBraceHandler extends EnterHandlerDelegateAdapter element = parent; } if (element.getTextOffset() != offset) { - return CharArrayUtil.shiftForwardUntil(text, offset, "\n"); - } - else { - return element.getTextRange().getEndOffset(); + return Pair.create(null, CharArrayUtil.shiftForwardUntil(text, offset, "\n")); } + return Pair.create(element, element.getTextRange().getEndOffset()); } - + public static boolean isAfterUnmatchedLBrace(Editor editor, int offset, FileType fileType) { return getUnmatchedLBracesNumberBefore(editor, offset, fileType) > 0; } /** * Calculates number of unmatched left braces before the given offset. - * - * @param editor target editor - * @param offset target offset - * @param fileType target file type - * @return number of unmatched braces before the given offset; - * negative value if it's not possible to perform the calculation or if there are no unmatched left braces before - * the given offset + * + * @param editor target editor + * @param offset target offset + * @param fileType target file type + * @return number of unmatched braces before the given offset; + * negative value if it's not possible to perform the calculation or if there are no unmatched left braces before + * the given offset */ protected static int getUnmatchedLBracesNumberBefore(Editor editor, int offset, FileType fileType) { if (offset == 0) { @@ -237,7 +352,7 @@ public class EnterAfterUnmatchedBraceHandler extends EnterHandlerDelegateAdapter } boolean beforeOffset = iterator.getStart() < offset; - + if (braceMatcher.isLBraceToken(iterator, chars, fileType)) { if (beforeOffset) { lBracesBeforeOffset++; @@ -255,7 +370,7 @@ public class EnterAfterUnmatchedBraceHandler extends EnterHandlerDelegateAdapter } } } - + return lBracesBeforeOffset - rBracesBeforeOffset - (rBracesAfterOffset - lBracesAfterOffset); } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/QuickEditHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/QuickEditHandler.java index 556b821b3569..54b85bbaab06 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/QuickEditHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/QuickEditHandler.java @@ -96,7 +96,6 @@ public class QuickEditHandler extends DocumentAdapter implements Disposable { private EditorWindow mySplittedWindow; private boolean myCommittingToOriginal; - @Nullable private final PsiFile myInjectedFile; private final List> myMarkers = ContainerUtil.newLinkedList(); @@ -199,7 +198,9 @@ public class QuickEditHandler extends DocumentAdapter implements Disposable { } public boolean isValid() { - boolean valid = myNewVirtualFile.isValid() && (myAltFullRange == null && myInjectedFile.isValid() || myAltFullRange.isValid()); + boolean valid = myNewVirtualFile.isValid() && + (myAltFullRange == null && myInjectedFile.isValid() || + myAltFullRange != null && myAltFullRange.isValid()); if (valid) { for (Trinity t : myMarkers) { if (!t.first.isValid() || !t.second.isValid() || t.third.getElement() == null) { diff --git a/platform/lang-impl/src/com/intellij/execution/ExecutorRegistryImpl.java b/platform/lang-impl/src/com/intellij/execution/ExecutorRegistryImpl.java index 2e1bf628e5bf..e479c5c94a25 100644 --- a/platform/lang-impl/src/com/intellij/execution/ExecutorRegistryImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/ExecutorRegistryImpl.java @@ -47,7 +47,7 @@ public class ExecutorRegistryImpl extends ExecutorRegistry { private static final Logger LOG = Logger.getInstance(ExecutorRegistryImpl.class); @NonNls public static final String RUNNERS_GROUP = "RunnerActions"; - @NonNls public static final String RUN_CONTEXT_GROUP = "RunContextGroup"; + @NonNls public static final String RUN_CONTEXT_GROUP = "RunContextGroupInner"; private List myExecutors = new ArrayList(); private ActionManager myActionManager; diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java b/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java index 63a11eda9f76..ad841c1d1c98 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java @@ -574,7 +574,10 @@ public class ExecutionManagerImpl extends ExecutionManager implements Disposable myProject.getMessageBus().syncPublisher(EXECUTION_TOPIC).processTerminated(myProfile, myProcessHandler); - SaveAndSyncHandler.getInstance().scheduleRefresh(); + SaveAndSyncHandler saveAndSyncHandler = SaveAndSyncHandler.getInstance(); + if (saveAndSyncHandler != null) { + saveAndSyncHandler.scheduleRefresh(); + } } @Override diff --git a/platform/lang-impl/src/com/intellij/formatting/CoreFormatterUtil.java b/platform/lang-impl/src/com/intellij/formatting/CoreFormatterUtil.java index b9b8bbf3eb83..1af833a35194 100644 --- a/platform/lang-impl/src/com/intellij/formatting/CoreFormatterUtil.java +++ b/platform/lang-impl/src/com/intellij/formatting/CoreFormatterUtil.java @@ -248,7 +248,7 @@ public class CoreFormatterUtil { } if (indent.getType() == Indent.Type.LABEL) return new IndentData(options.LABEL_INDENT_SIZE); if (indent.getType() == Indent.Type.NONE) return new IndentData(0); - if (indent.getType() == Indent.Type.SPACES) return new IndentData(0, indent.getSpaces()); + if (indent.getType() == Indent.Type.SPACES) return new IndentData(indent.getSpaces(), 0); return new IndentData(options.INDENT_SIZE); } diff --git a/platform/lang-impl/src/com/intellij/ide/actions/CreateDirectoryOrPackageHandler.java b/platform/lang-impl/src/com/intellij/ide/actions/CreateDirectoryOrPackageHandler.java index ec89d0930e1b..ff616e497f5c 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/CreateDirectoryOrPackageHandler.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/CreateDirectoryOrPackageHandler.java @@ -207,15 +207,13 @@ public class CreateDirectoryOrPackageHandler implements InputValidatorEx { final Runnable run = new Runnable() { @Override public void run() { - LocalHistoryAction action = LocalHistoryAction.NULL; + String dirPath = myDirectory.getVirtualFile().getPresentableUrl(); + String actionName = IdeBundle.message("progress.creating.directory", dirPath, File.separator, subDirName); + LocalHistoryAction action = LocalHistory.getInstance().startAction(actionName); try { - String actionName; - String dirPath = myDirectory.getVirtualFile().getPresentableUrl(); - actionName = IdeBundle.message("progress.creating.directory", dirPath, File.separator, subDirName); - action = LocalHistory.getInstance().startAction(actionName); - if (createFile) { - myCreatedElement = myDirectory.createFile(subDirName); + CreateFileAction.MkDirs mkdirs = new CreateFileAction.MkDirs(subDirName, myDirectory); + myCreatedElement = mkdirs.directory.createFile(mkdirs.newName); } else { createDirectories(subDirName); } diff --git a/platform/lang-impl/src/com/intellij/ide/extensionResources/ExtensionsRootType.java b/platform/lang-impl/src/com/intellij/ide/extensionResources/ExtensionsRootType.java index ffa1d04199f4..7f8fa57f153e 100644 --- a/platform/lang-impl/src/com/intellij/ide/extensionResources/ExtensionsRootType.java +++ b/platform/lang-impl/src/com/intellij/ide/extensionResources/ExtensionsRootType.java @@ -56,11 +56,11 @@ import java.util.Set; *

Bundled resources are updated automatically upon plugin version change. For bundled plugins, application version is used.

*/ public class ExtensionsRootType extends RootType { - public static final String EXTENSIONS_PATH = "extensions"; - public static final String BACKUP_FILE_EXTENSION = "old"; - static final Logger LOG = Logger.getInstance(ExtensionsRootType.class); + private static final String HASH_ALGORITHM = "MD5"; + private static final String EXTENSIONS_PATH = "extensions"; + private static final String BACKUP_FILE_EXTENSION = "old"; ExtensionsRootType() { super(EXTENSIONS_PATH, "Extensions"); @@ -129,6 +129,11 @@ public class ExtensionsRootType extends RootType { return super.substituteName(project, file); } + public boolean isBackupFile(@NotNull VirtualFile file) { + String extension = file.getExtension(); + return !file.isDirectory() && extension != null && extension.startsWith(BACKUP_FILE_EXTENSION); + } + @Nullable String getPath(@Nullable VirtualFile resource) { VirtualFile pluginResourcesDir = getPluginResourcesDirectoryFor(resource); diff --git a/platform/lang-impl/src/com/intellij/ide/script/IdeStartupScripts.java b/platform/lang-impl/src/com/intellij/ide/script/IdeStartupScripts.java index 086a9a23f48c..b8cecbe387c0 100644 --- a/platform/lang-impl/src/com/intellij/ide/script/IdeStartupScripts.java +++ b/platform/lang-impl/src/com/intellij/ide/script/IdeStartupScripts.java @@ -17,6 +17,7 @@ package com.intellij.ide.script; import com.intellij.ide.extensionResources.ExtensionsRootType; import com.intellij.ide.plugins.PluginManagerCore; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ApplicationComponent; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.PluginId; @@ -51,12 +52,15 @@ import java.util.concurrent.atomic.AtomicBoolean; class IdeStartupScripts extends ApplicationComponent.Adapter { @SuppressWarnings("FieldCanBeLocal") private static String SCRIPT_DIR_NAME = "startup"; - private static String README_FILE_NAME = "readme.txt"; private static Logger LOG = Logger.getInstance(IdeStartupScripts.class); @Override public void initComponent() { + if (ApplicationManager.getApplication().isUnitTestMode()) { + return; + } + List scripts = getScripts(); if (scripts.isEmpty()) { LOG.debug("No startup scripts detected"); @@ -170,9 +174,10 @@ class IdeStartupScripts extends ApplicationComponent.Adapter { } scripts = ContainerUtil.filter(scripts, new Condition() { + private final ExtensionsRootType myExtensionsRootType = ExtensionsRootType.getInstance(); @Override public boolean value(VirtualFile file) { - return !file.isDirectory() && !README_FILE_NAME.equals(file.getName()); + return !file.isDirectory() && !myExtensionsRootType.isBackupFile(file); } }); diff --git a/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleImpl.java b/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleImpl.java index d2a3076dcb3e..c3530848b03f 100644 --- a/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleImpl.java @@ -22,7 +22,7 @@ import com.intellij.openapi.application.impl.ApplicationInfoImpl; import com.intellij.openapi.components.*; import com.intellij.openapi.components.impl.ModuleServiceManagerImpl; import com.intellij.openapi.components.impl.PlatformComponentManagerImpl; -import com.intellij.openapi.components.impl.stores.FileBasedStorage; +import com.intellij.openapi.components.impl.stores.FileStorage; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.AreaInstance; import com.intellij.openapi.extensions.ExtensionPointName; @@ -83,8 +83,8 @@ public class ModuleImpl extends PlatformComponentManagerImpl implements ModuleEx } @NotNull - private static FileBasedStorage getMainStorage(@NotNull Module module) { - return (FileBasedStorage)ComponentsPackage.getStateStore(module).getStateStorageManager().getStateStorage(StoragePathMacros.MODULE_FILE, RoamingType.PER_USER); + private static FileStorage getMainStorage(@NotNull Module module) { + return (FileStorage)ComponentsPackage.getStateStore(module).getStateStorageManager().getStateStorage(StoragePathMacros.MODULE_FILE, RoamingType.PER_USER); } @Override @@ -150,7 +150,7 @@ public class ModuleImpl extends PlatformComponentManagerImpl implements ModuleEx @Override @NotNull public String getModuleFilePath() { - return getMainStorage(this).getFilePath(); + return ComponentsPackage.getStateStore(this).getStateStorageManager().expandMacros(StoragePathMacros.MODULE_FILE); } @Override diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/PushedFilePropertiesUpdaterImpl.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/PushedFilePropertiesUpdaterImpl.java index 4f27cb072c8d..804bf10d41ea 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/PushedFilePropertiesUpdaterImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/PushedFilePropertiesUpdaterImpl.java @@ -29,10 +29,7 @@ import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.util.ProgressWrapper; -import com.intellij.openapi.project.DumbModeTask; -import com.intellij.openapi.project.DumbService; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.project.ProjectManager; +import com.intellij.openapi.project.*; import com.intellij.openapi.roots.*; import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.util.Computable; @@ -114,7 +111,7 @@ public class PushedFilePropertiesUpdaterImpl extends PushedFilePropertiesUpdater // avoid dumb mode for just one file doPushRecursively(file, pushers, ProjectRootManager.getInstance(myProject).getFileIndex()); } - else { + else if (!ProjectCoreUtil.isProjectOrWorkspaceFile(file)) { ContainerUtil.addIfNotNull(delayedTasks, createRecursivePushTask(file, pushers)); } } else if (event instanceof VFileMoveEvent) { diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/storage/ClasspathStorage.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/storage/ClasspathStorage.java index ca5e1cfa4754..d12449f476fe 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/storage/ClasspathStorage.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/storage/ClasspathStorage.java @@ -17,8 +17,11 @@ package com.intellij.openapi.roots.impl.storage; import com.intellij.application.options.PathMacrosCollector; import com.intellij.ide.highlighter.ModuleFileType; +import com.intellij.openapi.application.AccessToken; +import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.components.ComponentManager; import com.intellij.openapi.components.StateStorage; +import com.intellij.openapi.components.TrackingPathMacroSubstitutor; import com.intellij.openapi.components.impl.stores.StateStorageBase; import com.intellij.openapi.components.impl.stores.StateStorageManager; import com.intellij.openapi.components.impl.stores.StorageDataBase; @@ -57,8 +60,10 @@ public class ClasspathStorage extends StateStorageBase componentNames) { // if some file changed, so, changed componentNames.add("NewModuleRootManager"); - if (myStorageData != null) { - myStorageData.loaded = false; + MyStorageData storageData = storageDataRef.get(); + if (storageData != null) { + storageData.loaded = false; } } diff --git a/platform/lang-impl/src/com/intellij/psi/codeStyle/autodetect/DetectedIndentOptionsNotificationProvider.java b/platform/lang-impl/src/com/intellij/psi/codeStyle/autodetect/DetectedIndentOptionsNotificationProvider.java index f8a8915f3159..a1e7a3788ceb 100644 --- a/platform/lang-impl/src/com/intellij/psi/codeStyle/autodetect/DetectedIndentOptionsNotificationProvider.java +++ b/platform/lang-impl/src/com/intellij/psi/codeStyle/autodetect/DetectedIndentOptionsNotificationProvider.java @@ -116,12 +116,14 @@ public class DetectedIndentOptionsNotificationProvider extends EditorNotificatio if (!ApplicationManager.getApplication().isHeadlessEnvironment() || ApplicationManager.getApplication().isUnitTestMode() && myShowNotificationInTest) { - FileEditor fileEditor = FileEditorManager.getInstance(file.getProject()).getSelectedEditor(vFile); + Project project = file.getProject(); + FileEditorManager fileEditorManager = FileEditorManager.getInstance(project); + FileEditor fileEditor = fileEditorManager.getSelectedEditor(vFile); if (fileEditor != null) { Boolean notifiedFlag = fileEditor.getUserData(NOTIFIED_FLAG); if (notifiedFlag == null || enforce) { fileEditor.putUserData(NOTIFIED_FLAG, Boolean.TRUE); - EditorNotifications.getInstance(file.getProject()).updateNotifications(vFile); + EditorNotifications.getInstance(project).updateNotifications(vFile); } } } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java index f69bd2974ae4..198d50920a5c 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java @@ -79,7 +79,7 @@ public class PsiDocumentManagerImpl extends PsiDocumentManagerBase implements Se busConnection.subscribe(DocumentBulkUpdateListener.TOPIC, new DocumentBulkUpdateListener.Adapter() { @Override public void updateFinished(@NotNull Document doc) { - documentCommitThread.queueCommit(project, doc, "Bulk update finished"); + documentCommitThread.queueCommit(project, doc, "Bulk update finished", ApplicationManager.getApplication().getCurrentModalityState()); } }); } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/file/impl/PsiVFSListener.java b/platform/lang-impl/src/com/intellij/psi/impl/file/impl/PsiVFSListener.java index 91fc23c52f69..5a0fc06a21cb 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/file/impl/PsiVFSListener.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/file/impl/PsiVFSListener.java @@ -42,7 +42,6 @@ import com.intellij.psi.*; import com.intellij.psi.impl.PsiManagerImpl; import com.intellij.psi.impl.PsiTreeChangeEventImpl; import com.intellij.psi.impl.smartPointers.SmartPointerManagerImpl; -import com.intellij.psi.impl.source.PsiFileImpl; import com.intellij.util.FileContentUtilCore; import com.intellij.util.messages.MessageBusConnection; import org.jetbrains.annotations.NotNull; @@ -365,7 +364,7 @@ public class PsiVFSListener extends VirtualFileAdapter { } } - ((SmartPointerManagerImpl)SmartPointerManager.getInstance(myManager.getProject())).fastenBelts(vFile, 0, null); + ((SmartPointerManagerImpl)SmartPointerManager.getInstance(myManager.getProject())).fastenBelts(vFile); ApplicationManager.getApplication().runWriteAction( new ExternalChangeAction() { @Override diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedFileViewProvider.java b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedFileViewProvider.java index ff7fa6c865ed..1dec8e119186 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedFileViewProvider.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedFileViewProvider.java @@ -22,7 +22,6 @@ import com.intellij.injected.editor.VirtualFileWindow; import com.intellij.lang.Language; import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Ref; @@ -32,8 +31,6 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.impl.FreeThreadedFileViewProvider; -import com.intellij.psi.impl.source.tree.MarkersHolderFileViewProvider; -import com.intellij.util.SmartList; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -42,8 +39,7 @@ import java.util.List; /** * @author cdr */ -public class InjectedFileViewProvider extends SingleRootFileViewProvider implements FreeThreadedFileViewProvider, - MarkersHolderFileViewProvider { +public class InjectedFileViewProvider extends SingleRootFileViewProvider implements FreeThreadedFileViewProvider { private Project myProject; private final Object myLock = new Object(); private final DocumentWindowImpl myDocumentWindow; @@ -205,16 +201,4 @@ public class InjectedFileViewProvider extends SingleRootFileViewProvider impleme myPatchingLeaves = patchingLeaves; } - @Override - @NotNull - public RangeMarker[] getCachedMarkers() { - List markers = new SmartList(); - for (PsiLanguageInjectionHost.Shred shred : myDocumentWindow.getShreds()) { - RangeMarker marker = (RangeMarker)shred.getHostRangeMarker(); - if (marker != null) { - markers.add(marker); - } - } - return markers.toArray(new RangeMarker[markers.size()]); - } } diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/ComboBoxAction.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/ComboBoxAction.java index 7ebe3c5951e2..344e67dddb4c 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/ComboBoxAction.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/ComboBoxAction.java @@ -65,7 +65,7 @@ public abstract class ComboBoxAction extends AnAction implements CustomComponent JRootPane rootPane = UIUtil.getParentOfType(JRootPane.class, contextComponent); if (rootPane != null) { button = (ComboBoxButton) - JBSwingUtilities.uiTraverser().bfsTraversal(rootPane).filter(new Condition() { + JBSwingUtilities.uiTraverser().withRoot(rootPane).bfsTraversal().filter(new Condition() { @Override public boolean value(Component component) { return component instanceof ComboBoxButton && ((ComboBoxButton)component).getMyAction() == ComboBoxAction.this; diff --git a/platform/platform-impl/src/com/intellij/ide/actions/ToggleToolbarAction.java b/platform/platform-impl/src/com/intellij/ide/actions/ToggleToolbarAction.java index 33168df48d50..1baf2a13af62 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/ToggleToolbarAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/ToggleToolbarAction.java @@ -107,7 +107,7 @@ public class ToggleToolbarAction extends ToggleAction implements DumbAware { @NotNull private static Iterable iterateToolbars(JComponent root) { - return JBSwingUtilities.uiTraverser().preOrderDfsTraversal(root).filter(ActionToolbar.class); + return JBSwingUtilities.uiTraverser().withRoot(root).preOrderDfsTraversal().filter(ActionToolbar.class); } private static class OptionsGroup extends ActionGroup implements DumbAware { diff --git a/platform/platform-impl/src/com/intellij/ide/ui/AppearanceConfigurable.java b/platform/platform-impl/src/com/intellij/ide/ui/AppearanceConfigurable.java index bbb103d6c9e4..17cf08ba0fab 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/AppearanceConfigurable.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/AppearanceConfigurable.java @@ -34,8 +34,6 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; import java.util.Dictionary; import java.util.Hashtable; @@ -199,6 +197,13 @@ public class AppearanceConfigurable extends BaseConfigurable implements Searchab update |= settings.NAVIGATE_TO_PREVIEW != (myComponent.myNavigateToPreviewCheckBox.isVisible() && myComponent.myNavigateToPreviewCheckBox.isSelected()); settings.NAVIGATE_TO_PREVIEW = myComponent.myNavigateToPreviewCheckBox.isSelected(); + ColorBlindness blindness = myComponent.myColorBlindnessPanel.getColorBlindness(); + if (settings.COLOR_BLINDNESS != blindness) { + settings.COLOR_BLINDNESS = blindness; + update = true; + //TODO:SAM: reload + } + update |= settings.DISABLE_MNEMONICS_IN_CONTROLS != myComponent.myDisableMnemonicInControlsCheckBox.isSelected(); settings.DISABLE_MNEMONICS_IN_CONTROLS = myComponent.myDisableMnemonicInControlsCheckBox.isSelected(); @@ -308,6 +313,7 @@ public class AppearanceConfigurable extends BaseConfigurable implements Searchab myComponent.myRightLayoutCheckBox.setSelected(settings.RIGHT_HORIZONTAL_SPLIT); myComponent.myNavigateToPreviewCheckBox.setSelected(settings.NAVIGATE_TO_PREVIEW); myComponent.myNavigateToPreviewCheckBox.setVisible(false);//disabled for a while + myComponent.myColorBlindnessPanel.setColorBlindness(settings.COLOR_BLINDNESS); myComponent.myDisableMnemonicInControlsCheckBox.setSelected(settings.DISABLE_MNEMONICS_IN_CONTROLS); boolean alphaModeEnabled = WindowManagerEx.getInstanceEx().isAlphaModeSupported(); @@ -355,6 +361,7 @@ public class AppearanceConfigurable extends BaseConfigurable implements Searchab isModified |= myComponent.myLeftLayoutCheckBox.isSelected() != settings.LEFT_HORIZONTAL_SPLIT; isModified |= myComponent.myRightLayoutCheckBox.isSelected() != settings.RIGHT_HORIZONTAL_SPLIT; isModified |= myComponent.myNavigateToPreviewCheckBox.isSelected() != settings.NAVIGATE_TO_PREVIEW; + isModified |= myComponent.myColorBlindnessPanel.getColorBlindness() != settings.COLOR_BLINDNESS; isModified |= myComponent.myHideIconsInQuickNavigation.isSelected() != settings.SHOW_ICONS_IN_QUICK_NAVIGATION; @@ -429,9 +436,7 @@ public class AppearanceConfigurable extends BaseConfigurable implements Searchab private JSlider myInitialTooltipDelaySlider; private ComboBox myPresentationModeFontSize; private JCheckBox myNavigateToPreviewCheckBox; - private JCheckBox myAllowStatusBar; - private JCheckBox myAllowLineNumbers; - private JCheckBox myAllowAnnotations; + private ColorBlindnessPanel myColorBlindnessPanel; public MyComponent() { myOverrideLAFFonts.addActionListener( new ActionListener() { diff --git a/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form b/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form index 548ce66676e7..2f74192e7759 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form +++ b/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form @@ -3,7 +3,7 @@ - + @@ -16,7 +16,7 @@ - + @@ -226,6 +226,12 @@ + + + + + + diff --git a/platform/platform-impl/src/com/intellij/ide/ui/ColorBlindnessPanel.java b/platform/platform-impl/src/com/intellij/ide/ui/ColorBlindnessPanel.java new file mode 100644 index 000000000000..36acab939e15 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ide/ui/ColorBlindnessPanel.java @@ -0,0 +1,104 @@ +/* + * 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.ide.ui; + +import com.intellij.ide.IdeBundle; +import com.intellij.openapi.ui.ComboBox; +import com.intellij.ui.components.panels.HorizontalLayout; +import com.intellij.util.ui.JBUI; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +/** + * @author Sergey.Malenkov + */ +final class ColorBlindnessPanel extends JPanel implements ChangeListener { + private final JCheckBox myCheckBox = new JCheckBox(); + private final JComboBox myComboBox = new ComboBox(); + + public ColorBlindnessPanel() { + super(new HorizontalLayout(JBUI.scale(10))); + add(HorizontalLayout.LEFT, myCheckBox); + add(HorizontalLayout.LEFT, myComboBox); + myCheckBox.setSelected(false); + myCheckBox.addChangeListener(this); + myCheckBox.setText(IdeBundle.message("checkbox.color.blindness")); + int count = 0; + for (ColorBlindness blindness : ColorBlindness.values()) { + String name = IdeBundle.message("combobox.color.blindness." + blindness.name()); + if (!name.isEmpty()) { + myComboBox.addItem(new Item(blindness, name)); + count++; + } + } + myComboBox.setEnabled(false); + myComboBox.setVisible(count > 1); + setVisible(count > 0); + } + + @Override + public void stateChanged(ChangeEvent event) { + myComboBox.setEnabled(myCheckBox.isSelected()); + } + + public ColorBlindness getColorBlindness() { + if (myCheckBox.isSelected()) { + Object object = myComboBox.getSelectedItem(); + if (object instanceof Item) { + Item item = (Item)object; + return item.myBlindness; + } + } + return null; + } + + public void setColorBlindness(ColorBlindness blindness) { + Item item = null; + if (blindness != null) { + int count = myComboBox.getItemCount(); + for (int i = 0; i < count && item == null; i++) { + Object object = myComboBox.getItemAt(i); + if (object instanceof Item) { + item = (Item)object; + if (item.myBlindness != blindness) { + item = null; + } + } + } + } + myCheckBox.setSelected(item != null); + if (item != null) { + myComboBox.setSelectedItem(item); + } + } + + private static final class Item { + private final ColorBlindness myBlindness; + private final String myName; + + private Item(ColorBlindness blindness, String name) { + myBlindness = blindness; + myName = name; + } + + @Override + public String toString() { + return myName; + } + } +} diff --git a/platform/platform-impl/src/com/intellij/ide/util/TipUIUtil.java b/platform/platform-impl/src/com/intellij/ide/util/TipUIUtil.java index 7ebf2a57a998..9ff495b63a61 100644 --- a/platform/platform-impl/src/com/intellij/ide/util/TipUIUtil.java +++ b/platform/platform-impl/src/com/intellij/ide/util/TipUIUtil.java @@ -20,10 +20,12 @@ import com.intellij.ide.BrowserUtil; import com.intellij.ide.IdeBundle; import com.intellij.ide.plugins.IdeaPluginDescriptor; import com.intellij.ide.plugins.PluginManagerCore; +import com.intellij.ide.ui.LafManager; import com.intellij.openapi.actionSystem.KeyboardShortcut; import com.intellij.openapi.actionSystem.Shortcut; import com.intellij.openapi.application.ApplicationInfo; import com.intellij.openapi.application.ApplicationNamesInfo; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.PluginDescriptor; import com.intellij.openapi.keymap.Keymap; import com.intellij.openapi.keymap.KeymapManager; @@ -33,7 +35,6 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.ObjectUtils; import com.intellij.util.ResourceUtil; import com.intellij.util.ui.UIUtil; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -53,7 +54,8 @@ import java.net.URL; * @author Konstantin Bulenkov */ public class TipUIUtil { - @NonNls private static final String SHORTCUT_ENTITY = "&shortcut:"; + private static final Logger LOG = Logger.getInstance(TipUIUtil.class); + private static final String SHORTCUT_ENTITY = "&shortcut:"; private TipUIUtil() { } @@ -102,6 +104,14 @@ public class TipUIUtil { if (UIUtil.isUnderDarcula()) { replaced = replaced.replace("css/tips.css", "css/tips_darcula.css"); } + if (browser.getUI() == null) { + browser.updateUI(); + boolean succeed = browser.getUI() != null; + String message = "reinit JEditorPane.ui: " + (succeed ? "OK" : "FAIL") + + ", laf=" + LafManager.getInstance().getCurrentLookAndFeel(); + if (succeed) LOG.warn(message); + else LOG.error(message); + } browser.read(new StringReader(replaced), url); } catch (IOException e) { diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ToolbarUpdater.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ToolbarUpdater.java index 877d502e2815..77aeb2332163 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ToolbarUpdater.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ToolbarUpdater.java @@ -122,7 +122,7 @@ public abstract class ToolbarUpdater implements Activatable { protected abstract void updateActionsImpl(boolean transparentOnly, boolean forced); protected void updateActionTooltips() { - for (ActionButton actionButton : JBSwingUtilities.uiTraverser().preOrderDfsTraversal(myComponent).filter(ActionButton.class)) { + for (ActionButton actionButton : JBSwingUtilities.uiTraverser().withRoot(myComponent).preOrderDfsTraversal().filter(ActionButton.class)) { actionButton.updateToolTipText(); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.java index 10db5de0a74c..1faa0919f336 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/ServiceManagerImpl.java @@ -18,7 +18,6 @@ package com.intellij.openapi.components.impl; import com.intellij.ide.plugins.PluginManager; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.AccessToken; -import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.components.BaseComponent; @@ -289,7 +288,6 @@ public class ServiceManagerImpl implements BaseComponent { } public static boolean isUseReadActionToInitService() { - Application app = ApplicationManager.getApplication(); - return Registry.is("use.read.action.to.init.service", !(app.isUnitTestMode() || app.isInternal())); + return Registry.is("use.read.action.to.init.service", true); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java index e78af518779c..dd18182739d1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java @@ -25,9 +25,9 @@ import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.LineSeparator; +import com.intellij.util.PairConsumer; import com.intellij.util.SystemProperties; import com.intellij.util.containers.SmartHashSet; -import gnu.trove.TObjectObjectProcedure; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -43,8 +43,10 @@ public class DirectoryBasedStorage extends StateStorageBase componentNames) { // todo reload only changed file, compute diff - DirectoryStorageData oldData = myStorageData; + DirectoryStorageData oldData = storageDataRef.get(); DirectoryStorageData newData = loadData(); - myStorageData = newData; + storageDataRef.set(newData); if (oldData == null) { componentNames.addAll(newData.getComponentNames()); } @@ -155,7 +157,7 @@ public class DirectoryBasedStorage extends StateStorageBase() { + copiedStorageData.processComponent(componentName, new PairConsumer() { @Override - public boolean execute(String fileName, Object state) { + public void consume(String fileName, Object state) { if (!dirtyFileNames.contains(fileName)) { - return true; + return; } Element element = null; @@ -192,7 +194,7 @@ public class DirectoryBasedStorage extends StateStorageBase changedComponentNames, boolean deleted) { - if (myRoamingType == RoamingType.DISABLED) { - // storage roaming was changed to DISABLED, but settings repository has old state - return; - } - - try { - Element newElement = deleted ? null : loadDataFromStreamProvider(); - if (newElement == null) { - StorageUtil.deleteFile(myFile, this, myCachedVirtualFile); - // if data was loaded, mark as changed all loaded components - if (myStorageData != null) { - changedComponentNames.addAll(myStorageData.getComponentNames()); - myStorageData = null; - } - } - else if (myStorageData != null) { - StorageData newStorageData = createStorageData(); - loadState(newStorageData, newElement); - changedComponentNames.addAll(myStorageData.getChangedComponentNames(newStorageData, myPathMacroSubstitutor)); - myStorageData = newStorageData; - } - } - catch (Throwable e) { - LOG.error(e); - } - } - - @Override - public String toString() { - return getFilePath(); - } -} diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/tree/MarkersHolderFileViewProvider.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/FileStorage.java similarity index 54% rename from platform/core-impl/src/com/intellij/psi/impl/source/tree/MarkersHolderFileViewProvider.java rename to platform/platform-impl/src/com/intellij/openapi/components/impl/stores/FileStorage.java index 03f7e62fca3a..ce74a828eb32 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/tree/MarkersHolderFileViewProvider.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/FileStorage.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,13 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.psi.impl.source.tree; +package com.intellij.openapi.components.impl.stores; -import com.intellij.openapi.editor.RangeMarker; +import com.intellij.openapi.components.StateStorage; +import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -public interface MarkersHolderFileViewProvider { +import java.io.File; + +public interface FileStorage extends StateStorage { + @Nullable + VirtualFile getVirtualFile(); @NotNull - RangeMarker[] getCachedMarkers(); -} + File getFile(); + + // todo remove + StorageDataBase getStorageData(); + + // todo remove + void setFile(@Nullable VirtualFile file, @Nullable File ioFileIfChanged); +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/SaveSessionBase.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/SaveSessionBase.java index 63e5b3e85b8f..344d589102aa 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/SaveSessionBase.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/SaveSessionBase.java @@ -26,7 +26,7 @@ import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -abstract class SaveSessionBase implements StateStorage.SaveSession, StateStorage.ExternalizationSession, SafeWriteRequestor { +public abstract class SaveSessionBase implements StateStorage.SaveSession, StateStorage.ExternalizationSession, SafeWriteRequestor { private SkipDefaultsSerializationFilter serializationFilter; @SuppressWarnings("deprecation") diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageBase.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageBase.java index 19ebb15813c4..4e212a3b3e8f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageBase.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageBase.java @@ -16,23 +16,19 @@ package com.intellij.openapi.components.impl.stores; import com.intellij.openapi.components.StateStorage; -import com.intellij.openapi.components.TrackingPathMacroSubstitutor; import com.intellij.openapi.diagnostic.Logger; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.concurrent.atomic.AtomicReference; + public abstract class StateStorageBase implements StateStorage { protected static final Logger LOG = Logger.getInstance(StateStorageBase.class); private boolean mySavingDisabled = false; - protected final TrackingPathMacroSubstitutor myPathMacroSubstitutor; - protected T myStorageData; - - protected StateStorageBase(@Nullable TrackingPathMacroSubstitutor trackingPathMacroSubstitutor) { - myPathMacroSubstitutor = trackingPathMacroSubstitutor; - } + protected final AtomicReference storageDataRef = new AtomicReference(); @Override @Nullable @@ -60,12 +56,18 @@ public abstract class StateStorageBase implements Sta @NotNull protected final T getStorageData(boolean reload) { - if (myStorageData != null && !reload) { - return myStorageData; + final T storageData = storageDataRef.get(); + if (storageData != null && !reload) { + return storageData; } - myStorageData = loadData(); - return myStorageData; + T newStorageData = loadData(); + if (storageDataRef.compareAndSet(storageData, newStorageData)) { + return newStorageData; + } + else { + return getStorageData(false); + } } protected abstract T loadData(); diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java index b9f50f389b0e..dc631ec0a3f2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java @@ -38,7 +38,7 @@ public interface StateStorageManager { StateStorage getStateStorage(@NotNull String fileSpec, @NotNull RoamingType roamingType); @NotNull - Couple> getCachedFileStateStorages(@NotNull Collection changed, @NotNull Collection deleted); + Couple> getCachedFileStateStorages(@NotNull Collection changed, @NotNull Collection deleted); /** * Rename file diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java index 77a753d80ded..428783737fb8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java @@ -349,15 +349,6 @@ public class StorageUtil { } } - /** - * You must call {@link StreamProvider#isApplicable(String, com.intellij.openapi.components.RoamingType)} before - */ - public static void sendContent(@NotNull StreamProvider provider, @NotNull String fileSpec, @NotNull Element element, @NotNull RoamingType type) throws IOException { - // we should use standard line-separator (\n) - stream provider can share file content on any OS - BufferExposingByteArrayOutputStream content = writeToBytes(element, "\n"); - provider.saveContent(fileSpec, content.getInternalBuffer(), content.size(), type); - } - public static boolean isProjectOrModuleFile(@NotNull String fileSpec) { return StoragePathMacros.PROJECT_FILE.equals(fileSpec) || fileSpec.startsWith(StoragePathMacros.PROJECT_CONFIG_DIR) || fileSpec.equals(StoragePathMacros.MODULE_FILE); } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.kt b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.kt index 9482b774c358..6767af9599cb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.kt +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.kt @@ -23,16 +23,11 @@ public interface StreamProvider { public open val enabled: Boolean get() = true - /** - * fileSpec Only main fileSpec, not version - */ public open fun isApplicable(fileSpec: String, roamingType: RoamingType): Boolean = true /** * @param fileSpec - * * * @param content bytes of content, size of array is not actual size of data, you must use `size` - * * * @param size actual size of data */ public fun saveContent(fileSpec: String, content: ByteArray, size: Int, roamingType: RoamingType) diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java deleted file mode 100644 index 52fa97fea64a..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java +++ /dev/null @@ -1,229 +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.openapi.components.impl.stores; - -import com.intellij.openapi.components.RoamingType; -import com.intellij.openapi.components.TrackingPathMacroSubstitutor; -import com.intellij.openapi.util.JDOMUtil; -import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; -import com.intellij.util.containers.ContainerUtil; -import gnu.trove.THashMap; -import org.jdom.Element; -import org.jdom.JDOMException; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.IOException; -import java.util.Map; -import java.util.Set; - -public abstract class XmlElementStorage extends StateStorageBase { - @NotNull protected final String myRootElementName; - protected final StreamProvider myStreamProvider; - protected final String myFileSpec; - protected boolean myBlockSavingTheContent = false; - - protected final RoamingType myRoamingType; - - protected XmlElementStorage(@NotNull String fileSpec, - @Nullable RoamingType roamingType, - @Nullable TrackingPathMacroSubstitutor pathMacroSubstitutor, - @NotNull String rootElementName, - @Nullable StreamProvider streamProvider) { - super(pathMacroSubstitutor); - - myFileSpec = fileSpec; - myRoamingType = roamingType == null ? RoamingType.PER_USER : roamingType; - myRootElementName = rootElementName; - myStreamProvider = myRoamingType == RoamingType.DISABLED ? null : streamProvider; - } - - @Nullable - protected abstract Element loadLocalData(); - - @Nullable - @Override - protected Element getStateAndArchive(@NotNull StorageData storageData, Object component, @NotNull String componentName) { - return storageData.getStateAndArchive(componentName); - } - - @Override - @NotNull - protected StorageData loadData() { - StorageData result = createStorageData(); - Element element; - // we don't use local data if has stream provider - if (myStreamProvider != null && myStreamProvider.getEnabled()) { - try { - element = loadDataFromStreamProvider(); - if (element != null) { - loadState(result, element); - } - } - catch (Exception e) { - LOG.error(e); - element = null; - } - } - else { - element = loadLocalData(); - } - - if (element != null) { - loadState(result, element); - } - return result; - } - - @Nullable - protected final Element loadDataFromStreamProvider() throws IOException, JDOMException { - assert myStreamProvider != null; - return JDOMUtil.load(myStreamProvider.loadContent(myFileSpec, myRoamingType)); - } - - protected final void loadState(@NotNull StorageData result, @NotNull Element element) { - result.load(element, myPathMacroSubstitutor, true); - } - - @NotNull - protected StorageData createStorageData() { - return new StorageData(myRootElementName); - } - - public void setDefaultState(@NotNull Element element) { - myStorageData = createStorageData(); - loadState(myStorageData, element); - } - - @Override - @Nullable - public final XmlElementStorageSaveSession startExternalization() { - return checkIsSavingDisabled() ? null : createSaveSession(getStorageData()); - } - - @NotNull - protected abstract XmlElementStorageSaveSession createSaveSession(@NotNull StorageData storageData); - - @Nullable - protected final Element getElement(@NotNull StorageData data, @NotNull Map newLiveStates) throws IOException { - Element element = data.save(newLiveStates); - if (element == null || JDOMUtil.isEmpty(element)) { - return null; - } - - if (myPathMacroSubstitutor != null) { - try { - myPathMacroSubstitutor.collapsePaths(element); - } - finally { - myPathMacroSubstitutor.reset(); - } - } - - return element; - } - - @Override - public void analyzeExternalChangesAndUpdateIfNeed(@NotNull Set componentNames) { - StorageData oldData = myStorageData; - StorageData newData = getStorageData(true); - if (oldData == null) { - if (LOG.isDebugEnabled()) { - LOG.debug("analyzeExternalChangesAndUpdateIfNeed: old data null, load new for " + toString()); - } - componentNames.addAll(newData.getComponentNames()); - } - else { - Set changedComponentNames = oldData.getChangedComponentNames(newData, myPathMacroSubstitutor); - if (LOG.isDebugEnabled()) { - LOG.debug("analyzeExternalChangesAndUpdateIfNeed: changedComponentNames + " + changedComponentNames + " for " + toString()); - } - if (!ContainerUtil.isEmpty(changedComponentNames)) { - componentNames.addAll(changedComponentNames); - } - } - } - - public abstract class XmlElementStorageSaveSession extends SaveSessionBase { - private final StorageData myOriginalStorageData; - @Nullable - private StorageData myCopiedStorageData; - - private final Map myNewLiveStates = new THashMap(); - - public XmlElementStorageSaveSession(@NotNull StorageData storageData) { - myOriginalStorageData = storageData; - } - - @Nullable - @Override - public final SaveSession createSaveSession() { - return checkIsSavingDisabled() || (myCopiedStorageData == null && !myOriginalStorageData.isDirty()) ? null : this; - } - - @Override - protected void setSerializedState(@NotNull Object component, @NotNull String componentName, @Nullable Element element) { - if (myCopiedStorageData == null) { - myCopiedStorageData = StorageData.setStateAndCloneIfNeed(componentName, element, myOriginalStorageData, myNewLiveStates); - } - else { - myCopiedStorageData.setState(componentName, element, myNewLiveStates); - } - } - - @Override - public final void save() throws IOException { - if (myBlockSavingTheContent) { - return; - } - - StorageData storageData = myCopiedStorageData; - if (storageData == null) { - storageData = myOriginalStorageData; - if (!storageData.isDirty()) { - LOG.warn("Copied storage data must be not null because original storage data is not dirty"); - } - } - - doSave(getElement(storageData, myNewLiveStates)); - myStorageData = storageData; - } - - protected abstract void doSave(@Nullable Element element) throws IOException; - - protected final void saveForProvider(@Nullable BufferExposingByteArrayOutputStream content, @Nullable Element element) throws IOException { - if (!myStreamProvider.isApplicable(myFileSpec, myRoamingType)) { - return; - } - - if (element == null) { - myStreamProvider.delete(myFileSpec, myRoamingType); - } - else { - doSaveForProvider(element, myRoamingType, content); - } - } - - private void doSaveForProvider(@NotNull Element element, @NotNull RoamingType roamingType, @Nullable BufferExposingByteArrayOutputStream content) throws IOException { - if (content == null) { - StorageUtil.sendContent(myStreamProvider, myFileSpec, element, roamingType); - } - else { - myStreamProvider.saveContent(myFileSpec, content.getInternalBuffer(), content.size(), myRoamingType); - } - } - } -} diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java index 3aa26b6f66e7..e68fddb7745e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java @@ -144,6 +144,8 @@ public class EditorColorsManagerImpl extends EditorColorsManager implements Pers loadAdditionalTextAttributes(); + upgradeSchemesFromPreviousVersion(); + String wizardEditorScheme = WelcomeWizardUtil.getWizardEditorScheme(); EditorColorsScheme scheme = null; if (wizardEditorScheme != null) { @@ -153,6 +155,14 @@ public class EditorColorsManagerImpl extends EditorColorsManager implements Pers setGlobalSchemeInner(scheme == null ? getDefaultScheme() : scheme); } + private void upgradeSchemesFromPreviousVersion() { + for (EditorColorsScheme scheme : mySchemeManager.getAllSchemes()) { + if (scheme instanceof AbstractColorsScheme && !(scheme instanceof ReadOnlyColorsScheme)) { + ((AbstractColorsScheme)scheme).upgradeSchemeFromPreviousVersion(); + } + } + } + public static void schemeChangedOrSwitched() { EditorFactory.getInstance().refreshAllEditors(); // refreshAllEditors is not enough - for example, change "Errors and warnings -> Typo" from green (default) to red diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index ac2f73eba5af..0cbe383b83e5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -44,8 +44,10 @@ import com.intellij.openapi.editor.colors.*; import com.intellij.openapi.editor.colors.impl.DelegateColorScheme; import com.intellij.openapi.editor.event.*; import com.intellij.openapi.editor.ex.*; +import com.intellij.openapi.editor.ex.util.EditorUIUtil; import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.editor.ex.util.EmptyEditorHighlighter; +import com.intellij.openapi.editor.ex.util.LexerEditorHighlighter; import com.intellij.openapi.editor.highlighter.EditorHighlighter; import com.intellij.openapi.editor.highlighter.HighlighterClient; import com.intellij.openapi.editor.impl.event.MarkupModelListener; @@ -318,6 +320,16 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi private boolean myDocumentChangeInProgress; private boolean myErrorStripeNeedsRepaint; + // Characters that excluded from zero-latency painting after key typing + private static final Set KEY_CHARS_TO_SKIP = new HashSet(Arrays.asList('\n', '\t', '(', ')', '[', ']', '{', '}', '"', '\'')); + + // Characters that excluded from zero-latency painting after document update + private static final Set DOCUMENT_CHARS_TO_SKIP = new HashSet(Arrays.asList(')', ']', '}', '"', '\'')); + + private Rectangle myOldArea = new Rectangle(0, 0, 0, 0); + private Rectangle myOldTailArea = new Rectangle(0, 0, 0, 0); + private boolean myEventPaintedAsChar; + static { ourCaretBlinkingCommand.start(); } @@ -1135,6 +1147,12 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi ActionManagerEx actionManager = ActionManagerEx.getInstanceEx(); DataContext dataContext = getDataContext(); + + if (isZeroLatencyTypingEnabled() && myDocument.isWritable() && !isViewer() && canPaintImmediately(c)) { + paintImmediately(myCaretModel.getOffset(), c); + myEventPaintedAsChar = true; + } + actionManager.fireBeforeEditorTyping(c, dataContext); MacUIUtil.hideCursor(); EditorActionManager.getInstance().getTypedAction().actionPerformed(this, c, dataContext); @@ -1881,12 +1899,31 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi if (!mySoftWrapModel.isSoftWrappingEnabled() && !myUseNewRendering) { mySizeContainer.beforeChange(e); } + + if (isZeroLatencyTypingEnabled() && !myEventPaintedAsChar && canPaintImmediately(e)) { + int offset = e.getOffset(); + int length = e.getOldLength(); + + myOldArea = lineRectangleBetween(offset, offset + length); + + myOldTailArea = lineRectangleBetween(offset + length, myDocument.getLineEndOffset(myDocument.getLineNumber(offset))); + if (myOldTailArea.isEmpty()) { + myOldTailArea.width += EditorUtil.getSpaceWidth(Font.PLAIN, this); // include possible caret + } + } } private void changedUpdate(DocumentEvent e) { myDocumentChangeInProgress = false; if (myDocument.isInBulkUpdate()) return; + if (isZeroLatencyTypingEnabled()) { + if (!myEventPaintedAsChar && canPaintImmediately(e)) { + paintImmediately(e); + } + myEventPaintedAsChar = false; + } + if (myErrorStripeNeedsRepaint) { myMarkupModel.repaint(e.getOffset(), e.getOffset() + e.getNewLength()); myErrorStripeNeedsRepaint = false; @@ -2100,6 +2137,146 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi putUserData(BUFFER, image); } + private static boolean isZeroLatencyTypingEnabled() { + return Registry.is("editor.zero.latency.typing"); + } + + private boolean canPaintImmediately(char c) { + return myDocument instanceof DocumentImpl && + myHighlighter instanceof LexerEditorHighlighter && + myDocument.getTextLength() > 0 && + myCaretModel.getCaretCount() == 1 && + !mySelectionModel.hasSelection() && + !KEY_CHARS_TO_SKIP.contains(c); + } + + // Called to display a single character insertion before starting a write action and the general painting routine. + // Bypasses RepaintManager (c.repaint, c.paintComponent) and double buffering (g.paintImmediately) to minimize visual lag. + // TODO Should be replaced with the generic paintImmediately(event) call when we implement typing without starting write actions. + private void paintImmediately(int offset, char c) { + Graphics g = myEditorComponent.getGraphics(); + + if (g == null) return; // editor component is currently not displayable + + TextAttributes attributes = ((LexerEditorHighlighter)myHighlighter).getAttributes((DocumentImpl)myDocument, offset, c); + + int fontType = attributes.getFontType(); + Font font = fontFor(fontType); + + int charWidth = g.getFontMetrics(font).charWidth(c); + + Rectangle tailArea = lineRectangleBetween(offset, myDocument.getLineEndOffset(offsetToLogicalLine(offset))); + if (tailArea.isEmpty()) { + tailArea.width += EditorUtil.getSpaceWidth(fontType, this); // include caret + } + + Color lineColor = getCaretRowBackground(); + + Rectangle newArea = lineRectangleBetween(offset, offset); + newArea.width += charWidth; + + String newText = Character.toString(c); + Point point = newArea.getLocation(); + int ascent = getAscent(); + Color color = attributes.getForegroundColor(); + + EditorUIUtil.setupAntialiasing(g); + + // pre-compute all the arguments beforehand to minimize delays between the calls (as there's no double-buffering) + shift(g, tailArea, charWidth); + fill(g, newArea, lineColor); + print(g, newText, point, ascent, font, color); + } + + private static boolean canPaintImmediately(@NotNull DocumentEvent e) { + return !contains(e.getOldFragment(), '\n') && + !contains(e.getNewFragment(), '\n') && + !(e.getNewLength() == 1 && DOCUMENT_CHARS_TO_SKIP.contains(e.getNewFragment().charAt(0))); + } + + private static boolean contains(@NotNull CharSequence chars, char c) { + for (int i = 0; i < chars.length(); i++) { + if (chars.charAt(i) == c) { + return true; + } + } + return false; + } + + // Called to display insertion / deletion / replacement within a single line before the general painting routine. + // Bypasses RepaintManager (c.repaint, c.paintComponent) and double buffering (g.paintImmediately) to minimize visual lag. + private void paintImmediately(@NotNull DocumentEvent e) { + Graphics g = myEditorComponent.getGraphics(); + + if (g == null) return; // editor component is currently not displayable + + int offset = e.getOffset(); + String newText = e.getNewFragment().toString(); + Rectangle newArea = lineRectangleBetween(offset, offset + newText.length()); + int delta = newArea.width - myOldArea.width; + Color lineColor = getCaretRowBackground(); + + if (delta != 0) { + shift(g, myOldTailArea, delta); + + if (delta < 0) { + Rectangle remainingArea = new Rectangle(myOldTailArea.x + myOldTailArea.width + delta, + myOldTailArea.y, -delta, myOldTailArea.height); + fill(g, remainingArea, lineColor); + } + } + + if (!newArea.isEmpty()) { + TextAttributes attributes = myHighlighter.createIterator(offset).getTextAttributes(); + + Point point = newArea.getLocation(); + int ascent = getAscent(); + Font font = fontFor(attributes.getFontType()); + Color color = attributes.getForegroundColor(); + + EditorUIUtil.setupAntialiasing(g); + + // pre-compute all the arguments beforehand to minimize delay between the calls (as there's no double-buffering) + fill(g, newArea, lineColor); + print(g, newText, point, ascent, font, color); + } + } + + @NotNull + private Rectangle lineRectangleBetween(int begin, int end) { + Point p1 = offsetToXY(begin, false); + Point p2 = offsetToXY(end, false); + LOG.assertTrue(p1.y == p2.y); + return new Rectangle(p1.x, p1.y, p2.x - p1.x, getLineHeight()); + } + + @NotNull + private Color getCaretRowBackground() { + Color color = myScheme.getColor(EditorColors.CARET_ROW_COLOR); + return color == null ? myScheme.getDefaultBackground() : color; + } + + @NotNull + private Font fontFor(int fontType) { + return myScheme.getFont(EditorFontType.values()[fontType]); + } + + private static void shift(@NotNull Graphics g, @NotNull Rectangle r, int delta) { + g.copyArea(r.x, r.y, r.width, r.height, delta, 0); + } + + private static void fill(@NotNull Graphics g, @NotNull Rectangle r, @NotNull Color color) { + g.setColor(color); + g.fillRect(r.x, r.y, r.width, r.height); + } + + private static void print(@NotNull Graphics g, @NotNull String text, @NotNull Point point, + int ascent, @NotNull Font font, @NotNull Color color) { + g.setFont(font); + g.setColor(color); + g.drawString(text, point.x, point.y + ascent); + } + void paint(@NotNull Graphics2D g) { Rectangle clip = g.getClipBounds(); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorComposite.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorComposite.java index 6a343a2049dd..5491f1cdef55 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorComposite.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorComposite.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -171,9 +171,11 @@ public abstract class EditorComposite implements Disposable { }); wrapper.getTabs().getComponent().setBorder(new EmptyBorder(0, 0, 1, 0)); + boolean firstEditor = true; for (FileEditor editor : editors) { - JComponent component = myEditors.length == 1 && editor == myEditors[0] ? (JComponent)myComponent.getComponent(0) : createEditorComponent(editor); + JComponent component = firstEditor && myComponent != null ? (JComponent)myComponent.getComponent(0) : createEditorComponent(editor); wrapper.addTab(editor.getName(), component); + firstEditor = false; } wrapper.addChangeListener(new MyChangeListener()); @@ -458,9 +460,9 @@ public abstract class EditorComposite implements Disposable { void addEditor(@NotNull FileEditor editor) { ApplicationManager.getApplication().assertIsDispatchThread(); - FileEditor[] editors = ArrayUtil.append(myEditors, editor); + myEditors = ArrayUtil.append(myEditors, editor); if (myTabbedPaneWrapper == null) { - myTabbedPaneWrapper = createTabbedPaneWrapper(editors); + myTabbedPaneWrapper = createTabbedPaneWrapper(myEditors); myComponent.setComponent(myTabbedPaneWrapper.getComponent()); } else { @@ -469,7 +471,6 @@ public abstract class EditorComposite implements Disposable { } myFocusWatcher.deinstall(myFocusWatcher.getTopComponent()); myFocusWatcher.install(myComponent); - myEditors = editors; } private static class TopBottomPanel extends JPanel { diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWithProviderComposite.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWithProviderComposite.java index 63677ed684c1..7e6f4d371014 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWithProviderComposite.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWithProviderComposite.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsDialog.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsDialog.java index 17264b5cfc42..915cd2d85031 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsDialog.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsDialog.java @@ -21,8 +21,6 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.help.HelpManager; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurableGroup; -import com.intellij.openapi.project.DumbModePermission; -import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import org.jetbrains.annotations.NonNls; @@ -143,12 +141,7 @@ public class SettingsDialog extends DialogWrapper implements DataProvider { @Override public void doOKAction() { if (myEditor.apply()) { - DumbService.allowStartingDumbModeInside(DumbModePermission.MAY_START_BACKGROUND, new Runnable() { - @Override - public void run() { - ApplicationManager.getApplication().saveAll(); - } - }); + ApplicationManager.getApplication().saveAll(); super.doOKAction(); } } 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 bdbf1b57368a..17117b22af2a 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 @@ -695,8 +695,8 @@ public class ProjectManagerImpl extends ProjectManagerEx implements Disposable { public void saveChangedProjectFile(@NotNull VirtualFile file, @NotNull Project project) { StateStorageManager storageManager = ComponentsPackage.getStateStore(project).getStateStorageManager(); String fileSpec = storageManager.collapseMacros(file.getPath()); - Couple> storages = storageManager.getCachedFileStateStorages(Collections.singletonList(fileSpec), Collections.emptyList()); - FileBasedStorage storage = ContainerUtil.getFirstItem(storages.first); + Couple> storages = storageManager.getCachedFileStateStorages(Collections.singletonList(fileSpec), Collections.emptyList()); + FileStorage storage = ContainerUtil.getFirstItem(storages.first); // if empty, so, storage is not yet loaded, so, we don't have to reload if (storage != null) { registerProjectToReload(project, storage); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeader.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeader.java index ca3547ceea2f..6b64443d3acf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeader.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeader.java @@ -212,7 +212,7 @@ public abstract class ToolWindowHeader extends JPanel implements Disposable, UIS @Override protected void updateActionTooltips() { - for (ActionButton actionButton : JBSwingUtilities.uiTraverser().preOrderDfsTraversal(myButtonPanel).filter(ActionButton.class)) { + for (ActionButton actionButton : JBSwingUtilities.uiTraverser().withRoot(myButtonPanel).preOrderDfsTraversal().filter(ActionButton.class)) { actionButton.updateTooltip(); } } diff --git a/platform/platform-impl/src/com/intellij/ui/mac/MacFileChooserDialogImpl.java b/platform/platform-impl/src/com/intellij/ui/mac/MacFileChooserDialogImpl.java index 623fe0e2ba1e..ba54fd98bab4 100644 --- a/platform/platform-impl/src/com/intellij/ui/mac/MacFileChooserDialogImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/mac/MacFileChooserDialogImpl.java @@ -17,6 +17,7 @@ package com.intellij.ui.mac; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationNamesInfo; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileChooser.FileChooser; import com.intellij.openapi.fileChooser.FileChooserDescriptor; @@ -62,6 +63,7 @@ public class MacFileChooserDialogImpl implements PathChooserDialog { private final FileChooserDescriptor myChooserDescriptor; private final Project myProject; + private ModalityState myModalityState; private Consumer> myCallback; private static final Callback SHOULD_ENABLE_CALLBACK = new Callback() { @@ -160,14 +162,14 @@ public class MacFileChooserDialogImpl implements PathChooserDialog { } }); } - }); + }, impl.myModalityState); } else if (impl.myCallback instanceof FileChooser.FileChooserConsumer) { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { ((FileChooser.FileChooserConsumer)impl.myCallback).cancelled(); } - }); + }, impl.myModalityState); } } finally { @@ -324,6 +326,7 @@ public class MacFileChooserDialogImpl implements PathChooserDialog { ExtensionsInitializer.initialize(); myCallback = callback; + myModalityState = ModalityState.current(); final VirtualFile lastOpenedFile = FileChooserUtil.getLastOpenedFile(myProject); final VirtualFile selectFile = FileChooserUtil.getFileToSelect(myChooserDescriptor, myProject, toSelect, lastOpenedFile); diff --git a/platform/platform-impl/src/com/intellij/util/IJSwingUtilities.java b/platform/platform-impl/src/com/intellij/util/IJSwingUtilities.java index c6b0c044ad8a..7f1750de8701 100644 --- a/platform/platform-impl/src/com/intellij/util/IJSwingUtilities.java +++ b/platform/platform-impl/src/com/intellij/util/IJSwingUtilities.java @@ -231,7 +231,7 @@ public class IJSwingUtilities extends JBSwingUtilities { */ public static void updateComponentTreeUI(@Nullable Component c) { if (c == null) return; - for (Component component : uiTraverser().postOrderDfsTraversal(c)) { + for (Component component : uiTraverser().withRoot(c).postOrderDfsTraversal()) { if (component instanceof JComponent) ((JComponent)component).updateUI(); } c.invalidate(); diff --git a/platform/platform-resources-en/src/messages/DiagnosticBundle.properties b/platform/platform-resources-en/src/messages/DiagnosticBundle.properties index 9b044dc411e7..4dedc81be079 100644 --- a/platform/platform-resources-en/src/messages/DiagnosticBundle.properties +++ b/platform/platform-resources-en/src/messages/DiagnosticBundle.properties @@ -67,7 +67,7 @@ log.monitor.edit.aliases.show.all.checkbox.title=&Show all files coverable by pa log.console.filter.show.errors=errors log.console.filter.show.errors.and.warnings=warnings log.console.filter.show.all=all -title.submitting.error.report=Submitting error report +title.submitting.error.report=Submitting Error Report log.console.filter.show.errors.warnings.and.infos=infos error.comments.tab.title=Comments error.dialog.error.prompt=&Error message diff --git a/platform/platform-resources-en/src/messages/IdeBundle.properties b/platform/platform-resources-en/src/messages/IdeBundle.properties index 524ab5648139..f1695f65c1e6 100644 --- a/platform/platform-resources-en/src/messages/IdeBundle.properties +++ b/platform/platform-resources-en/src/messages/IdeBundle.properties @@ -1182,3 +1182,11 @@ new.dir.project.error.buck=Project directory name must not contain the ''$'' cha go.to.file.toolwindow.title=Files matching pattern go.to.class.toolwindow.title=Classes matching pattern go.to.class.dumb.mode.message=Go To Class action is not available until indices are built, using Go To File instead + +checkbox.color.blindness=Adjust for red-green color deficiency (restart required) +# define at least one name to show checkbox +# define more that one name to show combobox +combobox.color.blindness.protanopia=Protanopia +combobox.color.blindness.deuteranopia= +combobox.color.blindness.tritanopia= +combobox.color.blindness.achromatopsia= diff --git a/platform/platform-resources/src/brokenPlugins.txt b/platform/platform-resources/src/brokenPlugins.txt index 42907a5adba7..2f8d7013f0b5 100644 --- a/platform/platform-resources/src/brokenPlugins.txt +++ b/platform/platform-resources/src/brokenPlugins.txt @@ -1,14 +1,14 @@ // This file contains list of broken plugins. // Each line contains plugin ID and list of versions that are broken. // If plugin name or version contains a space you can quote it like in command line. -NodeJS 142.2492 142.2481 142.2064 141.1108 140.2045 140.1669 140.642 139.173 139.105 139.496 139.1 139.8 138.2196 138.2254 138.1684 138.1744 138.1879 138.2051 138.1367 138.1495 138.1189 138.1145 138.937 138.1013 138.921 138.447 138.172 138.317 138.21 138.35 138.96 138.85 136.1205 134.1276 134.1163 134.1145 134.1081 134.1039 134.985 134.680 134.31 134.307 134.262 134.198 134.125 136.1141 +NodeJS 142.3224 142.2650 142.2492 142.2481 142.2064 141.1108 140.2045 140.1669 140.642 139.173 139.105 139.496 139.1 139.8 138.2196 138.2254 138.1684 138.1744 138.1879 138.2051 138.1367 138.1495 138.1189 138.1145 138.937 138.1013 138.921 138.447 138.172 138.317 138.21 138.35 138.96 138.85 136.1205 134.1276 134.1163 134.1145 134.1081 134.1039 134.985 134.680 134.31 134.307 134.262 134.198 134.125 136.1141 com.jetbrains.php 140.2765 141.332 139.732 139.659 139.496 139.173 139.105 138.2502 138.2000.2262 138.1751 138.1806 138.1505 138.1161 138.826 136.1768 136.1672 134.1456 133.982 133.679 133.51 133.326 131.98 131.374 131.332 131.235 131.205 130.1639 130.1481 130.1176 129.91 129.814 129.672 129.362 127.67 127.100 126.334 123.66 122.875 121.62 121.390 121.215 121.12 com.jetbrains.lang.ejs 131.17 131.12 com.jetbrains.twig 133.51 130.1639 -org.jetbrains.plugins.ruby 7.0.0.20150216 7.0.0.20150204 7.0.0.20141010 7.0.0.20140915 7.0.0.20140901 7.0.0.20140807 6.0.0.20140207 6.5.2.20140512 7.0.0.20140704 7.0.0.20140707 7.0.0.20140724 -Pythonid "4.1 141.4 EAP" 3.1 4.0.25 4.0.26 +org.jetbrains.plugins.ruby 8.0.0.20150624 8.0.0.20150616 7.0.0.20150216 7.0.0.20150204 7.0.0.20141010 7.0.0.20140915 7.0.0.20140901 7.0.0.20140807 6.0.0.20140207 6.5.2.20140512 7.0.0.20140704 7.0.0.20140707 7.0.0.20140724 +Pythonid "142.176 v4.5" 4.1 "4.1 141.4 EAP" 3.1 4.0.25 4.0.26 Karma 140.2109 138.1684 138.1879 138.317 138.21 134.1163 134.1039 134.686 134.31 -org.intellij.scala 1.6.0 1.2.67.3.EAP 1.3.2-14.1 1.3.3-14.1 1.0.93.10.EAP 1.0.93.4.EAP 1.0.93.9.EAP 1.0.58 1.0.16 1.0.14 1.0.13 0.42.28 0.42.23 0.40.20 0.40.18 0.40.16 0.32.593 0.32.562 0.32.558 0.32.550 0.32.520 0.32.512 +org.intellij.scala 1.7.4 1.7.2 1.6.2 1.6.1 1.6.0 1.2.67.3.EAP 1.3.2-14.1 1.3.3-14.1 1.0.93.10.EAP 1.0.93.4.EAP 1.0.93.9.EAP 1.0.58 1.0.16 1.0.14 1.0.13 0.42.28 0.42.23 0.40.20 0.40.18 0.40.16 0.32.593 0.32.562 0.32.558 0.32.550 0.32.520 0.32.512 org.jetbrains.kannotator 0.2.420 org.jetbrains.kotlin 0.10.555 0.10.4 0.8.1615 0.7.1360 0.7.1376 0.8.7 SBT 1.0.0 1.1.0 1.2.0 1.3.0 1.3.1 1.4.0 1.5.0 1.5.1 1.6.0 1.6.1 @@ -28,4 +28,5 @@ AWSCloudFormation 0.3.9 com.jetbrains.typoscript 0.1.1 0.1.0 Dart 133.239 133.432 133.726 130.1494 130.549 131.17 132.1045 132.207 132.835 130.1494 131.17 0.5.1 IdeaVIM 0.15.59 0.16.60 0.20.75 0.20.80 0.21.86 0.22.90 0.23.111 0.23.115 0.23.93 0.24-148 0.25 0.26 0.27 0.28 0.29 0.30 0.31 0.32 0.33 0.34 0.35 0.36 0.37 0.38 0.39 -IDETalk 0.5 0.5.1 0.5.2 0.5.3 0.5.5 0.5.6 0.5.6.1 0.5.6.2 0.5.4 0.1.2 0.1.1 141.177.4 \ No newline at end of file +IDETalk 0.5 0.5.1 0.5.2 0.5.3 0.5.5 0.5.6 0.5.6.1 0.5.6.2 0.5.4 0.1.2 0.1.1 141.177.4 +com.jetbrains.plugins.meteor 142.2481 \ No newline at end of file diff --git a/platform/platform-resources/src/idea/LangActions.xml b/platform/platform-resources/src/idea/LangActions.xml index 1f043ec05433..2589cbd4903e 100644 --- a/platform/platform-resources/src/idea/LangActions.xml +++ b/platform/platform-resources/src/idea/LangActions.xml @@ -12,6 +12,9 @@ + + + @@ -849,7 +852,6 @@ - diff --git a/platform/platform-tests/testSrc/com/intellij/history/integration/ui/FileHistoryDialogTest.java b/platform/platform-tests/testSrc/com/intellij/history/integration/ui/FileHistoryDialogTest.java index 8a138b961877..19680575a3b6 100644 --- a/platform/platform-tests/testSrc/com/intellij/history/integration/ui/FileHistoryDialogTest.java +++ b/platform/platform-tests/testSrc/com/intellij/history/integration/ui/FileHistoryDialogTest.java @@ -110,8 +110,8 @@ public class FileHistoryDialogTest extends LocalHistoryUITestCase { DiffContent left = getLeftDiffContent(m); DiffContent right = getRightDiffContent(m); - assertEquals(leftContent, left); - assertEquals(rightContent, right); + assertContent(leftContent, left); + assertContent(rightContent, right); } private DiffContent getLeftDiffContent(FileHistoryDialogModel m) { diff --git a/platform/platform-tests/testSrc/com/intellij/history/integration/ui/LocalHistoryUITestCase.java b/platform/platform-tests/testSrc/com/intellij/history/integration/ui/LocalHistoryUITestCase.java index 36dcb951c1b0..3de6a99e6dbd 100644 --- a/platform/platform-tests/testSrc/com/intellij/history/integration/ui/LocalHistoryUITestCase.java +++ b/platform/platform-tests/testSrc/com/intellij/history/integration/ui/LocalHistoryUITestCase.java @@ -23,7 +23,7 @@ import com.intellij.testFramework.SkipInHeadlessEnvironment; @SkipInHeadlessEnvironment public abstract class LocalHistoryUITestCase extends IntegrationTestCase { - protected void assertEquals(String expected, DiffContent actual) { + protected void assertContent(String expected, DiffContent actual) { actual.onAssigned(true); try { assertEquals(expected, ((DocumentContent)actual).getDocument().getText()); diff --git a/platform/platform-tests/testSrc/com/intellij/history/integration/ui/SelectionHistoryDialogTest.java b/platform/platform-tests/testSrc/com/intellij/history/integration/ui/SelectionHistoryDialogTest.java index 008a9bb166a9..b97af0ef8787 100644 --- a/platform/platform-tests/testSrc/com/intellij/history/integration/ui/SelectionHistoryDialogTest.java +++ b/platform/platform-tests/testSrc/com/intellij/history/integration/ui/SelectionHistoryDialogTest.java @@ -90,8 +90,8 @@ public class SelectionHistoryDialogTest extends LocalHistoryUITestCase { DiffContent left = dm.getLeftDiffContent(new NullRevisionsProgress()); DiffContent right = dm.getRightDiffContent(new NullRevisionsProgress()); - assertEquals("b", left); - assertEquals("bc", right); + assertContent("b", left); + assertContent("bc", right); } public void testDiffContentsAndTitleForCurrentRevision() throws IOException { @@ -101,7 +101,7 @@ public class SelectionHistoryDialogTest extends LocalHistoryUITestCase { DiffContent right = dm.getRightDiffContent(new NullRevisionsProgress()); - assertEquals("bcd", right); + assertContent("bcd", right); assertTrue(right instanceof DocumentFragmentContent); } @@ -114,8 +114,8 @@ public class SelectionHistoryDialogTest extends LocalHistoryUITestCase { initModelOnSecondLineAndSelectRevisions(3, 3); - assertEquals("b", dm.getLeftDiffContent(new NullRevisionsProgress())); - assertEquals("bcd", dm.getRightDiffContent(new NullRevisionsProgress())); + assertContent("b", dm.getLeftDiffContent(new NullRevisionsProgress())); + assertContent("bcd", dm.getRightDiffContent(new NullRevisionsProgress())); } public void testRevert() throws IOException { diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/LaterInvocatorTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/LaterInvocatorTest.java new file mode 100644 index 000000000000..7b72162a0903 --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/LaterInvocatorTest.java @@ -0,0 +1,559 @@ +package com.intellij.openapi.application.impl; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Conditions; +import com.intellij.testFramework.PlatformTestCase; +import com.intellij.testFramework.SkipInHeadlessEnvironment; +import com.intellij.testFramework.UsefulTestCase; +import com.intellij.util.ui.UIUtil; +import junit.framework.TestCase; + +import javax.swing.*; +import java.awt.*; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; + +@SuppressWarnings({"SSBasedInspection", "SynchronizeOnThis"}) +@SkipInHeadlessEnvironment +public class LaterInvocatorTest extends PlatformTestCase { + private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.application.impl.LaterInvokatorTest"); + + private final ArrayList myOrder = new ArrayList(); + + @SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized") private Window myWindow1; + private Window myWindow2; + + private final Runnable LEAVE_MODAL = new Runnable() { + @Override + public void run() { + LaterInvocator.leaveModal(myWindow1); + } + public String toString() { + return "leave modal later"; + } + + }; + private final Runnable ENTER_MODAL = new Runnable() { + @Override + public void run() { + LaterInvocator.enterModal(myWindow1); + } + + public String toString() { + return "enter modal later"; + } + }; + + @Override + protected void setUp() throws Exception { + myWindow1 = new Frame() { + public String toString() { + return "Window1"; + } + }; + myWindow2 = new Frame() { + public String toString() { + return "Window2"; + } + }; + final Exception[] exception = {null}; + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + try { + LaterInvocatorTest.super.setUp(); + final Object[] modalEntities = LaterInvocator.getCurrentModalEntities(); + if (modalEntities.length > 0) { + LOG.error( + "Expect no modal entries. Probably some of the previous tests didn't left their entries. Top entry is: " + modalEntities[0]); + } + } + catch (Exception e) { + exception[0] = e; + } + } + }); + if (exception[0] != null) throw exception[0]; + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + TestCase.assertFalse("Can't run test", LaterInvocator.isInModalContext()); + } + }); + + flushSwingQueue(); + } + + @Override + protected void runBareRunnable(Runnable runnable) throws Throwable { + runnable.run(); + } + + @Override + protected void tearDown() throws Exception { + myOrder.clear(); + final boolean[] inModalState = {true}; + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + synchronized (inModalState) { + inModalState[0] = false; + } + } + }, ModalityState.NON_MODAL); + flushSwingQueue(); + flushSwingQueue(); + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + while (true) { + synchronized (inModalState) { + if (!inModalState[0]) break; + } + flushSwingQueue(); + synchronized (inModalState) { + if (inModalState[0] && LaterInvocator.isInModalContext()) LaterInvocator.leaveAllModals(); + } + } + } + }); + + final Exception[] exception = {null}; + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + try { + LaterInvocatorTest.super.tearDown(); + } + catch (Exception e) { + exception[0] = e; + } + } + + public String toString() { + return "super teardown"; + } + }); + if (exception[0] != null) throw exception[0]; + } + + public void testReorder() throws InterruptedException, InvocationTargetException { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + LaterInvocator.enterModal(myWindow1); + synchronized (LaterInvocatorTest.this) { + blockSwingThread(); + ApplicationManager.getApplication().invokeLater(new MyRunnable("1"), ModalityState.NON_MODAL); + SwingUtilities.invokeLater(ENTER_MODAL); + LaterInvocator.leaveModal(myWindow1); + SwingUtilities.invokeLater(LEAVE_MODAL); + ApplicationManager.getApplication().invokeLater(new MyRunnable("2"), ModalityState.NON_MODAL); + } + flushSwingQueue(); + checkOrder(2); + } + }); + } + + public void testTrueReorder() throws InvocationTargetException, InterruptedException { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + LaterInvocator.enterModal(myWindow1); + ApplicationManager.getApplication().invokeLater(new MyRunnable("1"), ModalityState.NON_MODAL); + SwingUtilities.invokeLater(ENTER_MODAL); + LaterInvocator.leaveModal(myWindow1); + SwingUtilities.invokeLater(LEAVE_MODAL); + ApplicationManager.getApplication().invokeLater(new MyRunnable("2"), ModalityState.NON_MODAL); + } + }); + flushSwingQueue(); + flushSwingQueue(); + checkOrder(2); + } + + public void testEverInvoked() throws InvocationTargetException, InterruptedException { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + LaterInvocator.enterModal(myWindow1); + ApplicationManager.getApplication().invokeLater(ENTER_MODAL, ModalityState.NON_MODAL); + ApplicationManager.getApplication().invokeLater(new MyRunnable("1"), ModalityState.NON_MODAL); + LaterInvocator.leaveModal(myWindow1); + flushSwingQueue(); + flushSwingQueue(); + checkOrder(0); + SwingUtilities.invokeLater(LEAVE_MODAL); + flushSwingQueue(); + flushSwingQueue(); + checkOrder(1); + } + }); + } + + public void testDoesNotInvokeWhenModal() throws InvocationTargetException, InterruptedException { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + LaterInvocator.enterModal(myWindow1); + + ApplicationManager.getApplication().invokeLater(ENTER_MODAL, ModalityState.NON_MODAL); + ApplicationManager.getApplication().invokeLater(new MyRunnable("1"), ModalityState.NON_MODAL); + LaterInvocator.leaveModal(myWindow1); + flushSwingQueue(); + checkOrder(0); + LaterInvocator.leaveModal(myWindow1); + flushSwingQueue(); + checkOrder(1); + } + }); + } + + public void testRunQueuedRunnablesOnLeavingModality() { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + LaterInvocator.invokeLater(ENTER_MODAL, ModalityState.NON_MODAL); + LaterInvocator.invokeLater(new MyRunnable("3"), ModalityState.NON_MODAL); + flushSwingQueue(); + checkOrder(0); + + final ModalityState modalityState = ModalityState.stateForComponent(myWindow1); + LaterInvocator.invokeLater(new MyRunnable("1") { + @Override + public void run() { + super.run(); + checkOrder(1); + LaterInvocator.invokeLater(new MyRunnable("2"), modalityState); + checkOrder(1); + LaterInvocator.leaveModal(myWindow1); + checkOrder(2); + } + }, modalityState); + flushSwingQueue(); // let "1" run + + flushSwingQueue(); // let "3" run + checkOrder(3); + } + }); + } + + public void testStress() throws Exception { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + int N = 1000; + //long start = System.currentTimeMillis(); + for (int i = 0; i < N; i++) { + /* + if (i % 10 == 0) { + long elapsed = System.currentTimeMillis() - start; + System.out.println("i = " + i+"; elapsed="+elapsed); + start = System.currentTimeMillis(); + } + */ + + //assertEquals(null, Toolkit.getDefaultToolkit().getSystemEventQueue().peekEvent()); + //assertEquals(null, LaterInvocator.dumpQueue()); + + UsefulTestCase.assertEmpty(LaterInvocator.getCurrentModalEntities()); + LaterInvocator.enterModal(myWindow2); + //some weird things like MyFireIdleRequest may still sneak in + //assertEquals(null, LaterInvocator.dumpQueue()); + TestCase.assertTrue(LaterInvocator.isInModalContext()); + TestCase.assertEquals(1, LaterInvocator.getCurrentModalEntities().length); + + LaterInvocator.invokeLater(new Runnable() { + @Override + public void run() { + TestCase.assertTrue(!LaterInvocator.isInModalContext()); + } + + public String toString() { + return "ass2"; + } + }, ModalityState.NON_MODAL); + LaterInvocator.invokeLater(ENTER_MODAL, ModalityState.NON_MODAL); + + LaterInvocator.invokeLater(new MyRunnable("1"), ModalityState.NON_MODAL); + + //some weird things like MyFireIdleRequest may still sneak in + //java.util.List dump = LaterInvocator.dumpQueue(); + //assertEquals(dump.toString(), 3, dump.size()); + + LaterInvocator.leaveModal(myWindow2); + flushSwingQueue(); + if (!LaterInvocator.isInModalContext()) { + //System.out.println("inv queue" + LaterInvocator.dumpQueue()); + TestCase.fail(); + } + + checkOrder(0); + + SwingUtilities.invokeLater(LEAVE_MODAL); + flushSwingQueue(); + TestCase.assertTrue(!LaterInvocator.isInModalContext()); + + checkOrder(1); + + + LaterInvocator.leaveAllModals(); + myOrder.clear(); + flushSwingQueue(); + } + } + }); + } + + + public void testOrderWithSwingInvokeLater2() throws InterruptedException, InvocationTargetException { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + LaterInvocator.enterModal(myWindow1); + synchronized (LaterInvocatorTest.this) { + blockSwingThread(); + ApplicationManager.getApplication().invokeLater(new MyRunnable("2"), ModalityState.NON_MODAL); + SwingUtilities.invokeLater(LEAVE_MODAL); + SwingUtilities.invokeLater(new MyRunnable("1")); + ApplicationManager.getApplication().invokeLater(new MyRunnable("3"), ModalityState.NON_MODAL); + } + flushSwingQueue(); + checkOrder(3); + } + }); + } + + public void testOrderWithSwingInvokeLater3() throws InterruptedException, InvocationTargetException { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + LaterInvocator.enterModal(myWindow1); + synchronized (LaterInvocatorTest.this) { + blockSwingThread(); + SwingUtilities.invokeLater(new MyRunnable("1")); + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + ApplicationManager.getApplication().invokeLater(new MyRunnable("3"), ModalityState.NON_MODAL); + } + }, ModalityState.NON_MODAL); + SwingUtilities.invokeLater(LEAVE_MODAL); + ApplicationManager.getApplication().invokeLater(new MyRunnable("2"), ModalityState.NON_MODAL); + } + flushSwingQueue(); + checkOrder(3); + } + }); + } + + public void testExpired() throws Exception { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + final ArrayList consumed = new ArrayList(); + synchronized (LaterInvocatorTest.this) { + blockSwingThread(); + ApplicationManager.getApplication().getInvokator().invokeLater(new Runnable() { + @Override + public void run() { + ApplicationManager.getApplication().invokeLater(new MyRunnable("1") { + @Override + public void run() { + super.run(); + TestCase.fail("Should not be executed"); + } + }, Conditions.alwaysTrue()); + } + }, ModalityState.NON_MODAL).doWhenDone(new Runnable() { + @Override + public void run() { + consumed.add("1"); + } + }); + ApplicationManager.getApplication().getInvokator().invokeLater(new MyRunnable("2"), ModalityState.NON_MODAL) + .doWhenDone(new Runnable() { + @Override + public void run() { + consumed.add("2"); + } + }); + } + flushSwingQueue(); + + TestCase.assertEquals(consumed.toString(), 2, consumed.size()); + } + }); + } + + public void testOrderWithSwingInvokeLater4() throws InterruptedException, InvocationTargetException { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + SwingUtilities.invokeLater(ENTER_MODAL); + flushSwingQueue(); + synchronized (LaterInvocatorTest.this) { + blockSwingThread(); + SwingUtilities.invokeLater(new MyRunnable("1")); + ApplicationManager.getApplication().invokeLater(new MyRunnable("3"), ModalityState.NON_MODAL); + SwingUtilities.invokeLater(LEAVE_MODAL); + SwingUtilities.invokeLater(ENTER_MODAL); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + SwingUtilities.invokeLater(new MyRunnable("2")); + ApplicationManager.getApplication().invokeLater(new MyRunnable("5"), ModalityState.NON_MODAL); + } + }); + ApplicationManager.getApplication().invokeLater(new MyRunnable("4"), ModalityState.NON_MODAL); + } + flushSwingQueue(); + SwingUtilities.invokeLater(LEAVE_MODAL); + flushSwingQueue(); + checkOrder(5); + } + }); + } + + public void testDeadLock() throws InvocationTargetException, InterruptedException { + final Object lock = new Object(); + final boolean[] started = { false }; + final Thread thread = new Thread("later invokator test") { + @Override + public void run() { + synchronized (lock) { + started[0] = true; + ApplicationManager.getApplication().invokeLater(new MyRunnable("1"), ModalityState.NON_MODAL); + lock.notifyAll(); + } + } + }; + + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + synchronized (LaterInvocatorTest.this) { + blockSwingThread(); + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + thread.start(); + while (!started[0]) { + try { + //noinspection BusyWait + Thread.sleep(5); + } + catch (InterruptedException e) { + TestCase.fail(e.getMessage()); + } + } + synchronized (lock) { + checkOrder(0); + } + } + }, ModalityState.NON_MODAL); + } + flushSwingQueue(); + flushSwingQueue(); + checkOrder(1); + } + }); + } + + private static void flushSwingQueue() { + + try { + Thread.sleep(10); + } + catch (InterruptedException e) { + throw new RuntimeException(e); + } + + if (SwingUtilities.isEventDispatchThread()) { + UIUtil.dispatchAllInvocationEvents(); + } + else { + UIUtil.pump(); + } + /* + final AtomicBoolean hasEventsInQueue = new AtomicBoolean(true); + while (hasEventsInQueue.get()) { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + hasEventsInQueue.set(Toolkit.getDefaultToolkit().getSystemEventQueue().peekEvent() != null); + } + }); + } + */ + + //some weird things like MyFireIdleRequest may still sneak in + //assertEquals(null, Toolkit.getDefaultToolkit().getSystemEventQueue().peekEvent()); + } + + private void blockSwingThread() { + SwingUtilities.invokeLater(new Lock(this)); + } + + private void checkOrder(int count) { + synchronized(myOrder) { + String order = printOrder(); + if (count != myOrder.size()) { + TestCase.fail(order + "Expected " + count + " items but was " + myOrder.size()); + } + for (int i = 1; i <= count; i++) + if (!String.valueOf(i).equals(myOrder.get(i - 1))) { + //System.out.println("Count: " + count); + TestCase.fail(order + "Bad order"); + } + } + } + + private String printOrder() { + StringBuilder out = new StringBuilder("Order: "); + for (String order : myOrder) { + out.append(order).append(" "); + } + out.append('\n'); + return out.toString(); + } + + private class MyRunnable implements Runnable { + private final String myId; + + public MyRunnable(String id) { + myId = id; + } + + @Override + public void run() { + //System.out.println("executed: " + myId); + synchronized(myOrder) { + myOrder.add(myId); + } + } + + public String toString() { + return "myrun " + myId; + } + } + + private static class Lock implements Runnable { + private final Object myLock; + + public Lock(Object lock) { + myLock = lock; + } + + @Override + public void run() { + //noinspection EmptySynchronizedStatement + synchronized (myLock) { + //System.out.print(""); + } + } + } +} diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/XmlElementStorageTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/XmlElementStorageTest.java deleted file mode 100644 index 001361010703..000000000000 --- a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/XmlElementStorageTest.java +++ /dev/null @@ -1,152 +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.openapi.components.impl; - -import com.intellij.openapi.Disposable; -import com.intellij.openapi.components.RoamingType; -import com.intellij.openapi.components.StateStorage; -import com.intellij.openapi.components.TrackingPathMacroSubstitutor; -import com.intellij.openapi.components.impl.stores.StorageData; -import com.intellij.openapi.components.impl.stores.XmlElementStorage; -import com.intellij.openapi.util.Disposer; -import com.intellij.testFramework.LightPlatformTestCase; -import org.jdom.Element; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; -import java.util.Collections; -import java.util.Set; - -import static com.intellij.openapi.util.JDOMBuilder.attr; -import static com.intellij.openapi.util.JDOMBuilder.tag; - -/** - * @author mike - */ -public class XmlElementStorageTest extends LightPlatformTestCase { - private Disposable myParentDisposable; - - @Override - public void setUp() throws Exception { - super.setUp(); - myParentDisposable = Disposer.newDisposable(); - } - - @Override - public void tearDown() throws Exception { - Disposer.dispose(myParentDisposable); - super.tearDown(); - } - - public void testGetStateSucceeded() throws Exception { - MyXmlElementStorage storage = - new MyXmlElementStorage(tag("root", tag("component", attr("name", "test"), tag("foo")))); - Element state = storage.getState(this, "test", Element.class, null); - assertNotNull(state); - assertEquals("component", state.getName()); - assertNotNull(state.getChild("foo")); - } - - public void testGetStateNotSucceeded() throws Exception { - MyXmlElementStorage storage = new MyXmlElementStorage(tag("root")); - Element state = storage.getState(this, "test", Element.class, null); - assertNull(state); - } - - public void testSetStateOverridesOldState() throws Exception { - MyXmlElementStorage storage = - new MyXmlElementStorage(tag("root", tag("component", attr("name", "test"), tag("foo")))); - Element newState = tag("component", attr("name", "test"), tag("bar")); - StateStorage.ExternalizationSession externalizationSession = storage.startExternalization(); - externalizationSession.setState(this, "test", newState, null); - externalizationSession.createSaveSession().save(); - assertNotNull(storage.mySavedElement); - assertNotNull(storage.mySavedElement.getChild("component").getChild("bar")); - assertNull(storage.mySavedElement.getChild("component").getChild("foo")); - } - - - private static class MyXmlElementStorage extends XmlElementStorage { - private final Element myElement; - private Element mySavedElement; - - public MyXmlElementStorage(Element element) { - super("", RoamingType.PER_USER, new MyPathMacroManager(), "root", null); - myElement = element; - } - - @Override - protected Element loadLocalData() { - return myElement; - } - - @NotNull - @Override - protected XmlElementStorageSaveSession createSaveSession(@NotNull StorageData storageData) { - return new XmlElementStorageSaveSession(storageData) { - @Override - protected void doSave(@Nullable Element element) { - mySavedElement = element == null ? null : element.clone(); - } - }; - } - } - - private static class MyPathMacroManager implements TrackingPathMacroSubstitutor { - @Override - public void expandPaths(@NotNull final Element element) { - } - - @Override - public void reset() { - } - - @NotNull - @Override - public Collection getComponents(@NotNull Collection macros) { - return Collections.emptyList(); - } - - @Override - public void collapsePaths(@NotNull final Element element) { - } - - @Override - public String expandPath(final String path) { - throw new UnsupportedOperationException("Method expandPath not implemented in " + getClass()); - } - - @Override - public String collapsePath(@Nullable String path) { - throw new UnsupportedOperationException("Method collapsePath not implemented in " + getClass()); - } - - @NotNull - @Override - public Collection getUnknownMacros(final String componentName) { - return Collections.emptySet(); - } - - @Override - public void invalidateUnknownMacros(@NotNull Set macros) { - } - - @Override - public void addUnknownMacros(@NotNull String componentName, @NotNull Collection unknownMacros) { - } - } -} diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/colors/impl/EditorColorsSchemeImplTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/editor/colors/impl/EditorColorsSchemeImplTest.java index 86941a1f5757..0a2a04ab1503 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/editor/colors/impl/EditorColorsSchemeImplTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/colors/impl/EditorColorsSchemeImplTest.java @@ -22,11 +22,18 @@ import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.WriteExternalException; import com.intellij.testFramework.LightPlatformCodeInsightTestCase; import org.jdom.Element; +import org.jdom.input.DOMBuilder; import org.jdom.output.Format; import org.jdom.output.XMLOutputter; import org.jetbrains.annotations.NotNull; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; +import java.io.StringReader; import java.io.StringWriter; import java.util.Arrays; import java.util.Collections; @@ -223,6 +230,45 @@ public class EditorColorsSchemeImplTest extends LightPlatformCodeInsightTestCase assertSame(fallbackAttrs, result.second); } + + /** + * Check that the attributes missing in a custom scheme with a version prior to 142 are explicitly added with fallback enabled, + * not taken from parent scheme. + * + * @throws Exception + */ + public void testUpgradeFromVer141() throws Exception { + TextAttributesKey constKey = DefaultLanguageHighlighterColors.CONSTANT; + TextAttributesKey fallbackKey = constKey.getFallbackAttributeKey(); + assertNotNull(fallbackKey); + + EditorColorsScheme scheme = loadScheme( + "\n" + + "\n" + + "\n" + ); + + TextAttributes constAttrs = scheme.getAttributes(constKey); + TextAttributes fallbackAttrs = scheme.getAttributes(fallbackKey); + assertSame(fallbackAttrs, constAttrs); + } + + + private static EditorColorsScheme loadScheme(@NotNull String docText) throws ParserConfigurationException, IOException, SAXException { + DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + InputSource inputSource = new InputSource(new StringReader(docText)); + org.w3c.dom.Document doc = docBuilder.parse(inputSource); + Element root = new DOMBuilder().build(doc.getDocumentElement()); + + EditorColorsScheme defaultScheme = EditorColorsManager.getInstance().getScheme(EditorColorsScheme.DEFAULT_SCHEME_NAME); + EditorColorsScheme targetScheme = new EditorColorsSchemeImpl(defaultScheme); + + targetScheme.readExternal(root); + ((AbstractColorsScheme)targetScheme).upgradeSchemeFromPreviousVersion(); + + return targetScheme; + } + @NotNull public Pair doTestWriteRead(TextAttributesKey key, TextAttributes attributes) throws WriteExternalException { diff --git a/platform/platform-tests/testSrc/com/intellij/psi/PsiModificationTrackerTreeChangesUpdatesTest.java b/platform/platform-tests/testSrc/com/intellij/psi/PsiModificationTrackerTreeChangesUpdatesTest.java new file mode 100644 index 000000000000..af1eb62b4973 --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/psi/PsiModificationTrackerTreeChangesUpdatesTest.java @@ -0,0 +1,81 @@ +/* + * 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.psi; + +import com.intellij.openapi.application.Result; +import com.intellij.openapi.application.WriteAction; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.impl.PsiManagerImpl; +import com.intellij.psi.impl.PsiModificationTrackerImpl; +import com.intellij.testFramework.PlatformTestCase; +import org.jetbrains.annotations.NotNull; + +public class PsiModificationTrackerTreeChangesUpdatesTest extends PlatformTestCase { + private PsiModificationTrackerImpl myTracker; + + @Override + public void setUp() throws Exception { + super.setUp(); + // since we want to test PsiModificationTrackerImpl in isolation, we create a separate instance: + // The existing PsiModificationTrackerImpl is affected by various components. + myTracker = new PsiModificationTrackerImpl(getProject()); + ((PsiManagerImpl)PsiManager.getInstance(getProject())).addTreeChangePreprocessor(myTracker); + } + + @Override + public void tearDown() throws Exception { + ((PsiManagerImpl)PsiManager.getInstance(getProject())).removeTreeChangePreprocessor(myTracker); + super.tearDown(); + } + + public void testMoveFile() throws Exception { + new WriteAction() { + @Override + protected void run(@NotNull Result result) throws Throwable { + final VirtualFile dir1 = getProject().getBaseDir().createChildDirectory(this, "dir1"); + final VirtualFile dir2 = getProject().getBaseDir().createChildDirectory(this, "dir2"); + VirtualFile child = dir1.createChildData(this, "child"); + + long outOfCodeBlockCount = myTracker.getOutOfCodeBlockModificationCount(); + child.move(this, dir2); + assertFalse(myTracker.getOutOfCodeBlockModificationCount() == outOfCodeBlockCount); + + outOfCodeBlockCount = myTracker.getOutOfCodeBlockModificationCount(); + child.move(this, dir1); + assertFalse(myTracker.getOutOfCodeBlockModificationCount() == outOfCodeBlockCount); + } + }.execute(); + } + + public void testMoveDir() throws Exception { + new WriteAction() { + @Override + protected void run(@NotNull Result result) throws Throwable { + final VirtualFile dir1 = getProject().getBaseDir().createChildDirectory(this, "dir1"); + final VirtualFile dir2 = getProject().getBaseDir().createChildDirectory(this, "dir2"); + VirtualFile child = dir1.createChildDirectory(this, "child"); + + long outOfCodeBlockCount = myTracker.getOutOfCodeBlockModificationCount(); + child.move(this, dir2); + assertFalse(myTracker.getOutOfCodeBlockModificationCount() == outOfCodeBlockCount); + + outOfCodeBlockCount = myTracker.getOutOfCodeBlockModificationCount(); + child.move(this, dir1); + assertFalse(myTracker.getOutOfCodeBlockModificationCount() == outOfCodeBlockCount); + } + }.execute(); + } +} diff --git a/platform/platform-tests/testSrc/com/intellij/psi/impl/PsiDocumentManagerImplTest.java b/platform/platform-tests/testSrc/com/intellij/psi/impl/PsiDocumentManagerImplTest.java index 71bdbf819ce2..647a7f2fe29d 100644 --- a/platform/platform-tests/testSrc/com/intellij/psi/impl/PsiDocumentManagerImplTest.java +++ b/platform/platform-tests/testSrc/com/intellij/psi/impl/PsiDocumentManagerImplTest.java @@ -19,6 +19,10 @@ import com.intellij.diagnostic.ThreadDumper; import com.intellij.ide.impl.ProjectUtil; import com.intellij.mock.MockDocument; import com.intellij.mock.MockPsiFile; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.impl.LaterInvocator; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.impl.DocumentImpl; @@ -26,6 +30,7 @@ import com.intellij.openapi.editor.impl.event.DocumentEventImpl; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ex.ProjectManagerEx; +import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.ThrowableComputable; import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.util.text.StringUtil; @@ -41,7 +46,9 @@ import com.intellij.testFramework.PlatformTestUtil; import com.intellij.util.Processor; import com.intellij.util.concurrency.Semaphore; import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.Nullable; +import javax.swing.*; import java.io.File; import java.io.IOException; import java.util.concurrent.atomic.AtomicInteger; @@ -60,6 +67,16 @@ public class PsiDocumentManagerImplTest extends PlatformTestCase { UIUtil.dispatchAllInvocationEvents(); } + @Override + protected void tearDown() throws Exception { + Object[] entities = LaterInvocator.getCurrentModalEntities(); + for (int i = entities.length - 1; i >= 0; i--) { + Object state = entities[i]; + LaterInvocator.leaveModal(state); + } + super.tearDown(); + } + public void testGetCachedPsiFile_NoFile() throws Exception { final PsiFile file = getPsiDocumentManager().getCachedPsiFile(new MockDocument()); assertNull(file); @@ -471,4 +488,75 @@ public class PsiDocumentManagerImplTest extends PlatformTestCase { } }); } + + public void testCommitDocumentInModalDialog() throws IOException { + VirtualFile vFile = getVirtualFile(createTempFile("a.txt", "abc")); + PsiFile psiFile = getPsiManager().findFile(vFile); + final Document document = getPsiDocumentManager().getDocument(psiFile); + + final DialogWrapper dialog = new DialogWrapper(getProject()) { + @Nullable + @Override + protected JComponent createCenterPanel() { + return null; + } + }; + + disposeOnTearDown(new Disposable() { + @Override + public void dispose() { + dialog.close(DialogWrapper.OK_EXIT_CODE); + } + }); + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + // commit thread is paused + document.setText("xx"); + + LaterInvocator.enterModal(dialog); + } + }); + assertNotSame(ModalityState.NON_MODAL, ApplicationManager.getApplication().getCurrentModalityState()); + + long start = System.currentTimeMillis(); + while (System.currentTimeMillis() - start < 10000) { + UIUtil.dispatchAllInvocationEvents(); + // must not be committed until exit modal dialog + assertFalse(getPsiDocumentManager().isCommitted(document)); + } + LaterInvocator.leaveModal(dialog); + assertEquals(ModalityState.NON_MODAL, ApplicationManager.getApplication().getCurrentModalityState()); + + start = System.currentTimeMillis(); + + // must committ + while (System.currentTimeMillis() - start < 10000 && !getPsiDocumentManager().isCommitted(document)) { + UIUtil.dispatchAllInvocationEvents(); + } + + assertTrue(getPsiDocumentManager().isCommitted(document)); + + + + // check that inside modal dialog commit is possible + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + // commit thread is paused + + LaterInvocator.enterModal(dialog); + document.setText("yyy"); + } + }); + assertNotSame(ModalityState.NON_MODAL, ApplicationManager.getApplication().getCurrentModalityState()); + + // must committ + while (System.currentTimeMillis() - start < 10000 && !getPsiDocumentManager().isCommitted(document)) { + UIUtil.dispatchAllInvocationEvents(); + } + + assertTrue(getPsiDocumentManager().isCommitted(document)); + LaterInvocator.leaveModal(dialog); + } } diff --git a/platform/platform-tests/testSrc/com/intellij/util/containers/TreeTraverserTest.java b/platform/platform-tests/testSrc/com/intellij/util/containers/TreeTraverserTest.java index 4af63d7dc399..0911b7c74fbb 100644 --- a/platform/platform-tests/testSrc/com/intellij/util/containers/TreeTraverserTest.java +++ b/platform/platform-tests/testSrc/com/intellij/util/containers/TreeTraverserTest.java @@ -68,6 +68,16 @@ public class TreeTraverserTest extends TestCase { } }; + private static final Function FIBONACCI2 = new JBIterable.StatefulTransform() { + int k0; + @Override + public Integer fun(Integer k) { + int t = k0; + k0 = k; + return t + k; + } + }; + @NotNull private static Condition LESS_THAN(final int max) { return new Condition() { @@ -90,6 +100,13 @@ public class TreeTraverserTest extends TestCase { // JBIterable ---------------------------------------------- + public void testAppend() { + JBIterable it = JBIterable.of(1, 2, 3).append(JBIterable.of(4, 5, 6)).append(7); + assertEquals(7, it.size()); + assertEquals(Arrays.asList(1, 2, 3, 4, 5, 6, 7), it.toList()); + assertTrue(it.contains(5)); + } + public void testGenerateRepeat() { JBIterable it = JBIterable.generate(1, INCREMENT).take(3).repeat(3); assertEquals(9, it.size()); @@ -137,26 +154,43 @@ public class TreeTraverserTest extends TestCase { } } - // TreeTraverser ---------------------------------------------- + public void testStatefulFilter() { + JBIterable it = JBIterable.generate(1, INCREMENT).take(5).filter(new JBIterable.StatefulFilter() { + int prev; + @Override + public boolean value(Integer integer) { + boolean b = integer > prev; + if (b) prev = integer; + return b; + } + }); + assertEquals(Arrays.asList(1, 2, 3, 4, 5), it.toList()); + assertEquals(Arrays.asList(1, 2, 3, 4, 5), it.toList()); + } + + public void testStatefulGenerator() { + JBIterable it = JBIterable.generate(1, FIBONACCI2).take(8); + assertEquals(Arrays.asList(1, 1, 2, 3, 5, 8, 13, 21), it.toList()); + assertEquals(Arrays.asList(1, 1, 2, 3, 5, 8, 13, 21), it.toList()); + } + + // TreeTraversal ---------------------------------------------- @NotNull - private static TreeTraverser traverser() { - return new TreeTraverser(Functions.fromMap(numbers())); + private static Function> traverser(TreeTraversal t) { + return t.traversal(Functions.fromMap(numbers())); } public void testSimplePreOrderDfs() { - TreeTraverser t = traverser(); - assertEquals(Arrays.asList(1, 2, 5, 6, 7, 3, 8, 9, 10, 4, 11, 12, 13), t.preOrderDfsTraversal(1).toList()); + assertEquals(Arrays.asList(1, 2, 5, 6, 7, 3, 8, 9, 10, 4, 11, 12, 13), traverser(TreeTraversal.PRE_ORDER_DFS).fun(1).toList()); } public void testSimplePostOrderDfs() { - TreeTraverser t = traverser(); - assertEquals(Arrays.asList(5, 6, 7, 2, 8, 9, 10, 3, 11, 12, 13, 4, 1), t.postOrderDfsTraversal(1).toList()); + assertEquals(Arrays.asList(5, 6, 7, 2, 8, 9, 10, 3, 11, 12, 13, 4, 1), traverser(TreeTraversal.POST_ORDER_DFS).fun(1).toList()); } public void testSimpleBfs() { - TreeTraverser t = traverser(); - assertEquals(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13), t.bfsTraversal(1).toList()); + assertEquals(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13), traverser(TreeTraversal.PLAIN_BFS).fun(1).toList()); } // FilteredTraverser ---------------------------------------------- @@ -219,8 +253,8 @@ public class TreeTraverserTest extends TestCase { return JBIterable.generate(1, k, FIBONACCI).skip(2).take(3); } }); - TreeTraverser.TracingIt it = t.withRoot(1).preOrderDfsTraversal().skip(20).typedIterator(); - TreeTraverser.TracingIt cursor = JBIterator.cursor(it).first(); + TreeTraversal.TracingIt it = t.withRoot(1).preOrderDfsTraversal().skip(20).typedIterator(); + TreeTraversal.TracingIt cursor = JBIterator.cursor(it).first(); assertNotNull(cursor); assertSame(cursor, it); assertEquals(Arrays.asList(20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1), cursor.backtrace().toList()); diff --git a/platform/projectModel-api/src/com/intellij/openapi/components/Storage.java b/platform/projectModel-api/src/com/intellij/openapi/components/Storage.java index 38771e8b2bd8..0e27bb2bc05d 100644 --- a/platform/projectModel-api/src/com/intellij/openapi/components/Storage.java +++ b/platform/projectModel-api/src/com/intellij/openapi/components/Storage.java @@ -25,9 +25,6 @@ import java.lang.annotation.RetentionPolicy; public @interface Storage { @NonNls String id() default "default"; - @Deprecated - boolean isDefault() default true; - @NonNls String file() default ""; StorageScheme scheme() default StorageScheme.DEFAULT; diff --git a/platform/projectModel-impl/src/com/intellij/core/CoreModuleManager.java b/platform/projectModel-impl/src/com/intellij/core/CoreModuleManager.java index e3b9a2380066..c16de22e8a39 100644 --- a/platform/projectModel-impl/src/com/intellij/core/CoreModuleManager.java +++ b/platform/projectModel-impl/src/com/intellij/core/CoreModuleManager.java @@ -16,7 +16,7 @@ package com.intellij.core; import com.intellij.openapi.Disposable; -import com.intellij.openapi.components.impl.stores.StorageData; +import com.intellij.openapi.components.impl.stores.StateMap; import com.intellij.openapi.module.impl.ModuleEx; import com.intellij.openapi.module.impl.ModuleManagerImpl; import com.intellij.openapi.project.Project; @@ -53,7 +53,7 @@ public class CoreModuleManager extends ModuleManagerImpl { VirtualFile vFile = StandardFileSystems.local().findFileByPath(filePath); try { assert vFile != null; - StorageData storageData = CoreProjectLoader.loadStorageFile(module, vFile); + StateMap storageData = CoreProjectLoader.loadStorageFile(module, vFile); ModuleRootManagerImpl.ModuleRootManagerState state = new ModuleRootManagerImpl.ModuleRootManagerState(); state.readExternal(storageData.getState("NewModuleRootManager")); ((ModuleRootManagerImpl) ModuleRootManager.getInstance(module)).loadState(state); diff --git a/platform/projectModel-impl/src/com/intellij/core/CoreProjectLoader.java b/platform/projectModel-impl/src/com/intellij/core/CoreProjectLoader.java index a94f8daa11f6..0c6f3543044d 100644 --- a/platform/projectModel-impl/src/com/intellij/core/CoreProjectLoader.java +++ b/platform/projectModel-impl/src/com/intellij/core/CoreProjectLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,8 @@ import com.intellij.openapi.components.ComponentManager; import com.intellij.openapi.components.PathMacroManager; import com.intellij.openapi.components.impl.stores.DefaultStateSerializer; import com.intellij.openapi.components.impl.stores.DirectoryStorageData; -import com.intellij.openapi.components.impl.stores.StorageData; +import com.intellij.openapi.components.impl.stores.StateMap; +import com.intellij.openapi.components.impl.stores.StorageDataBase; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectRootManager; @@ -63,7 +64,7 @@ public class CoreProjectLoader { if (modulesXml == null) throw new FileNotFoundException("Missing 'modules.xml' in " + dotIdea.getPath()); - StorageData storageData = loadStorageFile(project, modulesXml); + StateMap storageData = loadStorageFile(project, modulesXml); final Element moduleManagerState = storageData.getState("ProjectModuleManager"); if (moduleManagerState == null) { throw new JDOMException("cannot find ProjectModuleManager state in modules.xml"); @@ -94,9 +95,9 @@ public class CoreProjectLoader { } @NotNull - public static StorageData loadStorageFile(@NotNull ComponentManager componentManager, @NotNull VirtualFile modulesXml) throws JDOMException, IOException { - StorageData storageData = new StorageData("project"); - storageData.load(JDOMUtil.loadDocument(modulesXml.contentsToByteArray()).getRootElement(), PathMacroManager.getInstance(componentManager), false); - return storageData; + public static StateMap loadStorageFile(@NotNull ComponentManager componentManager, @NotNull VirtualFile modulesXml) throws JDOMException, IOException { + StateMap states = new StateMap(); + StorageDataBase.load(states, JDOMUtil.loadDocument(modulesXml.contentsToByteArray()).getRootElement(), PathMacroManager.getInstance(componentManager), false); + return states; } } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/DirectoryStorageData.java b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/DirectoryStorageData.java index 9f8818fb2193..0aa0a256fbab 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/DirectoryStorageData.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/DirectoryStorageData.java @@ -19,14 +19,13 @@ import com.intellij.application.options.PathMacrosCollector; import com.intellij.openapi.components.StateSplitter; import com.intellij.openapi.components.StateSplitterEx; import com.intellij.openapi.components.TrackingPathMacroSubstitutor; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.text.StringUtilRt; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.PairConsumer; import com.intellij.util.SmartList; import com.intellij.util.containers.StringInterner; import gnu.trove.THashMap; -import gnu.trove.TObjectObjectProcedure; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,8 +39,6 @@ import java.util.Set; import static com.intellij.openapi.components.impl.stores.StateMap.getNewByteIfDiffers; public class DirectoryStorageData extends StorageDataBase { - private static final Logger LOG = Logger.getInstance(DirectoryStorageData.class); - public static final String DEFAULT_EXT = ".xml"; private final Map myStates; @@ -87,12 +84,12 @@ public class DirectoryStorageData extends StorageDataBase { } Element element = JDOMUtil.load(file.getInputStream()); - String name = StorageData.getComponentNameIfValid(element); + String name = getComponentNameIfValid(element); if (name == null) { continue; } - if (!element.getName().equals(StorageData.COMPONENT)) { + if (!element.getName().equals(COMPONENT)) { LOG.error("Incorrect root tag name (" + element.getName() + ") in " + file.getPresentableUrl()); continue; } @@ -224,7 +221,7 @@ public class DirectoryStorageData extends StorageDataBase { fileToState.put(fileName, state); } - void processComponent(@NotNull String componentName, @NotNull TObjectObjectProcedure consumer) { + void processComponent(@NotNull String componentName, @NotNull PairConsumer consumer) { StateMap map = myStates.get(componentName); if (map != null) { map.forEachEntry(consumer); @@ -249,7 +246,7 @@ public class DirectoryStorageData extends StorageDataBase { @Nullable public Element getCompositeStateAndArchive(@NotNull String componentName, @SuppressWarnings("deprecation") @NotNull StateSplitter splitter) { StateMap fileToState = myStates.get(componentName); - Element state = new Element(StorageData.COMPONENT); + Element state = new Element(COMPONENT); if (fileToState == null || fileToState.isEmpty()) { return state; } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/StateMap.java b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/StateMap.java index 69c4edf02a21..21faf8231b66 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/StateMap.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/StateMap.java @@ -21,9 +21,9 @@ import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.util.ArrayUtil; +import com.intellij.util.PairConsumer; import com.intellij.util.SystemProperties; -import gnu.trove.THashMap; -import gnu.trove.TObjectObjectProcedure; +import com.intellij.util.containers.ContainerUtil; import org.iq80.snappy.SnappyInputStream; import org.iq80.snappy.SnappyOutputStream; import org.jdom.Element; @@ -40,6 +40,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentMap; @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") public final class StateMap { @@ -50,14 +51,15 @@ public final class StateMap { setOmitEncoding(true). setOmitDeclaration(true); - private final THashMap states; + private final ConcurrentMap states; public StateMap() { - states = new THashMap(); + states = ContainerUtil.newConcurrentMap(); } public StateMap(StateMap stateMap) { - states = new THashMap((Map)stateMap.states); + states = ContainerUtil.newConcurrentMap(stateMap.states.size()); + states.putAll(stateMap.states); } @NotNull @@ -194,8 +196,12 @@ public final class StateMap { return null; } - states.put(key, archiveState((Element)state)); - return (Element)state; + if (states.replace(key, state, archiveState((Element)state))) { + return (Element)state; + } + else { + return getStateAndArchive(key); + } } @NotNull @@ -230,7 +236,9 @@ public final class StateMap { return states.size(); } - public void forEachEntry(@NotNull TObjectObjectProcedure consumer) { - states.forEachEntry(consumer); + public void forEachEntry(@NotNull PairConsumer consumer) { + for (Map.Entry entry : states.entrySet()) { + consumer.consume(entry.getKey(), entry.getValue()); + } } } \ No newline at end of file diff --git a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/StorageData.java b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/StorageData.java index 3d792dbd86d7..a741fc582e5b 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/StorageData.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/StorageData.java @@ -15,49 +15,38 @@ */ package com.intellij.openapi.components.impl.stores; -import com.intellij.application.options.PathMacrosCollector; import com.intellij.openapi.components.PathMacroSubstitutor; -import com.intellij.openapi.components.TrackingPathMacroSubstitutor; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.JDOMUtil; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.SmartHashSet; -import com.intellij.util.containers.StringInterner; import org.jdom.Attribute; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.IOException; -import java.util.*; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; import static com.intellij.openapi.components.impl.stores.StateMap.getNewByteIfDiffers; public class StorageData extends StorageDataBase { private static final Logger LOG = Logger.getInstance(StorageData.class); - public static final String COMPONENT = "component"; - public static final String NAME = "name"; private final StateMap myStates; - protected final String myRootElementName; - - public StorageData() { - this(COMPONENT); - } - public boolean isDirty() { return false; } - public StorageData(@NotNull String rootElementName) { + public StorageData() { myStates = new StateMap(); - myRootElementName = rootElementName; } protected StorageData(@NotNull StorageData storageData) { - myRootElementName = storageData.myRootElementName; myStates = new StateMap(storageData.myStates); } @@ -68,51 +57,16 @@ public class StorageData extends StorageDataBase { } public void load(@NotNull Element rootElement, @Nullable PathMacroSubstitutor pathMacroSubstitutor, boolean intern) { - if (pathMacroSubstitutor != null) { - pathMacroSubstitutor.expandPaths(rootElement); - } - - StringInterner interner = intern ? new StringInterner() : null; - for (Iterator iterator = rootElement.getChildren(COMPONENT).iterator(); iterator.hasNext(); ) { - Element element = iterator.next(); - String name = getComponentNameIfValid(element); - if (name == null || !(element.getAttributes().size() > 1 || !element.getChildren().isEmpty())) { - continue; - } - - iterator.remove(); - if (interner != null) { - JDOMUtil.internElement(element, interner); - } - - myStates.put(name, element); - - if (pathMacroSubstitutor instanceof TrackingPathMacroSubstitutor) { - ((TrackingPathMacroSubstitutor)pathMacroSubstitutor).addUnknownMacros(name, PathMacrosCollector.getMacroNames(element)); - } - - // remove only after "getMacroNames" - some PathMacroFilter requires element name attribute - element.removeAttribute(NAME); - } + load(myStates, rootElement, pathMacroSubstitutor, intern); } @Nullable - static String getComponentNameIfValid(@NotNull Element element) { - String name = element.getAttributeValue(NAME); - if (StringUtil.isEmpty(name)) { - LOG.warn("No name attribute for component in " + JDOMUtil.writeElement(element)); - return null; - } - return name; - } - - @Nullable - protected Element save(@NotNull Map newLiveStates) throws IOException { + public Element save(@NotNull Map newLiveStates, @NotNull String rootElementName) throws IOException { if (myStates.isEmpty()) { return null; } - Element rootElement = new Element(myRootElementName); + Element rootElement = new Element(rootElementName); String[] componentNames = ArrayUtil.toStringArray(myStates.keys()); Arrays.sort(componentNames); for (String componentName : componentNames) { @@ -154,7 +108,7 @@ public class StorageData extends StorageDataBase { } @Nullable - static StorageData setStateAndCloneIfNeed(@NotNull String componentName, @Nullable Element newState, @NotNull StorageData storageData, @NotNull Map newLiveStates) { + public static StorageData setStateAndCloneIfNeed(@NotNull String componentName, @Nullable Element newState, @NotNull StorageData storageData, @NotNull Map newLiveStates) { Object oldState = storageData.myStates.get(componentName); if (newState == null || JDOMUtil.isEmpty(newState)) { if (oldState == null) { @@ -189,7 +143,7 @@ public class StorageData extends StorageDataBase { } @Nullable - final Object setState(@NotNull String componentName, @Nullable Element newState, @NotNull Map newLiveStates) { + public final Object setState(@NotNull String componentName, @Nullable Element newState, @NotNull Map newLiveStates) { if (newState == null || JDOMUtil.isEmpty(newState)) { return myStates.remove(componentName); } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/StorageDataBase.java b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/StorageDataBase.java index f28b3da1900a..4815faf1c4df 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/StorageDataBase.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/StorageDataBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,13 +15,67 @@ */ package com.intellij.openapi.components.impl.stores; +import com.intellij.application.options.PathMacrosCollector; +import com.intellij.openapi.components.PathMacroSubstitutor; +import com.intellij.openapi.components.TrackingPathMacroSubstitutor; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.JDOMUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.containers.StringInterner; +import org.jdom.Element; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.util.Iterator; import java.util.Set; public abstract class StorageDataBase { + protected static final Logger LOG = Logger.getInstance(StorageDataBase.class); + + public static final String COMPONENT = "component"; + public static final String NAME = "name"; + @NotNull public abstract Set getComponentNames(); public abstract boolean hasState(@NotNull String componentName); + + @Nullable + static String getComponentNameIfValid(@NotNull Element element) { + String name = element.getAttributeValue(NAME); + if (StringUtil.isEmpty(name)) { + LOG.warn("No name attribute for component in " + JDOMUtil.writeElement(element)); + return null; + } + return name; + } + + public static void load(@NotNull StateMap states, @NotNull Element rootElement, @Nullable PathMacroSubstitutor pathMacroSubstitutor, boolean intern) { + if (pathMacroSubstitutor != null) { + pathMacroSubstitutor.expandPaths(rootElement); + } + + StringInterner interner = intern ? new StringInterner() : null; + for (Iterator iterator = rootElement.getChildren(COMPONENT).iterator(); iterator.hasNext(); ) { + Element element = iterator.next(); + String name = getComponentNameIfValid(element); + if (name == null || !(element.getAttributes().size() > 1 || !element.getChildren().isEmpty())) { + continue; + } + + iterator.remove(); + if (interner != null) { + JDOMUtil.internElement(element, interner); + } + + states.put(name, element); + + if (pathMacroSubstitutor instanceof TrackingPathMacroSubstitutor) { + ((TrackingPathMacroSubstitutor)pathMacroSubstitutor).addUnknownMacros(name, PathMacrosCollector.getMacroNames(element)); + } + + // remove only after "getMacroNames" - some PathMacroFilter requires element name attribute + element.removeAttribute(NAME); + } + } } \ No newline at end of file diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleRootManagerImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleRootManagerImpl.java index 1d590146419a..8bb4106692b0 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleRootManagerImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ModuleRootManagerImpl.java @@ -16,7 +16,9 @@ package com.intellij.openapi.roots.impl; +import com.intellij.openapi.application.AccessToken; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.*; import com.intellij.openapi.project.Project; @@ -338,8 +340,14 @@ public class ModuleRootManagerImpl extends ModuleRootManager implements ModuleCo } public void loadState(ModuleRootManagerState object) { - loadState(object, myLoaded || isModuleAdded); - myLoaded = true; + AccessToken token = ReadAction.start(); + try { + loadState(object, myLoaded || isModuleAdded); + myLoaded = true; + } + finally { + token.finish(); + } } protected void loadState(ModuleRootManagerState object, boolean throwEvent) { diff --git a/platform/testFramework/src/com/intellij/testFramework/ParsingTestCase.java b/platform/testFramework/src/com/intellij/testFramework/ParsingTestCase.java index 31b0cc5a1312..70ebc35c5aad 100644 --- a/platform/testFramework/src/com/intellij/testFramework/ParsingTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/ParsingTestCase.java @@ -31,23 +31,31 @@ import com.intellij.openapi.fileEditor.impl.LoadTextUtil; import com.intellij.openapi.fileTypes.FileTypeFactory; import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.options.SchemesManagerFactory; +import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.impl.ProgressManagerImpl; import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.pom.PomModel; +import com.intellij.pom.core.impl.PomModelImpl; +import com.intellij.pom.tree.TreeAspect; import com.intellij.psi.*; import com.intellij.psi.impl.DebugUtil; import com.intellij.psi.impl.PsiCachedValuesFactory; import com.intellij.psi.impl.PsiFileFactoryImpl; import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry; import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistryImpl; +import com.intellij.psi.impl.source.text.BlockSupportImpl; +import com.intellij.psi.impl.source.text.DiffLog; import com.intellij.psi.util.CachedValuesManager; import com.intellij.util.CachedValuesManagerImpl; import com.intellij.util.Function; import com.intellij.util.messages.MessageBus; import com.intellij.util.messages.MessageBusFactory; +import junit.framework.TestCase; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.picocontainer.*; @@ -129,6 +137,11 @@ public abstract class ParsingTestCase extends PlatformLiteFixture { if (myDefinitions.length > 0) { configureFromParserDefinition(myDefinitions[0], myFileExt); } + + // That's for reparse routines + final PomModelImpl pomModel = new PomModelImpl(myProject); + myProject.registerService(PomModel.class, pomModel); + new TreeAspect(pomModel); } public void configureFromParserDefinition(ParserDefinition definition, String extension) { @@ -212,6 +225,7 @@ public abstract class ParsingTestCase extends PlatformLiteFixture { assertEquals("virtual file text mismatch", text, LoadTextUtil.loadText(myFile.getVirtualFile())); assertEquals("doc text mismatch", text, myFile.getViewProvider().getDocument().getText()); assertEquals("psi text mismatch", text, myFile.getText()); + ensureCorrectReparse(myFile); if (checkResult){ checkResult(name, myFile); } @@ -314,4 +328,15 @@ public abstract class ParsingTestCase extends PlatformLiteFixture { } }); } + + public static void ensureCorrectReparse(@NotNull final PsiFile file) { + final String psiToStringDefault = DebugUtil.psiToString(file, false, false); + + final String fileText = file.getText(); + final DiffLog diffLog = new BlockSupportImpl(file.getProject()).reparseRange( + file, TextRange.allOf(fileText), fileText, new EmptyProgressIndicator(), fileText); + diffLog.performActualPsiChange(file); + + TestCase.assertEquals(psiToStringDefault, DebugUtil.psiToString(file, false, false)); + } } diff --git a/platform/testFramework/src/com/intellij/testFramework/PsiTestUtil.java b/platform/testFramework/src/com/intellij/testFramework/PsiTestUtil.java index 624d25c8c782..afa500ee4c7e 100644 --- a/platform/testFramework/src/com/intellij/testFramework/PsiTestUtil.java +++ b/platform/testFramework/src/com/intellij/testFramework/PsiTestUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -299,10 +299,10 @@ public class PsiTestUtil { final String libName, final List classesRoots, final List sourceRoots) { + final LibraryTable libraryTable = ProjectLibraryTable.getInstance(module.getProject()); RunResult result = new WriteAction() { @Override protected void run(@NotNull Result result) throws Throwable { - LibraryTable libraryTable = ProjectLibraryTable.getInstance(module.getProject()); Library library = libraryTable.createLibrary(libName); Library.ModifiableModel libraryModel = library.getModifiableModel(); try { diff --git a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java index a850c0424dba..e89f250a6559 100644 --- a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java +++ b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java @@ -56,9 +56,7 @@ import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.ActionManagerEx; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.Result; -import com.intellij.openapi.application.WriteAction; +import com.intellij.openapi.application.*; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.editor.*; @@ -1540,16 +1538,20 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig private PsiFile configureInner(@NotNull final VirtualFile copy, @NotNull final SelectionAndCaretMarkupLoader loader) { assertInitialized(); - new WriteCommandAction.Simple(getProject()) { + ApplicationManager.getApplication().invokeAndWait(new Runnable() { @Override public void run() { if (!copy.getFileType().isBinary()) { + AccessToken token = WriteAction.start(); try { copy.setBinaryContent(loader.newFileText.getBytes(copy.getCharset())); } catch (IOException e) { throw new RuntimeException(e); } + finally { + token.finish(); + } } myFile = copy; myEditor = createEditor(copy); @@ -1571,7 +1573,7 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig setupEditorForInjectedLanguage(); } } - }.execute().throwException(); + }, ModalityState.any()); return getFile(); } diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 5affd420ca95..b6e22632e3fb 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -531,6 +531,9 @@ editor.disable.rtl.description=Disables RTL support in editor (all characters ar editor.new.rendering=false editor.new.rendering.description=Enables new editor rendering code, with support for RTL. +editor.zero.latency.typing=false +editor.zero.latency.typing.description=Display single-line changes immediately (before write action and general painting routine) + editor.enable.optional.ligatures=false editor.enable.optional.ligatures.description=Enables optional ligatures supported by font in use editor.enable.optional.ligatures.restartRequired=true @@ -641,7 +644,3 @@ ruby.remote.debugger.supports.catchpoint.removal=true use.read.action.to.init.service=true ruby.debugger.support.file.filtering=false - -color.blindness= -color.blindness.restartRequired=true -color.blindness.description=Use one of the following values: deuteranopia, protanopia, tritanopia, achromatopsia diff --git a/platform/util/src/com/intellij/util/containers/FilteredTraverser.java b/platform/util/src/com/intellij/util/containers/FilteredTraverser.java index 2d0106a243b9..21d16ca51756 100644 --- a/platform/util/src/com/intellij/util/containers/FilteredTraverser.java +++ b/platform/util/src/com/intellij/util/containers/FilteredTraverser.java @@ -31,6 +31,6 @@ public class FilteredTraverser extends FilteredTraverserBase newInstance(Meta meta) { - return new FilteredTraverser(meta, treeStructure); + return new FilteredTraverser(meta, tree); } } \ No newline at end of file diff --git a/platform/util/src/com/intellij/util/containers/FilteredTraverserBase.java b/platform/util/src/com/intellij/util/containers/FilteredTraverserBase.java index 940fa0aa164f..fa2e6770027e 100644 --- a/platform/util/src/com/intellij/util/containers/FilteredTraverserBase.java +++ b/platform/util/src/com/intellij/util/containers/FilteredTraverserBase.java @@ -25,12 +25,13 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; -public abstract class FilteredTraverserBase> extends TreeTraverser implements Iterable { +public abstract class FilteredTraverserBase> implements Iterable { protected final Meta meta; + protected final Function> tree; - protected FilteredTraverserBase(@Nullable Meta meta, Function> provider) { - super(provider); + protected FilteredTraverserBase(@Nullable Meta meta, Function> tree) { + this.tree = tree; this.meta = meta == null ? Meta.empty() : meta; } @@ -46,50 +47,56 @@ public abstract class FilteredTraverserBase iterator() { - return rawIterable().iterator(); + return traverse().iterator(); } @NotNull protected abstract Self newInstance(Meta meta); @NotNull - public JBIterable rawIterable() { + public JBIterable traverse(TreeTraversal traversal) { + Function> adjusted = new Function>() { + @Override + public Iterable fun(T t) { + return children(t); + } + }; + return traversal.traversal(getRoots(), adjusted).filter(meta.resultFilter); + } + + @NotNull + public JBIterable traverse() { return meta.skipExpanded ? leavesOnlyDfsTraversal() : preOrderDfsTraversal(); } @NotNull public final JBIterable preOrderDfsTraversal() { - return adjust(preOrderDfsTraversal(getRoots())); + return traverse(TreeTraversal.PRE_ORDER_DFS); } @NotNull public final JBIterable postOrderDfsTraversal() { - return adjust(postOrderDfsTraversal(getRoots())); + return traverse(TreeTraversal.POST_ORDER_DFS); } @NotNull public final JBIterable leavesOnlyDfsTraversal() { - return adjust(leavesOnlyDfsTraversal(getRoots())); + return traverse(TreeTraversal.LEAVES_ONLY_DFS); } @NotNull public final JBIterable bfsTraversal() { - return adjust(bfsTraversal(getRoots())); + return traverse(TreeTraversal.PLAIN_BFS); } @NotNull public final JBIterable tracingBfsTraversal() { - return adjust(tracingBfsTraversal(getRoots())); + return traverse(TreeTraversal.TRACING_BFS); } @NotNull public final JBIterable leavesOnlyBfsTraversal() { - return adjust(leavesOnlyBfsTraversal(getRoots())); - } - - @NotNull - private JBIterable adjust(JBIterable traversal) { - return traversal.filter(meta.resultFilter); + return traverse(TreeTraversal.LEAVES_ONLY_BFS); } @NotNull @@ -139,7 +146,7 @@ public abstract class FilteredTraverserBase JBIterable filter(@NotNull Class type) { - return rawIterable().filter(type); + return traverse().filter(type); } @NotNull @@ -148,13 +155,12 @@ public abstract class FilteredTraverserBase children(@NotNull T node) { if (isAlwaysLeaf(node)) return JBIterable.empty(); - Iterable children = super.children(node); - if (meta.childFilter == Conditions.TRUE) return JBIterable.from(children).filter(Conditions.not(meta.excludeFilter)); + JBIterable children = JBIterable.from(tree.fun(node)); + if (meta.childFilter == Conditions.TRUE) return children.filter(Conditions.not(meta.excludeFilter)); // traverse subtree to select accepted children - return newInstance(meta.forChildren(children)).rawIterable(); + return newInstance(meta.forChildren(children)).traverse(); } protected boolean isAlwaysLeaf(@NotNull T node) { @@ -163,12 +169,12 @@ public abstract class FilteredTraverserBase toList() { - return rawIterable().toList(); + return traverse().toList(); } @Override public String toString() { - return rawIterable().toString(); + return traverse().toString(); } diff --git a/platform/util/src/com/intellij/util/containers/JBIterable.java b/platform/util/src/com/intellij/util/containers/JBIterable.java index c2d34b994d58..9893805e5bca 100644 --- a/platform/util/src/com/intellij/util/containers/JBIterable.java +++ b/platform/util/src/com/intellij/util/containers/JBIterable.java @@ -108,6 +108,7 @@ public abstract class JBIterable implements Iterable { return new JBIterable() { @Override public Iterator iterator() { + final Function fun = Stateful.copy(generator); return new JBIterator() { E cur = first; @@ -115,7 +116,7 @@ public abstract class JBIterable implements Iterable { public E nextImpl() { E result = cur; if (result == null) return stop(); - cur = generator.fun(cur); + cur = fun.fun(cur); return result; } }; @@ -150,8 +151,8 @@ public abstract class JBIterable implements Iterable { * Returns a {@code JBIterable} containing {@code elements} in the specified order. */ @NotNull - public static JBIterable of(E... elements) { - return from(ContainerUtil.newArrayList(elements)); + public static JBIterable of(@Nullable E... elements) { + return elements == null ? JBIterable.empty() : from(ContainerUtil.newArrayList(elements)); } private static final JBIterable EMPTY = new JBIterable() { @@ -228,14 +229,14 @@ public abstract class JBIterable implements Iterable { * {@code Iterator} supports it. */ public final JBIterable append(@Nullable Iterable other) { - return other == null ? this : this == EMPTY ? from(other) : from(ContainerUtil.concat(myIterable, other)); + return other == null ? this : this == EMPTY ? from(other) : of(myIterable, other).flatten(Functions., Iterable>identity()); } public final JBIterable append(@Nullable Iterable other, @NotNull Function> fun) { return other == null ? this : this == EMPTY ? from(other).flatten(fun) : append(from(other).flatten(fun)); } - public final JBIterable repeat(final int count) { + public final JBIterable repeat(int count) { Function, JBIterable> fun = Functions.identity(); return generate(this, fun).take(count).flatten(fun); } @@ -259,7 +260,7 @@ public abstract class JBIterable implements Iterable { return intercept(new Function, Iterator>() { @Override public Iterator fun(Iterator iterator) { - return JBIterator.from(iterator).filter(condition); + return JBIterator.from(iterator).filter(Stateful.copy(condition)); } }); } @@ -288,7 +289,7 @@ public abstract class JBIterable implements Iterable { return intercept(new Function, Iterator>() { @Override public Iterator fun(Iterator iterator) { - return JBIterator.from(iterator).takeWhile(condition); + return JBIterator.from(iterator).takeWhile(Stateful.copy(condition)); } }); } @@ -308,7 +309,7 @@ public abstract class JBIterable implements Iterable { return intercept(new Function, Iterator>() { @Override public Iterator fun(Iterator iterator) { - return JBIterator.from(iterator).skipWhile(condition); + return JBIterator.from(iterator).skipWhile(Stateful.copy(condition)); } }); } @@ -326,7 +327,7 @@ public abstract class JBIterable implements Iterable { return intercept(new Function, Iterator>() { @Override public Iterator fun(Iterator iterator) { - return JBIterator.from(iterator).transform(function); + return JBIterator.from(iterator).transform(Stateful.copy(function)); } }); } @@ -344,13 +345,14 @@ public abstract class JBIterable implements Iterable { return intercept(new Function, Iterator>() { @Override public Iterator fun(final Iterator iterator) { + final Function> fun = Stateful.copy(function); return new JBIterator() { Iterator cur; @Override public T nextImpl() { while ((cur == null || !cur.hasNext()) && iterator.hasNext()) { - cur = function.fun(iterator.next()).iterator(); + cur = fun.fun(iterator.next()).iterator(); } if (cur == null || !cur.hasNext()) return stop(); return cur.next(); @@ -456,4 +458,33 @@ public abstract class JBIterable implements Iterable { } return collection; } + + @SuppressWarnings("unchecked") + public abstract static class Stateful implements Cloneable { + + @NotNull + static T copy(@NotNull T o) { + if (!(o instanceof Stateful)) { + return o; + } + return (T)((Stateful)o).clone(); + } + + public Self clone() { + try { + return (Self)super.clone(); + } + catch (CloneNotSupportedException e) { + throw new AssertionError(e); + } + } + } + + public abstract static class StatefulFilter extends Stateful implements Condition { + + } + + public abstract static class StatefulTransform extends Stateful implements Function { + + } } diff --git a/platform/util/src/com/intellij/util/containers/JBIterator.java b/platform/util/src/com/intellij/util/containers/JBIterator.java index c197d479ca61..ababafdf6474 100644 --- a/platform/util/src/com/intellij/util/containers/JBIterator.java +++ b/platform/util/src/com/intellij/util/containers/JBIterator.java @@ -38,7 +38,7 @@ import java.util.NoSuchElementException; * * @see JBIterable#transform(Function) * @see JBIterable#filter(Condition) - * @see TreeTraverser.TracingIt + * @see TreeTraversal.TracingIt * * @author gregsh * @@ -68,7 +68,7 @@ public abstract class JBIterator implements Iterator { } private Object cur = INIT; - private final Op firstOp = new Op(); + private final Op firstOp = new Op(null); private Op lastOp = firstOp; protected abstract E nextImpl(); @@ -125,31 +125,21 @@ public abstract class JBIterator implements Iterator { } @NotNull - public final JBIterator transform(@NotNull final Function function) { - return addOp(new Op() { + public final JBIterator transform(@NotNull Function function) { + return addOp(new Op>(function) { @Override public Object apply(Object o) { - return function.fun((E)o); - } - - @Override - public String toString() { - return toShortString(function); + return impl.fun((E)o); } }); } @NotNull - public final JBIterator filter(@NotNull final Condition condition) { - return addOp(new Op() { + public final JBIterator filter(@NotNull Condition condition) { + return addOp(new Op>(condition) { @Override public Object apply(Object o) { - return condition.value((E)o) ? o : SKIP; - } - - @Override - public String toString() { - return toShortString(condition); + return impl.value((E)o) ? o : SKIP; } }); } @@ -160,16 +150,16 @@ public abstract class JBIterator implements Iterator { } @NotNull - public final JBIterator takeWhile(@NotNull final Condition condition) { - return addOp(new Op() { + public final JBIterator takeWhile(@NotNull Condition condition) { + return addOp(new Op>(condition) { @Override public Object apply(Object o) { - return condition.value((E)o) ? o : stop(); + return impl.value((E)o) ? o : stop(); } @Override public String toString() { - return "while:" + toShortString(condition); + return "takeWhile:" + super.toString(); } }); } @@ -181,7 +171,7 @@ public abstract class JBIterator implements Iterator { @NotNull public final JBIterator skipWhile(@NotNull final Condition condition) { - return addOp(new Op() { + return addOp(new Op>(condition) { boolean active = true; @@ -194,7 +184,7 @@ public abstract class JBIterator implements Iterator { @Override public String toString() { - return "skip:" + toShortString(condition); + return "skipWhile:" + super.toString(); } }); } @@ -218,13 +208,28 @@ public abstract class JBIterator implements Iterator { @Override public String toString() { - JBIterable ops = JBIterable.generate(firstOp.nextOp, new Function() { + JBIterable ops = operationsImpl(); + return "{cur=" + cur + "; ops[" + ops.size() + "]=" + ops + "}"; + } + + @NotNull + public JBIterable operations() { + return operationsImpl().transform(new Function() { + @Override + public Object fun(Op op) { + return op.impl; + } + }); + } + + @NotNull + private JBIterable operationsImpl() { + return JBIterable.generate(firstOp.nextOp, new Function() { @Override public Op fun(Op op) { return op.nextOp; } }); - return "{cur=" + cur + "; ops[" + ops.size() + "]=" + ops + "}"; } private static String toShortString(@NotNull Object o) { @@ -239,12 +244,22 @@ public abstract class JBIterator implements Iterator { } }; - private static class Op { + private static class Op { + final T impl; Op nextOp; + public Op(T impl) { + this.impl = impl; + } + Object apply(Object o) { throw new UnsupportedOperationException(); } + + @Override + public String toString() { + return impl == null ? "" : toShortString(impl); + } } private static class CountDown implements Condition { diff --git a/platform/util/src/com/intellij/util/containers/TreeTraverser.java b/platform/util/src/com/intellij/util/containers/TreeTraversal.java similarity index 55% rename from platform/util/src/com/intellij/util/containers/TreeTraverser.java rename to platform/util/src/com/intellij/util/containers/TreeTraversal.java index fc13a0c1232a..fc0e98006519 100644 --- a/platform/util/src/com/intellij/util/containers/TreeTraverser.java +++ b/platform/util/src/com/intellij/util/containers/TreeTraversal.java @@ -27,8 +27,10 @@ import java.util.Iterator; import java.util.Map; /** - * A pruned version of com.google.common.collect.TreeTraverser. - * + * A redesigned version of com.google.common.collect.TreeTraversal. + *

+ * The original JavaDoc: + *

* Views elements of a type {@code T} as nodes in a tree, and provides methods to traverse the trees * induced by this traverser. * @@ -43,31 +45,70 @@ import java.util.Map; * / | \ f * a b c } * - *

can be iterated over in preorder (hdabcegf), postorder (abcdefgh), or breadth-first order + *

can be iterated over in pre-order (hdabcegf), post-order (abcdefgh), or breadth-first order * (hdegabcf). * *

Null nodes are strictly forbidden. * * @author Louis Wasserman + *

+ * + * @author gregsh */ -public class TreeTraverser { +public abstract class TreeTraversal { - protected final Function> treeStructure; + private final String debugName; - public TreeTraverser(Function> provider) { - treeStructure = provider; + protected TreeTraversal(@NotNull String debugName) { + this.debugName = debugName; + } + + @NotNull + public JBIterable traversal(@NotNull final Iterable roots, @NotNull final Function> tree) { + return new JBIterable() { + @NotNull + @Override + public Iterator iterator() { + return createIterator(roots, tree); + } + }; + } + + @NotNull + public JBIterable traversal(@Nullable final T root, @NotNull final Function> tree) { + return traversal(ContainerUtil.createMaybeSingletonList(root), tree); + } + + @NotNull + public Function> traversal(@NotNull final Function> tree) { + return new Function>() { + @Override + public JBIterable fun(T t) { + return traversal(t, tree); + } + }; } /** - * Returns the children of the specified node. Must not contain null. + * Creates a new iterator for this type of traversal. + * @param roots tree roots + * @param tree tree structure the children for parent function. + * May return null (useful for map representation). */ @NotNull - public Iterable children(@NotNull T root) { - Iterable result = treeStructure.fun(root); - return result != null ? result : JBIterable.empty(); + public abstract It createIterator(@NotNull Iterable roots, @NotNull Function> tree); + + @Override + public String toString() { + return debugName; } - public static abstract class TracingIt extends JBIterator { + public static abstract class It extends JBIterator { + + } + + public static abstract class TracingIt extends It { + @Nullable public abstract T parent(); @@ -75,113 +116,86 @@ public class TreeTraverser { public abstract JBIterable backtrace(); } + @NotNull + private static Iterable children(@NotNull T root, @NotNull Function> tree) { + return JBIterable.from(tree.fun(root)); + } + /** - * Returns an unmodifiable iterable over the nodes in a tree structure, using pre-order + * Returns an iterator over the nodes in a tree structure, using pre-order * traversal. That is, each node's subtrees are traversed after the node itself is returned. * *

No guarantees are made about the behavior of the traversal when nodes change while * iteration is in progress or when the iterators generated by {@link #children} are advanced. */ @NotNull - public final JBIterable preOrderDfsTraversal(@NotNull final Iterable roots) { - return new JBIterable() { - @Override - public TracingIt iterator() { - return new PreOrderIt((Iterable)roots); - } - }; - } + public static final TreeTraversal PRE_ORDER_DFS = new TreeTraversal("PRE_ORDER_DFS") { + @NotNull + @Override + public It createIterator(@NotNull Iterable roots, @NotNull Function> tree) { + return new PreOrderIt(roots, tree); + } + }; /** - * @see #preOrderDfsTraversal(Iterable) - */ - @NotNull - public final JBIterable preOrderDfsTraversal(@Nullable T root) { - return preOrderDfsTraversal(ContainerUtil.createMaybeSingletonList(root)); - } - - - /** - * Returns an unmodifiable iterable over the nodes in a tree structure, using post-order + * Returns an iterator over the nodes in a tree structure, using post-order * traversal. That is, each node's subtrees are traversed before the node itself is returned. *

*

No guarantees are made about the behavior of the traversal when nodes change while * iteration is in progress or when the iterators generated by {@link #children} are advanced. */ @NotNull - public final JBIterable postOrderDfsTraversal(@NotNull final Iterable roots) { - return new JBIterable() { - @Override - public Iterator iterator() { - return new PostOrderIt(roots); - } - }; - } + public static final TreeTraversal POST_ORDER_DFS = new TreeTraversal("POST_ORDER_DFS") { + @NotNull + @Override + public It createIterator(@NotNull Iterable roots, @NotNull Function> tree) { + return new PostOrderIt(roots, tree); + } + }; - /** - * @see #postOrderDfsTraversal(Iterable) - */ - @NotNull - public final JBIterable postOrderDfsTraversal(@Nullable T root) { - return postOrderDfsTraversal(ContainerUtil.createMaybeSingletonList(root)); - } @NotNull - public final JBIterable leavesOnlyDfsTraversal(@NotNull final Iterable roots) { - return new JBIterable() { - @Override - public Iterator iterator() { - return new LeavesDfsIt(roots); - } - }; - } + public static final TreeTraversal LEAVES_ONLY_DFS = new TreeTraversal("LEAVES_ONLY_DFS") { + @NotNull + @Override + public It createIterator(@NotNull Iterable roots, @NotNull Function> tree) { + return new LeavesDfsIt(roots, tree); + } + }; /** - * Returns an unmodifiable iterable over the nodes in a tree structure, using breadth-first + * Returns an iterator over the nodes in a tree structure, using breadth-first * traversal. That is, all the nodes of depth 0 are returned, then depth 1, then 2, and so on. *

*

No guarantees are made about the behavior of the traversal when nodes change while * iteration is in progress or when the iterators generated by {@link #children} are advanced. */ @NotNull - public final JBIterable bfsTraversal(@NotNull final Iterable roots) { - return new JBIterable() { - @Override - public Iterator iterator() { - return new BfsIterator(roots); - } - }; - } + public static final TreeTraversal PLAIN_BFS = new TreeTraversal("PLAIN_BFS") { + @NotNull + @Override + public It createIterator(@NotNull Iterable roots, @NotNull Function> tree) { + return new BfsIterator(roots, tree); + } + }; @NotNull - public final JBIterable bfsTraversal(@Nullable T root) { - return bfsTraversal(ContainerUtil.createMaybeSingletonList(root)); - } + public static final TreeTraversal TRACING_BFS = new TreeTraversal("TRACING_BFS") { + @NotNull + @Override + public It createIterator(@NotNull Iterable roots, @NotNull Function> tree) { + return new TracingBfsIt(roots, tree); + } + }; @NotNull - public final JBIterable tracingBfsTraversal(@NotNull final Iterable roots) { - return new JBIterable() { - @Override - public TracingIt iterator() { - return new TracingBfsIt(roots); - } - }; - } - - @NotNull - public final JBIterable tracingBfsTraversal(@Nullable T root) { - return tracingBfsTraversal(ContainerUtil.createMaybeSingletonList(root)); - } - - @NotNull - public final JBIterable leavesOnlyBfsTraversal(@NotNull final Iterable roots) { - return new JBIterable() { - @Override - public Iterator iterator() { - return new LeavesBfsIt(roots); - } - }; - } + public static final TreeTraversal LEAVES_ONLY_BFS = new TreeTraversal("LEAVES_ONLY_BFS") { + @NotNull + @Override + public It createIterator(@NotNull Iterable roots, @NotNull Function> tree) { + return new LeavesBfsIt(roots, tree); + } + }; // ----------------------------------------------------------------------------- @@ -211,12 +225,14 @@ public class TreeTraverser { } } - private final class PreOrderIt extends DfsIt { + private final static class PreOrderIt extends DfsIt { + final Function> tree; int doneCount; - PreOrderIt(@NotNull Iterable roots) { - Iterator iterator = roots.iterator(); + PreOrderIt(@NotNull Iterable roots, Function> tree) { + this.tree = tree; + Iterator iterator = roots.iterator(); if (iterator.hasNext()) { stack.addLast(Pair.>create(null, iterator)); } @@ -232,18 +248,21 @@ public class TreeTraverser { } T result = top.second.next(); if (!top.second.hasNext()) doneCount++; - Iterator childItr = children(result).iterator(); + Iterator childItr = children(result, tree).iterator(); stack.addLast(Pair.>create(result, childItr)); if (!childItr.hasNext()) doneCount++; return result; } } - private final class PostOrderIt extends DfsIt { + private static final class PostOrderIt extends DfsIt { - PostOrderIt(@NotNull Iterable roots) { + final Function> tree; + + PostOrderIt(@NotNull Iterable roots, Function> tree) { + this.tree = tree; for (T root : roots) { - stack.addLast(Pair.>create(root, children(root).iterator())); + stack.addLast(Pair.>create(root, children(root, tree).iterator())); } } @@ -253,7 +272,7 @@ public class TreeTraverser { Pair> top = stack.getLast(); if (top.second.hasNext()) { T child = top.second.next(); - stack.addLast(Pair.>create(child, children(child).iterator())); + stack.addLast(Pair.>create(child, children(child, tree).iterator())); } else { stack.removeLast(); @@ -264,11 +283,14 @@ public class TreeTraverser { } } - private final class LeavesDfsIt extends DfsIt { + private final static class LeavesDfsIt extends DfsIt { - LeavesDfsIt(@NotNull Iterable roots) { + final Function> tree; + + LeavesDfsIt(@NotNull Iterable roots, Function> tree) { + this.tree = tree; for (T root : roots) { - Iterator childrenIt = children(root).iterator(); + Iterator childrenIt = children(root, tree).iterator(); stack.addLast(Pair.>create(root, childrenIt.hasNext() ? childrenIt : null)); } } @@ -279,7 +301,7 @@ public class TreeTraverser { Pair> top = stack.getLast(); if (top.second != null && top.second.hasNext()) { T child = top.second.next(); - Iterator childrenIt = children(child).iterator(); + Iterator childrenIt = children(child, tree).iterator(); if (childrenIt.hasNext()) { stack.addLast(Pair.>create(child, childrenIt)); } @@ -296,10 +318,13 @@ public class TreeTraverser { } } - private final class BfsIterator extends JBIterator { + private static final class BfsIterator extends It { + + final Function> tree; final ArrayDeque queue = new ArrayDeque(); - BfsIterator(@NotNull Iterable roots) { + BfsIterator(@NotNull Iterable roots, Function> tree) { + this.tree = tree; JBIterable.from(roots).addAllTo(queue); } @@ -307,15 +332,18 @@ public class TreeTraverser { public T nextImpl() { if (queue.isEmpty()) return stop(); T result = queue.remove(); - for (T t : children(result)) queue.add(t); + for (T t : children(result, tree)) queue.add(t); return result; } } - private final class LeavesBfsIt extends JBIterator { + private static final class LeavesBfsIt extends It { + + final Function> tree; final ArrayDeque queue = new ArrayDeque(); - LeavesBfsIt(@NotNull Iterable roots) { + LeavesBfsIt(@NotNull Iterable roots, Function> tree) { + this.tree = tree; JBIterable.from(roots).addAllTo(queue); } @@ -323,7 +351,7 @@ public class TreeTraverser { public T nextImpl() { while (!queue.isEmpty()) { T result = queue.remove(); - Iterator childrenIt = children(result).iterator(); + Iterator childrenIt = children(result, tree).iterator(); if (!childrenIt.hasNext()) return result; while (childrenIt.hasNext()) queue.add(childrenIt.next()); } @@ -331,12 +359,15 @@ public class TreeTraverser { } } - private final class TracingBfsIt extends TracingIt { + private final static class TracingBfsIt extends TracingIt { + + final Function> tree; final ArrayDeque queue = new ArrayDeque(); final Map paths = ContainerUtil.newTroveMap(ContainerUtil.identityStrategy()); T cur; - TracingBfsIt(@NotNull Iterable roots) { + TracingBfsIt(@NotNull Iterable roots, Function> tree) { + this.tree = tree; JBIterable.from(roots).addAllTo(queue); } @@ -344,7 +375,7 @@ public class TreeTraverser { public T nextImpl() { if (queue.isEmpty()) return stop(); T result = queue.remove(); - for (T t : children(result)) { + for (T t : children(result, tree)) { if (paths.containsKey(t)) continue; queue.add(t); paths.put(t, result); diff --git a/platform/util/src/com/intellij/util/ui/JBUI.java b/platform/util/src/com/intellij/util/ui/JBUI.java index 1684f553268f..91dd0dd296d2 100644 --- a/platform/util/src/com/intellij/util/ui/JBUI.java +++ b/platform/util/src/com/intellij/util/ui/JBUI.java @@ -85,6 +85,10 @@ public class JBUI { else if (scale < 2f) scale = 1.75f; else scale = 2.0f; + if (SystemInfo.isLinux && scale == 1.25f) { + //Default UI font size for Unity and Gnome is 15. Scaling factor 1.25f works badly on Linux + scale = 1f; + } SCALE_FACTOR = scale; IconLoader.setScale(scale); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/UpdateRequestsQueue.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/UpdateRequestsQueue.java index dfe9c9b8474d..bdc806669ae0 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/UpdateRequestsQueue.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/UpdateRequestsQueue.java @@ -28,6 +28,7 @@ import com.intellij.util.Consumer; import com.intellij.util.concurrency.Semaphore; import com.intellij.util.io.storage.HeavyProcessLatch; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; import javax.swing.*; import java.util.ArrayList; @@ -150,6 +151,7 @@ public class UpdateRequestsQueue { LOG.debug("Stop finished for project: " + myProject.getName()); } + @TestOnly public void waitUntilRefreshed() { while (true) { final Semaphore semaphore = new Semaphore(); @@ -158,6 +160,10 @@ public class UpdateRequestsQueue { return; } + if (!myRequestRunning) { + myExecutor.get().schedule(new MyRunnable(), 0, TimeUnit.MILLISECONDS); + } + semaphore.down(); myWaitingUpdateCompletionSemaphores.add(semaphore); } @@ -236,6 +242,8 @@ public class UpdateRequestsQueue { final List copy = new ArrayList(myWaitingUpdateCompletionQueue.size()); try { synchronized (myLock) { + if (!myRequestSubmitted) return; + LOG.assertTrue(!myRequestRunning); myRequestRunning = true; if (myStopped) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/RollbackAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/RollbackAction.java index ba057fede2ab..ca07a1675a4f 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/RollbackAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/RollbackAction.java @@ -134,9 +134,13 @@ public class RollbackAction extends AnAction implements DumbAware { }); } - if (!changes.isEmpty() || !hasChanges) { + if (!changes.isEmpty()) { RollbackChangesDialog.rollbackChanges(project, changes); } + else if (!hasChanges) { + List changelistChanges = getCurrentChangelistChanges(project); + RollbackChangesDialog.rollbackChanges(project, changelistChanges); + } } @NotNull @@ -158,13 +162,16 @@ public class RollbackAction extends AnAction implements DumbAware { if (changes != null && changes.length > 0) { return ContainerUtil.newArrayList(changes); } + return Collections.emptyList(); + } + @NotNull + private static List getCurrentChangelistChanges(final Project project) { final ChangeListManager clManager = ChangeListManager.getInstance(project); ChangeList list = clManager.getDefaultChangeList(); if (list != null) { return ContainerUtil.newArrayList(list.getChanges()); } - return Collections.emptyList(); } diff --git a/platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/AbstractVcsTestCase.java b/platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/AbstractVcsTestCase.java index c4468ae8cb14..d459e971b910 100644 --- a/platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/AbstractVcsTestCase.java +++ b/platform/vcs-impl/testSrc/com/intellij/testFramework/vcs/AbstractVcsTestCase.java @@ -77,11 +77,11 @@ public abstract class AbstractVcsTestCase { ChangeListManagerImpl.getInstanceImpl(myProject).waitUntilRefreshed(); } - protected static void refreshVfs() { + protected void refreshVfs() { UsefulTestCase.edt(new Runnable() { @Override public void run() { - LocalFileSystem.getInstance().refresh(false); + myWorkingCopyDir.refresh(false, true); } }); } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/handlers/XDebuggerEvaluateActionHandler.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/handlers/XDebuggerEvaluateActionHandler.java index 92e353b9346c..9afff78ef6e5 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/handlers/XDebuggerEvaluateActionHandler.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/handlers/XDebuggerEvaluateActionHandler.java @@ -79,12 +79,14 @@ public class XDebuggerEvaluateActionHandler extends XDebuggerActionHandler { value.calculateEvaluationExpression().done(new Consumer() { @Override public void consume(final XExpression expression) { - AppUIUtil.invokeOnEdt(new Runnable() { - @Override - public void run() { - showDialog(session, file, editorsProvider, stackFrame, evaluator, expression); - } - }); + if (expression != null) { + AppUIUtil.invokeOnEdt(new Runnable() { + @Override + public void run() { + showDialog(session, file, editorsProvider, stackFrame, evaluator, expression); + } + }); + } } }); return; @@ -100,7 +102,7 @@ public class XDebuggerEvaluateActionHandler extends XDebuggerActionHandler { XDebuggerEditorsProvider editorsProvider, XStackFrame stackFrame, XDebuggerEvaluator evaluator, - XExpression expression) { + @NotNull XExpression expression) { if (expression.getLanguage() == null) { Language language = null; if (stackFrame != null) { diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointManagerImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointManagerImpl.java index 0c669f7b691a..fed4cdc556c7 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointManagerImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointManagerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,9 @@ package com.intellij.xdebugger.impl.breakpoints; import com.intellij.openapi.Disposable; +import com.intellij.openapi.application.AccessToken; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; @@ -405,33 +407,39 @@ public class XBreakpointManagerImpl implements XBreakpointManager, PersistentSta myDefaultBreakpoints.clear(); myBreakpointsDefaults.clear(); - for (BreakpointState breakpointState : state.getDefaultBreakpoints()) { - loadBreakpoint(breakpointState, true); - } - for (XBreakpointType type : XBreakpointUtil.getBreakpointTypes()) { - if (!myDefaultBreakpoints.containsKey(type)) { - addDefaultBreakpoint(type); + AccessToken token = ReadAction.start(); + try { + for (BreakpointState breakpointState : state.getDefaultBreakpoints()) { + loadBreakpoint(breakpointState, true); } - } - - for (XBreakpointBase breakpoint : myBreakpoints.values()) { - doRemoveBreakpoint(breakpoint); - } - for (BreakpointState breakpointState : state.getBreakpoints()) { - loadBreakpoint(breakpointState, false); - } - - for (BreakpointState defaults : state.getBreakpointsDefaults()) { - XBreakpointType type = XBreakpointUtil.findType(defaults.getTypeId()); - if (type != null) { - myBreakpointsDefaults.put(type, defaults); + for (XBreakpointType type : XBreakpointUtil.getBreakpointTypes()) { + if (!myDefaultBreakpoints.containsKey(type)) { + addDefaultBreakpoint(type); + } } - else { - LOG.warn("Unknown breakpoint type " + defaults.getTypeId()); - } - } - myDependentBreakpointManager.loadState(); + for (XBreakpointBase breakpoint : myBreakpoints.values()) { + doRemoveBreakpoint(breakpoint); + } + for (BreakpointState breakpointState : state.getBreakpoints()) { + loadBreakpoint(breakpointState, false); + } + + for (BreakpointState defaults : state.getBreakpointsDefaults()) { + XBreakpointType type = XBreakpointUtil.findType(defaults.getTypeId()); + if (type != null) { + myBreakpointsDefaults.put(type, defaults); + } + else { + LOG.warn("Unknown breakpoint type " + defaults.getTypeId()); + } + } + + myDependentBreakpointManager.loadState(); + } + finally { + token.finish(); + } myLineBreakpointManager.updateBreakpointsUI(); myTime = state.getTime(); myDefaultGroup = state.getDefaultGroup(); diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/VariableAccessVisitor.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/VariableAccessVisitor.java index 3c7f63982889..6060e3cc953a 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/VariableAccessVisitor.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/VariableAccessVisitor.java @@ -28,6 +28,7 @@ import org.jetbrains.annotations.NotNull; import java.util.Collection; import java.util.HashSet; import java.util.Set; +import java.util.Stack; class VariableAccessVisitor extends JavaRecursiveElementWalkingVisitor { @@ -44,6 +45,7 @@ class VariableAccessVisitor extends JavaRecursiveElementWalkingVisitor { private final Set usedMethods = new HashSet(); private boolean m_inInitializer; private int m_inSynchronizedContextCount; + private final Stack contextStack = new Stack(); private boolean privateMethodUsagesCalculated; private final boolean countGettersAndSetters; @@ -56,14 +58,16 @@ class VariableAccessVisitor extends JavaRecursiveElementWalkingVisitor { public void visitClass(PsiClass classToVisit) { calculatePrivateMethodUsagesIfNecessary(); if (!classToVisit.equals(aClass)) { - m_inSynchronizedContextCount -= 1000; + contextStack.push(m_inSynchronizedContextCount); + m_inSynchronizedContextCount = 0; } super.visitClass(classToVisit); } @Override public void visitLambdaExpression(PsiLambdaExpression expression) { - m_inSynchronizedContextCount -= 1000; + contextStack.push(m_inSynchronizedContextCount); + m_inSynchronizedContextCount = 0; super.visitLambdaExpression(expression); } @@ -127,7 +131,7 @@ class VariableAccessVisitor extends JavaRecursiveElementWalkingVisitor { @Override public void visitCodeBlock(PsiCodeBlock block) { if (block.getParent() instanceof PsiSynchronizedStatement) { - m_inSynchronizedContextCount ++; + m_inSynchronizedContextCount++; } super.visitCodeBlock(block); } @@ -137,7 +141,7 @@ class VariableAccessVisitor extends JavaRecursiveElementWalkingVisitor { public void visitAssertStatement(PsiAssertStatement statement) { final PsiExpression condition = statement.getAssertCondition(); if (SynchronizationUtil.isCallToHoldsLock(condition)) { - m_inSynchronizedContextCount ++; + m_inSynchronizedContextCount++; PsiElement codeBlock = statement.getParent(); if (codeBlock != null) { codeBlock.putUserData(CODE_BLOCK_CONTAINS_HOLDS_LOCK_CALL, true); @@ -349,11 +353,11 @@ class VariableAccessVisitor extends JavaRecursiveElementWalkingVisitor { m_inInitializer = false; } if (element instanceof PsiClass && !element.equals(aClass) || element instanceof PsiLambdaExpression) { - m_inSynchronizedContextCount += 1000; + m_inSynchronizedContextCount = contextStack.pop(); } if (element instanceof PsiCodeBlock && element.getParent() instanceof PsiSynchronizedStatement || element instanceof PsiMethod && ((PsiMethod)element).hasModifierProperty(PsiModifier.SYNCHRONIZED)) { - m_inSynchronizedContextCount --; + m_inSynchronizedContextCount--; } if (element.getUserData(CODE_BLOCK_CONTAINS_HOLDS_LOCK_CALL) != null) { m_inSynchronizedContextCount --; diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/threading/field_accessed_synchronized_and_unsynchronized/FieldAccessedSynchronizedAndUnsynchronized.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/threading/field_accessed_synchronized_and_unsynchronized/FieldAccessedSynchronizedAndUnsynchronized.java index a46cd52de9df..1011ef5c92c9 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/threading/field_accessed_synchronized_and_unsynchronized/FieldAccessedSynchronizedAndUnsynchronized.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/threading/field_accessed_synchronized_and_unsynchronized/FieldAccessedSynchronizedAndUnsynchronized.java @@ -54,4 +54,23 @@ class Test { synchronized void setObject(Object object) { this.object = object; } +} +class AAAAAAA { + + private boolean ready; + private final Object LOCK = new Object(); + + void h(int i) { + synchronized (LOCK) { + ready = true; + } + new Runnable () { + @Override + public void run() { + synchronized (LOCK) { + ready = false; + } + } + }; + } } \ No newline at end of file diff --git a/plugins/devkit/resources/META-INF/plugin.xml b/plugins/devkit/resources/META-INF/plugin.xml index 4692132964e0..1730a1cef508 100644 --- a/plugins/devkit/resources/META-INF/plugin.xml +++ b/plugins/devkit/resources/META-INF/plugin.xml @@ -136,7 +136,6 @@ - @@ -210,14 +209,11 @@ + - - - - diff --git a/plugins/git4idea/src/git4idea/commands/GitCommandResult.java b/plugins/git4idea/src/git4idea/commands/GitCommandResult.java index 0683d738a9bf..e031fe47b72c 100644 --- a/plugins/git4idea/src/git4idea/commands/GitCommandResult.java +++ b/plugins/git4idea/src/git4idea/commands/GitCommandResult.java @@ -17,6 +17,7 @@ package git4idea.commands; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.Function; +import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import git4idea.GitUtil; import org.jetbrains.annotations.NotNull; @@ -49,6 +50,26 @@ public class GitCommandResult { myException = exception; } + @NotNull + public static GitCommandResult merge(@Nullable GitCommandResult first, @NotNull GitCommandResult second) { + if (first == null) return second; + + int mergedExitCode; + if (first.myExitCode == 0) { + mergedExitCode = second.myExitCode; + } + else if (second.myExitCode == 0) { + mergedExitCode = first.myExitCode; + } + else { + mergedExitCode = second.myExitCode; // take exit code of the latest command + } + return new GitCommandResult(first.success() && second.success(), mergedExitCode, + ContainerUtil.concat(first.myErrorOutput, second.myErrorOutput), + ContainerUtil.concat(first.myOutput, second.myOutput), + ObjectUtils.chooseNotNull(second.myException, first.myException)); + } + /** * @return we think that the operation succeeded */ diff --git a/plugins/git4idea/src/git4idea/commands/GitImpl.java b/plugins/git4idea/src/git4idea/commands/GitImpl.java index e9da68d2e065..db3f45b77aa9 100644 --- a/plugins/git4idea/src/git4idea/commands/GitImpl.java +++ b/plugins/git4idea/src/git4idea/commands/GitImpl.java @@ -23,8 +23,13 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.Function; +import com.intellij.util.ObjectUtils; +import com.intellij.util.containers.ContainerUtil; import com.intellij.vcsUtil.VcsFileUtil; -import git4idea.*; +import git4idea.GitCommit; +import git4idea.GitExecutionException; +import git4idea.GitVcs; import git4idea.config.GitVersionSpecialty; import git4idea.history.GitHistoryUtils; import git4idea.repo.GitRemote; @@ -50,7 +55,7 @@ import static java.util.Collections.singleton; @SuppressWarnings("StringToUpperCaseOrToLowerCaseWithoutLocale") public class GitImpl implements Git { - private final Logger LOG = Logger.getInstance(Git.class); + private static final Logger LOG = Logger.getInstance(Git.class); public GitImpl() { } @@ -173,13 +178,25 @@ public class GitImpl implements Git { @NotNull @Override - public GitCommandResult checkAttr(@NotNull GitRepository repository, @NotNull Collection attributes, + public GitCommandResult checkAttr(@NotNull final GitRepository repository, + @NotNull final Collection attributes, @NotNull Collection files) { - final GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.CHECK_ATTR); - h.addParameters(new ArrayList(attributes)); - h.endOptions(); - h.addRelativeFiles(files); - return run(h); + List> listOfPaths = VcsFileUtil.chunkFiles(repository.getRoot(), files); + return runAll(ContainerUtil.map(listOfPaths, new Function, Computable>() { + @Override + public Computable fun(final List relativePaths) { + return new Computable() { + @Override + public GitCommandResult compute() { + final GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.CHECK_ATTR); + h.addParameters(new ArrayList(attributes)); + h.endOptions(); + h.addParameters(relativePaths); + return run(h); + } + }; + } + })); } @NotNull @@ -638,6 +655,19 @@ public class GitImpl implements Git { }); } + @NotNull + private static GitCommandResult runAll(@NotNull List> commands) { + if (commands.isEmpty()) { + LOG.error("List of commands should not be empty", new Exception()); + return GitCommandResult.error("Internal error"); + } + GitCommandResult compoundResult = null; + for (Computable command : commands) { + compoundResult = GitCommandResult.merge(compoundResult, command.compute()); + } + return ObjectUtils.assertNotNull(compoundResult); + } + /** * Check if the line looks line an error message */ diff --git a/plugins/git4idea/src/git4idea/repo/GitConfig.java b/plugins/git4idea/src/git4idea/repo/GitConfig.java index f5c86c1b611f..fc35665e2f9d 100644 --- a/plugins/git4idea/src/git4idea/repo/GitConfig.java +++ b/plugins/git4idea/src/git4idea/repo/GitConfig.java @@ -51,7 +51,7 @@ public class GitConfig { private static final Logger LOG = Logger.getInstance(GitConfig.class); - private static final Pattern REMOTE_SECTION = Pattern.compile("(?:svn-)?remote(?: \"(.*)\")?"); + private static final Pattern REMOTE_SECTION = Pattern.compile("(?:svn-)?remote \"(.*)\""); private static final Pattern URL_SECTION = Pattern.compile("url \"(.*)\""); private static final Pattern BRANCH_INFO_SECTION = Pattern.compile("branch \"(.*)\""); private static final Pattern BRANCH_COMMON_PARAMS_SECTION = Pattern.compile("branch"); @@ -246,13 +246,11 @@ public class GitConfig { String sectionName = stringSectionEntry.getKey(); Profile.Section section = stringSectionEntry.getValue(); - if (sectionName.startsWith("remote") || sectionName.startsWith("svn-remote")) { - Remote remote = parseRemoteSection(sectionName, section, classLoader); - if (remote != null) { - remotes.add(remote); - } + Remote remote = parseRemoteSection(sectionName, section, classLoader); + if (remote != null) { + remotes.add(remote); } - else if (sectionName.startsWith("url")) { + else { Url url = parseUrlSection(sectionName, section, classLoader); if (url != null) { urls.add(url); @@ -372,27 +370,19 @@ public class GitConfig { private static Remote parseRemoteSection(@NotNull String sectionName, @NotNull Profile.Section section, @NotNull ClassLoader classLoader) { - RemoteBean remoteBean = section.as(RemoteBean.class, classLoader); Matcher matcher = REMOTE_SECTION.matcher(sectionName); - if (matcher.matches()) { - String remoteName = matcher.group(1); - if (remoteName == null) { // e.g. remote.pushdefault generic setting not specific to any remote - return null; - } - return new Remote(remoteName, remoteBean); + if (matcher.matches() && matcher.groupCount() == 1) { + return new Remote(matcher.group(1), section.as(RemoteBean.class, classLoader)); } - LOG.error(String.format("Invalid remote section format in .git/config. sectionName: %s section: %s", sectionName, section)); return null; } @Nullable private static Url parseUrlSection(@NotNull String sectionName, @NotNull Profile.Section section, @NotNull ClassLoader classLoader) { - UrlBean urlBean = section.as(UrlBean.class, classLoader); Matcher matcher = URL_SECTION.matcher(sectionName); - if (matcher.matches()) { - return new Url(matcher.group(1), urlBean); + if (matcher.matches() && matcher.groupCount() == 1) { + return new Url(matcher.group(1), section.as(UrlBean.class, classLoader)); } - LOG.error(String.format("Invalid url section format in .git/config. sectionName: %s section: %s", sectionName, section)); return null; } diff --git a/plugins/git4idea/tests/git4idea/repo/GitConfigTest.java b/plugins/git4idea/tests/git4idea/repo/GitConfigTest.java index 1781a004a085..94bb705dd9c6 100644 --- a/plugins/git4idea/tests/git4idea/repo/GitConfigTest.java +++ b/plugins/git4idea/tests/git4idea/repo/GitConfigTest.java @@ -57,8 +57,8 @@ public class GitConfigTest extends GitPlatformTest { //inspired by IDEA-135557 public void test_branch_with_hash_symbol() throws IOException { - GitTestUtil.createRepository(myProject, myProjectPath, true); - git("remote add origin http://example.git"); // define a remote to be able to set up tracking + createRepository(); + addRemote("http://example.git"); git("update-ref refs/remotes/origin/a#branch HEAD"); git("branch --track a#branch origin/a#branch"); @@ -78,13 +78,30 @@ public class GitConfigTest extends GitPlatformTest { // IDEA-143363 Check that remote.pushdefault (generic, without remote name) doesn't fail the config parsing procedure public void test_remote_unspecified_section() throws Exception { - GitTestUtil.createRepository(myProject, myProjectPath, true); - git("remote add origin git@github.com:foo/bar.git"); + createRepository(); + addRemote("git@github.com:foo/bar.git"); git("config remote.pushdefault origin"); - File gitDir = new File(myProjectPath, ".git"); - GitConfig config = GitConfig.read(myPlatformFacade, new File(gitDir, "config")); - Collection remotes = config.parseRemotes(); + assertSingleRemoteInConfig(); + } + + public void test_invalid_section_with_remote_prefix_is_ignored() throws Exception { + createRepository(); + addRemote("git@github.com:foo/bar.git"); + git("config remote-cfg.newkey newval"); + + assertSingleRemoteInConfig(); + } + + private static void addRemote(@NotNull String url) { + git("remote add origin " + url); + } + + private void createRepository() { + GitTestUtil.createRepository(myProject, myProjectPath, true); + } + + private static void assertSingleRemote(@NotNull Collection remotes) { assertEquals(1, remotes.size()); GitRemote remote = ContainerUtil.getFirstItem(remotes); assertNotNull(remote); @@ -92,6 +109,12 @@ public class GitConfigTest extends GitPlatformTest { assertEquals("git@github.com:foo/bar.git", remote.getFirstUrl()); } + private void assertSingleRemoteInConfig() { + File gitDir = new File(myProjectPath, ".git"); + Collection remotes = GitConfig.read(myPlatformFacade, new File(gitDir, "config")).parseRemotes(); + assertSingleRemote(remotes); + } + private void doTestRemotes(String testName, File configFile, File resultFile) throws IOException { GitConfig config = GitConfig.read(myPlatformFacade, configFile); VcsTestUtil.assertEqualCollections(testName, config.parseRemotes(), readRemoteResults(resultFile)); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManager.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManager.java index f1b1d1d5d6af..f86495fa97d3 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManager.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManager.java @@ -72,6 +72,7 @@ public class GroovyPositionManager implements PositionManager { @Override @NotNull public List locationsOfLine(@NotNull ReferenceType type, @NotNull SourcePosition position) throws NoDataException { + checkGroovyFile(position); try { if (LOG.isDebugEnabled()) { LOG.debug("locationsOfLine: " + type + "; " + position); diff --git a/plugins/junit/src/META-INF/plugin.xml b/plugins/junit/src/META-INF/plugin.xml index a80c2bde3994..52e8d652e22f 100644 --- a/plugins/junit/src/META-INF/plugin.xml +++ b/plugins/junit/src/META-INF/plugin.xml @@ -78,7 +78,7 @@ - + diff --git a/plugins/settings-repository/src/IcsManager.kt b/plugins/settings-repository/src/IcsManager.kt index 2ed16ba1592a..04a3074f9997 100644 --- a/plugins/settings-repository/src/IcsManager.kt +++ b/plugins/settings-repository/src/IcsManager.kt @@ -184,7 +184,7 @@ class IcsManager(dir: File) { return } - registerProjectLevelProviders(project) + //registerProjectLevelProviders(project) autoSyncManager.registerListeners(project) } diff --git a/plugins/settings-repository/src/sync.kt b/plugins/settings-repository/src/sync.kt index bfdc57dd3f7d..da1955841877 100644 --- a/plugins/settings-repository/src/sync.kt +++ b/plugins/settings-repository/src/sync.kt @@ -17,11 +17,12 @@ package org.jetbrains.settingsRepository import com.intellij.configurationStore.ComponentStoreImpl import com.intellij.configurationStore.SchemeManagerFactoryBase +import com.intellij.configurationStore.XmlElementStorage import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.WriteAction import com.intellij.openapi.application.impl.ApplicationImpl import com.intellij.openapi.components.StateStorage -import com.intellij.openapi.components.impl.stores.FileBasedStorage +import com.intellij.openapi.components.impl.stores.FileStorage import com.intellij.openapi.components.impl.stores.IComponentStore import com.intellij.openapi.components.impl.stores.StoreUtil import com.intellij.openapi.components.stateStore @@ -183,10 +184,10 @@ private fun updateStoragesFromStreamProvider(store: IComponentStore, updateResul })!! } -private fun updateStateStorage(changedComponentNames: Set, stateStorages: Collection, deleted: Boolean) { +private fun updateStateStorage(changedComponentNames: MutableSet, stateStorages: Collection, deleted: Boolean) { for (stateStorage in stateStorages) { try { - stateStorage.updatedFromStreamProvider(changedComponentNames, deleted) + (stateStorage as XmlElementStorage).updatedFromStreamProvider(changedComponentNames, deleted) } catch (e: Throwable) { LOG.error(e) diff --git a/python/edu/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java b/python/edu/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java index 1f82439e6ca3..3b8c4154f24b 100644 --- a/python/edu/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java +++ b/python/edu/course-creator-python/src/com/jetbrains/edu/coursecreator/PyCCProjectGenerator.java @@ -8,6 +8,7 @@ import com.intellij.ide.fileTemplates.FileTemplateManager; import com.intellij.ide.fileTemplates.FileTemplateUtil; import com.intellij.ide.util.DirectoryUtil; import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.Project; @@ -30,6 +31,7 @@ import javax.swing.*; public class PyCCProjectGenerator extends PythonProjectGenerator implements DirectoryProjectGenerator { + private static final Logger LOG = Logger.getInstance(PyCCProjectGenerator.class); private CCNewProjectPanel mySettingsPanel; @Nls @@ -83,7 +85,11 @@ public class PyCCProjectGenerator extends PythonProjectGenerator implements Dire catch (Exception ignored) { } DirectoryUtil.createSubdirectories(EduNames.HINTS, projectDir, "\\/"); - final PsiDirectory lessonDir = CCCreateLesson.createLessonDir(project, 1, null, null); + PsiDirectory lessonDir = CCCreateLesson.createLesson(null, project, baseDir, course); + if (lessonDir == null) { + LOG.error("Failed to create lesson"); + return; + } CCCreateTask.createTask(null, project, lessonDir, false); } }.execute(); diff --git a/python/edu/interactive-learning-python/resources/courses/Introduction to Python.zip b/python/edu/interactive-learning-python/resources/courses/Introduction to Python.zip index 372a7c257b06..04e50d0a7686 100644 Binary files a/python/edu/interactive-learning-python/resources/courses/Introduction to Python.zip and b/python/edu/interactive-learning-python/resources/courses/Introduction to Python.zip differ diff --git a/python/edu/src/META-INF/PyCharmEduPlugin.xml b/python/edu/src/META-INF/PyCharmEduPlugin.xml index b93c60e27277..149b839ef69f 100644 --- a/python/edu/src/META-INF/PyCharmEduPlugin.xml +++ b/python/edu/src/META-INF/PyCharmEduPlugin.xml @@ -32,20 +32,6 @@ - - - - - - - - - - - - - - diff --git a/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java b/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java index 083956f79691..902df9c7f6f2 100644 --- a/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java +++ b/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java @@ -32,6 +32,8 @@ import com.intellij.ide.util.PropertiesComponent; import com.intellij.ide.util.TipAndTrickBean; import com.intellij.notification.EventLog; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.colors.EditorColorsManager; +import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.ex.EditorSettingsExternalizable; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.extensions.ExtensionsArea; @@ -70,6 +72,7 @@ import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; +import java.util.HashSet; import java.util.Set; /** @@ -84,6 +87,30 @@ public class PyCharmEduInitialConfigurator { private static final Set UNRELATED_TIPS = Sets.newHashSet("LiveTemplatesDjango.html", "TerminalOpen.html", "Terminal.html", "ConfiguringTerminal.html"); + private static final Set HIDDEN_ACTIONS = ContainerUtil.newHashSet("CopyAsPlainText", "CopyAsRichText", "EditorPasteSimple", + "Folding", "Generate", "CompareClipboardWithSelection", + "ChangeFileEncodingAction", "CloseAllUnmodifiedEditors", + "CloseAllUnpinnedEditors", "CloseAllEditorsButActive", + "CopyReference", "MoveTabRight", "MoveTabDown", "External Tools", + "MoveEditorToOppositeTabGroup", "OpenEditorInOppositeTabGroup", + "ChangeSplitOrientation", "PinActiveTab", "Tabs Placement", + "TabsAlphabeticalMode", "AddNewTabToTheEndMode", "NextTab", + "PreviousTab", "Add to Favorites", "Add All To Favorites", + "ValidateXml", "NewHtmlFile", "CleanPyc", "Images.ShowThumbnails", + "CompareFileWithEditor", "SynchronizeCurrentFile", + "Mark Directory As", "CompareTwoFiles", "ShowFilePath", + "ChangesView.ApplyPatch", "TemplateProjectProperties", + "ExportToHTML", "SaveAll", "Export/Import Actions", + "Synchronize", "Line Separators", "ToggleReadOnlyAttribute", + "Macros", "EditorToggleCase", "EditorJoinLines", "FillParagraph", + "Convert Indents", "TemplateParametersNavigation", "EscapeEntities", + "QuickDefinition", "ExpressionTypeInfo", "EditorContextInfo", + "ShowErrorDescription", "RecentChanges", "CompareActions", + "GotoCustomRegion", "JumpToLastChange", "JumpToNextChange", + "SelectIn", "GotoTypeDeclaration", "QuickChangeScheme", + "GotoTest", "GotoRelated", "Hierarchy Actions", "Bookmarks", + "Goto Error/Bookmark Actions", "GoToEditPointGroup", + "Change Navigation Actions", "Method Navigation Actions"); public static class First { @@ -143,6 +170,8 @@ public class PyCharmEduInitialConfigurator { }); PyCodeInsightSettings.getInstance().SHOW_IMPORT_POPUP = false; } + final EditorColorsScheme editorColorsScheme = EditorColorsManager.getInstance().getScheme(EditorColorsScheme.DEFAULT_SCHEME_NAME); + editorColorsScheme.setEditorFontSize(14); if (!propertiesComponent.isValueSet(DISPLAYED_PROPERTY)) { @@ -211,68 +240,36 @@ public class PyCharmEduInitialConfigurator { actionsTree.setModel(model); schema.fillActionGroups(root); - hideActionFromMainMenu(root, schema); - + for (int i = 0; i < root.getChildCount(); i++) { + final DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)root.getChildAt(i); + if ("Main menu".equals(getItemId(treeNode))) { + hideActionFromMainMenu(root, schema, treeNode); + } + hideActions(schema, root, treeNode, HIDDEN_ACTIONS); + } CustomActionsSchema.getInstance().copyFrom(schema); } private static void hideActionFromMainMenu(@NotNull final DefaultMutableTreeNode root, - @NotNull final CustomActionsSchema schema){ - final TreeNode mainMenu = root.getFirstChild(); - - for (int i = 0; i < mainMenu.getChildCount(); i++) { - final DefaultMutableTreeNode menuItem = (DefaultMutableTreeNode)mainMenu.getChildAt(i); - if ("File".equals(getItemId(menuItem))) { - final String[] fileItems = {"ExportToHTML", "SaveAll", "Export/Import Actions", "Synchronize", "ChangeFileEncodingAction", - "Line Separators", "ToggleReadOnlyAttribute"}; - for (String item : fileItems) { - hideAction(schema, root, menuItem, item); - } - } - else if ("Edit".equals(getItemId(menuItem))) { - final String[] fileItems = {"CopyAsPlainText", "CopyAsReachText", "CopyReference", "EditorPasteSimple", "Macros", "EditorToggleCase", - "TemplateParametersNavigation", "EscapeEntities"}; - for (String item : fileItems) { - hideAction(schema, root, menuItem, item); - } - } - else if ("View".equals(getItemId(menuItem))) { - final String[] fileItems = {"QuickDefinition", "ExpressionTypeInfo", "EditorContextInfo", "ShowErrorDescription", - "RecentChanges", "CompareActions", "QuickChangeScheme"}; - for (String item : fileItems) { - hideAction(schema, root, menuItem, item); - } - } - else if ("Navigate".equals(getItemId(menuItem))) { - final String[] fileItems = {"GotoCustomRegion", "JumpToLastChange", "JumpToNextChange", "SelectIn", "GotoTypeDeclaration", - "GotoTest", "GotoRelated", "ShowFilePath", "Hierarchy Actions", "Goto Error/Bookmark Actions", "GoToEditPointGroup", - "Change Navigation Actions", "Method Navigation Actions"}; - for (String item : fileItems) { - hideAction(schema, root, menuItem, item); - } - } - } - - final String[] menuItems = {"Tools", "VCS", "Refactor", "Code", "Window", "Run"}; - for (String item : menuItems) { - hideAction(schema, root, mainMenu, item); - } + @NotNull final CustomActionsSchema schema, DefaultMutableTreeNode mainMenu){ + final HashSet menuItems = ContainerUtil.newHashSet("Tools", "VCS", "Refactor", "Code", "Window", "Run"); + hideActions(schema, root, mainMenu, menuItems); } - private static void hideAction(@NotNull final CustomActionsSchema schema, @NotNull final DefaultMutableTreeNode root, - @NotNull final TreeNode actionGroup, @NotNull final String actionId) { + private static void hideActions(@NotNull CustomActionsSchema schema, @NotNull DefaultMutableTreeNode root, + @NotNull final TreeNode actionGroup, Set items) { for(int i = 0; i < actionGroup.getChildCount(); i++){ final DefaultMutableTreeNode child = (DefaultMutableTreeNode)actionGroup.getChildAt(i); final int childCount = child.getChildCount(); - if (childCount > 0) { - hideAction(schema, child, child, actionId); - } final String childId = getItemId(child); - if (childId != null && childId.equals(actionId)){ + if (childId != null && items.contains(childId)){ final TreePath treePath = TreeUtil.getPath(root, child); final ActionUrl url = CustomizationUtil.getActionUrl(treePath, ActionUrl.DELETED); schema.addAction(url); } + else if (childCount > 0) { + hideActions(schema, child, child, items); + } } } diff --git a/python/educational/course-creator/course-creator.iml b/python/educational/course-creator/course-creator.iml index b54f05e2bd7e..46da5de45628 100644 --- a/python/educational/course-creator/course-creator.iml +++ b/python/educational/course-creator/course-creator.iml @@ -12,5 +12,6 @@ + \ No newline at end of file diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/CCFileDeletedListener.java b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/CCFileDeletedListener.java index 095cc44746b1..a28b44709bae 100644 --- a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/CCFileDeletedListener.java +++ b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/CCFileDeletedListener.java @@ -55,7 +55,7 @@ class CCFileDeletedListener extends VirtualFileAdapter { public StudyOrderable fun(VirtualFile file) { return course.getLesson(file.getName()); } - }, removedLesson, EduNames.LESSON); + }, removedLesson.getIndex(), EduNames.LESSON, -1); course.getLessons().remove(removedLesson); } @@ -77,7 +77,7 @@ class CCFileDeletedListener extends VirtualFileAdapter { public StudyOrderable fun(VirtualFile file) { return lesson.getTask(file.getName()); } - }, task, EduNames.TASK); + }, task.getIndex(), EduNames.TASK, -1); ModifiableRootModel model = EduUtils.getModel(lessonDir, project); if (model == null) { return; diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java index aaaccb4939c8..9148afad4a32 100644 --- a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java +++ b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/CCUtils.java @@ -1,5 +1,7 @@ package com.jetbrains.edu.coursecreator; +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; import com.intellij.lang.Language; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; @@ -7,12 +9,14 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.util.Function; +import com.jetbrains.edu.EduUtils; import com.jetbrains.edu.courseFormat.Course; import com.jetbrains.edu.courseFormat.StudyOrderable; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.IOException; +import java.util.*; public class CCUtils { private static final Logger LOG = Logger.getInstance(CCUtils.class); @@ -20,7 +24,7 @@ public class CCUtils { @Nullable public static CCLanguageManager getStudyLanguageManager(@NotNull final Course course) { Language language = Language.findLanguageByID(course.getLanguage()); - return language == null ? null : CCLanguageManager.INSTANCE.forLanguage(language); + return language == null ? null : CCLanguageManager.INSTANCE.forLanguage(language); } public static boolean isAnswerFile(PsiElement element) { @@ -34,37 +38,54 @@ public class CCUtils { /** * This method decreases index and updates directory names of * all tasks/lessons that have higher index than specified object - * @param dirs directories that are used to get tasks/lessons + * + * @param dirs directories that are used to get tasks/lessons * @param getStudyOrderable function that is used to get task/lesson from VirtualFile. This function can return null - * @param thresholdObject task/lesson which index is used as threshold - * @param prefix task or lesson directory name prefix + * @param threshold index is used as threshold + * @param prefix task or lesson directory name prefix */ public static void updateHigherElements(VirtualFile[] dirs, - @NotNull Function getStudyOrderable, - @NotNull final StudyOrderable thresholdObject, - final String prefix) { - int threshold = thresholdObject.getIndex(); - for (final VirtualFile dir : dirs) { - final StudyOrderable orderable = getStudyOrderable.fun(dir); - if (orderable == null) { - continue; - } - int index = orderable.getIndex(); - if (index > threshold) { - final int newIndex = index - 1; - orderable.setIndex(newIndex); - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - try { - dir.rename(this, prefix + newIndex); - } - catch (IOException e) { - LOG.error(e); - } + @NotNull final Function getStudyOrderable, + final int threshold, + final String prefix, + final int delta) { + ArrayList dirsToRename = new ArrayList + (Collections2.filter(Arrays.asList(dirs), new Predicate() { + @Override + public boolean apply(VirtualFile dir) { + final StudyOrderable orderable = getStudyOrderable.fun(dir); + if (orderable == null) { + return false; } - }); + int index = orderable.getIndex(); + return index > threshold; + } + })); + Collections.sort(dirsToRename, new Comparator() { + @Override + public int compare(VirtualFile o1, VirtualFile o2) { + StudyOrderable orderable1 = getStudyOrderable.fun(o1); + StudyOrderable orderable2 = getStudyOrderable.fun(o2); + //if we delete some dir we should start increasing numbers in dir names from the end + return (-delta) * EduUtils.INDEX_COMPARATOR.compare(orderable1, orderable2); } + }); + + for (final VirtualFile dir : dirsToRename) { + final StudyOrderable orderable = getStudyOrderable.fun(dir); + final int newIndex = orderable.getIndex() + delta; + orderable.setIndex(newIndex); + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + try { + dir.rename(this, prefix + newIndex); + } + catch (IOException e) { + LOG.error(e); + } + } + }); } } } diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateLesson.java b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateLesson.java index a305acffeaf6..7259d34afa80 100644 --- a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateLesson.java +++ b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCCreateLesson.java @@ -7,30 +7,33 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.LangDataKeys; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiManager; +import com.intellij.util.Function; import com.intellij.util.PlatformIcons; import com.jetbrains.edu.EduNames; import com.jetbrains.edu.EduUtils; import com.jetbrains.edu.courseFormat.Course; import com.jetbrains.edu.courseFormat.Lesson; +import com.jetbrains.edu.courseFormat.StudyOrderable; import com.jetbrains.edu.coursecreator.CCProjectService; +import com.jetbrains.edu.coursecreator.CCUtils; +import com.jetbrains.edu.coursecreator.ui.CCCreateStudyItemDialog; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.IOException; -import java.util.List; +import java.util.Collections; public class CCCreateLesson extends DumbAwareAction { - private static final Logger LOG = Logger.getInstance(CCCreateLesson.class.getName()); + public static final String TITLE = "Create New " + EduNames.LESSON_TITLED; public CCCreateLesson() { - super("Lesson", "Create new Lesson", PlatformIcons.DIRECTORY_CLOSED_ICON); + super(EduNames.LESSON_TITLED, TITLE, PlatformIcons.DIRECTORY_CLOSED_ICON); } @Override @@ -47,85 +50,87 @@ public class CCCreateLesson extends DumbAwareAction { if (course == null) { return; } - //"Create Lesson" invoked from project root creates new lesson as last lesson - if (directory.getVirtualFile().equals(project.getBaseDir())) { - final int size = course.getLessons().size(); - createLesson(project, size + 1, view); - return; - } - //"Create Lesson" invoked from any of lesson directories creates new lesson as next lesson - Lesson lesson = course.getLesson(directory.getName()); - if (lesson != null) { - int index = lesson.getIndex(); - List lessons = course.getLessons(); - int lessonNum = lessons.size(); - for (int i = lessonNum; i >= index + 1; i--) { - updateLesson(project, EduNames.LESSON, i); - } - final PsiDirectory parent = directory.getParent(); - if (parent == null) { - return; - } - createLesson(project, index + 1, view); - } - } - - private static void createLesson(@NotNull final Project project, - final int index, - final IdeView view) { - final String lessonName = Messages.showInputDialog("Name:", "Lesson Name", null, EduNames.LESSON + index, null); - if (lessonName == null) { - return; - } - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - createLessonDir(project, index, lessonName, view); - } - }); - } - - private void updateLesson(@NotNull final Project project, final String lessonDirName, int i) { - final VirtualFile lessonDir = project.getBaseDir().findChild(lessonDirName + i); - if (lessonDir == null) { - return; - } - final CCProjectService service = CCProjectService.getInstance(project); - Lesson l = service.getCourse().getLesson(lessonDir.getName()); - if (l == null) { - return; - } - l.setIndex(l.getIndex() + 1); - final int next = i + 1; - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - try { - lessonDir.rename(this, lessonDirName + next); - } - catch (IOException e1) { - LOG.error(e1); - } - } - }); + createLesson(view, project, directory.getVirtualFile(), course); } @Nullable - public static PsiDirectory createLessonDir(@NotNull final Project project, int index, String name, final IdeView view) { - String lessonFolderName = EduNames.LESSON + index; - final PsiDirectory projectDir = PsiManager.getInstance(project).findDirectory(project.getBaseDir()); - final PsiDirectory lessonDirectory = DirectoryUtil.createSubdirectories(EduNames.LESSON + index, projectDir, "\\/"); - final CCProjectService service = CCProjectService.getInstance(project); - if (lessonDirectory != null) { - if (view != null) { - view.selectElement(lessonDirectory); - } - final Lesson lesson = new Lesson(); - lesson.setName(name != null ? name : lessonFolderName); - lesson.setIndex(index); - service.getCourse().addLesson(lesson); + public static PsiDirectory createLesson(@Nullable IdeView view, @NotNull final Project project, + @NotNull final VirtualFile directory, @NotNull final Course course) { + Lesson lesson = getLesson(directory, project, course, view); + if (lesson == null) { + return null; } - return lessonDirectory; + VirtualFile courseDir = project.getBaseDir(); + int lessonIndex = lesson.getIndex(); + CCUtils.updateHigherElements(courseDir.getChildren(), new Function() { + @Override + public StudyOrderable fun(VirtualFile file) { + return course.getLesson(file.getName()); + } + }, lessonIndex - 1, EduNames.LESSON, 1); + course.addLesson(lesson); + Collections.sort(course.getLessons(), EduUtils.INDEX_COMPARATOR); + return createLessonDir(project, lessonIndex, view); + } + + @Nullable + private static Lesson getLesson(@NotNull final VirtualFile sourceDirectory, + @NotNull final Project project, + @NotNull final Course course, + @Nullable IdeView view) { + + VirtualFile courseDir = project.getBaseDir(); + String lessonName; + int lessonIndex; + if (sourceDirectory.equals(courseDir)) { + lessonIndex = course.getLessons().size() + 1; + String suggestedName = EduNames.LESSON + lessonIndex; + lessonName = view == null ? suggestedName : Messages.showInputDialog("Name:", TITLE, null, suggestedName, null); + } else { + Lesson sourceLesson = course.getLesson(sourceDirectory.getName()); + if (sourceLesson == null) { + return null; + } + final int index = sourceLesson.getIndex(); + CCCreateStudyItemDialog dialog = new CCCreateStudyItemDialog(project, EduNames.LESSON, sourceLesson.getName(), index); + dialog.show(); + if (dialog.getExitCode() != DialogWrapper.OK_EXIT_CODE) { + return null; + } + lessonName = dialog.getName(); + lessonIndex = index + dialog.getIndexDelta(); + } + if (lessonName == null) { + return null; + } + return createAndInitLesson(course, lessonName, lessonIndex); + } + + @NotNull + private static Lesson createAndInitLesson(@NotNull final Course course, @NotNull String lessonName, int lessonIndex) { + Lesson lesson = new Lesson(); + lesson.setName(lessonName); + lesson.setCourse(course); + lesson.setIndex(lessonIndex); + return lesson; + } + + @Nullable + public static PsiDirectory createLessonDir(@NotNull final Project project, final int index, final IdeView view) { + final PsiDirectory projectDir = PsiManager.getInstance(project).findDirectory(project.getBaseDir()); + final PsiDirectory[] lessonDirectory = new PsiDirectory[1]; + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + lessonDirectory[0] = DirectoryUtil.createSubdirectories(EduNames.LESSON + index, projectDir, "\\/"); + } + }); + if (lessonDirectory[0] != null) { + if (view != null) { + view.selectElement(lessonDirectory[0]); + } + } + return lessonDirectory[0]; } @Override @@ -151,9 +156,9 @@ public class CCCreateLesson extends DumbAwareAction { } final CCProjectService service = CCProjectService.getInstance(project); Course course = service.getCourse(); - - if (directory != null && course != null && project.getBaseDir().equals(directory)) { - EduUtils.enableAction(event, false); + if (directory != null && course != null && course.getLesson(directory.getName()) != null) { + EduUtils.enableAction(event, true); + return; } EduUtils.enableAction(event, false); } diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCRunTestsAction.java b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCRunTestsAction.java index aa065bf35451..eb04c491c0ef 100644 --- a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCRunTestsAction.java +++ b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/actions/CCRunTestsAction.java @@ -77,6 +77,7 @@ public abstract class CCRunTestsAction extends AnAction { final CCProjectService service = CCProjectService.getInstance(project); final Course course = service.getCourse(); final PsiDirectory taskDir = psiFile.getContainingDirectory(); + if (taskDir == null) return; final PsiDirectory lessonDir = taskDir.getParent(); if (lessonDir == null) return; if (course == null) return; diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateAnswerPlaceholderPanel.java b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateAnswerPlaceholderPanel.java index 1b0b2804290d..cc55b58ec101 100644 --- a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateAnswerPlaceholderPanel.java +++ b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateAnswerPlaceholderPanel.java @@ -16,6 +16,8 @@ public class CCCreateAnswerPlaceholderPanel extends JPanel { super(new BorderLayout()); add(myPanel, BorderLayout.CENTER); enableHint(false); + myHintText.setLineWrap(true); + myHintText.setWrapStyleWord(true); myCreateHintCheckBox.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateStudyItemDialog.java b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateStudyItemDialog.java new file mode 100644 index 000000000000..f986357dc695 --- /dev/null +++ b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateStudyItemDialog.java @@ -0,0 +1,33 @@ +package com.jetbrains.edu.coursecreator.ui; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.text.StringUtil; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +public class CCCreateStudyItemDialog extends DialogWrapper { + private final CCCreateStudyItemPanel myPanel; + + public CCCreateStudyItemDialog(@Nullable Project project, String itemName, String thresholdName, int thresholdIndex) { + super(project); + myPanel = new CCCreateStudyItemPanel(itemName, thresholdName, thresholdIndex); + setTitle("Create New " + StringUtil.toTitleCase(itemName)); + init(); + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + return myPanel; + } + + public String getName() { + return myPanel.getItemName(); + } + + public int getIndexDelta() { + return myPanel.getIndexDelta(); + } +} diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateStudyItemPanel.form b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateStudyItemPanel.form new file mode 100644 index 000000000000..c1af900e81b3 --- /dev/null +++ b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateStudyItemPanel.form @@ -0,0 +1,42 @@ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateStudyItemPanel.java b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateStudyItemPanel.java new file mode 100644 index 000000000000..adf934b325b5 --- /dev/null +++ b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCCreateStudyItemPanel.java @@ -0,0 +1,33 @@ +package com.jetbrains.edu.coursecreator.ui; + +import javax.swing.*; +import java.awt.*; + +public class CCCreateStudyItemPanel extends JPanel { + private final String myItemName; + private JPanel myPanel; + private JTextField myNameField; + private CCItemPositionPanel myPositionalPanel; + private String myThresholdName; + private int myThresholdIndex; + + public CCCreateStudyItemPanel(String itemName, String thresholdName, int thresholdIndex) { + myThresholdName = thresholdName; + myThresholdIndex = thresholdIndex; + myItemName = itemName; + myNameField.setText(itemName + thresholdIndex); + add(myPanel, BorderLayout.CENTER); + } + + private void createUIComponents() { + myPositionalPanel = new CCItemPositionPanel(myItemName, myThresholdName, myThresholdIndex); + } + + public String getItemName() { + return myNameField.getText(); + } + + public int getIndexDelta() { + return myPositionalPanel.getIndexDelta(); + } +} diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCItemPositionPanel.form b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCItemPositionPanel.form new file mode 100644 index 000000000000..922a21098153 --- /dev/null +++ b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCItemPositionPanel.form @@ -0,0 +1,37 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCItemPositionPanel.java b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCItemPositionPanel.java new file mode 100644 index 000000000000..72fa3666b9ee --- /dev/null +++ b/python/educational/course-creator/src/com/jetbrains/edu/coursecreator/ui/CCItemPositionPanel.java @@ -0,0 +1,30 @@ +package com.jetbrains.edu.coursecreator.ui; + +import com.intellij.ui.components.JBLabel; +import com.intellij.ui.components.JBRadioButton; + +import javax.swing.*; +import java.awt.*; + +public class CCItemPositionPanel extends JPanel { + private JPanel myPanel; + private JBRadioButton myBeforeButton; + private JBRadioButton myAfterButton; + private JBLabel mySpecifyPositionLabel; + + public CCItemPositionPanel(String itemName, String thresholdName, int thresholdNum) { + this.add(myPanel, BorderLayout.CENTER); + mySpecifyPositionLabel.setText("Specify " + itemName + " position:"); + String postfix = itemName + " " + thresholdNum + " '" + thresholdName + "'"; + ButtonGroup group = new ButtonGroup(); + group.add(myBeforeButton); + group.add(myAfterButton); + myBeforeButton.setText("before " + postfix); + myAfterButton.setText("after " + postfix); + myBeforeButton.setSelected(true); + } + + public int getIndexDelta() { + return myBeforeButton.isSelected() ? 0 : 1; + } +} diff --git a/python/educational/interactive-learning/resources/META-INF/plugin.xml b/python/educational/interactive-learning/resources/META-INF/plugin.xml index 829febfd5e29..d657d2f94516 100644 --- a/python/educational/interactive-learning/resources/META-INF/plugin.xml +++ b/python/educational/interactive-learning/resources/META-INF/plugin.xml @@ -66,10 +66,11 @@ - + + shortcut : myDeletedShortcuts.entrySet()) { final Keymap keymap = KeymapManager.getInstance().getActiveKeymap(); diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyUtils.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyUtils.java index 7bc796002890..1d129c8c8a3d 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyUtils.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/StudyUtils.java @@ -35,6 +35,7 @@ import com.jetbrains.edu.courseFormat.*; import com.jetbrains.edu.learning.editor.StudyEditor; import com.jetbrains.edu.learning.run.StudyExecutor; import com.jetbrains.edu.learning.run.StudyTestRunner; +import com.jetbrains.edu.learning.ui.ProgressToolWindowFactory; import com.jetbrains.edu.learning.ui.StudyToolWindowFactory; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -105,25 +106,24 @@ public class StudyUtils { public static void updateAction(@NotNull final AnActionEvent e) { final Presentation presentation = e.getPresentation(); presentation.setEnabled(false); - presentation.setVisible(false); final Project project = e.getProject(); if (project != null) { - final FileEditor[] editors = FileEditorManager.getInstance(project).getAllEditors(); - for (FileEditor editor : editors) { - if (editor instanceof StudyEditor) { - presentation.setEnabled(true); - presentation.setVisible(true); - } + final StudyEditor studyEditor = getSelectedStudyEditor(project); + if (studyEditor != null) { + presentation.setEnabled(true); } } } - public static void updateStudyToolWindow(@NotNull final Project project) { - ToolWindowManager.getInstance(project).getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW). - getContentManager().removeAllContents(false); + public static void updateToolWindows(@NotNull final Project project) { + final ToolWindowManager windowManager = ToolWindowManager.getInstance(project); + windowManager.getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW).getContentManager().removeAllContents(false); StudyToolWindowFactory factory = new StudyToolWindowFactory(); - factory.createToolWindowContent(project, ToolWindowManager.getInstance(project). - getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW)); + factory.createToolWindowContent(project, windowManager.getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW)); + + windowManager.getToolWindow(ProgressToolWindowFactory.ID).getContentManager().removeAllContents(false); + ProgressToolWindowFactory windowFactory = new ProgressToolWindowFactory(); + windowFactory.createToolWindowContent(project, windowManager.getToolWindow(ProgressToolWindowFactory.ID)); } public static void deleteFile(@NotNull final VirtualFile file) { diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java index f170f468cf60..596446f3daaa 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyCheckAction.java @@ -7,6 +7,7 @@ import com.intellij.ide.projectView.ProjectView; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.KeyboardShortcut; +import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.diagnostic.Logger; @@ -47,7 +48,7 @@ import com.jetbrains.edu.learning.editor.StudyEditor; import com.jetbrains.edu.learning.navigation.StudyNavigator; import com.jetbrains.edu.learning.run.StudySmartChecker; import com.jetbrains.edu.learning.run.StudyTestRunner; -import com.jetbrains.edu.learning.stepic.StudySettings; +import com.jetbrains.edu.stepic.StudySettings; import com.jetbrains.edu.stepic.EduStepicConnector; import icons.InteractiveLearningIcons; import org.jetbrains.annotations.NotNull; @@ -195,7 +196,7 @@ public class StudyCheckAction extends DumbAwareAction { return new com.intellij.openapi.progress.Task.Backgroundable(project, "Checking task", true) { @Override public void onSuccess() { - StudyUtils.updateStudyToolWindow(project); + StudyUtils.updateToolWindows(project); drawAllPlaceholders(project, task, taskDir); ProjectView.getInstance(project).refresh(); EduUtils.deleteWindowDescriptions(task, taskDir); @@ -396,6 +397,10 @@ public class StudyCheckAction extends DumbAwareAction { @Override public void update(AnActionEvent e) { - e.getPresentation().setEnabled(!checkInProgress); + final Presentation presentation = e.getPresentation(); + StudyUtils.updateAction(e); + if (presentation.isEnabled()) { + presentation.setEnabled(!checkInProgress); + } } } diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java index 151aec37dd85..bca245a7e9ca 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyRefreshTaskFileAction.java @@ -101,7 +101,7 @@ public class StudyRefreshTaskFileAction extends DumbAwareAction { } resetAnswerPlaceholders(taskFile, project); ProjectView.getInstance(project).refresh(); - StudyUtils.updateStudyToolWindow(project); + StudyUtils.updateToolWindows(project); return true; } @@ -175,13 +175,13 @@ public class StudyRefreshTaskFileAction extends DumbAwareAction { @Override public void update(AnActionEvent event) { - EduUtils.enableAction(event, false); + StudyUtils.updateAction(event); final Project project = event.getProject(); if (project != null) { StudyEditor studyEditor = StudyUtils.getSelectedStudyEditor(project); StudyState studyState = new StudyState(studyEditor); - if (studyState.isValid()) { - EduUtils.enableAction(event, true); + if (!studyState.isValid()) { + event.getPresentation().setEnabled(false); } } } diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java index fca1daadea3e..25111e7b52c5 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/actions/StudyTaskNavigationAction.java @@ -115,4 +115,9 @@ abstract public class StudyTaskNavigationAction extends DumbAwareAction { protected abstract String getNavigationFinishedMessage(); protected abstract Task getTargetTask(@NotNull final Task sourceTask); + + @Override + public void update(AnActionEvent e) { + StudyUtils.updateAction(e); + } } diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java index 0d9821f94450..18b01f4c7c01 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/courseGeneration/StudyProjectGenerator.java @@ -8,6 +8,8 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.project.DumbModePermission; +import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.io.FileUtil; @@ -66,17 +68,21 @@ public class StudyProjectGenerator { new Runnable() { @Override public void run() { - ApplicationManager.getApplication().runWriteAction(new Runnable() { + DumbService.allowStartingDumbModeInside(DumbModePermission.MAY_START_BACKGROUND, new Runnable() { @Override public void run() { - course.initCourse(false); - final File courseDirectory = new File(ourCoursesDir, course.getName()); - StudyGenerator.createCourse(course, baseDir, courseDirectory, project); - course.setCourseDirectory(new File(ourCoursesDir, mySelectedCourseInfo.getName()).getAbsolutePath()); - VirtualFileManager.getInstance().refreshWithoutFileWatcher(true); - StudyProjectComponent.getInstance(project).registerStudyToolwindow(course); - openFirstTask(course, project); - + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + course.initCourse(false); + final File courseDirectory = new File(ourCoursesDir, course.getName()); + StudyGenerator.createCourse(course, baseDir, courseDirectory, project); + course.setCourseDirectory(new File(ourCoursesDir, mySelectedCourseInfo.getName()).getAbsolutePath()); + VirtualFileManager.getInstance().refreshWithoutFileWatcher(true); + StudyProjectComponent.getInstance(project).registerStudyToolwindow(course); + openFirstTask(course, project); + } + }); } }); } @@ -467,12 +473,12 @@ public class StudyProjectGenerator { courseInfo = new CourseInfo(); courseInfo.setName(courseName); courseInfo.setDescription(courseDescription); - final ArrayList instructors = new ArrayList(); + final ArrayList authors = new ArrayList(); for (JsonElement author : courseAuthors) { - final String authorAsString = author.getAsString(); - instructors.add(new CourseInfo.Instructor(authorAsString)); + final JsonObject authorAsJsonObject = author.getAsJsonObject(); + authors.add(new CourseInfo.Author(authorAsJsonObject.get("first_name").getAsString(), authorAsJsonObject.get("last_name").getAsString())); } - courseInfo.setInstructors(instructors); + courseInfo.setAuthors(authors); } } catch (Exception e) { diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/editor/StudyEditorFactoryListener.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/editor/StudyEditorFactoryListener.java index 9a5be326b733..37fbd9ada832 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/editor/StudyEditorFactoryListener.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/editor/StudyEditorFactoryListener.java @@ -82,7 +82,7 @@ public class StudyEditorFactoryListener implements EditorFactoryListener { editor.addEditorMouseListener(new WindowSelectionListener(taskFile)); final ToolWindow studyToolWindow = ToolWindowManager.getInstance(project).getToolWindow(StudyToolWindowFactory.STUDY_TOOL_WINDOW); if (studyToolWindow != null) { - StudyUtils.updateStudyToolWindow(project); + StudyUtils.updateToolWindows(project); studyToolWindow.show(null); } diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/navigation/StudyNavigator.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/navigation/StudyNavigator.java index 1476fd0ba372..4121c84445f1 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/navigation/StudyNavigator.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/navigation/StudyNavigator.java @@ -3,6 +3,7 @@ package com.jetbrains.edu.learning.navigation; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.LogicalPosition; import com.intellij.openapi.project.Project; +import com.jetbrains.edu.EduNames; import com.jetbrains.edu.courseFormat.AnswerPlaceholder; import com.jetbrains.edu.courseFormat.Lesson; import com.jetbrains.edu.courseFormat.Task; @@ -52,7 +53,10 @@ public class StudyNavigator { if (nextLessonIndex >= lessons.size()) { return null; } - return lessons.get(nextLessonIndex); + final Lesson nextLesson = lessons.get(nextLessonIndex); + if (EduNames.PYCHARM_ADDITIONAL.equals(nextLesson.getName())) + return null; + return nextLesson; } public static Lesson previousLesson(@NotNull final Lesson lesson) { diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/stepic/StudySettingsPanel.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/stepic/StudySettingsPanel.java index c1fad93a3938..b3295c2d9ad8 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/stepic/StudySettingsPanel.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/stepic/StudySettingsPanel.java @@ -18,6 +18,7 @@ package com.jetbrains.edu.learning.stepic; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.DocumentAdapter; import com.jetbrains.edu.stepic.EduStepicConnector; +import com.jetbrains.edu.stepic.StudySettings; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -112,7 +113,9 @@ public class StudySettingsPanel { final StudySettings studySettings = StudySettings.getInstance(); studySettings.setLogin(getLogin()); studySettings.setPassword(getPassword()); - EduStepicConnector.login(getLogin(), getPassword()); + if (!StringUtil.isEmptyOrSpaces(getLogin()) && !StringUtil.isEmptyOrSpaces(getPassword())) { + EduStepicConnector.login(getLogin(), getPassword()); + } } resetCredentialsModification(); } diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/ProgressToolWindowFactory.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/ProgressToolWindowFactory.java new file mode 100644 index 000000000000..69d6f59980aa --- /dev/null +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/ProgressToolWindowFactory.java @@ -0,0 +1,78 @@ +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.EduNames; +import com.jetbrains.edu.courseFormat.Course; +import com.jetbrains.edu.courseFormat.Lesson; +import com.jetbrains.edu.courseFormat.Task; +import com.jetbrains.edu.learning.StudyTaskManager; +import com.jetbrains.edu.learning.courseFormat.StudyStatus; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.util.List; + +public class ProgressToolWindowFactory implements ToolWindowFactory, DumbAware { + public static final String ID = "Course Progress"; + + + @Override + public void createToolWindowContent(@NotNull final Project project, @NotNull final ToolWindow toolWindow) { + 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, taskManager); + } + 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, StudyTaskManager taskManager) { + int solved = 0; + for (Task task : lesson.getTaskList()) { + if (taskManager.getStatus(task) == StudyStatus.Solved) { + solved += 1; + } + } + return solved; + } +} diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.form b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.form index 8d60f3ad7ce4..c13bc229b973 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.form +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.form @@ -93,7 +93,7 @@ - + diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java index a005bd7714c8..a32674311844 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java @@ -16,14 +16,15 @@ import com.intellij.util.Consumer; import com.jetbrains.edu.courseFormat.Course; import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; -import com.jetbrains.edu.learning.stepic.StudySettings; import com.jetbrains.edu.stepic.CourseInfo; import com.jetbrains.edu.stepic.EduStepicConnector; +import com.jetbrains.edu.stepic.StudySettings; import icons.InteractiveLearningIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; @@ -51,6 +52,8 @@ public class StudyNewProjectPanel{ public StudyNewProjectPanel(StudyProjectGenerator generator) { myGenerator = generator; myAvailableCourses = myGenerator.getCourses(false); + myBrowseButton.setPreferredSize(new Dimension(28, 28)); + myRefreshButton.setPreferredSize(new Dimension(28, 28)); if (myAvailableCourses.isEmpty()) { setError(CONNECTION_ERROR); } @@ -58,10 +61,12 @@ public class StudyNewProjectPanel{ for (CourseInfo courseInfo : myAvailableCourses) { myCoursesComboBox.addItem(courseInfo); } - myAuthorLabel.setText("Author: " + Course.getAuthorsString(StudyUtils.getFirst(myAvailableCourses).getInstructors())); - myDescriptionLabel.setText(StudyUtils.getFirst(myAvailableCourses).getDescription()); + final CourseInfo selectedCourse = StudyUtils.getFirst(myAvailableCourses); + final String authorsString = Course.getAuthorsString(selectedCourse.getAuthors()); + myAuthorLabel.setText(!StringUtil.isEmptyOrSpaces(authorsString) ? "Author: " + authorsString : ""); + myDescriptionLabel.setText(selectedCourse.getDescription()); //setting the first course in list as selected - myGenerator.setSelectedCourse(StudyUtils.getFirst(myAvailableCourses)); + myGenerator.setSelectedCourse(selectedCourse); setOK(); } initListeners(); @@ -208,7 +213,8 @@ public class StudyNewProjectPanel{ myDescriptionLabel.setText(""); return; } - myAuthorLabel.setText("Author: " + Course.getAuthorsString(selectedCourse.getInstructors())); + final String authorsString = Course.getAuthorsString(selectedCourse.getAuthors()); + myAuthorLabel.setText(!StringUtil.isEmptyOrSpaces(authorsString) ?"Author: " + authorsString : ""); myCoursesComboBox.removeItem(CourseInfo.INVALID_COURSE); myDescriptionLabel.setText(selectedCourse.getDescription()); myGenerator.setSelectedCourse(selectedCourse); diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyProgressBar.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyProgressBar.java index 2935d76e6691..c21aef13acb7 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyProgressBar.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyProgressBar.java @@ -20,11 +20,10 @@ public class StudyProgressBar extends JComponent implements DumbAware { private final int myHeight; private final int myIndent; private double myFraction = 0.0; - private Color myColor = JBColor.BLUE; + private static Color myColor = JBColor.GREEN; public StudyProgressBar(double fraction, int height, int indent) { myFraction = fraction; - myColor = JBColor.GREEN; myHeight = height; myIndent = indent; } @@ -43,7 +42,7 @@ public class StudyProgressBar extends JComponent implements DumbAware { Dimension size = getSize(); double width = size.getWidth() - 2*myIndent; - g2.setPaint(UIUtil.getListBackground()); + g2.setPaint(UIUtil.getTextFieldBackground()); Rectangle2D rect = new Rectangle2D.Double(myIndent, 0, width, myHeight); g2.fill(rect); @@ -53,7 +52,6 @@ public class StudyProgressBar extends JComponent implements DumbAware { int arcHeight = 5; g2.drawRoundRect(myIndent, 0, (int)width, myHeight, arcWidth, arcHeight); g2.setPaint(SHADOW2); - g2.drawRoundRect(myIndent, 0, (int)width, myHeight, arcWidth, arcHeight); int y_center = myHeight / 2; int y_steps = myHeight / 2 - 3; @@ -82,6 +80,13 @@ public class StudyProgressBar extends JComponent implements DumbAware { config.restore(); } + @Override + public Dimension getPreferredSize() { + Dimension dimension = super.getPreferredSize(); + dimension.height = myHeight + 10; + return dimension; + } + @Override public Dimension getMaximumSize() { Dimension dimension = super.getMaximumSize(); diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java index 9b2c505cb343..841162e1dadf 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyToolWindow.java @@ -21,6 +21,7 @@ import com.intellij.openapi.actionSystem.ActionToolbar; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.editor.colors.EditorColorsManager; +import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.FileEditorManagerEvent; import com.intellij.openapi.fileEditor.FileEditorManagerListener; @@ -40,35 +41,57 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.border.EmptyBorder; +import javax.swing.text.html.HTMLDocument; +import javax.swing.text.html.HTMLEditorKit; +import java.awt.*; public class StudyToolWindow extends SimpleToolWindowPanel implements DataProvider, Disposable { + private static final String EMPTY_TASK_TEXT = "Please, open any task to see task description"; + public StudyToolWindow(final Project project) { super(true, true); JPanel toolbarPanel = createToolbarPanel(); setToolbar(toolbarPanel); final StudyEditor studyEditor = StudyUtils.getSelectedStudyEditor(project); - if (studyEditor == null) return; - Task task = studyEditor.getTaskFile().getTask(); + final JTextPane taskTextPane = createTaskTextPane(); + + if (studyEditor == null) { + taskTextPane.setText(EMPTY_TASK_TEXT); + setContent(taskTextPane); + return; + } + final Task task = studyEditor.getTaskFile().getTask(); if (task != null) { final String taskText = task.getText(); - - final JTextPane taskTextPane = new JTextPane(); JBScrollPane scrollPane = new JBScrollPane(taskTextPane); - taskTextPane.setContentType("text/html"); - taskTextPane.setEditable(false); taskTextPane.setText(taskText); taskTextPane.addHyperlinkListener(BrowserHyperlinkListener.INSTANCE); - taskTextPane.setBackground(EditorColorsManager.getInstance().getGlobalScheme().getDefaultBackground()); - taskTextPane.setBorder(new EmptyBorder(15, 20, 0, 100)); + setContent(scrollPane); final FileEditorManagerListener listener = new StudyFileEditorManagerListener(project, taskTextPane); project.getMessageBus().connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, listener); } + } + @NotNull + private JTextPane createTaskTextPane() { + final JTextPane taskTextPane = new JTextPane(); + taskTextPane.setContentType(new HTMLEditorKit().getContentType()); + final EditorColorsScheme editorColorsScheme = EditorColorsManager.getInstance().getGlobalScheme(); + int fontSize = editorColorsScheme.getEditorFontSize(); + final String fontName = editorColorsScheme.getEditorFontName(); + final Font font = new Font(fontName, Font.PLAIN, fontSize); + String bodyRule = "body { font-family: " + font.getFamily() + "; " + + "font-size: " + font.getSize() + "pt; }"; + ((HTMLDocument)taskTextPane.getDocument()).getStyleSheet().addRule(bodyRule); + taskTextPane.setEditable(false); + taskTextPane.setBackground(EditorColorsManager.getInstance().getGlobalScheme().getDefaultBackground()); + taskTextPane.setBorder(new EmptyBorder(15, 20, 0, 100)); + return taskTextPane; } public void dispose() { @@ -93,22 +116,24 @@ public class StudyToolWindow extends SimpleToolWindowPanel implements DataProvid private Project myProject; private JTextPane myTaskTextPane; - StudyFileEditorManagerListener(@NotNull final Project project, JTextPane taskTextPane){ + StudyFileEditorManagerListener(@NotNull final Project project, JTextPane taskTextPane) { myProject = project; myTaskTextPane = taskTextPane; } - @Override - public void fileOpened (@NotNull FileEditorManager source, @NotNull VirtualFile file){ + + @Override + public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) { Task task = getTask(file); setTaskText(task); } - @Override - public void fileClosed (@NotNull FileEditorManager source, @NotNull VirtualFile file){ + @Override + public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) { + myTaskTextPane.setText(EMPTY_TASK_TEXT); } - @Override - public void selectionChanged (@NotNull FileEditorManagerEvent event){ + @Override + public void selectionChanged(@NotNull FileEditorManagerEvent event) { VirtualFile file = event.getNewFile(); if (file != null) { Task task = getTask(file); @@ -116,8 +141,8 @@ public class StudyToolWindow extends SimpleToolWindowPanel implements DataProvid } } - @Nullable - private Task getTask (@NotNull VirtualFile file){ + @Nullable + private Task getTask(@NotNull VirtualFile file) { TaskFile taskFile = StudyUtils.getTaskFile(myProject, file); if (taskFile != null) { return taskFile.getTask(); @@ -129,6 +154,7 @@ public class StudyToolWindow extends SimpleToolWindowPanel implements DataProvid private void setTaskText(@Nullable final Task task) { if (task == null) { + myTaskTextPane.setText(EMPTY_TASK_TEXT); return; } String text = task.getText(); diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java index 4522064be221..d847b4606468 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java +++ b/python/educational/interactive-learning/src/com/jetbrains/edu/learning/ui/StudyToolWindowFactory.java @@ -25,7 +25,6 @@ public class StudyToolWindowFactory implements ToolWindowFactory, DumbAware { final Content content = contentManager.getFactory().createContent(studyToolWindow, null, false); contentManager.addContent(content); Disposer.register(project, studyToolWindow); - } } diff --git a/python/educational/src/com/jetbrains/edu/EduNames.java b/python/educational/src/com/jetbrains/edu/EduNames.java index 4c45d9e33a7e..e6b2fd99a427 100644 --- a/python/educational/src/com/jetbrains/edu/EduNames.java +++ b/python/educational/src/com/jetbrains/edu/EduNames.java @@ -15,6 +15,7 @@ */ package com.jetbrains.edu; +import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NonNls; @NonNls @@ -22,6 +23,7 @@ public class EduNames { public static final String TASK_HTML = "task.html"; public static final String HINTS = "hints"; public static final String LESSON = "lesson"; + public static final String LESSON_TITLED = StringUtil.toTitleCase(LESSON); public static final String COURSE = "course"; public static final String TEST_TAB_NAME = "test"; public static final String USER_TEST_INPUT = "input"; diff --git a/python/educational/src/com/jetbrains/edu/EduUtils.java b/python/educational/src/com/jetbrains/edu/EduUtils.java index 7808ad2d21f9..de3d7f0a2264 100644 --- a/python/educational/src/com/jetbrains/edu/EduUtils.java +++ b/python/educational/src/com/jetbrains/edu/EduUtils.java @@ -19,6 +19,7 @@ import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.jetbrains.edu.courseFormat.AnswerPlaceholder; +import com.jetbrains.edu.courseFormat.StudyOrderable; import com.jetbrains.edu.courseFormat.Task; import com.jetbrains.edu.courseFormat.TaskFile; import org.jetbrains.annotations.NotNull; @@ -28,6 +29,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.Collection; +import java.util.Comparator; import java.util.Map; public class EduUtils { @@ -35,6 +37,13 @@ public class EduUtils { } private static final Logger LOG = Logger.getInstance(EduUtils.class.getName()); + public static Comparator INDEX_COMPARATOR = new Comparator() { + @Override + public int compare(StudyOrderable o1, StudyOrderable o2) { + return o1.getIndex() - o2.getIndex(); + } + }; + public static void commitAndSaveModel(final ModifiableRootModel model) { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override @@ -83,10 +92,14 @@ public class EduUtils { * @return index of object */ public static int getIndex(@NotNull final String fullName, @NotNull final String logicalName) { - if (!fullName.contains(logicalName)) { + if (!fullName.startsWith(logicalName)) { + return -1; + } + try { + return Integer.parseInt(fullName.substring(logicalName.length())) - 1; + } catch(NumberFormatException e) { return -1; } - return Integer.parseInt(fullName.substring(logicalName.length())) - 1; } public static boolean indexIsValid(int index, Collection collection) { @@ -200,6 +213,10 @@ public class EduUtils { taskFile.sortAnswerPlaceholders(); for (int i = taskFile.getAnswerPlaceholders().size() - 1; i >= 0; i--) { final AnswerPlaceholder answerPlaceholder = taskFile.getAnswerPlaceholders().get(i); + if (answerPlaceholder.getRealStartOffset(document) > document.getTextLength() || answerPlaceholder.getRealStartOffset(document) + answerPlaceholder.getPossibleAnswerLength() > document.getTextLength()) { + LOG.error("Wrong startOffset: " + answerPlaceholder.getRealStartOffset(document) + "; document: " + file.getPath()); + return; + } replaceAnswerPlaceholder(project, document, answerPlaceholder); } CommandProcessor.getInstance().executeCommand(project, new Runnable() { diff --git a/python/educational/src/com/jetbrains/edu/courseFormat/Course.java b/python/educational/src/com/jetbrains/edu/courseFormat/Course.java index 98fdc0ea0e68..f891962d084a 100644 --- a/python/educational/src/com/jetbrains/edu/courseFormat/Course.java +++ b/python/educational/src/com/jetbrains/edu/courseFormat/Course.java @@ -20,7 +20,7 @@ public class Course implements Named { @Expose private String description; @Expose private String name; private String myCourseDirectory = ""; - @Expose private List authors = new ArrayList(); + @Expose private List authors = new ArrayList(); private boolean myUpToDate; @Expose @SerializedName("language") @@ -62,23 +62,24 @@ public class Course implements Named { } @NotNull - public List getAuthors() { + public List getAuthors() { return authors; } - public static String getAuthorsString(@NotNull List authors) { - return StringUtil.join(authors, new Function() { + public static String getAuthorsString(@NotNull List authors) { + return StringUtil.join(authors, new Function() { @Override - public String fun(CourseInfo.Instructor instructor) { - return instructor.getName(); + public String fun(CourseInfo.Author author) { + return author.getName(); } }, ", "); } public void setAuthors(String[] authors) { - this.authors = new ArrayList(); + this.authors = new ArrayList(); for (String name : authors) { - this.authors.add(new CourseInfo.Instructor(name)); + final List pair = StringUtil.split(name, " "); + this.authors.add(new CourseInfo.Author(pair.get(0), pair.size() > 1 ? pair.get(1) : "")); } } @@ -126,8 +127,8 @@ public class Course implements Named { myLanguage = language; } - public void setAuthors(List instructors) { - this.authors = instructors; + public void setAuthors(List authors) { + this.authors = authors; } public String getCourseType() { diff --git a/python/educational/src/com/jetbrains/edu/stepic/CourseInfo.java b/python/educational/src/com/jetbrains/edu/stepic/CourseInfo.java index 95a33aaf4ec3..195ea0dccf87 100644 --- a/python/educational/src/com/jetbrains/edu/stepic/CourseInfo.java +++ b/python/educational/src/com/jetbrains/edu/stepic/CourseInfo.java @@ -1,6 +1,7 @@ package com.jetbrains.edu.stepic; import com.google.gson.annotations.SerializedName; +import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -21,8 +22,9 @@ public class CourseInfo { //course type in format "pycharm " private String myType = "pycharm Python"; - @SerializedName("instructors_deprecated") - List myInstructors = new ArrayList(); + List instructors = new ArrayList(); + + List myAuthors = new ArrayList(); int id; public static CourseInfo INVALID_COURSE = new CourseInfo(); @@ -32,8 +34,8 @@ public class CourseInfo { } @NotNull - public List getInstructors() { - return myInstructors; + public List getAuthors() { + return myAuthors; } public String getDescription() { @@ -66,15 +68,17 @@ public class CourseInfo { return result; } - public static class Instructor { - String name; + public static class Author { + String first_name; + String last_name; - public Instructor(String name) { - this.name = name; + public Author(String firstName, String lastName) { + first_name = firstName; + last_name = lastName; } public String getName() { - return name; + return StringUtil.join(new String[]{first_name, last_name}, " "); } } @@ -82,8 +86,15 @@ public class CourseInfo { myName = name; } - public void setInstructors(List instructors) { - myInstructors = instructors; + public void setAuthors(List authors) { + myAuthors = authors; + } + + public void addAuthor(Author author) { + if (myAuthors == null) { + myAuthors = new ArrayList(); + } + myAuthors.add(author); } public void setDescription(String description) { diff --git a/python/educational/src/com/jetbrains/edu/stepic/EduStepicConnector.java b/python/educational/src/com/jetbrains/edu/stepic/EduStepicConnector.java index d9dcf15df729..1546b88d225c 100644 --- a/python/educational/src/com/jetbrains/edu/stepic/EduStepicConnector.java +++ b/python/educational/src/com/jetbrains/edu/stepic/EduStepicConnector.java @@ -57,7 +57,6 @@ public class EduStepicConnector { private static final String stepicUrl = "https://stepic.org/"; private static final String stepicApiUrl = stepicUrl + "api/"; private static final Logger LOG = Logger.getInstance(EduStepicConnector.class.getName()); - private static final String ourDomain = "stepic.org"; private static String ourCSRFToken = ""; private static CloseableHttpClient ourClient; @@ -139,6 +138,7 @@ public class EduStepicConnector { try { final CloseableHttpResponse response = ourClient.execute(request); + final String s = EntityUtils.toString(response.getEntity()); saveCSRFToken(); final StatusLine line = response.getStatusLine(); if (line.getStatusCode() != 302) { @@ -191,6 +191,12 @@ public class EduStepicConnector { if (StringUtil.isEmptyOrSpaces(courseType)) continue; final List typeLanguage = StringUtil.split(courseType, " "); if (typeLanguage.size() == 2 && PYCHARM_PREFIX.equals(typeLanguage.get(0))) { + + for (Integer instructor : info.instructors) { + final CourseInfo.Author author = getFromStepic("users/" + String.valueOf(instructor), AuthorWrapper.class).users.get(0); + info.addAuthor(author); + } + result.add(info); } } @@ -199,7 +205,7 @@ public class EduStepicConnector { public static Course getCourse(@NotNull final CourseInfo info) { final Course course = new Course(); - course.setAuthors(info.getInstructors()); + course.setAuthors(info.getAuthors()); course.setDescription(info.getDescription()); course.setName(info.getName()); String courseType = info.getType(); @@ -330,7 +336,17 @@ public class EduStepicConnector { public static void postCourse(Project project, @NotNull final Course course) { final HttpPost request = new HttpPost(stepicApiUrl + "courses"); if (ourClient == null) { - showLoginDialog(); + final String login = StudySettings.getInstance().getLogin(); + if (StringUtil.isEmptyOrSpaces(login)) { + showLoginDialog(); + } + else { + final boolean success = login(login, StudySettings.getInstance().getPassword()); + if (!success) { + LOG.error("Failed to push course. Failed to login."); // TODO: show notification + return; + } + } } setHeaders(request, "application/json"); @@ -692,6 +708,10 @@ public class EduStepicConnector { } } + static class AuthorWrapper { + List users; + } + static class SubmissionWrapper { Submission submission; diff --git a/python/educational/src/com/jetbrains/edu/stepic/LoginDialog.java b/python/educational/src/com/jetbrains/edu/stepic/LoginDialog.java index a670e83bc1b0..d71c6206bf86 100644 --- a/python/educational/src/com/jetbrains/edu/stepic/LoginDialog.java +++ b/python/educational/src/com/jetbrains/edu/stepic/LoginDialog.java @@ -39,6 +39,8 @@ public class LoginDialog extends DialogWrapper { @Override protected void doOKAction() { AuthDataHolder authData = myLoginPanel.getAuthData(); + StudySettings.getInstance().setLogin(authData.email); + StudySettings.getInstance().setPassword(authData.password); final boolean success = EduStepicConnector.login(authData.email, authData.password); if (!success) { setErrorText("Log in failed"); diff --git a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/stepic/StudySettings.java b/python/educational/src/com/jetbrains/edu/stepic/StudySettings.java similarity index 97% rename from python/educational/interactive-learning/src/com/jetbrains/edu/learning/stepic/StudySettings.java rename to python/educational/src/com/jetbrains/edu/stepic/StudySettings.java index 466dd681e7d9..c3f673149d54 100644 --- a/python/educational/interactive-learning/src/com/jetbrains/edu/learning/stepic/StudySettings.java +++ b/python/educational/src/com/jetbrains/edu/stepic/StudySettings.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.jetbrains.edu.learning.stepic; +package com.jetbrains.edu.stepic; import com.intellij.ide.passwordSafe.PasswordSafe; import com.intellij.ide.passwordSafe.PasswordSafeException; @@ -25,7 +25,7 @@ import org.jetbrains.annotations.Nullable; @SuppressWarnings("MethodMayBeStatic") @State( - name = "GithubSettings", + name = "StudySettings", storages = {@Storage( file = StoragePathMacros.APP_CONFIG + "/stepic_settings.xml")}) public class StudySettings implements PersistentStateComponent { diff --git a/python/src/com/jetbrains/python/hierarchy/PyTypeHierarchyBrowser.java b/python/src/com/jetbrains/python/hierarchy/PyTypeHierarchyBrowser.java index 127630d7e73c..b066f303be4b 100644 --- a/python/src/com/jetbrains/python/hierarchy/PyTypeHierarchyBrowser.java +++ b/python/src/com/jetbrains/python/hierarchy/PyTypeHierarchyBrowser.java @@ -19,13 +19,8 @@ import com.intellij.ide.hierarchy.HierarchyNodeDescriptor; import com.intellij.ide.hierarchy.HierarchyTreeStructure; import com.intellij.ide.hierarchy.TypeHierarchyBrowserBase; import com.intellij.ide.util.treeView.NodeDescriptor; -import com.intellij.openapi.actionSystem.ActionGroup; -import com.intellij.openapi.actionSystem.ActionManager; -import com.intellij.openapi.actionSystem.ActionPlaces; -import com.intellij.openapi.actionSystem.IdeActions; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.PsiElement; -import com.intellij.ui.PopupHandler; import com.jetbrains.python.hierarchy.treestructures.PySubTypesHierarchyTreeStructure; import com.jetbrains.python.hierarchy.treestructures.PySuperTypesHierarchyTreeStructure; import com.jetbrains.python.hierarchy.treestructures.PyTypeHierarchyTreeStructure; @@ -59,8 +54,7 @@ public class PyTypeHierarchyBrowser extends TypeHierarchyBrowserBase { } protected void createTrees(@NotNull Map trees) { - ActionGroup group = (ActionGroup)ActionManager.getInstance().getAction("PyTypeHierarchyPopupMenu"); - createTreeAndSetupCommonActions(trees, group); + createTreeAndSetupCommonActions(trees, "PyTypeHierarchyPopupMenu"); } @Nullable diff --git a/resources-en/src/intentionDescriptions/DefineParamsDefaultValueAction/after.java.template b/resources-en/src/intentionDescriptions/DefineParamsDefaultValueAction/after.java.template index e14f067cdce2..5aeff4f6a7a5 100644 --- a/resources-en/src/intentionDescriptions/DefineParamsDefaultValueAction/after.java.template +++ b/resources-en/src/intentionDescriptions/DefineParamsDefaultValueAction/after.java.template @@ -2,7 +2,8 @@ class Test { void foo() { foo(|,); } - void foo(int a, int b){ + + void foo(int a, int b) { //do smth } } \ No newline at end of file diff --git a/resources-en/src/intentionDescriptions/DefineParamsDefaultValueAction/before.java.template b/resources-en/src/intentionDescriptions/DefineParamsDefaultValueAction/before.java.template index 90e1b2f320b2..967726ccb4de 100644 --- a/resources-en/src/intentionDescriptions/DefineParamsDefaultValueAction/before.java.template +++ b/resources-en/src/intentionDescriptions/DefineParamsDefaultValueAction/before.java.template @@ -1,5 +1,5 @@ class Test { - void foo(int a, int b){ + void foo(int a, int b) { //do smth } } \ No newline at end of file diff --git a/resources-en/src/intentionDescriptions/DefineParamsDefaultValueAction/description.html b/resources-en/src/intentionDescriptions/DefineParamsDefaultValueAction/description.html index 9d7a5b546eb4..2bd8b8ee307c 100644 --- a/resources-en/src/intentionDescriptions/DefineParamsDefaultValueAction/description.html +++ b/resources-en/src/intentionDescriptions/DefineParamsDefaultValueAction/description.html @@ -1,5 +1,5 @@ -This intention generates method which delegates to the current one setting the selected parameters with the default value. +Generates an overloaded method which delegates to the current one setting the selected parameters to the specified default values. diff --git a/resources-en/src/intentionDescriptions/DelegateWithDefaultParamValueIntentionAction/after.java.template b/resources-en/src/intentionDescriptions/DelegateWithDefaultParamValueIntentionAction/after.java.template index 97307fd767c3..8b1a0e1d4fe8 100644 --- a/resources-en/src/intentionDescriptions/DelegateWithDefaultParamValueIntentionAction/after.java.template +++ b/resources-en/src/intentionDescriptions/DelegateWithDefaultParamValueIntentionAction/after.java.template @@ -2,7 +2,8 @@ class Test { void foo(int a) { foo(a, |); } - void foo(int a, int b){ + + 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 index 90e1b2f320b2..0ecad2088cdf 100644 --- a/resources-en/src/intentionDescriptions/DelegateWithDefaultParamValueIntentionAction/before.java.template +++ b/resources-en/src/intentionDescriptions/DelegateWithDefaultParamValueIntentionAction/before.java.template @@ -1,5 +1,5 @@ class Test { - void foo(int a, int b){ + 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 index c476e38684df..821c26642038 100644 --- a/resources-en/src/intentionDescriptions/DelegateWithDefaultParamValueIntentionAction/description.html +++ b/resources-en/src/intentionDescriptions/DelegateWithDefaultParamValueIntentionAction/description.html @@ -1,5 +1,5 @@ -This intention generates method which delegates to the current one setting the selected parameter the default value. +Generates an overloaded method which delegates to the current one setting the selected parameter to the specified default value. diff --git a/resources/src/idea/RichPlatformActions.xml b/resources/src/idea/RichPlatformActions.xml index ab2573813a96..4c82664c9e6f 100644 --- a/resources/src/idea/RichPlatformActions.xml +++ b/resources/src/idea/RichPlatformActions.xml @@ -341,11 +341,7 @@ - - - - - + diff --git a/xml/impl/src/com/intellij/xml/actions/EscapeEntitiesAction.java b/xml/impl/src/com/intellij/xml/actions/EscapeEntitiesAction.java index 4dccfddd38de..9c275e856634 100644 --- a/xml/impl/src/com/intellij/xml/actions/EscapeEntitiesAction.java +++ b/xml/impl/src/com/intellij/xml/actions/EscapeEntitiesAction.java @@ -79,7 +79,7 @@ public class EscapeEntitiesAction extends SimpleCodeInsightAction { char c = text.charAt(i); final PsiElement element = file.findElementAt(start + i); if (element != null && isCharacterElement(element)) { - if (c == '<' || c == '>' || c == '&' || c == '"' || c == '\'' || c > 0xff) { + if (c == '<' || c == '>' || c == '&' || c == '"' || c == '\'' || c > 0x7f) { final String escape = ESCAPES.getValue(file).get(c); if (escape != null) { result.append("&").append(escape).append(";"); diff --git a/xml/tests/src/com/intellij/codeInsight/editorActions/EscapeEntitiesActionTest.java b/xml/tests/src/com/intellij/codeInsight/editorActions/EscapeEntitiesActionTest.java index 9cd5a3cdb71b..e6691070ec8d 100644 --- a/xml/tests/src/com/intellij/codeInsight/editorActions/EscapeEntitiesActionTest.java +++ b/xml/tests/src/com/intellij/codeInsight/editorActions/EscapeEntitiesActionTest.java @@ -16,6 +16,7 @@ package com.intellij.codeInsight.editorActions; import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.testFramework.PlatformTestUtil; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; /** @@ -23,6 +24,7 @@ import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; */ public class EscapeEntitiesActionTest extends LightCodeInsightFixtureTestCase { private static final String NDASH = new String(new byte[]{-30, -128, -109}, CharsetToolkit.UTF8_CHARSET); + private static final String COPY = new String(new byte[]{-62, -82}, CharsetToolkit.UTF8_CHARSET); public void testSimpleHtml() { doTest("<<<", "html", "<<<"); @@ -32,10 +34,14 @@ public class EscapeEntitiesActionTest extends LightCodeInsightFixtureTestCase { doTest(">>>", "xml", ">>>"); } - public void testWide() { + public void testVeryWide() { doTest(NDASH, "html", "–"); } + public void testWide() { + doTest(COPY, "html", "®"); + } + public void testAttributeValue() { doTest("
", "html", ""); } @@ -57,12 +63,15 @@ public class EscapeEntitiesActionTest extends LightCodeInsightFixtureTestCase { "<\n"); } - private void doTest(String text, String extension, String expected) { - if (!text.contains("")) { - text = "" + text + ""; - } - myFixture.configureByText(getTestName(true) + "." + extension, text); - myFixture.performEditorAction("EscapeEntities"); - myFixture.checkResult(expected); + private void doTest(String text, final String extension, final String expected) { + final String finalText = !text.contains("") ? "" + text + "" : text; + PlatformTestUtil.withEncoding("UTF8", new Runnable() { + @Override + public void run() { + myFixture.configureByText(getTestName(true) + "." + extension, finalText); + myFixture.performEditorAction("EscapeEntities"); + myFixture.checkResult(expected); + } + }); } }