Merge remote-tracking branch 'origin/master'
authorEkaterina Tuzova <Ekaterina.Tuzova@jetbrains.com>
Mon, 17 Nov 2014 11:18:00 +0000 (14:18 +0300)
committerEkaterina Tuzova <Ekaterina.Tuzova@jetbrains.com>
Mon, 17 Nov 2014 11:18:00 +0000 (14:18 +0300)
143 files changed:
java/debugger/impl/src/com/intellij/debugger/engine/JavaStackFrame.java
java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java
java/debugger/impl/src/com/intellij/debugger/impl/DebuggerContextUtil.java
java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java
java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaUtilImpl.java
java/java-impl/src/com/intellij/codeInspection/inferNullity/InferNullityAnnotationsAction.java
java/java-impl/src/com/intellij/refactoring/inline/InlineMethodProcessor.java
java/java-impl/src/com/intellij/refactoring/introduceField/InplaceIntroduceConstantPopup.java
java/java-impl/src/com/intellij/refactoring/introduceField/InplaceIntroduceFieldPopup.java
java/java-impl/src/com/intellij/refactoring/introduceParameter/InplaceIntroduceParameterPopup.java
java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java
java/java-impl/src/com/intellij/refactoring/introduceVariable/JavaVariableInplaceIntroducer.java
java/java-tests/testData/inspection/deadCode/functionalExpressions/expected.xml [new file with mode: 0644]
java/java-tests/testData/inspection/deadCode/functionalExpressions/src/Test.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/allIncomplete.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/allIncomplete_after.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/allInsertFinal.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/allInsertFinal_after.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/cast.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/castToObject.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/castToObject_after.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/cast_after.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/escapePosition.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/escapePositionIfTyped.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/escapePositionIfTyped_after.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/escapePosition_after.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/fromExpression.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/fromExpression_after.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/fromParenthesis.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/fromParenthesis_after.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/noWritable.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/noWritable_after.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/ranges.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/ranges_after.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/stopEditing.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/stopEditing_after.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/writable.java [new file with mode: 0644]
java/java-tests/testData/refactoring/inplaceIntroduceVariable/writable_after.java [new file with mode: 0644]
java/java-tests/testData/refactoring/introduceVariable/ReturnNonExportedArray.after.java
java/java-tests/testSrc/com/intellij/codeInspection/UnusedDeclarationTest.java
java/java-tests/testSrc/com/intellij/index/IndexTestGenerator.scala [new file with mode: 0644]
java/java-tests/testSrc/com/intellij/refactoring/AbstractJavaInplaceIntroduceTest.java
java/java-tests/testSrc/com/intellij/refactoring/InplaceIntroduceVariableTest.java [new file with mode: 0644]
java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java
platform/analysis-api/src/com/intellij/analysis/AnalysisScope.java
platform/duplicates-analysis/src/com/intellij/dupLocator/DuplicatesProfile.java
platform/duplicates-analysis/src/com/intellij/dupLocator/index/DuplicatesIndex.java
platform/dvcs-api/src/com/intellij/dvcs/push/PushTarget.java
platform/dvcs-impl/src/com/intellij/dvcs/push/PushController.java
platform/dvcs-impl/src/com/intellij/dvcs/push/ui/VcsPushDialog.java
platform/editor-ui-api/src/com/intellij/openapi/editor/FoldingModel.java
platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DataNode.java
platform/lang-impl/src/com/intellij/codeInsight/CodeInsightSettings.java
platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/IdentifierHighlighterPass.java
platform/lang-impl/src/com/intellij/codeInsight/editorActions/AbstractIndentingBackspaceHandler.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/codeInsight/editorActions/BackspaceHandler.java
platform/lang-impl/src/com/intellij/codeInsight/editorActions/IndentingBackspaceHandler.java [deleted file]
platform/lang-impl/src/com/intellij/codeInsight/editorActions/SimpleIndentingBackspaceHandler.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/codeInsight/editorActions/SmartIndentingBackspaceHandler.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/codeInsight/folding/impl/FoldingUpdate.java
platform/lang-impl/src/com/intellij/codeInsight/folding/impl/FoldingUtil.java
platform/lang-impl/src/com/intellij/injected/editor/FoldingModelWindow.java
platform/lang-impl/src/com/intellij/refactoring/introduce/inplace/AbstractInplaceIntroducer.java
platform/lang-impl/src/com/intellij/refactoring/rename/inplace/InplaceRefactoring.java
platform/lang-impl/testData/editor/indentingBackspace/beforeCommentWithTab-after.java [new file with mode: 0644]
platform/lang-impl/testData/editor/indentingBackspace/beforeCommentWithTab.java [new file with mode: 0644]
platform/lang-impl/testSources/com/intellij/codeInsight/editorActions/SimpleIndentingBackspaceHandlerTest.java [new file with mode: 0644]
platform/platform-api/src/com/intellij/openapi/ui/ThreeComponentsSplitter.java
platform/platform-api/src/com/intellij/openapi/wm/ToolWindowManager.java
platform/platform-api/src/com/intellij/ui/content/TabbedContent.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/ide/actions/ToggleToolbarAction.java
platform/platform-impl/src/com/intellij/openapi/command/impl/DocumentUndoProvider.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldRegionsTree.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldingModelImpl.java
platform/platform-impl/src/com/intellij/openapi/editor/textarea/TextComponentFoldingModel.java
platform/platform-impl/src/com/intellij/openapi/progress/impl/ProgressManagerImpl.java
platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java
platform/platform-impl/src/com/intellij/openapi/wm/impl/MaximizeToolWindowAction.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeader.java
platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeadlessManagerImpl.java
platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java
platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowsPane.java
platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabContentLayout.java
platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabbedContentTabLabel.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/ui/content/impl/TabbedContentImpl.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java
platform/platform-impl/src/com/intellij/util/MessageBusUtil.java
platform/platform-resources-en/src/messages/ActionsBundle.properties
platform/platform-resources/src/META-INF/LangExtensions.xml
platform/platform-resources/src/idea/Keymap_Default.xml
platform/platform-resources/src/idea/PlatformActions.xml
platform/platform-tests/testSrc/com/intellij/openapi/editor/FoldingTest.java
platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java
platform/testFramework/src/com/intellij/mock/Mock.java
platform/testFramework/src/com/intellij/refactoring/AbstractInplaceIntroduceTest.java
platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java
platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistorySessionPartner.java
platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUiImpl.java
platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/BranchFilterPopupComponent.java
platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/DateFilterPopupComponent.java
platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/FilterModel.java [new file with mode: 0644]
platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/FilterPopupComponent.java
platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/MultilinePopupBuilder.java
platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/MultipleValueFilterPopupComponent.java
platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/StructureFilterPopupComponent.java
platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/UserFilterPopupComponent.java
platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/VcsLogClassicFilterUi.java
platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/MainFrame.java
platform/xdebugger-api/src/com/intellij/xdebugger/frame/XInlineSourcePosition.java [new file with mode: 0644]
platform/xdebugger-api/src/com/intellij/xdebugger/frame/XNearestSourcePosition.java
platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/XDebuggerTree.java
platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/XValueNodeImpl.java
plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ImportUtils.java
plugins/InspectionGadgets/src/com/siyeh/ig/fixes/InlineCallFix.java
plugins/InspectionGadgets/test/com/siyeh/igfixes/performance/simple_setter/Parentheses.after.java [new file with mode: 0644]
plugins/InspectionGadgets/test/com/siyeh/igfixes/performance/simple_setter/Parentheses.java [new file with mode: 0644]
plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/performance/InlineCallFixTest.java [new file with mode: 0644]
plugins/copyright/src/com/maddyhome/idea/copyright/actions/UpdateCopyrightAction.java
plugins/copyright/src/com/maddyhome/idea/copyright/actions/UpdateCopyrightProcessor.java
plugins/copyright/src/com/maddyhome/idea/copyright/psi/UpdatePsiFileCopyright.java
plugins/git4idea/src/git4idea/push/GitPushTarget.java
plugins/hg4idea/src/org/zmlx/hg4idea/push/HgTarget.java
python/helpers/pydev/pydev_console_utils.py
python/helpers/pydev/pydevd_comm.py
python/helpers/pydev/pydevd_vars.py
python/pydevSrc/com/jetbrains/python/debugger/ArrayChunk.java [new file with mode: 0644]
python/pydevSrc/com/jetbrains/python/debugger/PyFrameAccessor.java
python/pydevSrc/com/jetbrains/python/debugger/pydev/GetArrayCommand.java
python/pydevSrc/com/jetbrains/python/debugger/pydev/MultiProcessDebugger.java
python/pydevSrc/com/jetbrains/python/debugger/pydev/ProcessDebugger.java
python/pydevSrc/com/jetbrains/python/debugger/pydev/ProtocolParser.java
python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoteDebugger.java
python/src/META-INF/python-core.xml
python/src/com/jetbrains/python/codeInsight/imports/ImportFromExistingAction.java
python/src/com/jetbrains/python/console/PydevConsoleCommunication.java
python/src/com/jetbrains/python/debugger/PyDebugProcess.java
python/src/com/jetbrains/python/debugger/array/AsyncArrayTableModel.java
python/src/com/jetbrains/python/debugger/array/NumpyArrayTable.java
python/src/com/jetbrains/python/editor/PythonBackspaceHandler.java [deleted file]
python/testData/inspections/PyUnresolvedReferencesInspection/DocstringTypeFromSubModule/a.py [new file with mode: 0644]
python/testData/inspections/PyUnresolvedReferencesInspection/DocstringTypeFromSubModule/pkg1/__init__.py [new file with mode: 0644]
python/testData/inspections/PyUnresolvedReferencesInspection/DocstringTypeFromSubModule/pkg1/m1.py [new file with mode: 0644]
python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java

index 463243a0d419c4913767c9565df6db9be9307cfd..913f64ead07197d3f2022ffedfe9fa6648d04d50 100644 (file)
@@ -34,19 +34,15 @@ import com.intellij.debugger.ui.tree.render.ClassRenderer;
 import com.intellij.debugger.ui.tree.render.DescriptorLabelListener;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.Computable;
 import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.pom.Navigatable;
 import com.intellij.ui.ColoredTextContainer;
 import com.intellij.xdebugger.XDebugSession;
 import com.intellij.xdebugger.XSourcePosition;
 import com.intellij.xdebugger.evaluation.XDebuggerEvaluator;
 import com.intellij.xdebugger.frame.*;
 import com.intellij.xdebugger.frame.presentation.XValuePresentation;
-import com.intellij.xdebugger.impl.XSourcePositionImpl;
 import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants;
 import com.intellij.xdebugger.settings.XDebuggerSettingsManager;
 import com.sun.jdi.*;
@@ -89,7 +85,7 @@ public class JavaStackFrame extends XStackFrame {
     myEqualityObject = update ? NodeManagerImpl.getContextKeyForFrame(myDescriptor.getFrameProxy()) : null;
     myDebugProcess = ((DebugProcessImpl)descriptor.getDebugProcess());
     myNodeManager = myDebugProcess.getXdebugProcess().getNodeManager();
-    myXSourcePosition = myDescriptor.getSourcePosition() != null ? new JavaXSourcePosition(myDescriptor.getSourcePosition()) : null;
+    myXSourcePosition = myDescriptor.getSourcePosition() != null ? DebuggerUtilsEx.toXSourcePosition(myDescriptor.getSourcePosition()) : null;
   }
 
   @NotNull
@@ -401,34 +397,4 @@ public class JavaStackFrame extends XStackFrame {
       return "JavaFrame position unknown";
     }
   }
-
-  private static class JavaXSourcePosition implements XSourcePosition {
-    private final SourcePosition mySourcePosition;
-
-    public JavaXSourcePosition(@NotNull SourcePosition sourcePosition) {
-      mySourcePosition = sourcePosition;
-    }
-
-    @Override
-    public int getLine() {
-      return mySourcePosition.getLine();
-    }
-
-    @Override
-    public int getOffset() {
-      return mySourcePosition.getOffset();
-    }
-
-    @NotNull
-    @Override
-    public VirtualFile getFile() {
-      return mySourcePosition.getFile().getVirtualFile();
-    }
-
-    @NotNull
-    @Override
-    public Navigatable createNavigatable(@NotNull Project project) {
-      return XSourcePositionImpl.createOpenFileDescriptor(project, this);
-    }
-  }
 }
index 43232ce3fd2d4c8686961a6f1dbb4d2677a97d54..12cafe763fe0fbb938f0ee412745c99017179258 100644 (file)
@@ -377,9 +377,16 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
 
   @Override
   public void computeSourcePosition(@NotNull final XNavigatable navigatable) {
+    if (navigatable instanceof XInlineSourcePosition && !(navigatable instanceof XNearestSourcePosition)
+        && !(myValueDescriptor instanceof ThisDescriptorImpl || myValueDescriptor instanceof LocalVariableDescriptor)) {
+      return;
+    }
     myEvaluationContext.getManagerThread().schedule(new SuspendContextCommandImpl(myEvaluationContext.getSuspendContext()) {
       @Override
       public Priority getPriority() {
+        if (navigatable instanceof XInlineSourcePosition) {
+          return Priority.LOW;
+        }
         return Priority.NORMAL;
       }
 
index c24a33742986371aaa9feb61abb6791232720bcf..92e983253c5470cf6dc44aa81829e132548f9f42 100644 (file)
@@ -80,7 +80,7 @@ public class DebuggerContextUtil {
 
           //final Editor editor = fileEditor instanceof TextEditorImpl ? ((TextEditorImpl)fileEditor).getEditor() : null;
           if (editor != null && position != null && file.getVirtualFile().equals(position.getFile())) {
-            final Couple<Collection<TextRange>> usages = IdentifierHighlighterPass.getHighlightUsages(psi, file);
+            final Couple<Collection<TextRange>> usages = IdentifierHighlighterPass.getHighlightUsages(psi, file, false);
             final List<TextRange> ranges = new ArrayList<TextRange>();
             ranges.addAll(usages.first);
             ranges.addAll(usages.second);
@@ -104,6 +104,6 @@ public class DebuggerContextUtil {
       catch (Exception ignore) {
       }
     }
-    return SourcePosition.createFromOffset(file, psi.getTextOffset());
+    return null;
   }
 }
index ae6bb5dd7b27b739816bc7b51f77edc518d37928..c4721ce254a9b7e1d483431739ac51e95e462e22 100644 (file)
@@ -42,6 +42,8 @@ import com.intellij.openapi.fileTypes.StdFileTypes;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.*;
 import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.pom.Navigatable;
 import com.intellij.psi.*;
 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
 import com.intellij.ui.classFilter.ClassFilter;
@@ -631,6 +633,36 @@ public abstract class DebuggerUtilsEx extends DebuggerUtils {
 
   @Nullable
   public static XSourcePosition toXSourcePosition(@NotNull SourcePosition position) {
-    return XSourcePositionImpl.create(position.getFile().getVirtualFile(), position.getLine());
+    return new JavaXSourcePosition(position);
+  }
+
+  private static class JavaXSourcePosition implements XSourcePosition {
+    private final SourcePosition mySourcePosition;
+
+    public JavaXSourcePosition(@NotNull SourcePosition sourcePosition) {
+      mySourcePosition = sourcePosition;
+    }
+
+    @Override
+    public int getLine() {
+      return mySourcePosition.getLine();
+    }
+
+    @Override
+    public int getOffset() {
+      return mySourcePosition.getOffset();
+    }
+
+    @NotNull
+    @Override
+    public VirtualFile getFile() {
+      return mySourcePosition.getFile().getVirtualFile();
+    }
+
+    @NotNull
+    @Override
+    public Navigatable createNavigatable(@NotNull Project project) {
+      return XSourcePositionImpl.createOpenFileDescriptor(project, this);
+    }
   }
 }
index 674311ec5fde25c1cd0116b74a1685cb2fc7d039..80cfbcd0b2ba60b670e982000d72950c24b563bf 100644 (file)
@@ -92,6 +92,29 @@ public class RefJavaUtilImpl extends RefJavaUtil{
             }
           }
 
+          @Override
+          public void visitLambdaExpression(PsiLambdaExpression expression) {
+            super.visitLambdaExpression(expression);
+            processFunctionalExpression(expression);
+          }
+
+          @Override
+          public void visitMethodReferenceExpression(PsiMethodReferenceExpression expression) {
+            super.visitMethodReferenceExpression(expression);
+            processFunctionalExpression(expression);
+          }
+
+          private void processFunctionalExpression(PsiFunctionalExpression expression) {
+            final PsiClass aClass = PsiUtil.resolveClassInType(expression.getFunctionalInterfaceType());
+            if (aClass != null) {
+              refFrom.addReference(refFrom.getRefManager().getReference(aClass), aClass, psiFrom, false, true, null);
+              final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(aClass);
+              if (interfaceMethod != null) {
+                refFrom.addReference(refFrom.getRefManager().getReference(interfaceMethod), interfaceMethod, psiFrom, false, true, null);
+              }
+            }
+          }
+
           @Nullable
           private RefMethod processNewLikeConstruct(final PsiMethod psiConstructor, final PsiExpressionList argumentList) {
             if (psiConstructor != null) {
index 8b6c9bcd595cc61f266a1ff6ce32fe702ddc565c..8385166f0296f4ffc6f208b325cb36cd34fe2131 100644 (file)
 package com.intellij.codeInspection.inferNullity;
 
 import com.intellij.analysis.AnalysisScope;
+import com.intellij.analysis.AnalysisScopeBundle;
 import com.intellij.analysis.BaseAnalysisAction;
 import com.intellij.analysis.BaseAnalysisActionDialog;
 import com.intellij.codeInsight.AnnotationUtil;
+import com.intellij.codeInsight.FileModificationService;
 import com.intellij.codeInsight.NullableNotNullManager;
 import com.intellij.codeInsight.daemon.QuickFixBundle;
 import com.intellij.codeInsight.daemon.impl.quickfix.LocateLibraryDialog;
@@ -59,14 +61,12 @@ import com.intellij.usages.*;
 import com.intellij.util.Function;
 import com.intellij.util.Processor;
 import com.intellij.util.SequentialModalProgressTask;
+import com.intellij.util.containers.ContainerUtil;
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
 
 import javax.swing.*;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 
 public class InferNullityAnnotationsAction extends BaseAnalysisAction {
   @NonNls private static final String INFER_NULLITY_ANNOTATIONS = "Infer Nullity Annotations";
@@ -84,37 +84,34 @@ public class InferNullityAnnotationsAction extends BaseAnalysisAction {
     final ProgressManager progressManager = ProgressManager.getInstance();
     final Set<Module> modulesWithoutAnnotations = new HashSet<Module>();
     final Set<Module> modulesWithLL = new HashSet<Module>();
+    final JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(project);
+    final String defaultNullable = NullableNotNullManager.getInstance(project).getDefaultNullable();
+    final int[] fileCount = new int[] {0};
     if (!progressManager.runProcessWithProgressSynchronously(new Runnable() {
       @Override
       public void run() {
-        final int totalFiles = scope.getFileCount();
-
         scope.accept(new PsiElementVisitor() {
-          private int myFileCount = 0;
           final private Set<Module> processed = new HashSet<Module>();
 
           @Override
           public void visitFile(PsiFile file) {
-            myFileCount++;
+            fileCount[0]++;
             final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
             if (progressIndicator != null) {
               final VirtualFile virtualFile = file.getVirtualFile();
               if (virtualFile != null) {
                 progressIndicator.setText2(ProjectUtil.calcRelativeToProjectPath(virtualFile, project));
               }
-              progressIndicator.setFraction(((double)myFileCount) / totalFiles);
+              progressIndicator.setText(AnalysisScopeBundle.message("scanning.scope.progress.title"));
             }
             final Module module = ModuleUtilCore.findModuleForPsiElement(file);
-            if (module != null && !processed.contains(module)) {
-              processed.add(module);
-              if (JavaPsiFacade.getInstance(project)
-                    .findClass(NullableNotNullManager.getInstance(project).getDefaultNullable(),
-                               GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module)) == null) {
-                modulesWithoutAnnotations.add(module);
-              }
+            if (module != null && processed.add(module)) {
               if (PsiUtil.getLanguageLevel(file).compareTo(LanguageLevel.JDK_1_5) < 0) {
                 modulesWithLL.add(module);
               }
+              else if (javaPsiFacade.findClass(defaultNullable, GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module)) == null) {
+                modulesWithoutAnnotations.add(module);
+              }
             }
           }
         });
@@ -128,8 +125,7 @@ public class InferNullityAnnotationsAction extends BaseAnalysisAction {
       return;
     }
     if (!modulesWithoutAnnotations.isEmpty()) {
-      final Library annotationsLib =
-        LibraryUtil.findLibraryByClass(NullableNotNullManager.getInstance(project).getDefaultNullable(), project);
+      final Library annotationsLib = LibraryUtil.findLibraryByClass(defaultNullable, project);
       if (annotationsLib != null) {
         String message = "Module" + (modulesWithoutAnnotations.size() == 1 ? " " : "s ");
         message += StringUtil.join(modulesWithoutAnnotations, new Function<Module, String>() {
@@ -185,10 +181,8 @@ public class InferNullityAnnotationsAction extends BaseAnalysisAction {
       }
       return;
     }
-    if (scope.checkScopeWritable(project)) return;
-
     PsiDocumentManager.getInstance(project).commitAllDocuments();
-    final UsageInfo[] usageInfos = findUsages(project, scope);
+    final UsageInfo[] usageInfos = findUsages(project, scope, fileCount[0]);
     if (usageInfos == null) return;
 
     if (usageInfos.length < 5) {
@@ -205,13 +199,13 @@ public class InferNullityAnnotationsAction extends BaseAnalysisAction {
   }
 
   private UsageInfo[] findUsages(@NotNull final Project project,
-                                 @NotNull final AnalysisScope scope) {
+                                 @NotNull final AnalysisScope scope, 
+                                          final int fileCount) {
     final NullityInferrer inferrer = new NullityInferrer(myAnnotateLocalVariablesCb.isSelected(), project);
     final PsiManager psiManager = PsiManager.getInstance(project);
     final Runnable searchForUsages = new Runnable() {
       @Override
       public void run() {
-        final int totalFiles = scope.getFileCount();
         scope.accept(new PsiElementVisitor() {
           int myFileCount = 0;
 
@@ -225,7 +219,7 @@ public class InferNullityAnnotationsAction extends BaseAnalysisAction {
             final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
             if (progressIndicator != null) {
               progressIndicator.setText2(ProjectUtil.calcRelativeToProjectPath(virtualFile, project));
-              progressIndicator.setFraction(((double)myFileCount) / totalFiles);
+              progressIndicator.setFraction(((double)myFileCount) / fileCount);
             }
             if (file instanceof PsiJavaFile) {
               inferrer.collect(file);
@@ -258,6 +252,16 @@ public class InferNullityAnnotationsAction extends BaseAnalysisAction {
             protected void run(@NotNull Result result) throws Throwable {
               final UsageInfo[] infos = computable.compute();
               if (infos.length > 0) {
+
+                final Set<PsiElement> elements = new LinkedHashSet<PsiElement>();
+                for (UsageInfo info : infos) {
+                  final PsiElement element = info.getElement();
+                  if (element != null) {
+                    ContainerUtil.addIfNotNull(elements, element.getContainingFile());
+                  }
+                }
+                if (!FileModificationService.getInstance().preparePsiElementsForWrite(elements)) return;
+
                 final SequentialModalProgressTask progressTask = new SequentialModalProgressTask(project, INFER_NULLITY_ANNOTATIONS, false);
                 progressTask.setMinIterationTime(200);
                 progressTask.setTask(new AnnotateTask(project, progressTask, infos));
@@ -332,7 +336,7 @@ public class InferNullityAnnotationsAction extends BaseAnalysisAction {
         return new UsageInfoSearcherAdapter() {
           @Override
           protected UsageInfo[] findUsages() {
-            return InferNullityAnnotationsAction.this.findUsages(project, scope);
+            return InferNullityAnnotationsAction.this.findUsages(project, scope, scope.getFileCount());
           }
 
           @Override
index 8ab4fe47ce7d6eebe98acdc0e915515631beb8ff..d158cb7b361f3c1536e74e11bdda4854b10f290e 100644 (file)
@@ -1252,7 +1252,7 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
 
   private static final Key<String> MARK_KEY = Key.create("");
 
-  private PsiReferenceExpression[] addBracesWhenNeeded(PsiReferenceExpression[] refs) throws IncorrectOperationException {
+  public PsiReferenceExpression[] addBracesWhenNeeded(PsiReferenceExpression[] refs) throws IncorrectOperationException {
     ArrayList<PsiReferenceExpression> refsVector = new ArrayList<PsiReferenceExpression>();
     ArrayList<PsiCodeBlock> addedBracesVector = new ArrayList<PsiCodeBlock>();
     myAddedClassInitializers = new HashMap<PsiField, PsiClassInitializer>();
index 98645dca001ec04737f2ef7cc1f17b10da88be26..ac2c06c28f0fc060a3c2cc169b0abfd9b08e7b6e 100644 (file)
@@ -230,7 +230,7 @@ public class InplaceIntroduceConstantPopup extends AbstractInplaceIntroduceField
 
   @Override
   protected boolean startsOnTheSameElement(RefactoringActionHandler handler, PsiElement element) {
-    return super.startsOnTheSameElement(handler, element) && handler instanceof IntroduceConstantHandler;
+    return handler instanceof IntroduceConstantHandler && super.startsOnTheSameElement(handler, element);
   }
 
   @Override
index 11f2d4a640796aafd7910bf18e7f9905b705e2be..1ad65a6b1b44706a7f82f5a06f943aa2e3ecbf6e 100644 (file)
@@ -172,7 +172,7 @@ public class InplaceIntroduceFieldPopup extends AbstractInplaceIntroduceFieldPop
 
   @Override
   protected boolean startsOnTheSameElement(RefactoringActionHandler handler, PsiElement element) {
-    return super.startsOnTheSameElement(handler, element) && handler instanceof IntroduceFieldHandler;
+    return handler instanceof IntroduceFieldHandler && super.startsOnTheSameElement(handler, element);
   }
 
   @Override
index 44b0d09dac091ee0f9b1657ca8ab3352b3e2df1f..f36212e505ebc80efa0418cc6645de998e8e7b67 100644 (file)
@@ -174,7 +174,7 @@ public class InplaceIntroduceParameterPopup extends AbstractJavaInplaceIntroduce
 
   @Override
   protected boolean startsOnTheSameElement(RefactoringActionHandler handler, PsiElement element) {
-    return super.startsOnTheSameElement(handler, element) && handler instanceof IntroduceParameterHandler;
+    return handler instanceof IntroduceParameterHandler && super.startsOnTheSameElement(handler, element);
   }
 
 
index ca51d31a50c9586e6b621445c9ea3f1449e8d836..572221b8cf78b1c5f0b8eeae1bf07c5db3223560 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2014 JetBrains s.r.o.
+ * Copyright 2000-2011 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@ package com.intellij.refactoring.introduceVariable;
 import com.intellij.codeInsight.CodeInsightUtil;
 import com.intellij.codeInsight.completion.JavaCompletionUtil;
 import com.intellij.codeInsight.highlighting.HighlightManager;
-import com.intellij.codeInsight.intention.impl.TypeExpression;
 import com.intellij.codeInsight.lookup.LookupManager;
 import com.intellij.codeInsight.unwrap.ScopeHighlighter;
 import com.intellij.featureStatistics.FeatureUsageTracker;
@@ -37,10 +36,7 @@ import com.intellij.openapi.editor.colors.EditorColorsManager;
 import com.intellij.openapi.editor.markup.TextAttributes;
 import com.intellij.openapi.fileEditor.FileDocumentManager;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.Key;
-import com.intellij.openapi.util.Pass;
-import com.intellij.openapi.util.Ref;
-import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.util.*;
 import com.intellij.openapi.util.registry.Registry;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.wm.WindowManager;
@@ -77,6 +73,7 @@ import com.intellij.util.containers.MultiMap;
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.TestOnly;
 
 import java.util.*;
 
@@ -91,6 +88,7 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
   
   protected static final String REFACTORING_NAME = RefactoringBundle.message("introduce.variable.title");
   public static final Key<Boolean> NEED_PARENTHESIS = Key.create("NEED_PARENTHESIS");
+  private JavaVariableInplaceIntroducer myInplaceIntroducer;
 
   public static SuggestedNameInfo getSuggestedName(@Nullable PsiType type, @NotNull final PsiExpression expression) {
     return getSuggestedName(type, expression, expression);
@@ -645,76 +643,74 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
     final Pass<OccurrencesChooser.ReplaceChoice> callback = new Pass<OccurrencesChooser.ReplaceChoice>() {
       @Override
       public void pass(final OccurrencesChooser.ReplaceChoice choice) {
-        final boolean allOccurences = choice == OccurrencesChooser.ReplaceChoice.ALL || choice == OccurrencesChooser.ReplaceChoice.NO_WRITE;
-        final Ref<SmartPsiElementPointer<PsiVariable>> variable = new Ref<SmartPsiElementPointer<PsiVariable>>();
-
-        final Editor topLevelEditor;
-        if (!InjectedLanguageManager.getInstance(project).isInjectedFragment(anchorStatement.getContainingFile())) {
-          topLevelEditor = InjectedLanguageUtil.getTopLevelEditor(editor);
-        } else {
-          topLevelEditor = editor;
-        }
-
-        final IntroduceVariableSettings settings;
-        final PsiElement chosenAnchor;
         if (choice != null) {
-          chosenAnchor = chooseAnchor(allOccurences, choice == OccurrencesChooser.ReplaceChoice.NO_WRITE, nonWrite, anchorStatementIfAll, anchorStatement);
-          settings = getSettings(project, topLevelEditor, expr, occurrences, typeSelectorManager, inFinalContext, hasWriteAccess, validator, chosenAnchor, choice);
-        }
-        else {
-          settings = getSettings(project, topLevelEditor, expr, occurrences, typeSelectorManager, inFinalContext, hasWriteAccess, validator, anchorStatement, choice);
-          chosenAnchor = chooseAnchor(settings.isReplaceAllOccurrences(), hasWriteAccess, nonWrite, anchorStatementIfAll, anchorStatement);
-        }
-        if (!settings.isOK()) {
-          wasSucceed[0] = false;
-          return;
-        }
-        typeSelectorManager.setAllOccurrences(allOccurences);
-        final TypeExpression expression = new TypeExpression(project, allOccurences ? typeSelectorManager.getTypesForAll() : typeSelectorManager.getTypesForOne());
-        final RangeMarker exprMarker = topLevelEditor.getDocument().createRangeMarker(expr.getTextRange());
-        final SuggestedNameInfo suggestedName = getSuggestedName(settings.getSelectedType(), expr, chosenAnchor);
-        final List<RangeMarker> occurrenceMarkers = new ArrayList<RangeMarker>();
-        final boolean noWrite = choice == OccurrencesChooser.ReplaceChoice.NO_WRITE;
-        for (PsiExpression occurrence : occurrences) {
-          if (allOccurences || (noWrite && !PsiUtil.isAccessedForWriting(occurrence))) {
-            occurrenceMarkers.add(topLevelEditor.getDocument().createRangeMarker(occurrence.getTextRange()));
+          final boolean replaceAll = choice == OccurrencesChooser.ReplaceChoice.ALL || choice == OccurrencesChooser.ReplaceChoice.NO_WRITE;
+          typeSelectorManager.setAllOccurrences(replaceAll);
+
+          final PsiElement chosenAnchor =
+            chooseAnchor(replaceAll, choice == OccurrencesChooser.ReplaceChoice.NO_WRITE, nonWrite, anchorStatementIfAll, anchorStatement);
+          final IntroduceVariableSettings settings =
+            getSettings(project, editor, expr, occurrences, typeSelectorManager, inFinalContext, hasWriteAccess, validator, chosenAnchor, choice);
+          
+          final boolean cantChangeFinalModifier = (hasWriteAccess || inFinalContext) && choice == OccurrencesChooser.ReplaceChoice.ALL;
+
+          final boolean noWrite = choice == OccurrencesChooser.ReplaceChoice.NO_WRITE;
+          final List<PsiExpression> allOccurrences = new ArrayList<PsiExpression>();
+          for (PsiExpression occurrence : occurrences) {
+            if (expr.equals(occurrence) && expr.getParent() instanceof PsiExpressionStatement) continue;
+            if (choice == OccurrencesChooser.ReplaceChoice.ALL || (noWrite && !PsiUtil.isAccessedForWriting(occurrence)) ||  expr.equals(occurrence)) {
+              allOccurrences.add(occurrence);
+            }
+          }
+          myInplaceIntroducer = new JavaVariableInplaceIntroducer(project,
+                                            settings,
+                                            chosenAnchor,
+                                            editor, expr, cantChangeFinalModifier,
+                                            allOccurrences.toArray(new PsiExpression[allOccurrences.size()]),
+                                            typeSelectorManager,
+                                            REFACTORING_NAME);
+          if (myInplaceIntroducer.startInplaceIntroduceTemplate()) {
+            return;
           }
         }
-        final RefactoringEventData beforeData = new RefactoringEventData();
-        beforeData.addElement(expr);
-        project.getMessageBus()
-          .syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC).refactoringStarted(REFACTORING_ID, beforeData);
-        final String expressionText = expr.getText();
-        final Runnable runnable = introduce(project, expr, topLevelEditor, chosenAnchor, occurrences, settings, variable);
+
         CommandProcessor.getInstance().executeCommand(
           project,
           new Runnable() {
             public void run() {
+              final Editor topLevelEditor ;
+              if (!InjectedLanguageManager.getInstance(project).isInjectedFragment(anchorStatement.getContainingFile())) {
+                topLevelEditor = InjectedLanguageUtil.getTopLevelEditor(editor);
+              } else {
+                topLevelEditor = editor;
+              }
+
+              PsiVariable variable = null;
               try {
-                ApplicationManager.getApplication().runWriteAction(runnable);
+                final IntroduceVariableSettings settings =
+                  getSettings(project, topLevelEditor, expr, occurrences, typeSelectorManager, inFinalContext, hasWriteAccess, validator, anchorStatement, choice);
+                if (!settings.isOK()) {
+                  wasSucceed[0] = false;
+                  return;
+                }
+
+                final RefactoringEventData beforeData = new RefactoringEventData();
+                beforeData.addElement(expr);
+                project.getMessageBus()
+                  .syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC).refactoringStarted(REFACTORING_ID, beforeData);
+
+                final PsiElement chosenAnchor =
+                  chooseAnchor(settings.isReplaceAllOccurrences(), hasWriteAccess, nonWrite, anchorStatementIfAll, anchorStatement);
+
+                variable = ApplicationManager.getApplication().runWriteAction(
+                  introduce(project, expr, topLevelEditor, chosenAnchor, occurrences, settings));
               }
               finally {
                 final RefactoringEventData afterData = new RefactoringEventData();
-                final SmartPsiElementPointer<PsiVariable> pointer = variable.get();
-                afterData.addElement(pointer != null ? pointer.getElement() : null);
+                afterData.addElement(variable);
                 project.getMessageBus()
                   .syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC).refactoringDone(REFACTORING_ID, afterData);
               }
-
-              if (isInplaceAvailableOnDataContext) {
-                final PsiVariable elementToRename = variable.get().getElement();
-                if (elementToRename != null) {
-                  topLevelEditor.getCaretModel().moveToOffset(elementToRename.getTextOffset());
-                  final boolean cantChangeFinalModifier = (hasWriteAccess || inFinalContext) && choice == OccurrencesChooser.ReplaceChoice.ALL;
-                  final JavaVariableInplaceIntroducer renamer =
-                    new JavaVariableInplaceIntroducer(project, expression, topLevelEditor, elementToRename, cantChangeFinalModifier,
-                                                  typeSelectorManager.getTypesForAll().length > 1, exprMarker, occurrenceMarkers,
-                                                  REFACTORING_NAME);
-                  renamer.initInitialText(expressionText);
-                  PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(topLevelEditor.getDocument());
-                  renamer.performInplaceRefactoring(new LinkedHashSet<String>(Arrays.asList(suggestedName.names)));
-                }
-              }
             }
           }, REFACTORING_NAME, null);
       }
@@ -724,16 +720,25 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
       callback.pass(null);
     }
     else {
-      OccurrencesChooser.<PsiExpression>simpleChooser(editor).showChooser(callback, occurrencesMap);
+      OccurrencesChooser.ReplaceChoice choice = getOccurrencesChoice();
+      if (choice != null) {
+        callback.pass(choice);
+      } else {
+        OccurrencesChooser.<PsiExpression>simpleChooser(editor).showChooser(callback, occurrencesMap);
+      }
     }
     return wasSucceed[0];
   }
+  
+  protected OccurrencesChooser.ReplaceChoice getOccurrencesChoice() {
+    return null;
+  }
 
-  protected PsiElement chooseAnchor(boolean allOccurences,
-                                    boolean hasWriteAccess,
-                                    List<PsiExpression> nonWrite,
-                                    PsiElement anchorStatementIfAll, 
-                                    PsiElement anchorStatement) {
+  protected static PsiElement chooseAnchor(boolean allOccurences,
+                                           boolean hasWriteAccess,
+                                           List<PsiExpression> nonWrite,
+                                           PsiElement anchorStatementIfAll,
+                                           PsiElement anchorStatement) {
     if (allOccurences) {
       if (hasWriteAccess) {
         return RefactoringUtil.getAnchorElementForMultipleExpressions(nonWrite.toArray(new PsiExpression[nonWrite.size()]), null);
@@ -792,13 +797,12 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
     return parent3 instanceof JspHolderMethod;
   }
 
-  private static Runnable introduce(final Project project,
-                                    final PsiExpression expr,
-                                    final Editor editor,
-                                    final PsiElement anchorStatement,
-                                    final PsiExpression[] occurrences,
-                                    final IntroduceVariableSettings settings,
-                                    final Ref<SmartPsiElementPointer<PsiVariable>> variable) {
+  public static Computable<PsiVariable> introduce(final Project project,
+                                                  final PsiExpression expr,
+                                                  final Editor editor,
+                                                  final PsiElement anchorStatement,
+                                                  final PsiExpression[] occurrences,
+                                                  final IntroduceVariableSettings settings) {
     final PsiElement container = anchorStatement.getParent();
     PsiElement child = anchorStatement;
     if (!RefactoringUtil.isLoopOrIf(container)) {
@@ -837,8 +841,9 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
 
     final PsiCodeBlock newDeclarationScope = PsiTreeUtil.getParentOfType(container, PsiCodeBlock.class, false);
     final FieldConflictsResolver fieldConflictsResolver = new FieldConflictsResolver(settings.getEnteredName(), newDeclarationScope);
-    return new Runnable() {
-      public void run() {
+    return new Computable<PsiVariable>() {
+      @Override
+      public PsiVariable compute() {
         try {
           PsiStatement statement = null;
           final boolean isInsideLoop = RefactoringUtil.isLoopOrIf(container);
@@ -913,11 +918,12 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
           declaration = (PsiDeclarationStatement)JavaCodeStyleManager.getInstance(project).shortenClassReferences(declaration);
           PsiVariable var = (PsiVariable) declaration.getDeclaredElements()[0];
           PsiUtil.setModifierProperty(var, PsiModifier.FINAL, settings.isDeclareFinal());
-          variable.set(SmartPointerManager.getInstance(project).createSmartPsiElementPointer(var));
           fieldConflictsResolver.fix();
+          return var;
         } catch (IncorrectOperationException e) {
           LOG.error(e);
         }
+        return null;
       }
 
       private PsiDeclarationStatement addDeclaration(PsiDeclarationStatement declaration, PsiExpression initializer) {
@@ -1165,6 +1171,6 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
 
   @Override
   public AbstractInplaceIntroducer getInplaceIntroducer() {
-    return null;
+    return myInplaceIntroducer;
   }
 }
index f9e88de162cf251aebf11d97056abaa3b9531da8..438009068cb90a93c9fab811ab3bbb45523884a6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2014 JetBrains s.r.o.
+ * Copyright 2000-2011 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.
@@ -21,7 +21,6 @@ import com.intellij.openapi.actionSystem.Shortcut;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.application.Result;
 import com.intellij.openapi.command.WriteCommandAction;
-import com.intellij.openapi.command.impl.StartMarkAction;
 import com.intellij.openapi.editor.Document;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.editor.RangeMarker;
@@ -30,87 +29,70 @@ import com.intellij.openapi.keymap.Keymap;
 import com.intellij.openapi.keymap.KeymapManager;
 import com.intellij.openapi.keymap.KeymapUtil;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.Comparing;
-import com.intellij.openapi.vfs.ReadonlyStatusHandler;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.TextRange;
 import com.intellij.psi.*;
 import com.intellij.psi.codeStyle.CodeStyleManager;
 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
-import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
+import com.intellij.psi.codeStyle.VariableKind;
 import com.intellij.psi.scope.processor.VariablesProcessor;
 import com.intellij.psi.util.PsiTreeUtil;
 import com.intellij.psi.util.TypeConversionUtil;
 import com.intellij.refactoring.JavaRefactoringSettings;
-import com.intellij.refactoring.introduce.inplace.InplaceVariableIntroducer;
+import com.intellij.refactoring.RefactoringActionHandler;
 import com.intellij.refactoring.introduceParameter.AbstractJavaInplaceIntroducer;
 import com.intellij.refactoring.rename.ResolveSnapshotProvider;
 import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer;
 import com.intellij.refactoring.ui.TypeSelectorManagerImpl;
+import com.intellij.refactoring.util.RefactoringUtil;
 import com.intellij.ui.NonFocusableCheckBox;
-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;
 import java.util.List;
 
 /**
  * User: anna
  * Date: 12/8/10
  */
-public class JavaVariableInplaceIntroducer extends InplaceVariableIntroducer<PsiExpression> {
-  protected final Project myProject;
-  private final SmartPsiElementPointer<PsiDeclarationStatement> myPointer;
+public class JavaVariableInplaceIntroducer extends AbstractJavaInplaceIntroducer {
 
-  private JCheckBox myCanBeFinalCb;
+  private SmartPsiElementPointer<PsiDeclarationStatement> myPointer;
 
+  private JCheckBox myCanBeFinalCb;
+  private IntroduceVariableSettings mySettings;
+  private SmartPsiElementPointer<PsiElement> myChosenAnchor;
   private final boolean myCantChangeFinalModifier;
-  private final String myTitle;
-  private String myExpressionText;
-  protected final SmartTypePointer myDefaultType;
-  protected final TypeExpression myExpression;
-
+  private boolean myHasTypeSuggestion;
   private ResolveSnapshotProvider.ResolveSnapshot myConflictResolver;
+  private TypeExpression myExpression;
+  private boolean myReplaceSelf;
+  private boolean myDeleteSelf = true;
 
   public JavaVariableInplaceIntroducer(final Project project,
-                                       final TypeExpression expression,
-                                       final Editor editor,
-                                       @NotNull final PsiVariable elementToRename,
+                                       IntroduceVariableSettings settings, PsiElement chosenAnchor, final Editor editor,
+                                       final PsiExpression expr,
                                        final boolean cantChangeFinalModifier,
-                                       final boolean hasTypeSuggestion,
-                                       final RangeMarker exprMarker,
-                                       final List<RangeMarker> occurrenceMarkers,
+                                       final PsiExpression[] occurrences,
+                                       final TypeSelectorManagerImpl selectorManager,
                                        final String title) {
-    super(elementToRename, editor, project, title, new PsiExpression[0], null);
-    myProject = project;
+    super(project, editor, RefactoringUtil.outermostParenthesizedExpression(expr), null, occurrences, selectorManager, title);
+    mySettings = settings;
+    myChosenAnchor = SmartPointerManager.getInstance(project).createSmartPsiElementPointer(chosenAnchor);
     myCantChangeFinalModifier = cantChangeFinalModifier;
+    myHasTypeSuggestion = selectorManager.getTypesForAll().length > 1;
     myTitle = title;
-    setExprMarker(exprMarker);
-    setOccurrenceMarkers(occurrenceMarkers);
-    final PsiDeclarationStatement declarationStatement = PsiTreeUtil.getParentOfType(elementToRename, PsiDeclarationStatement.class);
-    myPointer = declarationStatement != null ? SmartPointerManager.getInstance(project).createSmartPsiElementPointer(declarationStatement) : null;
-    editor.putUserData(ReassignVariableUtil.DECLARATION_KEY, myPointer);
-    if (occurrenceMarkers != null) {
-      final ArrayList<RangeMarker> rangeMarkers = new ArrayList<RangeMarker>(occurrenceMarkers);
-      rangeMarkers.add(exprMarker);
-      editor.putUserData(ReassignVariableUtil.OCCURRENCES_KEY,
-                         rangeMarkers.toArray(new RangeMarker[rangeMarkers.size()]));
-    }
-    myExpression = expression;
-    final PsiType defaultType = elementToRename.getType();
-    myDefaultType = SmartTypePointerManager.getInstance(project).createSmartTypePointer(defaultType);
-    setAdvertisementText(getAdvertisementText(declarationStatement, defaultType, hasTypeSuggestion));
-  }
+    myExpression = new TypeExpression(myProject, isReplaceAllOccurrences()
+                                                 ? myTypeSelectorManager.getTypesForAll()
+                                                 : myTypeSelectorManager.getTypesForOne());
 
-  public void initInitialText(String text) {
-    myExpressionText = text;
-  }
-
-  @Override
-  protected StartMarkAction startRename() throws StartMarkAction.AlreadyStartedException {
-    return StartMarkAction.start(myEditor, myProject, getCommandName());
+    final List<RangeMarker> rangeMarkers = getOccurrenceMarkers();
+    editor.putUserData(ReassignVariableUtil.OCCURRENCES_KEY,
+                       rangeMarkers.toArray(new RangeMarker[rangeMarkers.size()]));
+    myReplaceSelf = myExpr.getParent() instanceof PsiExpressionStatement;
   }
 
   @Override
@@ -122,7 +104,7 @@ public class JavaVariableInplaceIntroducer extends InplaceVariableIntroducer<Psi
 
   @Nullable
   protected PsiVariable getVariable() {
-    final PsiDeclarationStatement declarationStatement = myPointer.getElement();
+    final PsiDeclarationStatement declarationStatement = myPointer != null ? myPointer.getElement() : null;
     if (declarationStatement != null) {
       PsiElement[] declaredElements = declarationStatement.getDeclaredElements();
       return declaredElements.length == 0 ? null : (PsiVariable)declaredElements[0];
@@ -131,101 +113,94 @@ public class JavaVariableInplaceIntroducer extends InplaceVariableIntroducer<Psi
   }
 
   @Override
-  protected void moveOffsetAfter(boolean success) {
-    try {
-      if (success) {
-        final Document document = myEditor.getDocument();
-        @Nullable final PsiVariable psiVariable = getVariable();
-        if (psiVariable == null) {
-          return;
-        }
-        LOG.assertTrue(psiVariable.isValid());
-        TypeSelectorManagerImpl.typeSelected(psiVariable.getType(), myDefaultType.getType());
-        if (myCanBeFinalCb != null) {
-          JavaRefactoringSettings.getInstance().INTRODUCE_LOCAL_CREATE_FINALS = psiVariable.hasModifierProperty(PsiModifier.FINAL);
-        }
-        adjustLine(psiVariable, document);
+  protected String getActionName() {
+    return "IntroduceVariable";
+  }
 
-        int startOffset = getExprMarker() != null && getExprMarker().isValid() ? getExprMarker().getStartOffset() : psiVariable.getTextOffset();
-        final PsiFile file = psiVariable.getContainingFile();
-        final PsiReference referenceAt = file.findReferenceAt(startOffset);
-        if (referenceAt != null && referenceAt.resolve() instanceof PsiVariable) {
-          startOffset = referenceAt.getElement().getTextRange().getEndOffset();
-        }
-        else {
-          final PsiDeclarationStatement declarationStatement = PsiTreeUtil.getParentOfType(psiVariable, PsiDeclarationStatement.class);
-          if (declarationStatement != null) {
-            startOffset = declarationStatement.getTextRange().getEndOffset();
-          }
-        }
-        myEditor.getCaretModel().moveToOffset(startOffset);
-        myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
-        ApplicationManager.getApplication().runWriteAction(new Runnable() {
-          public void run() {
-            if (psiVariable.getInitializer() != null) {
-              appendTypeCasts(getOccurrenceMarkers(), file, myProject, psiVariable);
-            }
-            if (myConflictResolver != null && myInsertedName != null && isIdentifier(myInsertedName, psiVariable.getLanguage())) {
-              myConflictResolver.apply(psiVariable.getName());
-            }
-          }
-        });
+  @Override
+  protected void restoreState(PsiVariable psiField) {
+    if (myDeleteSelf) return;
+    super.restoreState(psiField);
+  }
+
+  @Override
+  protected boolean ensureValid() {
+    final PsiVariable variable = getVariable();
+    return variable != null && isIdentifier(getInputName(), variable.getLanguage());
+  }
+
+  @Override
+  protected void performCleanup() {
+    super.performCleanup();
+    super.restoreState(getVariable());
+  }
+
+  @Override
+  protected void deleteTemplateField(PsiVariable variable) {
+    if (!myDeleteSelf) return;
+    if (myReplaceSelf) {
+      variable.replace(variable.getInitializer());
+    } else {
+      super.deleteTemplateField(variable);
+    }
+  }
+
+  @Override
+  protected void performIntroduce() {
+    final PsiVariable psiVariable = getVariable();
+    if (psiVariable == null) {
+      return;
+    }
+    
+    TypeSelectorManagerImpl.typeSelected(psiVariable.getType(), myTypeSelectorManager.getDefaultType());
+    if (myCanBeFinalCb != null) {
+      JavaRefactoringSettings.getInstance().INTRODUCE_LOCAL_CREATE_FINALS = psiVariable.hasModifierProperty(PsiModifier.FINAL);
+    }
+
+    final Document document = myEditor.getDocument();
+    LOG.assertTrue(psiVariable.isValid());
+    adjustLine(psiVariable, document);
+
+    int startOffset = getExprMarker() != null && getExprMarker().isValid() ? getExprMarker().getStartOffset() : psiVariable.getTextOffset();
+    final PsiFile file = psiVariable.getContainingFile();
+    final PsiReference referenceAt = file.findReferenceAt(startOffset);
+    if (referenceAt != null && referenceAt.resolve() instanceof PsiVariable) {
+      startOffset = referenceAt.getElement().getTextRange().getEndOffset();
+    }
+    else {
+      final PsiDeclarationStatement declarationStatement = PsiTreeUtil.getParentOfType(psiVariable, PsiDeclarationStatement.class);
+      if (declarationStatement != null) {
+        startOffset = declarationStatement.getTextRange().getEndOffset();
       }
-      else {
-        RangeMarker exprMarker = getExprMarker();
-        if (exprMarker != null && exprMarker.isValid()) {
-          myEditor.getCaretModel().moveToOffset(exprMarker.getStartOffset());
-          myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
+    }
+
+    myEditor.getCaretModel().moveToOffset(startOffset);
+    myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
+
+    ApplicationManager.getApplication().runWriteAction(new Runnable() {
+      public void run() {
+        if (psiVariable.getInitializer() != null) {
+          appendTypeCasts(getOccurrenceMarkers(), file, myProject, psiVariable);
         }
-        if (myExpressionText != null) {
-          if (!ReadonlyStatusHandler.ensureDocumentWritable(myProject, InjectedLanguageUtil.getTopLevelEditor(myEditor).getDocument())) return;
-          ApplicationManager.getApplication().runWriteAction(new Runnable() {
-            public void run() {
-              final PsiDeclarationStatement element = myPointer.getElement();
-              if (element != null) {
-                final PsiElement[] vars = element.getDeclaredElements();
-                if (vars.length > 0 && vars[0] instanceof PsiVariable) {
-                  final PsiFile containingFile = element.getContainingFile();
-                  //todo pull up method restore state
-                  final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(myProject);
-                  final RangeMarker exprMarker = getExprMarker();
-                  if (exprMarker != null) {
-                    myExpr = AbstractJavaInplaceIntroducer.restoreExpression(containingFile, (PsiVariable)vars[0], elementFactory, exprMarker, myExpressionText);
-                    if (myExpr != null && myExpr.isPhysical()) {
-                      myExprMarker = createMarker(myExpr);
-                    }
-                  }
-                  List<RangeMarker> markers = getOccurrenceMarkers();
-                  for (RangeMarker occurrenceMarker : markers) {
-                    if (getExprMarker() != null && occurrenceMarker.getStartOffset() == getExprMarker().getStartOffset() && myExpr != null) {
-                      continue;
-                    }
-                    AbstractJavaInplaceIntroducer
-                          .restoreExpression(containingFile, (PsiVariable)vars[0], elementFactory, occurrenceMarker, myExpressionText);
-                  }
-                  final PsiExpression initializer = ((PsiVariable)vars[0]).getInitializer();
-                  if (initializer != null && Comparing.strEqual(initializer.getText(), myExpressionText) && myExpr == null) {
-                    element.replace(JavaPsiFacade.getInstance(myProject).getElementFactory().createStatementFromText(myExpressionText, element));
-                  } else {
-                    element.delete();
-                  }
-                }
-              }
-            }
-          });
+        if (myConflictResolver != null && myInsertedName != null && isIdentifier(myInsertedName, psiVariable.getLanguage())) {
+          myConflictResolver.apply(psiVariable.getName());
         }
       }
-    }
-    finally {
-      myEditor.putUserData(ReassignVariableUtil.DECLARATION_KEY, null);
-      for (RangeMarker occurrenceMarker : getOccurrenceMarkers()) {
-        occurrenceMarker.dispose();
-      }
-      myEditor.putUserData(ReassignVariableUtil.OCCURRENCES_KEY, null);
-      if (getExprMarker() != null) getExprMarker().dispose();
-    }
+    });
+  }
+
+  @Override
+  public boolean isReplaceAllOccurrences() {
+    return mySettings.isReplaceAllOccurrences();
   }
 
+  @Override
+  public void setReplaceAllOccurrences(boolean allOccurrences) {}
+
+  @Override
+  protected boolean startsOnTheSameElement(RefactoringActionHandler handler, PsiElement element) {
+    return handler instanceof IntroduceVariableHandler && super.startsOnTheSameElement(handler, element);
+  }
 
   @Nullable
   protected JComponent getComponent() {
@@ -265,8 +240,27 @@ public class JavaVariableInplaceIntroducer extends InplaceVariableIntroducer<Psi
   }
 
   protected void addAdditionalVariables(TemplateBuilderImpl builder) {
-    final PsiTypeElement typeElement = getVariable().getTypeElement();
-    builder.replaceElement(typeElement, "Variable_Type", AbstractJavaInplaceIntroducer.createExpression(myExpression, typeElement.getText()), true, true);
+    final PsiVariable variable = getVariable();
+    if (variable != null) {
+      final PsiTypeElement typeElement = variable.getTypeElement();
+      if (typeElement != null) {
+        builder.replaceElement(typeElement, "Variable_Type", AbstractJavaInplaceIntroducer.createExpression(myExpression, typeElement.getText()), true, true);
+      }
+    }
+  }
+
+  @Override
+  protected void collectAdditionalElementsToRename(List<Pair<PsiElement, TextRange>> stringUsages) {
+    if (isReplaceAllOccurrences()) {
+      for (PsiExpression expression : getOccurrences()) {
+        LOG.assertTrue(expression.isValid(), expression.getText());
+        stringUsages.add(Pair.<PsiElement, TextRange>create(expression, new TextRange(0, expression.getTextLength())));
+      }
+    } else if (getExpr() != null && !myReplaceSelf) {
+      final PsiExpression expr = getExpr();
+      LOG.assertTrue(expr.isValid(), expr.getText());
+      stringUsages.add(Pair.<PsiElement, TextRange>create(expr, new TextRange(0, expr.getTextLength())));
+    }
   }
 
   private static void appendTypeCasts(List<RangeMarker> occurrenceMarkers,
@@ -353,21 +347,49 @@ public class JavaVariableInplaceIntroducer extends InplaceVariableIntroducer<Psi
     });
   }
 
-
-  protected String getTitle() {
-    return myTitle;
+  @Override
+  protected PsiVariable createFieldToStartTemplateOn(String[] names, PsiType psiType) {
+    final PsiVariable variable = ApplicationManager.getApplication().runWriteAction(
+      IntroduceVariableBase.introduce(myProject, myExpr, myEditor, myChosenAnchor.getElement(), getOccurrences(), mySettings));
+    PsiDocumentManager.getInstance(myProject).doPostponedOperationsAndUnblockDocument(myEditor.getDocument());
+    final PsiDeclarationStatement declarationStatement = PsiTreeUtil.getParentOfType(variable, PsiDeclarationStatement.class);
+    myPointer = declarationStatement != null ? SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(declarationStatement) : null;
+    myEditor.putUserData(ReassignVariableUtil.DECLARATION_KEY, myPointer);
+    setAdvertisementText(getAdvertisementText(declarationStatement, variable.getType(), myHasTypeSuggestion));
+    final PsiIdentifier identifier = variable.getNameIdentifier();
+    if (identifier != null) {
+      myEditor.getCaretModel().moveToOffset(identifier.getTextOffset());
+    }
+    try {
+      myDeleteSelf = false;
+      restoreState(variable);
+    }
+    finally {
+      myDeleteSelf = true;
+    }
+    initOccurrencesMarkers();
+    return variable;
   }
 
-
-  @Nullable
-  private static String getAdvertisementText(final boolean hasTypeSuggestion) {
-    final Keymap keymap = KeymapManager.getInstance().getActiveKeymap();
-    if (hasTypeSuggestion) {
-      final Shortcut[] shortcuts = keymap.getShortcuts("PreviousTemplateVariable");
-      if (shortcuts.length > 0) {
-        return "Press " + shortcuts[0] + " to change type";
+  @Override
+  protected int getCaretOffset() {
+    final PsiVariable variable = getVariable();
+    if (variable != null) {
+      final PsiIdentifier identifier = variable.getNameIdentifier();
+      if (identifier != null) {
+        return identifier.getTextOffset();
       }
     }
-    return null;
+    return super.getCaretOffset();
+  }
+
+  @Override
+  protected String[] suggestNames(PsiType defaultType, String propName) {
+    return IntroduceVariableBase.getSuggestedName(defaultType, myExpr).names;
+  }
+
+  @Override
+  protected VariableKind getVariableKind() {
+    return VariableKind.LOCAL_VARIABLE;
   }
 }
diff --git a/java/java-tests/testData/inspection/deadCode/functionalExpressions/expected.xml b/java/java-tests/testData/inspection/deadCode/functionalExpressions/expected.xml
new file mode 100644 (file)
index 0000000..bdafc52
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<problems/>
+
+
diff --git a/java/java-tests/testData/inspection/deadCode/functionalExpressions/src/Test.java b/java/java-tests/testData/inspection/deadCode/functionalExpressions/src/Test.java
new file mode 100644 (file)
index 0000000..3246d24
--- /dev/null
@@ -0,0 +1,10 @@
+public class Test {
+  public static void main(String[] args) {
+    final Memento m = () -> isPlaying;
+    System.out.println(m);
+  }
+
+  public static interface Memento {
+    boolean isPlaying();
+  }
+}
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/allIncomplete.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/allIncomplete.java
new file mode 100644 (file)
index 0000000..377da56
--- /dev/null
@@ -0,0 +1,6 @@
+class C {
+    {
+        new <caret>C();
+        new C();
+    }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/allIncomplete_after.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/allIncomplete_after.java
new file mode 100644 (file)
index 0000000..f83b8e6
--- /dev/null
@@ -0,0 +1,6 @@
+class C {
+    {
+        C c = new C();
+        c;
+    }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/allInsertFinal.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/allInsertFinal.java
new file mode 100644 (file)
index 0000000..476e911
--- /dev/null
@@ -0,0 +1,11 @@
+class C {
+    {
+        C c = new <caret>C();
+        Runnable r = new Runnable() {
+            @Override
+            public void run() {
+                new C();
+            }
+        };
+    }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/allInsertFinal_after.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/allInsertFinal_after.java
new file mode 100644 (file)
index 0000000..c6f2df8
--- /dev/null
@@ -0,0 +1,12 @@
+class C {
+    {
+        final C c1 = new C();
+        C c = c1;
+        Runnable r = new Runnable() {
+            @Override
+            public void run() {
+                c1;
+            }
+        };
+    }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/cast.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/cast.java
new file mode 100644 (file)
index 0000000..f175eb2
--- /dev/null
@@ -0,0 +1,5 @@
+class C {
+    {
+        C c = new <caret>C();
+    }
+}
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/castToObject.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/castToObject.java
new file mode 100644 (file)
index 0000000..f175eb2
--- /dev/null
@@ -0,0 +1,5 @@
+class C {
+    {
+        C c = new <caret>C();
+    }
+}
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/castToObject_after.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/castToObject_after.java
new file mode 100644 (file)
index 0000000..0e98b27
--- /dev/null
@@ -0,0 +1,6 @@
+class C {
+    {
+        Object c1 = new C();
+        C c = (C) c1;
+    }
+}
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/cast_after.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/cast_after.java
new file mode 100644 (file)
index 0000000..04af17e
--- /dev/null
@@ -0,0 +1,6 @@
+class C {
+    {
+        Integer c1 = (Integer) new C();
+        C c = (C) c1;
+    }
+}
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/escapePosition.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/escapePosition.java
new file mode 100644 (file)
index 0000000..f175eb2
--- /dev/null
@@ -0,0 +1,5 @@
+class C {
+    {
+        C c = new <caret>C();
+    }
+}
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/escapePositionIfTyped.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/escapePositionIfTyped.java
new file mode 100644 (file)
index 0000000..f175eb2
--- /dev/null
@@ -0,0 +1,5 @@
+class C {
+    {
+        C c = new <caret>C();
+    }
+}
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/escapePositionIfTyped_after.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/escapePositionIfTyped_after.java
new file mode 100644 (file)
index 0000000..2cbdef7
--- /dev/null
@@ -0,0 +1,5 @@
+class C {
+    {
+        C c = <caret>new C();
+    }
+}
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/escapePosition_after.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/escapePosition_after.java
new file mode 100644 (file)
index 0000000..2cbdef7
--- /dev/null
@@ -0,0 +1,5 @@
+class C {
+    {
+        C c = <caret>new C();
+    }
+}
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/fromExpression.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/fromExpression.java
new file mode 100644 (file)
index 0000000..ea322fa
--- /dev/null
@@ -0,0 +1,5 @@
+class C {
+    {
+        C c = new <caret>C();
+    }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/fromExpression_after.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/fromExpression_after.java
new file mode 100644 (file)
index 0000000..f9a85d8
--- /dev/null
@@ -0,0 +1,6 @@
+class C {
+    {
+        C expr = new C();
+        C c = expr;
+    }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/fromParenthesis.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/fromParenthesis.java
new file mode 100644 (file)
index 0000000..5cdaaed
--- /dev/null
@@ -0,0 +1,5 @@
+class C {
+    {
+        (new <caret>C());
+    }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/fromParenthesis_after.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/fromParenthesis_after.java
new file mode 100644 (file)
index 0000000..eaf0793
--- /dev/null
@@ -0,0 +1,5 @@
+class C {
+    {
+        C expr = new C();
+    }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/noWritable.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/noWritable.java
new file mode 100644 (file)
index 0000000..f577e27
--- /dev/null
@@ -0,0 +1,7 @@
+class C {
+    {
+        int[] a = new int[1];
+        a[1] = 42;
+        System.out.println(a<caret>[1]);
+    }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/noWritable_after.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/noWritable_after.java
new file mode 100644 (file)
index 0000000..803af2c
--- /dev/null
@@ -0,0 +1,8 @@
+class C {
+    {
+        int[] a = new int[1];
+        a[1] = 42;
+        int x = a[1];
+        System.out.println(x);
+    }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/ranges.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/ranges.java
new file mode 100644 (file)
index 0000000..52c72d3
--- /dev/null
@@ -0,0 +1,9 @@
+class Bar {}
+class Foo {
+    static Bar bar;
+}
+class C {
+    {
+        Bar b = Foo.ba<caret>r;
+    }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/ranges_after.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/ranges_after.java
new file mode 100644 (file)
index 0000000..9c92767
--- /dev/null
@@ -0,0 +1,10 @@
+class Bar {}
+class Foo {
+    static Bar bar;
+}
+class C {
+    {
+        Bar expr = Foo.bar;
+        Bar b = expr;
+    }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/stopEditing.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/stopEditing.java
new file mode 100644 (file)
index 0000000..ea322fa
--- /dev/null
@@ -0,0 +1,5 @@
+class C {
+    {
+        C c = new <caret>C();
+    }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/stopEditing_after.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/stopEditing_after.java
new file mode 100644 (file)
index 0000000..26a7304
--- /dev/null
@@ -0,0 +1,8 @@
+class C {
+    {
+        C 
+        
+        c1 = new C();
+        C c = c1;
+    }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/writable.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/writable.java
new file mode 100644 (file)
index 0000000..f577e27
--- /dev/null
@@ -0,0 +1,7 @@
+class C {
+    {
+        int[] a = new int[1];
+        a[1] = 42;
+        System.out.println(a<caret>[1]);
+    }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/inplaceIntroduceVariable/writable_after.java b/java/java-tests/testData/refactoring/inplaceIntroduceVariable/writable_after.java
new file mode 100644 (file)
index 0000000..66a0280
--- /dev/null
@@ -0,0 +1,8 @@
+class C {
+    {
+        int[] a = new int[1];
+        int x = a[1];
+        x = 42;
+        System.out.println(x);
+    }
+}
\ No newline at end of file
index ebd5a17ad2314f3a198d56d231e8241d393b8d4c..bcf896651037bfc3498ee334bef208dcfd458dd1 100644 (file)
@@ -3,6 +3,6 @@ import java.io.File;
 class Test {
   java.io.File[] get() {return null;}
   {
-      File[] files = get();
+      File[] i = get();
   }
 }
\ No newline at end of file
index 8fc38600c7e93f5413ec07c2fef8ce059f4a399f..040e5622a9edc6923e43efbb05cc985c36512cd8 100644 (file)
@@ -190,4 +190,9 @@ public class UnusedDeclarationTest extends InspectionTestCase {
   public void testClassLiteralRef() {
     doTest();
   }
+
+  public void testFunctionalExpressions() {
+    LanguageLevelProjectExtension.getInstance(getJavaFacade().getProject()).setLanguageLevel(LanguageLevel.JDK_1_8);
+    doTest();
+  }
 }
diff --git a/java/java-tests/testSrc/com/intellij/index/IndexTestGenerator.scala b/java/java-tests/testSrc/com/intellij/index/IndexTestGenerator.scala
new file mode 100644 (file)
index 0000000..b2e528b
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.index
+
+import com.intellij.util.containers.ContainerUtil
+import groovy.lang.GroovyClassLoader
+import junit.framework.{TestResult, TestCase}
+import org.scalacheck.Arbitrary.arbitrary
+import org.scalacheck.Gen._
+import org.scalacheck.Prop.forAll
+import org.scalacheck._
+import scala.collection.JavaConverters._
+
+/**
+ * Run this class to generate randomized tests for IDEA VFS/document/PSI/index subsystem interaction using ScalaCheck.
+ * When a test fails, a test method code (in Groovy) is printed which should be copied to a normal test class and debugged there.
+ * The generated test may contain some excessive declarations and checks that should be corrected manually.
+ * After the fix, that generated test should be renamed according to the underlying issue it found and committed to the repository.
+ *
+ * @author peter
+ */
+object IndexTestGenerator {
+  val genAction: Gen[Action] = oneOf(
+    const(Gc),
+    const(Commit),
+    const(Save),
+    for (withImport <- arbitrary[Boolean]; viaDocument <- arbitrary[Boolean]) yield TextChange(viaDocument, withImport),
+    for (load <- arbitrary[Boolean]) yield UpdatePsiClassRef(load),
+    for (load <- arbitrary[Boolean]) yield UpdatePsiFileRef(load),
+    for (load <- arbitrary[Boolean]) yield UpdateASTNodeRef(load),
+    for (load <- arbitrary[Boolean]) yield UpdateDocumentRef(load)
+  )
+  val propIndexTest = forAll(Gen.nonEmptyListOf(genAction)) { actions =>
+    new IndexTestSeq(actions).isSuccessful
+  }
+
+  def main(args: Array[String]) {
+    propIndexTest.check
+  }
+}
+
+case class IndexTestSeq(actions: List[Action]) {
+  def printClass: String = {
+    val sb = StringBuilder.newBuilder
+    sb.append(prefix)
+    sb.append("\n" +
+      "public void \"" + testName + "\"() {\n" +
+      "def vFile =\n  myFixture.addFileToProject(\"Foo.java\", \"class Foo {}\").virtualFile\n" +
+      "def lastPsiName = \"Foo\"\n" +
+      "long counterBefore\n" +
+      "Document document\n" +
+      "PsiFile psiFile\n" +
+      "ASTNode astNode\n" +
+      "PsiClass psiClass\n" +
+      "def scope = GlobalSearchScope.allScope(project)\n")
+    var changeId = 0
+    var docClassName = "Foo"
+    for (action <- actions) {
+      sb.append("\n")
+      action match {
+        case Gc =>
+          sb.append("PlatformTestUtil.tryGcSoftlyReachableObjects()\n")
+        case Commit =>
+          sb.append("PsiDocumentManager.getInstance(project).commitAllDocuments()\nlastPsiName = \"" + docClassName + "\"\n")
+        case Save =>
+          sb.append("FileDocumentManager.instance.saveAllDocuments()\n")
+        case UpdatePsiClassRef(load) =>
+          sb.append("psiClass = " + (if (load) "JavaPsiFacade.getInstance(project).findClass(lastPsiName, scope)" else "null") + "\n")
+          if (load) sb.append("assert psiClass\n")
+        case UpdatePsiFileRef(load) =>
+          sb.append("psiFile = " + (if (load) "psiManager.findFile(vFile)" else "null") + "\n")
+        case UpdateASTNodeRef(load) =>
+          sb.append("astNode = " + (if (load) "JavaPsiFacade.getInstance(project).findClass(lastPsiName, scope).node" else "null") + "\n")
+        case UpdateDocumentRef(load) =>
+          sb.append("document = " + (if (load) "FileDocumentManager.instance.getDocument(vFile)" else "null") + "\n")
+        case TextChange(viaDocument, withImport) =>
+          changeId += 1
+          docClassName = "Foo" + changeId
+          val newText = (if (withImport) "import zoo.Zoo; "  else "") + "class " + docClassName + " {}"
+
+          sb.append("counterBefore =\n  psiManager.modificationTracker.javaStructureModificationCount\n")
+
+          if (viaDocument) {
+            sb.append("FileDocumentManager.instance.getDocument(vFile).text =\n  \"" + newText + "\"\n")
+          } else {
+            sb.append("//todo remove if statement or replace with its content \n")
+            sb.append("if (FileDocumentManager.instance.unsavedDocuments) {\n  FileDocumentManager.instance.saveAllDocuments()\n}\n")
+            sb.append("VfsUtil.saveText(vFile, \"" + newText + "\")\n")
+          }
+
+          sb.append(
+            "// todo replace if statement with assertions\n" +
+            "if (!PsiDocumentManager.getInstance(project).uncommittedDocuments) {\n" +
+            "  lastPsiName = \"" + docClassName + "\"\n" +
+            "  assert counterBefore !=\n    psiManager.modificationTracker.javaStructureModificationCount\n" +
+            "}\n")
+      }
+    }
+    sb.append("}\n}")
+    sb.toString()
+  }
+
+  val prefix = "import com.intellij.lang.ASTNode\n" +
+    "import com.intellij.openapi.command.WriteCommandAction\n" +
+    "import com.intellij.openapi.editor.Document\n" +
+    "import com.intellij.openapi.fileEditor.FileDocumentManager\n" +
+    "import com.intellij.openapi.util.Ref\n" +
+    "import com.intellij.openapi.vfs.VfsUtil\n" +
+    "import com.intellij.psi.*\n" +
+    "import com.intellij.psi.search.GlobalSearchScope\n" +
+    "import com.intellij.testFramework.PlatformTestUtil\n" +
+    "import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase\n" +
+    "import com.intellij.util.ObjectUtils\n" +
+    "import org.jetbrains.annotations.NotNull\n" +
+    "class DummyTest extends JavaCodeInsightFixtureTestCase {\n" +
+    "protected void invokeTestRunnable(Runnable runnable) {\n" +
+    "  WriteCommandAction.runWriteCommandAction(project, runnable)\n" +
+    "}\n"
+
+  val testName: String = "test please write a meaningful description here"
+
+  def isSuccessful: Boolean = {
+    println(actions)
+
+    val classText = printClass
+    val test = new GroovyClassLoader().parseClass(classText).newInstance().asInstanceOf[TestCase]
+    test.setName(testName)
+    val result: TestResult = test.run()
+    for (failure <- ContainerUtil.toList(result.failures()).asScala) {
+      println (failure.trace())
+    }
+    for (failure <- ContainerUtil.toList(result.errors()).asScala) {
+      println (failure.trace())
+    }
+    if (!result.wasSuccessful()) {
+      println(classText)
+    }
+    result.wasSuccessful()
+  }
+
+}
+
+class Action
+case object Gc extends Action
+case object Commit extends Action
+case object Save extends Action
+case class TextChange(viaDocument: Boolean, withImport: Boolean) extends Action
+case class UpdatePsiClassRef(load: Boolean) extends Action
+case class UpdatePsiFileRef(load: Boolean) extends Action
+case class UpdateDocumentRef(load: Boolean) extends Action
+case class UpdateASTNodeRef(load: Boolean) extends Action
index 4f2cb602af6720f453b0f16bd0c6fbca05cfde25..9cd5d069c875d2bf5bb8ae77bf0f54856a39dbb3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2014 JetBrains s.r.o.
+ * Copyright 2000-2012 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -72,6 +72,10 @@ public abstract class AbstractJavaInplaceIntroduceTest extends AbstractInplaceIn
   @Override
   protected AbstractInplaceIntroducer invokeRefactoring() {
     final MyIntroduceHandler introduceHandler = createIntroduceHandler();
+    return invokeRefactoring(introduceHandler);
+  }
+
+  protected AbstractInplaceIntroducer invokeRefactoring(MyIntroduceHandler introduceHandler) {
     final PsiExpression expression = getExpressionFromEditor();
     if (expression != null) {
       introduceHandler.invokeImpl(LightPlatformTestCase.getProject(), expression, getEditor());
diff --git a/java/java-tests/testSrc/com/intellij/refactoring/InplaceIntroduceVariableTest.java b/java/java-tests/testSrc/com/intellij/refactoring/InplaceIntroduceVariableTest.java
new file mode 100644 (file)
index 0000000..6e75fbb
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.refactoring;
+
+import com.intellij.codeInsight.template.impl.TemplateManagerImpl;
+import com.intellij.codeInsight.template.impl.TemplateState;
+import com.intellij.openapi.actionSystem.IdeActions;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.actionSystem.EditorActionManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Pass;
+import com.intellij.psi.PsiExpression;
+import com.intellij.psi.PsiLiteralExpression;
+import com.intellij.psi.PsiLocalVariable;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.refactoring.introduce.inplace.AbstractInplaceIntroducer;
+import com.intellij.refactoring.introduce.inplace.OccurrencesChooser;
+import com.intellij.refactoring.introduceVariable.IntroduceVariableHandler;
+import com.intellij.testFramework.MapDataContext;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class InplaceIntroduceVariableTest extends AbstractJavaInplaceIntroduceTest {
+
+  @Nullable
+  @Override
+  protected PsiExpression getExpressionFromEditor() {
+    final PsiExpression expression = super.getExpressionFromEditor();
+    if (expression != null) {
+      return expression;
+    }
+    final PsiExpression expr = PsiTreeUtil.getParentOfType(getFile().findElementAt(getEditor().getCaretModel().getOffset()), PsiExpression.class);
+    return expr instanceof PsiLiteralExpression ? expr : null;
+  }
+
+  public void testFromExpression() throws Exception {
+     doTest(new Pass<AbstractInplaceIntroducer>() {
+       @Override
+       public void pass(AbstractInplaceIntroducer inplaceIntroduceFieldPopup) {
+         type("expr");
+       }
+     });
+  }
+
+  public void testRanges() throws Exception {
+     doTest(new Pass<AbstractInplaceIntroducer>() {
+       @Override
+       public void pass(AbstractInplaceIntroducer inplaceIntroduceFieldPopup) {
+         type("expr");
+       }
+     });
+  }
+
+  public void testFromParenthesis() throws Exception {
+     doTest(new Pass<AbstractInplaceIntroducer>() {
+       @Override
+       public void pass(AbstractInplaceIntroducer inplaceIntroduceFieldPopup) {
+         type("expr");
+       }
+     });
+  }
+
+  public void testCast() throws Exception {
+    doTestTypeChange("Integer");
+  }
+
+  public void testCastToObject() throws Exception {
+    doTestTypeChange("Object");
+  }
+
+  public void testEscapePosition() {
+    doTestStopEditing(new Pass<AbstractInplaceIntroducer>() {
+      @Override
+      public void pass(AbstractInplaceIntroducer introducer) {
+        invokeEditorAction(IdeActions.ACTION_EDITOR_ESCAPE);
+        invokeEditorAction(IdeActions.ACTION_EDITOR_ESCAPE);
+      }
+    });
+  }
+
+  public void testEscapePositionIfTyped() {
+    doTestStopEditing(new Pass<AbstractInplaceIntroducer>() {
+      @Override
+      public void pass(AbstractInplaceIntroducer introducer) {
+        type("fooBar");
+        invokeEditorAction(IdeActions.ACTION_EDITOR_ESCAPE);
+      }
+    });
+  }
+
+  public void testWritable() throws Exception {
+    doTestReplaceChoice(OccurrencesChooser.ReplaceChoice.ALL);
+  }
+  
+  public void testNoWritable() throws Exception {
+    doTestReplaceChoice(OccurrencesChooser.ReplaceChoice.NO_WRITE);
+  }
+  
+  public void testAllInsertFinal() throws Exception {
+    doTestReplaceChoice(OccurrencesChooser.ReplaceChoice.ALL);
+  }
+  
+  public void testAllIncomplete() throws Exception {
+    doTestReplaceChoice(OccurrencesChooser.ReplaceChoice.ALL);
+  }
+
+  public void testStopEditing() {
+    doTestStopEditing(new Pass<AbstractInplaceIntroducer>() {
+      @Override
+      public void pass(AbstractInplaceIntroducer introducer) {
+        invokeEditorAction(IdeActions.ACTION_EDITOR_MOVE_CARET_LEFT);
+        invokeEditorAction(IdeActions.ACTION_EDITOR_ENTER);
+        invokeEditorAction(IdeActions.ACTION_EDITOR_ENTER);
+      }
+    });
+  }
+
+  private void doTestStopEditing(Pass<AbstractInplaceIntroducer> pass) {
+    String name = getTestName(true);
+    configureByFile(getBasePath() + name + getExtension());
+    final boolean enabled = getEditor().getSettings().isVariableInplaceRenameEnabled();
+    try {
+      TemplateManagerImpl.setTemplateTesting(getProject(), getTestRootDisposable());
+      getEditor().getSettings().setVariableInplaceRenameEnabled(true);
+
+      final AbstractInplaceIntroducer introducer = invokeRefactoring();
+      pass.pass(introducer);
+      checkResultByFile(getBasePath() + name + "_after" + getExtension());
+    }
+    finally {
+      TemplateState state = TemplateManagerImpl.getTemplateState(getEditor());
+      if (state != null) {
+        state.gotoEnd(true);
+      }
+      getEditor().getSettings().setVariableInplaceRenameEnabled(enabled);
+    }
+  }
+
+  private void doTestTypeChange(final String newType) {
+    final Pass<AbstractInplaceIntroducer> typeChanger = new Pass<AbstractInplaceIntroducer>() {
+      @Override
+      public void pass(AbstractInplaceIntroducer inplaceIntroduceFieldPopup) {
+        type(newType);
+      }
+    };
+    String name = getTestName(true);
+    configureByFile(getBasePath() + name + getExtension());
+    final boolean enabled = getEditor().getSettings().isVariableInplaceRenameEnabled();
+    try {
+      TemplateManagerImpl.setTemplateTesting(getProject(), getTestRootDisposable());
+      getEditor().getSettings().setVariableInplaceRenameEnabled(true);
+
+      final AbstractInplaceIntroducer introducer = invokeRefactoring();
+      TemplateState state = TemplateManagerImpl.getTemplateState(getEditor());
+      assert state != null;
+      state.previousTab();
+      typeChanger.pass(introducer);
+      state.gotoEnd(false);
+      checkResultByFile(getBasePath() + name + "_after" + getExtension());
+    }
+    finally {
+      getEditor().getSettings().setVariableInplaceRenameEnabled(enabled);
+    }
+  }
+
+  private void doTestReplaceChoice(OccurrencesChooser.ReplaceChoice choice) {
+    doTestReplaceChoice(choice, null);
+  }
+
+  private void doTestReplaceChoice(OccurrencesChooser.ReplaceChoice choice, Pass<AbstractInplaceIntroducer> pass) {
+    String name = getTestName(true);
+    configureByFile(getBasePath() + name + getExtension());
+    final boolean enabled = getEditor().getSettings().isVariableInplaceRenameEnabled();
+    try {
+      TemplateManagerImpl.setTemplateTesting(getProject(), getTestRootDisposable());
+      getEditor().getSettings().setVariableInplaceRenameEnabled(true);
+
+      MyIntroduceHandler handler = createIntroduceHandler();
+      ((MyIntroduceVariableHandler)handler).setChoice(choice);
+      final AbstractInplaceIntroducer introducer = invokeRefactoring(handler);
+      if (pass != null) {
+        pass.pass(introducer);
+      }
+      TemplateState state = TemplateManagerImpl.getTemplateState(getEditor());
+      assert state != null;
+      state.gotoEnd(false);
+      checkResultByFile(getBasePath() + name + "_after" + getExtension());
+    }
+    finally {
+      getEditor().getSettings().setVariableInplaceRenameEnabled(enabled);
+    }
+  }
+
+  private static void invokeEditorAction(String actionId) {
+    EditorActionManager.getInstance().getActionHandler(actionId)
+      .execute(getEditor(), getEditor().getCaretModel().getCurrentCaret(), new MapDataContext());
+  }
+
+
+  @Override
+  protected String getBasePath() {
+    return "/refactoring/inplaceIntroduceVariable/";
+  }
+
+  @Override
+  protected MyIntroduceHandler createIntroduceHandler() {
+    return new MyIntroduceVariableHandler();
+  }
+
+  public static class MyIntroduceVariableHandler extends IntroduceVariableHandler implements MyIntroduceHandler {
+    private OccurrencesChooser.ReplaceChoice myChoice = null;
+
+    public void setChoice(OccurrencesChooser.ReplaceChoice choice) {
+      myChoice = choice;
+    }
+
+    @Override
+    public boolean invokeImpl(Project project, @NotNull PsiExpression selectedExpr, Editor editor) {
+      return super.invokeImpl(project, selectedExpr, editor);
+    }
+
+    @Override
+    public boolean invokeImpl(Project project, PsiLocalVariable localVariable, Editor editor) {
+      return super.invokeImpl(project, localVariable, editor);
+    }
+
+    @Override
+    protected OccurrencesChooser.ReplaceChoice getOccurrencesChoice() {
+      return myChoice;
+    }
+
+    @Override
+    protected boolean isInplaceAvailableInTestMode() {
+      return true;
+    }
+  }
+}
index a540080d95d95392eaea43e7f1566c3bfbc12168..45ca790ef61a09e16f6e8984ac086b42d9a2803d 100644 (file)
@@ -306,10 +306,7 @@ public class JavaMatchingVisitor extends JavaElementVisitor {
 
   @Override
   public void visitNameValuePair(PsiNameValuePair pair) {
-    final PsiIdentifier nameIdentifier = pair.getNameIdentifier();
-
     final PsiNameValuePair elementNameValuePair = (PsiNameValuePair)myMatchingVisitor.getElement();
-    final PsiIdentifier otherIdentifier = elementNameValuePair.getNameIdentifier();
 
     final PsiAnnotationMemberValue annotationInitializer = pair.getValue();
     if (annotationInitializer != null) {
@@ -323,16 +320,20 @@ public class JavaMatchingVisitor extends JavaElementVisitor {
                                   ));
     }
     if (myMatchingVisitor.getResult()) {
-      final MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(nameIdentifier);
-
-      if (handler instanceof SubstitutionHandler) {
-        myMatchingVisitor.setResult(((SubstitutionHandler)handler).handle(otherIdentifier, myMatchingVisitor.getMatchContext()));
-      }
-      else if (nameIdentifier != null) {
-        myMatchingVisitor.setResult(myMatchingVisitor.match(nameIdentifier, otherIdentifier));
+      final PsiIdentifier nameIdentifier = pair.getNameIdentifier();
+      final PsiIdentifier otherIdentifier = elementNameValuePair.getNameIdentifier();
+      if (nameIdentifier == null) {
+        myMatchingVisitor.setResult(otherIdentifier == null ||
+                                    otherIdentifier.getText().equals(PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME));
       }
       else {
-        myMatchingVisitor.setResult(otherIdentifier == null || otherIdentifier.getText().equals("value"));
+        final MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(nameIdentifier);
+        if (handler instanceof SubstitutionHandler) {
+          myMatchingVisitor.setResult(((SubstitutionHandler)handler).handle(otherIdentifier, myMatchingVisitor.getMatchContext()));
+        }
+        else {
+          myMatchingVisitor.setResult(myMatchingVisitor.match(nameIdentifier, otherIdentifier));
+        }
       }
     }
   }
@@ -1412,8 +1413,13 @@ public class JavaMatchingVisitor extends JavaElementVisitor {
   @Override
   public void visitInstanceOfExpression(final PsiInstanceOfExpression instanceOf) {
     final PsiInstanceOfExpression instanceOf2 = (PsiInstanceOfExpression)myMatchingVisitor.getElement();
-    myMatchingVisitor.setResult(myMatchingVisitor.match(instanceOf.getOperand(), instanceOf2.getOperand()) &&
-                                matchType(instanceOf.getCheckType(), instanceOf2.getCheckType()));
+    myMatchingVisitor.setResult(myMatchingVisitor.match(instanceOf.getOperand(), instanceOf2.getOperand()));
+    if (myMatchingVisitor.getResult()) {
+      final PsiTypeElement checkType = instanceOf.getCheckType();
+      if (checkType != null) {
+        myMatchingVisitor.setResult(matchType(checkType, instanceOf2.getCheckType()));
+      }
+    }
   }
 
   @Override
index f7c4ca1bf274c69cace07a01a0a4e19c1df037c7..9f55e9337cdef6e49330df5c004781860827f2c7 100644 (file)
@@ -597,6 +597,10 @@ public class AnalysisScope {
     return myFilesSet.size();
   }
 
+  /**
+   * scope elements should be checked only when needed
+   */
+  @Deprecated
   public boolean checkScopeWritable(@NotNull Project project) {
     if (myFilesSet == null) initFilesSet();
     return !FileModificationService.getInstance().prepareVirtualFilesForWrite(project, myFilesSet);
index 56387b07bc8557cac2ea08e98471bb4f1d64eccb..6a29f2f9ed02c092e6ca58ef1011a129fe89ad94 100644 (file)
@@ -22,6 +22,7 @@ import com.intellij.lang.Language;
 import com.intellij.openapi.extensions.ExtensionPointName;
 import com.intellij.openapi.extensions.Extensions;
 import com.intellij.psi.PsiElement;
+import com.intellij.util.indexing.FileContent;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -59,6 +60,14 @@ public abstract class DuplicatesProfile {
     return true;
   }
 
+  public boolean supportDuplicatesIndex() {
+    return false;
+  }
+
+  public boolean acceptsContentForIndexing(FileContent fileContent) {
+    return true;
+  }
+
   private static final int FACTOR = 2;
   private static final int MAX_COST = 7000;
 
index bc0945b071ec9b78abf6b911bc0dfc808be6443d..26dbb106b753cd7949dbf545cef3f2ffe09ba662 100644 (file)
@@ -62,7 +62,7 @@ public class DuplicatesIndex extends FileBasedIndexExtension<Integer, TIntArrayL
   }
 
   @NonNls public static final ID<Integer, TIntArrayList> NAME = ID.create("DuplicatesIndex");
-  private static final int myBaseVersion = 12;
+  private static final int myBaseVersion = 13;
 
   private final FileBasedIndex.InputFilter myInputFilter = new FileBasedIndex.InputFilter() {
     @Override
@@ -113,30 +113,36 @@ public class DuplicatesIndex extends FileBasedIndexExtension<Integer, TIntArrayL
       FileType type = inputData.getFileType();
 
       DuplicatesProfile profile = findDuplicatesProfile(type);
-      if (profile == null) return Collections.emptyMap();
-
-      FileContentImpl fileContent = (FileContentImpl)inputData;
-
-      if (profile instanceof LightDuplicateProfile && ourEnabledLightProfiles) {
-        final THashMap<Integer, TIntArrayList> result = new THashMap<Integer, TIntArrayList>();
-        LighterAST ast = fileContent.getLighterASTForPsiDependentIndex();
-        assert ast != null;
-        ((LightDuplicateProfile)profile).process(ast, new LightDuplicateProfile.Callback() {
-          @Override
-          public void process(@NotNull LighterAST ast, @NotNull LighterASTNode node, int hash) {
-            TIntArrayList list = result.get(hash);
-            if (list == null) { result.put(hash, list = new TIntArrayList(1)); }
-            list.add(node.getStartOffset());
-          }
-        });
-        return result;
-      }
-      MyFragmentsCollector collector = new MyFragmentsCollector(profile, ((LanguageFileType)type).getLanguage());
-      DuplocateVisitor visitor = profile.createVisitor(collector, true);
+      if (profile == null || !profile.acceptsContentForIndexing(inputData)) return Collections.emptyMap();
+
+      try {
+        FileContentImpl fileContent = (FileContentImpl)inputData;
+
+        if (profile instanceof LightDuplicateProfile && ourEnabledLightProfiles) {
+          final THashMap<Integer, TIntArrayList> result = new THashMap<Integer, TIntArrayList>();
+          LighterAST ast = fileContent.getLighterASTForPsiDependentIndex();
+
+          ((LightDuplicateProfile)profile).process(ast, new LightDuplicateProfile.Callback() {
+            @Override
+            public void process(@NotNull LighterAST ast, @NotNull LighterASTNode node, int hash) {
+              TIntArrayList list = result.get(hash);
+              if (list == null) {
+                result.put(hash, list = new TIntArrayList(1));
+              }
+              list.add(node.getStartOffset());
+            }
+          });
+          return result;
+        }
+        MyFragmentsCollector collector = new MyFragmentsCollector(profile, ((LanguageFileType)type).getLanguage());
+        DuplocateVisitor visitor = profile.createVisitor(collector, true);
 
-      visitor.visitNode(fileContent.getPsiFileForPsiDependentIndex());
+        visitor.visitNode(fileContent.getPsiFileForPsiDependentIndex());
 
-      return collector.getMap();
+        return collector.getMap();
+      } catch (StackOverflowError ae) {
+        return Collections.emptyMap(); // todo Maksim
+      }
     }
   };
 
@@ -145,7 +151,7 @@ public class DuplicatesIndex extends FileBasedIndexExtension<Integer, TIntArrayL
     if (!(fileType instanceof LanguageFileType)) return null;
     Language language = ((LanguageFileType)fileType).getLanguage();
     DuplicatesProfile profile = DuplicatesProfile.findProfileForLanguage(language);
-    return profile != null && profile.supportIndex() ? profile : null;
+    return profile != null && (profile.supportDuplicatesIndex() || profile instanceof LightDuplicateProfile) ? profile : null;
   }
 
   @Override
index 8d99a0fa0145761623005093cc406ecab2249332..5191654b5b2f303730d9b76c5136e70a74fca9e0 100644 (file)
@@ -15,6 +15,8 @@
  */
 package com.intellij.dvcs.push;
 
+import org.jetbrains.annotations.NotNull;
+
 /**
  * Destination for push action. (Remote  for git or push-path for mercurial).
  */
@@ -28,4 +30,6 @@ public interface PushTarget {
    */
   boolean hasSomethingToPush();
 
+  @NotNull
+  String getPresentation();
 }
index f3b6f5fe3cf1b225d7e5fc9e27c1320996fe0c61..0d50bf15703f1716a7d350dfcf6f13fda154a8b8 100644 (file)
@@ -117,14 +117,17 @@ public class PushController implements Disposable {
     });
   }
 
-  public boolean isForcePushAllowed() {
-    return ContainerUtil.and(myView2Model.values(), new Condition<MyRepoModel>() {
+  @Nullable
+  public PushTarget getProhibitedTarget() {
+    MyRepoModel model = ContainerUtil.find(myView2Model.values(), new Condition<MyRepoModel>() {
       @Override
       public boolean value(MyRepoModel model) {
-        return !model.isSelected() ||
-               (model.getTarget() != null && model.getSupport().isForcePushAllowed(model.getRepository(), model.getTarget()));
+        PushTarget target = model.getTarget();
+        return model.isSelected() &&
+               target != null && !model.getSupport().isForcePushAllowed(model.getRepository(), target);
       }
     });
+    return model != null ? model.getTarget() : null;
   }
 
   private void startLoadingCommits() {
index 193babf3826bed8cfb3ce0b0ee8389ffd4a5c9bc..3e85985c3d12e6ed75d94ef45dca81a76069927d 100644 (file)
 package com.intellij.dvcs.push.ui;
 
 import com.intellij.CommonBundle;
-import com.intellij.dvcs.push.PushController;
-import com.intellij.dvcs.push.PushSupport;
-import com.intellij.dvcs.push.VcsPushOptionValue;
-import com.intellij.dvcs.push.VcsPushOptionsPanel;
+import com.intellij.dvcs.push.*;
 import com.intellij.dvcs.repo.Repository;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.ui.DialogWrapper;
@@ -100,14 +97,9 @@ public class VcsPushDialog extends DialogWrapper {
   @NotNull
   protected Action[] createActions() {
     final List<Action> actions = new ArrayList<Action>();
-    if (myController.isForcePushEnabled()) {
-      myForcePushAction = new ForcePushAction();
-      myForcePushAction.setEnabled(myController.isForcePushAllowed());
-      myPushAction = new ComplexPushAction(myForcePushAction);
-    }
-    else {
-      myPushAction = new SimplePushAction();
-    }
+    myForcePushAction = new ForcePushAction();
+    myForcePushAction.setEnabled(canForcePush());
+    myPushAction = new ComplexPushAction(myForcePushAction);
     myPushAction.putValue(DEFAULT_ACTION, Boolean.TRUE);
     actions.add(myPushAction);
     actions.add(getCancelAction());
@@ -115,6 +107,10 @@ public class VcsPushDialog extends DialogWrapper {
     return actions.toArray(new Action[actions.size()]);
   }
 
+  private boolean canForcePush() {
+    return myController.isForcePushEnabled() && myController.getProhibitedTarget() == null;
+  }
+
   @Nullable
   @Override
   public JComponent getPreferredFocusedComponent() {
@@ -131,11 +127,19 @@ public class VcsPushDialog extends DialogWrapper {
   protected String getHelpId() {
     return ID;
   }
-
   public void enableOkActions(boolean isEnabled) {
     myPushAction.setEnabled(isEnabled);
     if (myForcePushAction != null) {
-      myForcePushAction.setEnabled(isEnabled && myController.isForcePushAllowed());
+      boolean canForcePush = canForcePush();
+      myForcePushAction.setEnabled(isEnabled && canForcePush);
+      String tooltip = null;
+      if (!canForcePush) {
+        PushTarget target = myController.getProhibitedTarget();
+        tooltip = myController.isForcePushEnabled() && target != null
+                  ? "Force push to <b>" + target.getPresentation() + "</b> is prohibited"
+                  : "<b>Force Push</b> can be enabled in the Settings";
+      }
+      myForcePushAction.putValue(Action.SHORT_DESCRIPTION, tooltip);
     }
   }
 
@@ -145,17 +149,6 @@ public class VcsPushDialog extends DialogWrapper {
     return panel == null ? null : panel.getValue();
   }
 
-  private class SimplePushAction extends AbstractAction {
-    SimplePushAction() {
-      super("&Push");
-    }
-    @Override
-    public void actionPerformed(ActionEvent e) {
-      myController.push(false);
-      close(OK_EXIT_CODE);
-    }
-  }
-
   private class ForcePushAction extends AbstractAction {
     ForcePushAction() {
       super("&Force Push");
@@ -173,11 +166,18 @@ public class VcsPushDialog extends DialogWrapper {
     }
   }
 
-  private class ComplexPushAction extends SimplePushAction implements OptionAction {
+  private class ComplexPushAction extends AbstractAction implements OptionAction {
     private final Action[] myOptions;
 
     private ComplexPushAction(Action additionalAction) {
-      myOptions = new Action[] { additionalAction };
+      super("&Push");
+      myOptions = new Action[]{additionalAction};
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+      myController.push(false);
+      close(OK_EXIT_CODE);
     }
 
     @Override
@@ -194,5 +194,4 @@ public class VcsPushDialog extends DialogWrapper {
       return myOptions;
     }
   }
-
 }
index 289c8166ec8f46141bd31735a54949175edbf24b..96080cf19986ac262978bcb3fe96a82fc462868f 100644 (file)
@@ -85,6 +85,12 @@ public interface FoldingModel {
   @Nullable
   FoldRegion getCollapsedRegionAtOffset(int offset);
 
+  /**
+   * Returns fold region with given boundaries, if it exists, or <code>null</code> otherwise. 
+   */
+  @Nullable
+  FoldRegion getFoldRegion(int startOffset, int endOffset);
+
   /**
    * Runs an operation which is allowed to modify fold regions in the editor by calling
    * {@link #addFoldRegion(int, int, String)} and {@link #removeFoldRegion(FoldRegion)}.
index 10fc11acbf5fa0e929674b72f93dfc7adda03802..325c7cdd45b2856f860fe0f729c8f3ac2e44e160 100644 (file)
@@ -15,6 +15,7 @@
  */
 package com.intellij.openapi.externalSystem.model;
 
+import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.util.io.StreamUtil;
 import com.intellij.util.containers.ContainerUtilRt;
 import org.jetbrains.annotations.NotNull;
@@ -23,7 +24,10 @@ import org.jetbrains.annotations.Nullable;
 import java.io.*;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Proxy;
-import java.util.*;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
 
 /**
  * This class provides a generic graph infrastructure with ability to store particular data. The main purpose is to 
@@ -41,6 +45,7 @@ import java.util.*;
 public class DataNode<T> implements Serializable {
 
   private static final long serialVersionUID = 1L;
+  private static final Logger LOG = Logger.getInstance(DataNode.class);
 
   @NotNull private final List<DataNode<?>> myChildren = ContainerUtilRt.newArrayList();
 
@@ -270,6 +275,14 @@ public class DataNode<T> implements Serializable {
 
   @Override
   public String toString() {
-    return String.format("%s: %s", myKey, getData());
+    String dataDescription;
+    try {
+      dataDescription = getData().toString();
+    }
+    catch (Exception e) {
+      dataDescription = "failed to load";
+      LOG.debug(e);
+    }
+    return String.format("%s: %s", myKey, dataDescription);
   }
 }
index 99becc715a44c7507a8e6bb0b43777aa89158367..2d368c5f86cba070c7cbbe91a199a7a089f6583f 100644 (file)
@@ -76,10 +76,11 @@ public class CodeInsightSettings implements PersistentStateComponent<Element>, C
 
   public boolean SHOW_FULL_SIGNATURES_IN_PARAMETER_INFO = false;
 
-  @MagicConstant(intValues = {OFF, AUTOINDENT})
+  @MagicConstant(intValues = {OFF, AUTOINDENT, INDENT})
   public int SMART_BACKSPACE = AUTOINDENT;
   public static final int OFF = 0;
   public static final int AUTOINDENT = 1;
+  public static final int INDENT = 2;
 
   public boolean SMART_INDENT_ON_ENTER = true;
   public boolean INSERT_BRACE_ON_ENTER = true;
index 91c3f140ed68114832502d2144192c6264bbc320..1ac3921c9b9ac3bb9f919710f5e13779712491a1 100644 (file)
@@ -123,7 +123,7 @@ public class IdentifierHighlighterPass extends TextEditorHighlightingPass {
    * @param psiFile psi file for element
    * @return a pair where first element is read usages and second is write usages
    */
-  public static Couple<Collection<TextRange>> getHighlightUsages(@NotNull PsiElement target, PsiFile psiFile) {
+  public static Couple<Collection<TextRange>> getHighlightUsages(@NotNull PsiElement target, PsiFile psiFile, boolean withDecls) {
     Collection<TextRange> readRanges = new ArrayList<TextRange>();
     Collection<TextRange> writeRanges = new ArrayList<TextRange>();
     final ReadWriteAccessDetector detector = ReadWriteAccessDetector.findDetector(target);
@@ -143,13 +143,15 @@ public class IdentifierHighlighterPass extends TextEditorHighlightingPass {
       }
     }
 
-    final TextRange declRange = HighlightUsagesHandler.getNameIdentifierRange(psiFile, target);
-    if (declRange != null) {
-      if (detector != null && detector.isDeclarationWriteAccess(target)) {
-        writeRanges.add(declRange);
-      }
-      else {
-        readRanges.add(declRange);
+    if (withDecls) {
+      final TextRange declRange = HighlightUsagesHandler.getNameIdentifierRange(psiFile, target);
+      if (declRange != null) {
+        if (detector != null && detector.isDeclarationWriteAccess(target)) {
+          writeRanges.add(declRange);
+        }
+        else {
+          readRanges.add(declRange);
+        }
       }
     }
 
@@ -157,7 +159,7 @@ public class IdentifierHighlighterPass extends TextEditorHighlightingPass {
   }
 
   private void highlightTargetUsages(@NotNull PsiElement target) {
-    final Couple<Collection<TextRange>> usages = getHighlightUsages(target, myFile);
+    final Couple<Collection<TextRange>> usages = getHighlightUsages(target, myFile, true);
     myReadAccessRanges.addAll(usages.first);
     myWriteAccessRanges.addAll(usages.second);
   }
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/AbstractIndentingBackspaceHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/AbstractIndentingBackspaceHandler.java
new file mode 100644 (file)
index 0000000..bbe6509
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.codeInsight.editorActions;
+
+import com.intellij.codeInsight.CodeInsightSettings;
+import com.intellij.lang.Language;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider;
+
+abstract class AbstractIndentingBackspaceHandler extends BackspaceHandlerDelegate {
+  private final int myMode;
+  private boolean myEnabled;
+
+  AbstractIndentingBackspaceHandler(int mode) {
+    myMode = mode;
+  }
+
+  @Override
+  public void beforeCharDeleted(char c, PsiFile file, Editor editor) {
+    myEnabled = false;
+    if (!StringUtil.isWhiteSpace(c)) {
+      return;
+    }
+    int mode = getBackspaceMode(file.getLanguage());
+    if (mode != myMode) {
+      return;
+    }
+    doBeforeCharDeleted(c, file, editor);
+    myEnabled = true;
+  }
+
+  @Override
+  public boolean charDeleted(char c, PsiFile file, Editor editor) {
+    if (!myEnabled) {
+      return false;
+    }
+    return doCharDeleted(c, file, editor);
+  }
+
+  protected abstract void doBeforeCharDeleted(char c, PsiFile file, Editor editor);
+
+  protected abstract boolean doCharDeleted(char c, PsiFile file, Editor editor);
+
+  private static int getBackspaceMode(Language language) {
+    LanguageCodeStyleSettingsProvider codeStyleSettingsProvider = LanguageCodeStyleSettingsProvider.forLanguage(language);
+    if (codeStyleSettingsProvider != null && codeStyleSettingsProvider.isIndentBasedLanguageSemantics()) {
+      return CodeInsightSettings.INDENT;
+    }
+    else {
+      return CodeInsightSettings.getInstance().SMART_BACKSPACE;
+    }
+  }
+}
index 62088f6bff8ce91f90387f627d99d23db3aaa8a5..78a81f85591519bf623b40e512342028e8f4a46f 100644 (file)
@@ -23,6 +23,7 @@ import com.intellij.lang.injection.InjectedLanguageManager;
 import com.intellij.openapi.actionSystem.CommonDataKeys;
 import com.intellij.openapi.actionSystem.DataContext;
 import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.EditorModificationUtil;
 import com.intellij.openapi.editor.LogicalPosition;
 import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
 import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler;
@@ -36,6 +37,7 @@ import com.intellij.psi.PsiDocumentManager;
 import com.intellij.psi.PsiFile;
 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
 import com.intellij.psi.util.PsiUtilBase;
+import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import java.util.List;
@@ -167,7 +169,7 @@ public class BackspaceHandler extends EditorWriteActionHandler {
     if (editor.getSelectionModel().hasSelection() || editor.getSelectionModel().hasBlockSelection()) return null;
 
     final LogicalPosition caretPos = editor.getCaretModel().getLogicalPosition();
-    if (caretPos.line == 0 || caretPos.column == 0) {
+    if (caretPos.column == 0) {
       return null;
     }
     if (!isWhitespaceBeforeCaret(editor)) {
@@ -175,13 +177,21 @@ public class BackspaceHandler extends EditorWriteActionHandler {
     }
 
     // Decrease column down to indentation * n
-    final int indent = CodeStyleSettingsManager.getSettings(file.getProject()).getIndentSize(file.getFileType());
+    final int indent = CodeStyleSettingsManager.getSettings(file.getProject()).getIndentOptionsByFile(file).INDENT_SIZE;
     int column = (caretPos.column - 1) / indent * indent;
     if (column < 0) {
       column = 0;
     }
     return new LogicalPosition(caretPos.line, column);
   }
+  
+  public static void deleteToTargetPosition(@NotNull Editor editor, @NotNull LogicalPosition pos) {
+    final int offset = editor.getCaretModel().getOffset();
+    final int targetOffset = editor.logicalPositionToOffset(pos);
+    editor.getSelectionModel().setSelection(targetOffset, offset);
+    EditorModificationUtil.deleteSelectedText(editor);
+    editor.getCaretModel().moveToLogicalPosition(pos);
+  }
 
   public static boolean isWhitespaceBeforeCaret(Editor editor) {
     final LogicalPosition caretPos = editor.getCaretModel().getLogicalPosition();
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/IndentingBackspaceHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/IndentingBackspaceHandler.java
deleted file mode 100644 (file)
index 7429f55..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright 2000-2014 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.intellij.codeInsight.editorActions;
-
-import com.intellij.codeInsight.CodeInsightSettings;
-import com.intellij.codeStyle.CodeStyleFacade;
-import com.intellij.formatting.*;
-import com.intellij.lang.LanguageFormatting;
-import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.editor.CaretModel;
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.editor.LogicalPosition;
-import com.intellij.openapi.fileEditor.FileDocumentManager;
-import com.intellij.openapi.fileTypes.FileType;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.psi.PsiDocumentManager;
-import com.intellij.psi.PsiFile;
-import com.intellij.psi.codeStyle.CodeStyleSettings;
-import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
-import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider;
-import com.intellij.util.text.CharArrayUtil;
-import org.jetbrains.annotations.NotNull;
-
-/**
- * Makes Backspace action delete all whitespace till next valid indent position
- */
-public class IndentingBackspaceHandler extends BackspaceHandlerDelegate {
-  private static final Logger LOG = Logger.getInstance(IndentingBackspaceHandler.class);
-
-  private boolean isApplicable;
-  private boolean caretWasAtLineStart;
-  private String precalculatedSpacing;
-
-  @Override
-  public void beforeCharDeleted(char c, PsiFile file, Editor editor) {
-    if (CodeInsightSettings.getInstance().SMART_BACKSPACE != CodeInsightSettings.AUTOINDENT || !StringUtil.isWhiteSpace(c)) {
-      isApplicable = false;
-      return;
-    }
-    LanguageCodeStyleSettingsProvider codeStyleSettingsProvider = LanguageCodeStyleSettingsProvider.forLanguage(file.getLanguage());
-    if (codeStyleSettingsProvider != null && codeStyleSettingsProvider.isIndentBasedLanguageSemantics()) {
-      isApplicable = false;
-      return;
-    }
-    Document document = editor.getDocument();
-    CharSequence charSequence = document.getCharsSequence();
-    CaretModel caretModel = editor.getCaretModel();
-    int caretOffset = caretModel.getOffset();
-    LogicalPosition pos = caretModel.getLogicalPosition();
-    isApplicable = true;
-    caretWasAtLineStart = pos.column == 0;
-    precalculatedSpacing = null;
-    if (caretWasAtLineStart && pos.line > 0 && caretOffset < charSequence.length() && !StringUtil.isWhiteSpace(charSequence.charAt(caretOffset))) {
-      int prevLineEnd = document.getLineEndOffset(pos.line - 1);
-      if (prevLineEnd > 0 && !StringUtil.isWhiteSpace(charSequence.charAt(prevLineEnd - 1))) {
-        PsiDocumentManager.getInstance(file.getProject()).commitDocument(document);
-        precalculatedSpacing = getSpacing(file, caretOffset);
-      }
-    }
-  }
-
-  @Override
-  public boolean charDeleted(char c, PsiFile file, Editor editor) {
-    if (!isApplicable) {
-      return false;
-    }
-
-    Project project = file.getProject();
-    Document document = editor.getDocument();
-    CaretModel caretModel = editor.getCaretModel();
-
-    int caretOffset = caretModel.getOffset();
-    int offset = CharArrayUtil.shiftForward(document.getCharsSequence(), caretOffset, " \t");
-    int beforeWhitespaceOffset = CharArrayUtil.shiftBackward(document.getCharsSequence(), offset - 1, " \t") + 1;
-    LogicalPosition logicalPosition = caretOffset < offset ? editor.offsetToLogicalPosition(offset) : caretModel.getLogicalPosition();
-    int lineStartOffset = document.getLineStartOffset(logicalPosition.line);
-    if (lineStartOffset < beforeWhitespaceOffset) {
-      if (caretWasAtLineStart && beforeWhitespaceOffset <= offset) {
-        String spacing;
-        if (precalculatedSpacing == null) {
-          PsiDocumentManager.getInstance(project).commitDocument(document);
-          spacing = getSpacing(file, offset);
-        }
-        else {
-          spacing = precalculatedSpacing;
-        }
-        if (beforeWhitespaceOffset < offset || !spacing.isEmpty()) {
-          document.replaceString(beforeWhitespaceOffset, offset, spacing);
-          caretModel.moveToOffset(beforeWhitespaceOffset + spacing.length());
-          return true;
-        }
-      }
-      return false;
-    }
-
-    PsiDocumentManager.getInstance(project).commitDocument(document);
-    CodeStyleFacade codeStyleFacade = CodeStyleFacade.getInstance(project);
-    String indent = codeStyleFacade.getLineIndent(document, lineStartOffset);
-    if (indent == null) {
-      return false;
-    }
-
-    int tabSize = getTabSize(codeStyleFacade, document);
-    int targetColumn = getWidth(indent, tabSize);
-
-    if (logicalPosition.column == targetColumn) {
-      if (caretOffset < offset) {
-        caretModel.moveToLogicalPosition(logicalPosition);
-        return true;
-      }
-      return false;
-    }
-
-    if (caretWasAtLineStart || logicalPosition.column > targetColumn) {
-      document.replaceString(lineStartOffset, offset, indent);
-      caretModel.moveToLogicalPosition(new LogicalPosition(logicalPosition.line, targetColumn));
-      return true;
-    }
-
-    if (logicalPosition.line == 0) {
-      return false;
-    }
-
-    int prevLineStartOffset = document.getLineStartOffset(logicalPosition.line - 1);
-    int prevLineEndOffset = document.getLineEndOffset(logicalPosition.line - 1);
-    int targetOffset = CharArrayUtil.shiftBackward(document.getCharsSequence(), prevLineEndOffset - 1, " \t") + 1;
-
-    if (prevLineStartOffset < targetOffset) {
-      String spacing = getSpacing(file, offset);
-      document.replaceString(targetOffset, offset, spacing);
-      caretModel.moveToOffset(targetOffset + spacing.length());
-    }
-    else {
-      document.replaceString(prevLineStartOffset, offset, indent);
-      caretModel.moveToLogicalPosition(new LogicalPosition(logicalPosition.line - 1, targetColumn));
-    }
-    return true;
-  }
-
-  private static int getTabSize(@NotNull CodeStyleFacade codeStyleFacade, @NotNull Document document) {
-    VirtualFile file = FileDocumentManager.getInstance().getFile(document);
-    FileType fileType = file == null ? null : file.getFileType();
-    return codeStyleFacade.getTabSize(fileType);
-  }
-
-  private static int getWidth(@NotNull String indent, int tabSize) {
-    int width = 0;
-    for (int i = 0; i < indent.length(); i++) {
-      char c = indent.charAt(i);
-      switch (c) {
-        case '\t':
-          width = tabSize * (width / tabSize + 1);
-          break;
-        default:
-          LOG.error("Unexpected whitespace character: " + ((int)c));
-        case ' ':
-          width++;
-      }
-    }
-    return width;
-  }
-
-  private static String getSpacing(PsiFile file, int offset) {
-    FormattingModelBuilder builder = LanguageFormatting.INSTANCE.forContext(file);
-    if (builder == null) {
-      return "";
-    }
-    CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(file.getProject());
-    FormattingModel model = builder.createModel(file, settings);
-    int spacing = FormatterEx.getInstance().getSpacingForBlockAtOffset(model, offset);
-    return StringUtil.repeatSymbol(' ', spacing);
-  }
-}
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/SimpleIndentingBackspaceHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/SimpleIndentingBackspaceHandler.java
new file mode 100644 (file)
index 0000000..17e0e02
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.codeInsight.editorActions;
+
+import com.intellij.codeInsight.CodeInsightSettings;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.LogicalPosition;
+import com.intellij.psi.PsiFile;
+
+public class SimpleIndentingBackspaceHandler extends AbstractIndentingBackspaceHandler {
+  private LogicalPosition myTargetPosition;
+
+  public SimpleIndentingBackspaceHandler() {
+    super(CodeInsightSettings.INDENT);
+  }
+
+  @Override
+  protected void doBeforeCharDeleted(char c, PsiFile file, Editor editor) {
+    myTargetPosition = BackspaceHandler.getBackspaceUnindentPosition(file, editor);
+  }
+
+  @Override
+  protected boolean doCharDeleted(char c, PsiFile file, Editor editor) {
+    if (myTargetPosition != null) {
+      BackspaceHandler.deleteToTargetPosition(editor, myTargetPosition);
+      return true;
+    }
+    return false;
+  }
+}
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/SmartIndentingBackspaceHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/SmartIndentingBackspaceHandler.java
new file mode 100644 (file)
index 0000000..8ab82c4
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.codeInsight.editorActions;
+
+import com.intellij.codeInsight.CodeInsightSettings;
+import com.intellij.codeStyle.CodeStyleFacade;
+import com.intellij.formatting.FormatterEx;
+import com.intellij.formatting.FormattingModel;
+import com.intellij.formatting.FormattingModelBuilder;
+import com.intellij.lang.LanguageFormatting;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.*;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.codeStyle.CodeStyleSettings;
+import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
+import com.intellij.util.text.CharArrayUtil;
+import org.jetbrains.annotations.NotNull;
+
+public class SmartIndentingBackspaceHandler extends AbstractIndentingBackspaceHandler {
+  private static final Logger LOG = Logger.getInstance(SmartIndentingBackspaceHandler.class);
+
+  private String myReplacement;
+  private int myStartOffset;
+
+  public SmartIndentingBackspaceHandler() {
+    super(CodeInsightSettings.AUTOINDENT);
+  }
+
+  @Override
+  protected void doBeforeCharDeleted(char c, PsiFile file, Editor editor) {
+    Project project = file.getProject();
+    Document document = editor.getDocument();
+    CharSequence charSequence = document.getImmutableCharSequence();
+    CaretModel caretModel = editor.getCaretModel();
+    int caretOffset = caretModel.getOffset();
+    LogicalPosition pos = caretModel.getLogicalPosition();
+    int lineStartOffset = document.getLineStartOffset(pos.line);
+    int beforeWhitespaceOffset = CharArrayUtil.shiftBackward(charSequence, caretOffset - 1, " \t") + 1;
+    if (beforeWhitespaceOffset != lineStartOffset) {
+      myReplacement = null;
+      return;
+    }
+    PsiDocumentManager.getInstance(project).commitDocument(document);
+    CodeStyleFacade codeStyleFacade = CodeStyleFacade.getInstance(project);
+    myReplacement = codeStyleFacade.getLineIndent(document, lineStartOffset);
+    if (myReplacement == null) {
+      return;
+    }
+    int tabSize = getTabSize(codeStyleFacade, document);
+    int targetColumn = getWidth(myReplacement, tabSize);
+    int endOffset = CharArrayUtil.shiftForward(charSequence, caretOffset, " \t");
+    LogicalPosition logicalPosition = caretOffset < endOffset ? editor.offsetToLogicalPosition(endOffset) : pos;
+    int currentColumn = logicalPosition.column;
+    if (currentColumn > targetColumn) {
+      myStartOffset = lineStartOffset;
+    }
+    else if (logicalPosition.line == 0) {
+      myStartOffset = 0;
+      myReplacement = "";
+    }
+    else {
+      int prevLineEndOffset = document.getLineEndOffset(logicalPosition.line - 1);
+      myStartOffset = CharArrayUtil.shiftBackward(charSequence, prevLineEndOffset - 1, " \t") + 1;
+      if (myStartOffset != document.getLineStartOffset(logicalPosition.line - 1)) {
+        myReplacement = getSpacing(file, endOffset);
+      }
+    }
+  }
+
+  @Override
+  protected boolean doCharDeleted(char c, PsiFile file, Editor editor) {
+    if (myReplacement == null) {
+      return false;
+    }
+
+    Document document = editor.getDocument();
+    CaretModel caretModel = editor.getCaretModel();
+    int endOffset = CharArrayUtil.shiftForward(document.getImmutableCharSequence(), caretModel.getOffset(), " \t");
+
+    document.replaceString(myStartOffset, endOffset, myReplacement);
+    caretModel.moveToOffset(myStartOffset + myReplacement.length());
+
+    return true;
+  }
+
+  private static String getSpacing(PsiFile file, int offset) {
+    FormattingModelBuilder builder = LanguageFormatting.INSTANCE.forContext(file);
+    if (builder == null) {
+      return "";
+    }
+    CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(file.getProject());
+    FormattingModel model = builder.createModel(file, settings);
+    int spacing = FormatterEx.getInstance().getSpacingForBlockAtOffset(model, offset);
+    return StringUtil.repeatSymbol(' ', spacing);
+  }
+
+  private static int getTabSize(@NotNull CodeStyleFacade codeStyleFacade, @NotNull Document document) {
+    VirtualFile file = FileDocumentManager.getInstance().getFile(document);
+    FileType fileType = file == null ? null : file.getFileType();
+    return codeStyleFacade.getTabSize(fileType);
+  }
+
+  private static int getWidth(@NotNull String indent, int tabSize) {
+    int width = 0;
+    for (int i = 0; i < indent.length(); i++) {
+      char c = indent.charAt(i);
+      switch (c) {
+        case '\t':
+          width = tabSize * (width / tabSize + 1);
+          break;
+        default:
+          LOG.error("Unexpected whitespace character: " + ((int)c));
+        case ' ':
+          width++;
+      }
+    }
+    return width;
+  }
+}
index 62f641b37807cd375fd262c989ac69fad18347b0..07bd92cb687a4d4b3af0a4fee96341640cb3b45b 100644 (file)
@@ -59,11 +59,11 @@ public class FoldingUpdate {
   private static final Key<ParameterizedCachedValue<Runnable, Couple<Boolean>>> CODE_FOLDING_KEY = Key.create("code folding");
   private static final Key<String> CODE_FOLDING_FILE_EXTENSION_KEY = Key.create("code folding file extension");
 
-  private static final Comparator<PsiElement> COMPARE_BY_OFFSET = new Comparator<PsiElement>() {
+  private static final Comparator<PsiElement> COMPARE_BY_OFFSET_REVERSED = new Comparator<PsiElement>() {
     @Override
     public int compare(PsiElement element, PsiElement element1) {
-      int startOffsetDiff = element.getTextRange().getStartOffset() - element1.getTextRange().getStartOffset();
-      return startOffsetDiff == 0 ? element.getTextRange().getEndOffset() - element1.getTextRange().getEndOffset() : startOffsetDiff;
+      int startOffsetDiff = element1.getTextRange().getStartOffset() - element.getTextRange().getStartOffset();
+      return startOffsetDiff == 0 ? element1.getTextRange().getEndOffset() - element.getTextRange().getEndOffset() : startOffsetDiff;
     }
   };
 
@@ -284,7 +284,7 @@ public class FoldingUpdate {
     @NotNull
     @Override
     protected Map<PsiElement, Collection<FoldingDescriptor>> createMap() {
-      return new TreeMap<PsiElement, Collection<FoldingDescriptor>>(COMPARE_BY_OFFSET);
+      return new TreeMap<PsiElement, Collection<FoldingDescriptor>>(COMPARE_BY_OFFSET_REVERSED);
     }
 
     @NotNull
index f150f9924cb8f8dc81fefd88dc03c6f9aca006b6..8a00fc94d2cbb58d5d3f75e5a149e8da797ce78b 100644 (file)
@@ -29,17 +29,9 @@ public class FoldingUtil {
   private FoldingUtil() {}
 
   @Nullable
-  public static FoldRegion findFoldRegion(Editor editor, int startOffset, int endOffset) {
-    FoldRegion[] foldRegions = editor.getFoldingModel().getAllFoldRegions();
-    for (FoldRegion region : foldRegions) {
-      if (region.isValid() &&
-          region.getStartOffset() == startOffset
-          && region.getEndOffset() == endOffset) {
-        return region;
-      }
-    }
-
-    return null;
+  public static FoldRegion findFoldRegion(@NotNull Editor editor, int startOffset, int endOffset) {
+    FoldRegion region = editor.getFoldingModel().getFoldRegion(startOffset, endOffset);
+    return region != null && region.isValid() ? region : null;
   }
 
   @Nullable
index d87aa78c7ea3c2ccec095311d7161804e85fb008..3c839f2083127666ecd8f7bfb36941ab551475f5 100644 (file)
@@ -26,6 +26,7 @@ import com.intellij.openapi.editor.markup.TextAttributes;
 import com.intellij.openapi.util.Key;
 import com.intellij.openapi.util.TextRange;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.awt.*;
 import java.util.ArrayList;
@@ -114,6 +115,19 @@ public class FoldingModelWindow implements FoldingModelEx{
     return host; //todo convert to window?
   }
 
+  @Nullable
+  @Override
+  public FoldRegion getFoldRegion(int startOffset, int endOffset) {
+    TextRange range = new TextRange(startOffset, endOffset);
+    TextRange hostRange = myDocumentWindow.injectedToHost(range);
+    FoldRegion hostRegion = myDelegate.getFoldRegion(hostRange.getStartOffset(), hostRange.getEndOffset());
+    if (hostRegion == null) {
+      return null;
+    }
+    FoldingRegionWindow window = hostRegion.getUserData(FOLD_REGION_WINDOW);
+    return window != null && window.getEditor() == myEditorWindow ? window : null;
+  }
+
   @Override
   public void runBatchFoldingOperation(@NotNull Runnable operation) {
     myDelegate.runBatchFoldingOperation(operation);
index d16183c7d0694391312c48d35492a76b72380364..b8063e1edbde7f23b7d14779d70475d1372eec0c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2014 JetBrains s.r.o.
+ * Copyright 2000-2011 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.
@@ -473,10 +473,8 @@ public abstract class AbstractInplaceIntroducer<V extends PsiNameIdentifierOwner
         final RangeMarker exprMarker = getExprMarker();
         if (exprMarker != null) {
           myExpr = restoreExpression(containingFile, psiField, exprMarker, myExprText);
-          if (myExpr != null && myExpr.isPhysical()) {
-            myExprMarker = createMarker(myExpr);
-          }
         }
+
         if (myLocalMarker != null) {
           final PsiElement refVariableElement = containingFile.findElementAt(myLocalMarker.getStartOffset());
           if (refVariableElement != null) {
@@ -509,6 +507,9 @@ public abstract class AbstractInplaceIntroducer<V extends PsiNameIdentifierOwner
           }
         }
 
+        if (myExpr != null && myExpr.isPhysical()) {
+          myExprMarker = createMarker(myExpr);
+        }
         myOccurrenceMarkers = null;
         deleteTemplateField(psiField);
       }
@@ -523,24 +524,7 @@ public abstract class AbstractInplaceIntroducer<V extends PsiNameIdentifierOwner
 
   @Override
   protected boolean performRefactoring() {
-    final String newName = getInputName();
-    if (getLocalVariable() == null && myExpr == null ||
-        newName == null ||
-        getLocalVariable() != null && !getLocalVariable().isValid() ||
-        myExpr != null && !myExpr.isValid()) {
-      super.moveOffsetAfter(false);
-      return false;
-    }
-    if (getLocalVariable() != null) {
-      new WriteCommandAction(myProject, getCommandName(), getCommandName()) {
-        @Override
-        protected void run(Result result) throws Throwable {
-          getLocalVariable().setName(myLocalName);
-        }
-      }.execute();
-    }
-
-    if (!isIdentifier(newName, myExpr != null ? myExpr.getLanguage() : getLocalVariable().getLanguage())) return false;
+    if (!ensureValid()) return false;
     CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
       @Override
       public void run() {
@@ -562,6 +546,28 @@ public abstract class AbstractInplaceIntroducer<V extends PsiNameIdentifierOwner
     return false;
   }
 
+  protected boolean ensureValid() {
+    final String newName = getInputName();
+    if (getLocalVariable() == null && myExpr == null ||
+        newName == null ||
+        getLocalVariable() != null && !getLocalVariable().isValid() ||
+        myExpr != null && !myExpr.isValid()) {
+      super.moveOffsetAfter(false);
+      return false;
+    }
+    if (getLocalVariable() != null) {
+      new WriteCommandAction(myProject, getCommandName(), getCommandName()) {
+        @Override
+        protected void run(Result result) throws Throwable {
+          getLocalVariable().setName(myLocalName);
+        }
+      }.execute();
+    }
+
+    if (!isIdentifier(newName, myExpr != null ? myExpr.getLanguage() : getLocalVariable().getLanguage())) return false;
+    return true;
+  }
+
   @Override
   protected void moveOffsetAfter(boolean success) {
     if (getLocalVariable() != null && getLocalVariable().isValid()) {
index 92dcdd146020078a2cf8bbae200e8a1f7c22981c..bda2dcc2376491aa920c7a213c7c3155e43eaf41 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2014 JetBrains s.r.o.
+ * Copyright 2000-2013 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -639,6 +639,11 @@ public abstract class InplaceRefactoring {
 
   protected abstract boolean performRefactoring();
 
+  /**
+   * if brokenOff but not canceled
+   */
+  protected void performCleanup() {}
+
   private void addVariable(final PsiReference reference,
                            final PsiElement selectedElement,
                            final TemplateBuilderImpl builder,
@@ -847,6 +852,8 @@ public abstract class InplaceRefactoring {
         super.templateFinished(template, brokenOff);
         if (!brokenOff) {
           bind = performRefactoring();
+        } else {
+          performCleanup();
         }
         moveOffsetAfter(!brokenOff);
       }
diff --git a/platform/lang-impl/testData/editor/indentingBackspace/beforeCommentWithTab-after.java b/platform/lang-impl/testData/editor/indentingBackspace/beforeCommentWithTab-after.java
new file mode 100644 (file)
index 0000000..de69817
--- /dev/null
@@ -0,0 +1,3 @@
+class A {
+    <caret>// comment
+}
diff --git a/platform/lang-impl/testData/editor/indentingBackspace/beforeCommentWithTab.java b/platform/lang-impl/testData/editor/indentingBackspace/beforeCommentWithTab.java
new file mode 100644 (file)
index 0000000..3d98bc8
--- /dev/null
@@ -0,0 +1,4 @@
+class A {
+
+       <caret>// comment
+}
diff --git a/platform/lang-impl/testSources/com/intellij/codeInsight/editorActions/SimpleIndentingBackspaceHandlerTest.java b/platform/lang-impl/testSources/com/intellij/codeInsight/editorActions/SimpleIndentingBackspaceHandlerTest.java
new file mode 100644 (file)
index 0000000..514cf7f
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.codeInsight.editorActions;
+
+import com.intellij.codeInsight.CodeInsightSettings;
+import com.intellij.openapi.actionSystem.IdeActions;
+import com.intellij.testFramework.LightPlatformCodeInsightTestCase;
+
+public class SimpleIndentingBackspaceHandlerTest extends LightPlatformCodeInsightTestCase {
+  public void testBasicUnindent() {
+    doTest("       <caret>text",
+           "    <caret>text");
+  }
+
+  public void testAtLineStart() {
+    doTest("line1\n<caret>line2",
+           "line1<caret>line2");
+  }
+
+  private void doTest(String before, String after) {
+    int savedMode = CodeInsightSettings.getInstance().SMART_BACKSPACE;
+    try {
+      CodeInsightSettings.getInstance().SMART_BACKSPACE = CodeInsightSettings.INDENT;
+      configureFromFileText(getTestName(false) + ".txt", before);
+      executeAction(IdeActions.ACTION_EDITOR_BACKSPACE);
+      checkResultByText(after);
+    }
+    finally {
+      CodeInsightSettings.getInstance().SMART_BACKSPACE = savedMode;
+    }
+  }
+}
index 62864d01b91ac00ebbe8d3ea221908ce6ba7d689..e6311fbae473421fdfe04e7faef3ccd29cd1f923 100644 (file)
@@ -37,6 +37,8 @@ import java.awt.event.MouseEvent;
  * @author Vladimir Kondratyev
  */
 public class ThreeComponentsSplitter extends JPanel implements Disposable {
+  public static final int MIN_SIZE = 48;
+
   private int myDividerWidth;
   /**
    *                        /------/
@@ -189,14 +191,22 @@ public class ThreeComponentsSplitter extends JPanel implements Disposable {
     else {
       firstCompontSize = getFirstSize();
       lastComponentSize = getLastSize();
-      int sizeLack = firstCompontSize + lastComponentSize - (componentSize - dividersCount * dividerWidth);
+      int sizeLack = firstCompontSize + lastComponentSize - (componentSize - dividersCount * dividerWidth - MIN_SIZE);
       if (sizeLack > 0) {
-        // Lacking size. Reduce first component's size, inner -> empty
-        firstCompontSize -= sizeLack;
-        innerComponentSize = 0;
+        // Lacking size. Reduce first & last component's size, inner -> MIN_SIZE
+        double firstSizeRatio = (double)firstCompontSize / (firstCompontSize + lastComponentSize);
+        if (firstCompontSize > 0) {
+          firstCompontSize -= sizeLack * firstSizeRatio;
+          firstCompontSize = Math.max(MIN_SIZE, firstCompontSize);
+        }
+        if (lastComponentSize > 0) {
+          lastComponentSize -= sizeLack * (1 - firstSizeRatio);
+          lastComponentSize = Math.max(MIN_SIZE, lastComponentSize);
+        }
+        innerComponentSize = MIN_SIZE;
       }
       else {
-        innerComponentSize = componentSize - dividersCount * dividerWidth - getFirstSize() - getLastSize();
+        innerComponentSize = Math.max(MIN_SIZE, componentSize - dividersCount * dividerWidth - getFirstSize() - getLastSize());
       }
 
       if (!innerVisible()) {
@@ -395,6 +405,29 @@ public class ThreeComponentsSplitter extends JPanel implements Disposable {
     return lastVisible() ? myLastSize : 0;
   }
 
+  public int getMinSize(boolean first) {
+    return getMinSize(first? myFirstComponent : myLastComponent);
+  }
+
+  public int getMaxSize(boolean first) {
+    final int size = getOrientation() ? this.getHeight() : this.getWidth();
+    return size - (first? myLastSize: myFirstSize) - MIN_SIZE;
+  }
+
+  private int getMinSize(JComponent component) {
+    if (isHonorMinimumSize()) {
+      if (component != null && myFirstComponent != null && myFirstComponent.isVisible() && myLastComponent != null && myLastComponent.isVisible()) {
+        if (getOrientation()) {
+          return component.getMinimumSize().height;
+        }
+        else {
+          return component.getMinimumSize().width;
+        }
+      }
+    }
+    return MIN_SIZE;
+  }
+
   @Override
   public void dispose() {
     myLastComponent = null;
@@ -633,23 +666,24 @@ public class ThreeComponentsSplitter extends JPanel implements Disposable {
         myGlassPane.setCursor(getResizeCursor(), myListener);
 
         myPoint = SwingUtilities.convertPoint(this, e.getPoint(), ThreeComponentsSplitter.this);
+        final int size = getOrientation() ? ThreeComponentsSplitter.this.getHeight() : ThreeComponentsSplitter.this.getWidth();
         if (getOrientation()) {
-          if (getHeight() > 0 || myDividerZone > 0) {
+          if (size > 0 || myDividerZone > 0) {
             if (myIsFirst) {
-              setFirstSize(Math.max(getMinSize(myFirstComponent), myPoint.y));
+              setFirstSize(Math.min(size - myLastSize - MIN_SIZE, Math.max(getMinSize(myFirstComponent), myPoint.y)));
             }
             else {
-              setLastSize(Math.max(getMinSize(myLastComponent), ThreeComponentsSplitter.this.getHeight() - myPoint.y - getDividerWidth()));
+              setLastSize(Math.min(size - myFirstSize - MIN_SIZE, Math.max(getMinSize(myLastComponent), size - myPoint.y - getDividerWidth())));
             }
           }
         }
         else {
-          if (getWidth() > 0 || myDividerZone > 0) {
+          if (size > 0 || myDividerZone > 0) {
             if (myIsFirst) {
-              setFirstSize(Math.max(getMinSize(myFirstComponent), myPoint.x));
+              setFirstSize(Math.min(size - myLastSize - MIN_SIZE, Math.max(getMinSize(myFirstComponent), myPoint.x)));
             }
             else {
-              setLastSize(Math.max(getMinSize(myLastComponent), ThreeComponentsSplitter.this.getWidth() - myPoint.x - getDividerWidth()));
+              setLastSize(Math.min(size - myFirstSize - MIN_SIZE, Math.max(getMinSize(myLastComponent), size - myPoint.x - getDividerWidth())));
             }
           }
         }
@@ -670,20 +704,6 @@ public class ThreeComponentsSplitter extends JPanel implements Disposable {
       }
     }
 
-    private int getMinSize(JComponent component) {
-      if (isHonorMinimumSize()) {
-        if (component != null && myFirstComponent != null && myFirstComponent.isVisible() && myLastComponent != null && myLastComponent.isVisible()) {
-          if (getOrientation()) {
-            return component.getMinimumSize().height;
-          }
-          else {
-            return component.getMinimumSize().width;
-          }
-        }
-      }
-      return 0;
-    }
-
     protected void processMouseEvent(MouseEvent e) {
       super.processMouseEvent(e);
       if (!isShowing()) {
index b616429a11c8fb89dc2e4271a18fbad8c583b98b..654f43e0c2ecdf979561f30696a5ab453cc147b2 100644 (file)
@@ -154,4 +154,8 @@ public abstract class ToolWindowManager {
 
   @Nullable
   public abstract Balloon getToolWindowBalloon(String id);
+
+  public abstract boolean isMaximized(@NotNull ToolWindow wnd);
+
+  public abstract void setMaximized(@NotNull ToolWindow wnd, boolean maximized);
 }
diff --git a/platform/platform-api/src/com/intellij/ui/content/TabbedContent.java b/platform/platform-api/src/com/intellij/ui/content/TabbedContent.java
new file mode 100644 (file)
index 0000000..9d6e1b4
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.ui.content;
+
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.util.List;
+
+/**
+ * @author Konstantin Bulenkov
+ * @since 14.1
+ */
+public interface TabbedContent extends Content {
+  void addContent(@NotNull JComponent content, @NotNull String name, boolean selectTab);
+  void removeContent(@NotNull JComponent content);
+  void selectContent(int index);
+  List<Pair<String, JComponent>> getTabs();
+  String getTitlePrefix();
+  void setTitlePrefix(String titlePrefix);
+}
index b1ae68a05c50fd66ea0877a8986fa85782173399..9ea092f1304f17f6ae72331e7469e78805380979 100644 (file)
@@ -119,12 +119,6 @@ public class ToggleToolbarAction extends ToggleAction implements DumbAware {
       myToolWindow = toolWindow;
     }
 
-    @Override
-    public boolean isPopup() {
-      // configured in getChildren()
-      return super.isPopup();
-    }
-
     @Override
     public void update(AnActionEvent e) {
       e.getPresentation().setVisible(!ActionGroupUtil.isGroupEmpty(this, e));
@@ -138,22 +132,22 @@ public class ToggleToolbarAction extends ToggleAction implements DumbAware {
       JComponent contentComponent = selectedContent != null ? selectedContent.getComponent() : null;
       if (contentComponent == null) return EMPTY_ARRAY;
       List<AnAction> result = ContainerUtil.newSmartList();
-      boolean addSeparator = false;
       for (final ActionToolbar toolbar : iterateToolbars(contentComponent)) {
         JComponent c = toolbar.getComponent();
         if (c.isVisible() || !c.isValid()) continue;
-        if (!result.isEmpty()) result.add(Separator.getInstance());
+        if (!result.isEmpty() && !(ContainerUtil.getLastItem(result) instanceof Separator)) {
+          result.add(Separator.getInstance());
+        }
 
         List<AnAction> actions = toolbar.getActions(false);
         for (AnAction action : actions) {
-          if (!(action instanceof Separator && (addSeparator = true)) &&
-              action instanceof ToggleAction &&
-              !result.contains(action)) {
-            if (addSeparator && result.size() > 1 && !(result.get(result.size() - 2) instanceof Separator)) {
+          if (action instanceof ToggleAction && !result.contains(action)) {
+            result.add(action);
+          }
+          else if (action instanceof Separator) {
+            if (!result.isEmpty() && !(ContainerUtil.getLastItem(result) instanceof Separator)) {
               result.add(Separator.getInstance());
             }
-            result.add(action);
-            addSeparator = false;
           }
         }
       }
index 5895026caaf5af206d9709af5c7e3095ef19c0a1..777ec3a6ea1e8eef9d4965d9f63cc810451b9bc8 100644 (file)
@@ -20,12 +20,11 @@ import com.intellij.openapi.command.undo.DocumentReference;
 import com.intellij.openapi.command.undo.DocumentReferenceManager;
 import com.intellij.openapi.command.undo.UndoConstants;
 import com.intellij.openapi.command.undo.UndoManager;
-import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.editor.Document;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.editor.EditorFactory;
-import com.intellij.openapi.editor.event.DocumentAdapter;
 import com.intellij.openapi.editor.event.DocumentEvent;
+import com.intellij.openapi.editor.event.DocumentListener;
 import com.intellij.openapi.fileEditor.FileDocumentManager;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.Key;
@@ -33,7 +32,6 @@ import com.intellij.openapi.vfs.VirtualFile;
 import org.jetbrains.annotations.Nullable;
 
 public class DocumentUndoProvider implements Disposable {
-  private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.command.impl.DocumentUndoProvider");
   private static final Key<Boolean> UNDOING_EDITOR_CHANGE = Key.create("DocumentUndoProvider.UNDOING_EDITOR_CHANGE");
 
   private final Project myProject;
@@ -61,32 +59,39 @@ public class DocumentUndoProvider implements Disposable {
     if (doc != null) doc.putUserData(UNDOING_EDITOR_CHANGE, null);
   }
 
-  private class MyEditorDocumentListener extends DocumentAdapter {
+  private class MyEditorDocumentListener implements DocumentListener {
     @Override
-    public void documentChanged(final DocumentEvent e) {
+    public void beforeDocumentChange(DocumentEvent e) {
       Document document = e.getDocument();
+      if (shouldBeIgnored(document)) return;
 
-      // if we don't ignore copy's events, we will receive notification
-      // for the same event twice (from original document too)
-      // and undo will work incorrectly
-      if (UndoManagerImpl.isCopy(document)) return;
+      UndoManagerImpl undoManager = getUndoManager();
+      if (undoManager.isActive() && isUndoable(document) && (undoManager.isUndoInProgress() || undoManager.isRedoInProgress()) && 
+          document.getUserData(UNDOING_EDITOR_CHANGE) != Boolean.TRUE) {
+        throw new IllegalStateException("Do not change documents during undo as it will break undo sequence.");
+      }
+    }
 
-      if (allEditorsAreViewersFor(document)) return;
-      if (!shouldRecordActions(document)) return;
+    @Override
+    public void documentChanged(final DocumentEvent e) {
+      Document document = e.getDocument();
+      if (shouldBeIgnored(document)) return;
 
       UndoManagerImpl undoManager = getUndoManager();
-      if (!undoManager.isActive() || !isUndoable(document)) {
-        registerNonUndoableAction(document);
-        return;
+      if (undoManager.isActive() && isUndoable(document)) {
+        registerUndoableAction(e);
       }
-
-      if (undoManager.isUndoInProgress() || undoManager.isRedoInProgress()) {
-        if (document.getUserData(UNDOING_EDITOR_CHANGE) != Boolean.TRUE) {
-          LOG.error("Do not change documents during undo as it will break undo sequence.");
-        }
+      else {
+        registerNonUndoableAction(document);
       }
-
-      registerUndoableAction(e);
+    }
+    
+    private boolean shouldBeIgnored(Document document) {
+      return UndoManagerImpl.isCopy(document) // if we don't ignore copy's events, we will receive notification
+                                              // for the same event twice (from original document too)
+                                              // and undo will work incorrectly
+             || allEditorsAreViewersFor(document) 
+             || !shouldRecordActions(document);
     }
 
     private boolean shouldRecordActions(final Document document) {
index 32e31900b804f8df5da07ed32568a771eb5201f9..b339d670989f5f7ac259d1fbecc82150012f14b0 100644 (file)
  */
 package com.intellij.openapi.editor.impl;
 
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.FoldRegion;
-import com.intellij.openapi.editor.RangeMarker;
-import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.editor.*;
+import com.intellij.openapi.util.Key;
 import com.intellij.util.ArrayUtil;
 import com.intellij.util.containers.ContainerUtil;
 import org.jetbrains.annotations.NotNull;
@@ -30,19 +28,11 @@ import java.util.*;
 * User: cdr
 */
 abstract class FoldRegionsTree {
-
-  @SuppressWarnings("UseOfArchaicSystemPropertyAccessors")
-  public static final boolean DEBUG = Boolean.getBoolean("idea.editor.debug.folding");
-  
-  private FoldRegion[] myCachedVisible;
-  private FoldRegion[] myCachedTopLevelRegions;
-  private int[] myCachedEndOffsets;
-  private int[] myCachedStartOffsets;
-  private int[] myCachedFoldedLines;
-  int myCachedLastIndex = -1;
+  @NotNull private CachedData myCachedData = new CachedData();
 
   //sorted using RangeMarker.BY_START_OFFSET comparator
   //i.e., first by start offset, then, if start offsets are equal, by end offset
+  @NotNull
   private List<FoldRegion> myRegions = ContainerUtil.newArrayList();
 
   private static final Comparator<FoldRegion> BY_END_OFFSET = new Comparator<FoldRegion>() {
@@ -60,31 +50,19 @@ abstract class FoldRegionsTree {
   void clear() {
     clearCachedValues();
 
-    if (myRegions != null) {
-      for (FoldRegion region : myRegions) {
-        region.dispose();
-      }
+    for (FoldRegion region : myRegions) {
+      region.dispose();
     }
 
     myRegions = new ArrayList<FoldRegion>();
   }
 
   void clearCachedValues() {
-    myCachedVisible = null;
-    myCachedTopLevelRegions = null;
-    myCachedEndOffsets = null;
-    myCachedStartOffsets = null;
-    myCachedFoldedLines = null;
-  }
-
-  private boolean isFoldingEnabledAndUpToDate() {
-    return isFoldingEnabled() && myCachedVisible != null;
+    myCachedData = new CachedData();
   }
 
   protected abstract boolean isFoldingEnabled();
 
-  protected abstract boolean isBatchFoldingProcessing();
-
   void rebuild() {
     List<FoldRegion> topLevels = new ArrayList<FoldRegion>(myRegions.size() / 2);
     List<FoldRegion> visible = new ArrayList<FoldRegion>(myRegions.size());
@@ -116,13 +94,13 @@ abstract class FoldRegionsTree {
       myRegions = allValid;
     }
 
-    myCachedTopLevelRegions = toFoldArray(topLevels);
-    myCachedVisible = toFoldArray(visible);
+    FoldRegion[] topLevelRegions = toFoldArray(topLevels);
+    FoldRegion[] visibleRegions = toFoldArray(visible);
 
-    Arrays.sort(myCachedTopLevelRegions, BY_END_OFFSET);
-    Arrays.sort(myCachedVisible, BY_END_OFFSET_REVERSE);
+    Arrays.sort(topLevelRegions, BY_END_OFFSET);
+    Arrays.sort(visibleRegions, BY_END_OFFSET_REVERSE);
 
-    updateCachedOffsets();
+    updateCachedOffsets(visibleRegions, topLevelRegions);
   }
 
   private static void removeRegionsWithSameStartOffset(List<FoldRegion> regions, FoldRegion region) {
@@ -142,93 +120,87 @@ abstract class FoldRegionsTree {
   }
 
   void updateCachedOffsets() {
+    CachedData cachedData = myCachedData;
+    updateCachedOffsets(cachedData.visibleRegions, cachedData.topLevelRegions);
+  }
+  
+  private void updateCachedOffsets(FoldRegion[] visibleRegions, FoldRegion[] topLevelRegions) {
     if (!isFoldingEnabled()) {
       return;
     }
-    if (myCachedVisible == null) {
+    if (visibleRegions == null) {
       rebuild();
       return;
     }
 
-    for (FoldRegion foldRegion : myCachedVisible) {
+    for (FoldRegion foldRegion : visibleRegions) {
       if (!foldRegion.isValid()) {
         rebuild();
         return;
       }
     }
 
-    int length = myCachedTopLevelRegions.length;
-    if (myCachedEndOffsets == null || myCachedEndOffsets.length != length) {
-      if (length != 0) {
-        myCachedEndOffsets = new int[length];
-        myCachedStartOffsets = new int[length];
-        myCachedFoldedLines = new int[length];
-      }
-      else {
-        myCachedEndOffsets = ArrayUtil.EMPTY_INT_ARRAY;
-        myCachedStartOffsets = ArrayUtil.EMPTY_INT_ARRAY;
-        myCachedFoldedLines = ArrayUtil.EMPTY_INT_ARRAY;
-      }
-    }
-
+    int length = topLevelRegions.length;
+    int[] startOffsets = length == 0 ? ArrayUtil.EMPTY_INT_ARRAY : new int[length];
+    int[] endOffsets = length == 0 ? ArrayUtil.EMPTY_INT_ARRAY : new int[length];
+    int[] foldedLines = length == 0 ? ArrayUtil.EMPTY_INT_ARRAY : new int[length];
+    
     int sum = 0;
     for (int i = 0; i < length; i++) {
-      FoldRegion region = myCachedTopLevelRegions[i];
-      myCachedStartOffsets[i] = region.getStartOffset();
-      myCachedEndOffsets[i] = region.getEndOffset() - 1;
+      FoldRegion region = topLevelRegions[i];
+      startOffsets[i] = region.getStartOffset();
+      endOffsets[i] = region.getEndOffset() - 1;
       Document document = region.getDocument();
       sum += document.getLineNumber(region.getEndOffset()) - document.getLineNumber(region.getStartOffset());
-      myCachedFoldedLines[i] = sum;
+      foldedLines[i] = sum;
     }
+    
+    myCachedData = new CachedData(visibleRegions, topLevelRegions, startOffsets, endOffsets, foldedLines);
   }
 
   boolean addRegion(@NotNull FoldRegion range) {
-    // During batchProcessing elements are inserted in ascending order,
-    // binary search find acceptable insertion place first time
-    boolean canUseCachedValue = false;
-    if (isBatchFoldingProcessing() && myCachedLastIndex >= 0 && myCachedLastIndex < myRegions.size()) {
-      FoldRegion lastRegion = myRegions.get(myCachedLastIndex);
-      if (RangeMarker.BY_START_OFFSET.compare(lastRegion, range) < 0) {
-        canUseCachedValue = myCachedLastIndex == (myRegions.size() - 1)
-                            || RangeMarker.BY_START_OFFSET.compare(range, myRegions.get(myCachedLastIndex + 1)) <= 0;
-      }
-    }
-    int index = canUseCachedValue ? myCachedLastIndex + 1 : Collections.binarySearch(myRegions, range, RangeMarker.BY_START_OFFSET);
-    if (index < 0) index = -index - 1;
-
-    if (index < myRegions.size()) {
-      FoldRegion foldRegion = myRegions.get(index);
-      if (TextRange.areSegmentsEqual(foldRegion, range)) {
-        return false;
+    int start = range.getStartOffset();
+    int end = range.getEndOffset();
+    int insertionIndex = myRegions.size();
+    for (int i = 0; i < myRegions.size(); i++) {
+      FoldRegion region = myRegions.get(i);
+      int rStart = region.getStartOffset();
+      int rEnd = region.getEndOffset();
+      if (rStart < start) {
+        if (region.isValid() && start < rEnd && rEnd < end) {
+          return false;
+        }
       }
-    } 
-    
-    for (int i = index - 1; i >=0; --i) {
-      final FoldRegion region = myRegions.get(i);
-      if (region.getEndOffset() < range.getStartOffset()) break;
-      if (region.isValid() && intersects(region, range)) {
-        return false;
+      else if (rStart == start) {
+        if (rEnd == end) {
+          return false;
+        }
+        else if (rEnd > end) {
+          insertionIndex = Math.min(insertionIndex, i);
+        }
       }
-    }
-
-    for (int i = index; i < myRegions.size(); i++) {
-      final FoldRegion region = myRegions.get(i);
-      if (region.getStartOffset() > range.getEndOffset()) break;
-      if (region.isValid() && intersects(region, range)) {
-        return false;
+      else {
+        insertionIndex = Math.min(insertionIndex, i);
+        if (rStart > end) {
+          break;
+        }
+        if (region.isValid() && rStart < end && end < rEnd) {
+          return false;
+        }
       }
     }
 
-    myRegions.add(myCachedLastIndex = index,range);
+    myRegions.add(insertionIndex, range);
     return true;
   }
 
   @Nullable
   FoldRegion fetchOutermost(int offset) {
-    if (!isFoldingEnabledAndUpToDate()) return null;
+    CachedData cachedData = myCachedData;
+    if (cachedData.isUnavailable()) return null;
 
-    final int[] starts = myCachedStartOffsets;
-    final int[] ends = myCachedEndOffsets;
+    final int[] starts = cachedData.startOffsets;
+    final int[] ends = cachedData.endOffsets;
     if (starts == null || ends == null) {
       return null;
     }
@@ -244,20 +216,7 @@ abstract class FoldRegionsTree {
         start = i + 1;
       }
       else {
-        // We encountered situation when cached data is inconsistent. It's not clear what produced that, so, the following was done:
-        //     1. Corresponding check was added and cached data is rebuilt in case of inconsistency;
-        //     2. Debug asserts are activated if dedicated flag is on (it's off by default);
-        if (myCachedStartOffsets[i] != myCachedTopLevelRegions[i].getStartOffset()) {
-          if (DEBUG) {
-            assert false :
-              "inconsistent cached fold data detected. Start offsets: " + Arrays.toString(myCachedStartOffsets) 
-              + ", end offsets: " + Arrays.toString(myCachedEndOffsets) + ", top regions: " + Arrays.toString(myCachedTopLevelRegions)
-              + ", visible regions: " + Arrays.toString(myCachedVisible);
-          }
-          rebuild();
-          return fetchOutermost(offset);
-        }
-        return myCachedTopLevelRegions[i];
+        return cachedData.topLevelRegions[i];
       }
     }
 
@@ -265,34 +224,26 @@ abstract class FoldRegionsTree {
   }
 
   FoldRegion[] fetchVisible() {
-    if (!isFoldingEnabledAndUpToDate()) return FoldRegion.EMPTY_ARRAY;
-    return myCachedVisible;
+    CachedData cachedData = myCachedData;
+    return cachedData.isUnavailable() ? FoldRegion.EMPTY_ARRAY : cachedData.visibleRegions;
   }
 
   @Nullable
   FoldRegion[] fetchTopLevel() {
-    if (!isFoldingEnabledAndUpToDate()) return null;
-    return myCachedTopLevelRegions;
+    CachedData cachedData = myCachedData;
+    return cachedData.isUnavailable() ? null : cachedData.topLevelRegions;
   }
 
   private static boolean contains(FoldRegion outer, FoldRegion inner) {
     return outer.getStartOffset() <= inner.getStartOffset() && outer.getEndOffset() >= inner.getEndOffset();
   }
 
-  private static boolean intersects(FoldRegion r1, FoldRegion r2) {
-    final int s1 = r1.getStartOffset();
-    final int s2 = r2.getStartOffset();
-    final int e1 = r1.getEndOffset();
-    final int e2 = r2.getEndOffset();
-    return s1 < s2 && s2 < e1 && e1 < e2 || s2 < s1 && s1 < e2 && e2 < e1;
-  }
-
   static boolean contains(FoldRegion region, int offset) {
     return region.getStartOffset() < offset && region.getEndOffset() > offset;
   }
 
   public FoldRegion[] fetchCollapsedAt(int offset) {
-    if (!isFoldingEnabledAndUpToDate()) return FoldRegion.EMPTY_ARRAY;
+    if (myCachedData.isUnavailable()) return FoldRegion.EMPTY_ARRAY;
     ArrayList<FoldRegion> allCollapsed = new ArrayList<FoldRegion>();
     for (FoldRegion region : myRegions) {
       if (!region.isExpanded() && contains(region, offset)) {
@@ -316,7 +267,7 @@ abstract class FoldRegionsTree {
   }
 
   FoldRegion[] fetchAllRegions() {
-    if (!isFoldingEnabledAndUpToDate()) return FoldRegion.EMPTY_ARRAY;
+    if (myCachedData.isUnavailable()) return FoldRegion.EMPTY_ARRAY;
 
     return toFoldArray(myRegions);
   }
@@ -326,14 +277,19 @@ abstract class FoldRegionsTree {
   }
 
   int getFoldedLinesCountBefore(int offset) {
-    int idx = getLastTopLevelIndexBefore(offset);
+    CachedData snapshot = myCachedData;
+    int idx = getLastTopLevelIndexBefore(snapshot, offset);
     if (idx == -1) return 0;
-    return myCachedFoldedLines[idx];
+    return snapshot.foldedLines[idx];
   }
 
   public int getLastTopLevelIndexBefore(int offset) {
-    int[] endOffsets = myCachedEndOffsets;
-    if (!isFoldingEnabledAndUpToDate() || endOffsets == null) return -1;
+    return getLastTopLevelIndexBefore(myCachedData, offset);
+  }
+  
+  private static int getLastTopLevelIndexBefore(CachedData snapshot, int offset) {
+    int[] endOffsets = snapshot.endOffsets;
+    if (snapshot.isUnavailable() || endOffsets == null) return -1;
 
     offset--; // end offsets are decremented in cache
     int start = 0;
@@ -353,4 +309,137 @@ abstract class FoldRegionsTree {
 
     return end;
   }
+
+  @Nullable
+  public FoldRegion getRegionAt(int startOffset, int endOffset) {
+    int index = Collections.binarySearch(myRegions, new DummyFoldRegion(startOffset, endOffset), RangeMarker.BY_START_OFFSET);
+    return index < 0 ? null : myRegions.get(index);
+  }
+
+  private class CachedData implements Cloneable {
+    private final FoldRegion[] visibleRegions;
+    private final FoldRegion[] topLevelRegions;
+    private final int[] startOffsets;
+    private final int[] endOffsets;
+    private final int[] foldedLines;
+
+    private CachedData() {
+      this.visibleRegions = null;
+      this.topLevelRegions = null;
+      this.startOffsets = null;
+      this.endOffsets = null;
+      this.foldedLines = null;
+    }
+
+    private CachedData(FoldRegion[] visibleRegions, FoldRegion[] topLevelRegions, int[] startOffsets, int[] endOffsets, int[] foldedLines) {
+      this.visibleRegions = visibleRegions;
+      this.topLevelRegions = topLevelRegions;
+      this.startOffsets = startOffsets;
+      this.endOffsets = endOffsets;
+      this.foldedLines = foldedLines;
+    }
+
+    private boolean isUnavailable() {
+      return !isFoldingEnabled() || visibleRegions == null;
+    }
+  }
+
+  private static class DummyFoldRegion implements FoldRegion {
+    private final int myStartOffset;
+    private final int myEndOffset;
+
+    private DummyFoldRegion(int startOffset, int endOffset) {
+      myStartOffset = startOffset;
+      myEndOffset = endOffset;
+    }
+
+    @Override
+    public boolean isExpanded() {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setExpanded(boolean expanded) {
+      throw new UnsupportedOperationException();
+    }
+
+    @NotNull
+    @Override
+    public String getPlaceholderText() {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Editor getEditor() {
+      throw new UnsupportedOperationException();
+    }
+
+    @Nullable
+    @Override
+    public FoldingGroup getGroup() {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean shouldNeverExpand() {
+      throw new UnsupportedOperationException();
+    }
+
+    @NotNull
+    @Override
+    public Document getDocument() {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int getStartOffset() {
+      return myStartOffset;
+    }
+
+    @Override
+    public int getEndOffset() {
+      return myEndOffset;
+    }
+
+    @Override
+    public boolean isValid() {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setGreedyToLeft(boolean greedy) {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setGreedyToRight(boolean greedy) {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isGreedyToRight() {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isGreedyToLeft() {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void dispose() {
+      throw new UnsupportedOperationException();
+    }
+
+    @Nullable
+    @Override
+    public <T> T getUserData(@NotNull Key<T> key) {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) {
+      throw new UnsupportedOperationException();
+    }
+  }
 }
index 7dbd0e3e9211a98c1b9021dc8a7d76970a18f1ce..cec6a03feaec001f2c9964357d194cb94d3267d8 100644 (file)
@@ -79,11 +79,6 @@ public class FoldingModelImpl implements FoldingModelEx, PrioritizedDocumentList
       protected boolean isFoldingEnabled() {
         return FoldingModelImpl.this.isFoldingEnabled();
       }
-
-      @Override
-      protected boolean isBatchFoldingProcessing() {
-        return myIsBatchFoldingProcessing;
-      }
     };
     myFoldRegionsProcessed = false;
     refreshSettings();
@@ -210,12 +205,9 @@ public class FoldingModelImpl implements FoldingModelEx, PrioritizedDocumentList
     }
 
     myIsBatchFoldingProcessing = true;
-    myFoldTree.myCachedLastIndex = -1;
     try {
       operation.run();
     } finally {
-      myFoldTree.myCachedLastIndex = -1;
-
       if (!oldBatchFlag) {
         if (myFoldRegionsProcessed) {
           notifyBatchFoldingProcessingDone(moveCaret);
@@ -253,6 +245,13 @@ public class FoldingModelImpl implements FoldingModelEx, PrioritizedDocumentList
     return myFoldTree.fetchOutermost(offset);
   }
 
+  @Nullable
+  @Override
+  public FoldRegion getFoldRegion(int startOffset, int endOffset) {
+    assertReadAccess();
+    return myFoldTree.getRegionAt(startOffset, endOffset);
+  }
+
   @Override
   @Nullable
   public FoldRegion getFoldingPlaceholderAt(Point p) {
index e8a0fd840b8e863b6ce762edff90fdc2aa792003..a6350f7b3e4c6749f232aaec36118a3e8f21d44d 100644 (file)
@@ -18,6 +18,7 @@ package com.intellij.openapi.editor.textarea;
 import com.intellij.openapi.editor.FoldRegion;
 import com.intellij.openapi.editor.FoldingModel;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 /**
  * @author Denis Zhdanov
@@ -55,6 +56,12 @@ public class TextComponentFoldingModel implements FoldingModel {
     return null;
   }
 
+  @Nullable
+  @Override
+  public FoldRegion getFoldRegion(int startOffset, int endOffset) {
+    return null;
+  }
+
   @Override
   public void runBatchFoldingOperation(@NotNull Runnable operation) {
   }
index dc429fb8f5b60faa8afa0a16c918fa3a867e8f9a..7ed1c335ddbcbf0085ab98d977df3a70cb32646e 100644 (file)
@@ -323,7 +323,19 @@ public class ProgressManagerImpl extends ProgressManager implements Disposable {
       if (threads != null) {
         for (Thread thread : threads) {
           ProgressIndicator currentIndicator = getCurrentIndicator(thread);
-          if (currentIndicator == indicator) {
+          boolean underCancelledIndicator = currentIndicator == indicator;
+
+          if (!underCancelledIndicator && currentIndicator instanceof WrappedProgressIndicator) {
+            while(currentIndicator instanceof WrappedProgressIndicator) {
+              ProgressIndicator originalProgressIndicator = ((WrappedProgressIndicator)currentIndicator).getOriginalProgressIndicator();
+              if (originalProgressIndicator == indicator) {
+                underCancelledIndicator = true;
+                break;
+              }
+              currentIndicator = originalProgressIndicator;
+            }
+          }
+          if (underCancelledIndicator) {
             threadsUnderCanceledIndicator.add(thread);
           }
         }
index bf9a015bdfeb3305826b2e3b527d5332093c515b..371d847f169907a83afaeee143a078f299761fa6 100644 (file)
@@ -402,6 +402,7 @@ public final class InternalDecorator extends JPanel implements Queryable, DataPr
     resize.add(new ResizeToolWindowAction.Right(myToolWindow, this));
     resize.add(new ResizeToolWindowAction.Up(myToolWindow, this));
     resize.add(new ResizeToolWindowAction.Down(myToolWindow, this));
+    resize.add(ActionManager.getInstance().getAction("MaximizeToolWindow"));
 
     group.add(resize);
 
diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/MaximizeToolWindowAction.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/MaximizeToolWindowAction.java
new file mode 100644 (file)
index 0000000..29d68ad
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.openapi.wm.impl;
+
+import com.intellij.idea.ActionsBundle;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.wm.ToolWindow;
+import com.intellij.openapi.wm.ToolWindowManager;
+import org.jetbrains.annotations.NotNull;
+
+public class MaximizeToolWindowAction extends AnAction {
+  public MaximizeToolWindowAction() {
+    super();
+  }
+
+  @Override
+  public void actionPerformed(@NotNull AnActionEvent e) {
+    Project project = e.getProject();
+    if (project == null || project.isDisposed()) return;
+    ToolWindow toolWindow = e.getData(PlatformDataKeys.TOOL_WINDOW);
+    if (toolWindow == null) return;
+    ToolWindowManager manager = ToolWindowManager.getInstance(project);
+    manager.setMaximized(toolWindow, !manager.isMaximized(toolWindow));
+  }
+
+  @Override
+  public void update(@NotNull AnActionEvent e) {
+    e.getPresentation().setEnabled(true);
+    Project project = e.getProject();
+    if (project == null || project.isDisposed()) {
+      e.getPresentation().setEnabled(false);
+      return;
+    }
+    ToolWindow toolWindow = e.getData(PlatformDataKeys.TOOL_WINDOW);
+    if (toolWindow == null) {
+      e.getPresentation().setEnabled(false);
+      return;
+    }
+    ToolWindowManager manager = ToolWindowManager.getInstance(project);
+    e.getPresentation().setText(manager.isMaximized(toolWindow) ?
+                                ActionsBundle.message("action.ResizeToolWindowMaximize.text.alternative") :
+                                ActionsBundle.message("action.ResizeToolWindowMaximize.text"));
+  }
+}
index 101d3ab2762be2d1bd5e258c73f46231b162a237..7d64b1180e15a474cd52361a2c86d0dcb954a855 100644 (file)
@@ -25,9 +25,11 @@ import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
 import com.intellij.openapi.actionSystem.impl.*;
 import com.intellij.openapi.keymap.KeymapUtil;
 import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.SystemInfo;
 import com.intellij.openapi.wm.ToolWindow;
 import com.intellij.openapi.wm.ToolWindowAnchor;
+import com.intellij.openapi.wm.ToolWindowManager;
 import com.intellij.openapi.wm.ToolWindowType;
 import com.intellij.openapi.wm.impl.content.ToolWindowContentUi;
 import com.intellij.ui.InplaceButton;
@@ -210,6 +212,12 @@ public abstract class ToolWindowHeader extends JPanel implements Disposable, UIS
     };
   }
 
+  void switchMaximizedState(Project project) {
+    if (project == null || project.isDisposed()) return;
+    ToolWindowManager mgr = ToolWindowManager.getInstance(project);
+    mgr.setMaximized(myToolWindow, !mgr.isMaximized(myToolWindow));
+  }
+
   @Override
   public void uiSettingsChanged(UISettings source) {
     clearCaches();
index 7b9a7494c6405409959f353f8e8814cdb8ecf7cc..5fa360235cb22222500d0b5f183c3922daab5feb 100644 (file)
@@ -198,6 +198,15 @@ public class ToolWindowHeadlessManagerImpl extends ToolWindowManagerEx {
     return null;
   }
 
+  @Override
+  public boolean isMaximized(@NotNull ToolWindow wnd) {
+    return false;
+  }
+
+  @Override
+  public void setMaximized(@NotNull ToolWindow wnd, boolean maximized) {
+  }
+
   @Override
   public void initToolWindow(@NotNull ToolWindowEP bean) {
 
index c0c27ba8c128b4379084dc43f76f4a9f0b998e0e..8acebb5b5bfde7a40785abc161198052e39bfd06 100644 (file)
@@ -120,6 +120,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
   private final FileEditorManager myFileEditorManager;
   private final LafManager myLafManager;
   private final Map<String, Balloon> myWindow2Balloon = new HashMap<String, Balloon>();
+  private Pair<String, Integer> myMaximizedToolwindowSize = null;
 
   private KeyState myCurrentState = KeyState.waiting;
   private final Alarm myWaiterForSecondPress = new Alarm();
@@ -1982,6 +1983,16 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
     myToolWindowsPane.stretchWidth(toolWindow, value);
   }
 
+  @Override
+  public boolean isMaximized(@NotNull ToolWindow wnd) {
+    return myToolWindowsPane.isMaximized(wnd);
+  }
+
+  @Override
+  public void setMaximized(@NotNull ToolWindow wnd, boolean maximized) {
+    myToolWindowsPane.setMaximized(wnd, maximized);
+  }
+
   public void stretchHeight(ToolWindowImpl toolWindow, int value) {
     myToolWindowsPane.stretchHeight(toolWindow, value);
   }
index d1232749d3c81bb9bb86e0a45457a339bc6d01ca..3606939167a835d758ec71295b39bada6b4a5143 100644 (file)
@@ -22,20 +22,27 @@ import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.ui.Splitter;
 import com.intellij.openapi.ui.ThreeComponentsSplitter;
+import com.intellij.openapi.util.Condition;
 import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.registry.Registry;
+import com.intellij.openapi.wm.IdeGlassPaneUtil;
 import com.intellij.openapi.wm.ToolWindow;
 import com.intellij.openapi.wm.ToolWindowAnchor;
 import com.intellij.openapi.wm.ToolWindowType;
-import com.intellij.openapi.wm.ex.ToolWindowEx;
 import com.intellij.openapi.wm.impl.commands.FinalizableCommand;
 import com.intellij.ui.ScreenUtil;
 import com.intellij.ui.components.JBLayeredPane;
 import com.intellij.util.containers.HashMap;
 import com.intellij.util.ui.FadeInFadeOut;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
 import java.awt.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import java.util.ArrayList;
@@ -58,6 +65,7 @@ public final class ToolWindowsPane extends JBLayeredPane implements Disposable {
   private final HashMap<StripeButton, WindowInfoImpl> myButton2Info;
   private final HashMap<InternalDecorator, WindowInfoImpl> myDecorator2Info;
   private final HashMap<String, Float> myId2SplitProportion;
+  private Pair<ToolWindow, Integer> myMaximizedProportion = null;
   /**
    * This panel is the layered pane where all sliding tool windows are located. The DEFAULT
    * layer contains splitters. The PALETTE layer contains all sliding tool windows.
@@ -192,6 +200,24 @@ public final class ToolWindowsPane extends JBLayeredPane implements Disposable {
     super.addNotify();
     if (ScreenUtil.isStandardAddRemoveNotify(this)) {
       UISettings.getInstance().addUISettingsListener(myUISettingsListener, myDisposable);
+      IdeGlassPaneUtil.find(this).addMousePreprocessor(new MouseAdapter() {
+        @Override
+        public void mouseClicked(MouseEvent e) {
+          if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2 && e.getModifiersEx() == 0) {
+            e = SwingUtilities.convertMouseEvent(e.getComponent(), e, ToolWindowsPane.this);
+            Component component = SwingUtilities.getDeepestComponentAt(ToolWindowsPane.this, e.getX(), e.getY());
+            Component header = component == null ? null : UIUtil.findParentByCondition((JComponent)component, new Condition<Component>() {
+              @Override
+              public boolean value(Component component) {
+                return component instanceof ToolWindowHeader;
+              }
+            });
+            if (header instanceof ToolWindowHeader) {
+              ((ToolWindowHeader)header).switchMaximizedState(myFrame.getProject());
+            }
+          }
+        }
+      }, this);
     }
   }
 
@@ -490,7 +516,21 @@ public final class ToolWindowsPane extends JBLayeredPane implements Disposable {
   }
 
   private void stretch(ToolWindow wnd, int value) {
-    if (!wnd.isVisible()) return;
+    Pair<Resizer, Component> pair = findResizerAndComponent(wnd);
+    if (pair == null) return;
+
+    boolean vertical = wnd.getAnchor() == ToolWindowAnchor.TOP || wnd.getAnchor() == ToolWindowAnchor.BOTTOM;
+    int actualSize = (vertical ? pair.second.getHeight() : pair.second.getWidth()) + value;
+    boolean first = wnd.getAnchor() == ToolWindowAnchor.LEFT  || wnd.getAnchor() == ToolWindowAnchor.TOP;
+    int maxValue = vertical ? myVerticalSplitter.getMaxSize(first) : myHorizontalSplitter.getMaxSize(first);
+    int minValue = vertical ? myVerticalSplitter.getMinSize(first) : myHorizontalSplitter.getMinSize(first);;
+
+    pair.first.setSize(Math.max(minValue, Math.min(maxValue, actualSize)));
+  }
+
+  @Nullable
+  private Pair<Resizer, Component> findResizerAndComponent(ToolWindow wnd) {
+    if (!wnd.isVisible()) return null;
 
     Resizer resizer = null;
     Component cmp = null;
@@ -534,26 +574,7 @@ public final class ToolWindowsPane extends JBLayeredPane implements Disposable {
       }
     }
 
-    if (resizer == null) return;
-
-    int currentValue = wnd.getAnchor().isHorizontal() ? cmp.getHeight() : cmp.getWidth();
-
-    int actualSize = currentValue + value;
-
-    int minValue =
-      wnd.getAnchor().isHorizontal() ? ((ToolWindowEx)wnd).getDecorator().getHeaderHeight() : 16 + myHorizontalSplitter.getDividerWidth();
-    int maxValue = wnd.getAnchor().isHorizontal() ? myLayeredPane.getHeight() : myLayeredPane.getWidth();
-
-
-    if (actualSize < minValue) {
-      actualSize = minValue;
-    }
-
-    if (actualSize > maxValue) {
-      actualSize = maxValue;
-    }
-
-    resizer.setSize(actualSize);
+    return resizer != null ? Pair.create(resizer, cmp) : null;
   }
 
   private void updateLayout() {
@@ -598,6 +619,27 @@ public final class ToolWindowsPane extends JBLayeredPane implements Disposable {
     }
   }
 
+  public boolean isMaximized(@NotNull ToolWindow wnd) {
+      return myMaximizedProportion != null && myMaximizedProportion.first == wnd;
+  }
+
+  public void setMaximized(@NotNull ToolWindow wnd, boolean maximized) {
+    Pair<Resizer, Component> resizerAndComponent = findResizerAndComponent(wnd);
+    if (resizerAndComponent == null) return;
+
+    if (!maximized) {
+      ToolWindow maximizedWindow = myMaximizedProportion.first;
+      assert maximizedWindow == wnd;
+      resizerAndComponent.first.setSize(myMaximizedProportion.second);
+      myMaximizedProportion = null;
+    } else {
+      int size = wnd.getAnchor().isHorizontal() ? resizerAndComponent.second.getHeight() : resizerAndComponent.second.getWidth();
+      stretch(wnd, Short.MAX_VALUE);
+      myMaximizedProportion = Pair.create(wnd, size);
+    }
+    doLayout();
+  }
+
 
   interface Resizer {
     void setSize(int size);
index 62246de04248745d6ea63f66c02853d5a76f232e..e2bc60e1cf30d2698f9c7c6fd15a0a1924fb49f6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@ import com.intellij.ui.awt.RelativeRectangle;
 import com.intellij.ui.content.Content;
 import com.intellij.ui.content.ContentManager;
 import com.intellij.ui.content.ContentManagerEvent;
+import com.intellij.ui.content.TabbedContent;
 import com.intellij.util.ui.BaseButtonBehavior;
 import com.intellij.util.ui.UIUtil;
 import org.jetbrains.annotations.Nullable;
@@ -438,9 +439,15 @@ class TabContentLayout extends ContentLayout {
 
   @Override
   public void contentAdded(ContentManagerEvent event) {
-    final ContentTabLabel tab = new ContentTabLabel(event.getContent(), this);
+    final Content content = event.getContent();
+    final ContentTabLabel tab;
+    if (content instanceof TabbedContent) {
+      tab = new TabbedContentTabLabel((TabbedContent)content, this);
+    } else {
+      tab = new ContentTabLabel(content, this);
+    }
     myTabs.add(event.getIndex(), tab);
-    myContent2Tabs.put(event.getContent(), tab);
+    myContent2Tabs.put(content, tab);
     
     myCached.clear();
   }
diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabbedContentTabLabel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabbedContentTabLabel.java
new file mode 100644 (file)
index 0000000..55c021d
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.openapi.wm.impl.content;
+
+import com.intellij.ide.IdeEventQueue;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.util.Pair;
+import com.intellij.ui.ClickListener;
+import com.intellij.ui.components.JBList;
+import com.intellij.ui.content.TabbedContent;
+import com.intellij.util.NotNullFunction;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import java.awt.*;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public class TabbedContentTabLabel extends ContentTabLabel {
+  private final ComboIcon myComboIcon = new ComboIcon() {
+    @Override
+    public Rectangle getIconRec() {
+      return new Rectangle(getWidth() - getIconWidth() - 3, 0, getIconWidth(), getHeight());
+    }
+
+    @Override
+    public boolean isActive() {
+      return true;
+    }
+  };
+  private final TabbedContent myContent;
+
+  public TabbedContentTabLabel(TabbedContent content, TabContentLayout layout) {
+    super(content, layout);
+    myContent = content;
+    new ClickListener() {
+      @Override
+      public boolean onClick(@NotNull MouseEvent event, int clickCount) {
+        showPopup();
+        return true;
+      }
+    }.installOn(this);
+  }
+
+  private void showPopup() {
+    IdeEventQueue.getInstance().getPopupManager().closeAllPopups();
+    ArrayList<String> names = new ArrayList<String>();
+    for (Pair<String, JComponent> tab : myContent.getTabs()) {
+      names.add(tab.first);
+    }
+    final JBList list = new JBList(names);
+    list.installCellRenderer(new NotNullFunction<Object, JComponent>() {
+      final JLabel label = new JLabel();
+      {
+        label.setBorder(new EmptyBorder(UIUtil.getListCellPadding()));
+      }
+      @NotNull
+      @Override
+      public JComponent fun(Object dom) {
+        label.setText(dom.toString());
+        return label;
+      }
+    });
+    JBPopupFactory.getInstance().createListPopupBuilder(list).setItemChoosenCallback(new Runnable() {
+      @Override
+      public void run() {
+        int index = list.getSelectedIndex();
+        if (index != -1) {
+          myContent.selectContent(index);
+        }
+      }
+    }).createPopup().showUnderneathOf(this);
+  }
+
+  @Override
+  public void update() {
+    super.update();
+    if (myContent != null) {
+      setText(myContent.getTabName());
+    }
+    setHorizontalAlignment(LEFT);
+  }
+
+  @Override
+  public Dimension getPreferredSize() {
+    final Dimension size = super.getPreferredSize();
+    return new Dimension(size.width + 12, size.height);
+  }
+
+  @Override
+  protected void paintComponent(Graphics g) {
+    super.paintComponent(g);
+    myComboIcon.paintIcon(this, g);
+  }
+}
diff --git a/platform/platform-impl/src/com/intellij/ui/content/impl/TabbedContentImpl.java b/platform/platform-impl/src/com/intellij/ui/content/impl/TabbedContentImpl.java
new file mode 100644 (file)
index 0000000..42811a1
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.ui.content.impl;
+
+import com.intellij.openapi.util.Pair;
+import com.intellij.ui.content.TabbedContent;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public class TabbedContentImpl extends ContentImpl implements TabbedContent {
+  private final List<Pair<String, JComponent>> myTabs = new ArrayList<Pair<String, JComponent>>();
+  private String myPrefix;
+
+  public TabbedContentImpl(JComponent component, String displayName, boolean isPinnable, String titlePrefix) {
+    super(component, displayName, isPinnable);
+    myPrefix = titlePrefix;
+    addContent(component, displayName, true);
+  }
+
+  @Override
+  public void addContent(@NotNull JComponent content, @NotNull String name, boolean selectTab) {
+    Pair<String, JComponent> tab = Pair.create(name, content);
+    if (!myTabs.contains(tab)) {
+      myTabs.add(tab);
+    }
+    if (selectTab && getComponent() != content) {
+      setComponent(content);
+    }
+  }
+
+  @Override
+  public void setComponent(JComponent component) {
+    Container parent = getComponent().getParent();
+    parent.remove(getComponent());
+    parent.add(component);
+
+    super.setComponent(component);
+  }
+
+  @Override
+  public void removeContent(@NotNull JComponent content) {
+    Pair<String, JComponent> toRemove = null;
+    for (Pair<String, JComponent> tab : myTabs) {
+      if (tab.second == content) {
+        toRemove = tab;
+        break;
+      }
+    }
+    myTabs.remove(toRemove);
+  }
+
+  @Override
+  public void selectContent(int index) {
+    Pair<String, JComponent> tab = myTabs.get(index);
+    setDisplayName(tab.first);
+    setComponent(tab.second);
+  }
+
+  @Override
+  public String getTabName() {
+    String selected = findTabNameByComponent(getComponent());
+    if (myPrefix != null) {
+      selected = myPrefix + selected;
+    }
+    return selected;
+  }
+
+  private String findTabNameByComponent(JComponent c) {
+    for (Pair<String, JComponent> tab : myTabs) {
+      if (tab.second == c) {
+        return tab.first;
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public List<Pair<String, JComponent>> getTabs() {
+    return Collections.unmodifiableList(myTabs);
+  }
+
+  @Override
+  public String getTitlePrefix() {
+    return myPrefix;
+  }
+
+  @Override
+  public void setTitlePrefix(String titlePrefix) {
+    myPrefix = titlePrefix;
+  }
+}
index 04033418742f82ff0387c2ad5967b2d13041ef5d..84cc23a37ca903b7b1ec95b4aac638652b10ac95 100644 (file)
@@ -427,6 +427,7 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo
   private DockWindow createWindowFor(@Nullable String id, DockContainer container) {
     String windowId = id != null ? id : String.valueOf(myWindowIdCounter++);
     DockWindow window = new DockWindow(windowId, myProject, container, container instanceof DockContainer.Dialog);
+    Disposer.register(container, window);
     window.setDimensionKey("dock-window-" + windowId);
     myWindows.put(container, window);
     return window;
index 898bc9f450897fe466050ab49054a344a2c258d5..34dc44c6d460dceb054a6a7c203ddf4f0282a169 100644 (file)
  */
 package com.intellij.util;
 
+import com.intellij.openapi.Disposable;
 import com.intellij.openapi.application.Application;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.progress.ProcessCanceledException;
 import com.intellij.openapi.project.Project;
+import com.intellij.util.messages.MessageBus;
 import com.intellij.util.messages.Topic;
+import org.jetbrains.annotations.NotNull;
 
 /**
  * Created with IntelliJ IDEA.
@@ -57,4 +60,14 @@ public class MessageBusUtil {
       application.invokeLater(runnable);
     }
   }
+
+  @NotNull
+  public static Disposable newMessageBusDisposer(@NotNull final MessageBus messageBus) {
+    return new Disposable() {
+      @Override
+      public void dispose() {
+        messageBus.dispose();
+      }
+    };
+  }
 }
index 58adbf2195373fdabf95f8a61af3442139c27811..138a5e3b60b527b7caadb61ca4cf81c674de7ffa 100644 (file)
@@ -842,6 +842,9 @@ action.ResizeToolWindowUp.text=Stretch to Top
 action.ResizeToolWindowUp.description=Resize active tool window to the top
 action.ResizeToolWindowDown.text=Stretch to Bottom
 action.ResizeToolWindowDown.description=Resize active tool window to the bottom
+action.ResizeToolWindowMaximize.text=Maximize tool window
+action.ResizeToolWindowMaximize.text.alternative=Restore tool window size
+action.ResizeToolWindowMaximize.description=Maximize tool widow
 action.IncrementWindowWidth.text=Increment Width
 action.DecrementWindowWidth.text=Decrement Width
 action.IncrementWindowHeight.text=Increment Height
index 43c887388073e1a9306e342924e1ef5671788038..04d82a5513924ec25bbcbbdcbbc0fbe3efef0f9f 100644 (file)
                           id="EnterBetweenBracesHandler"/>
     <enterHandlerDelegate implementation="com.intellij.codeInsight.editorActions.enter.EnterAfterJavadocTagHandler"/>
 
-    <backspaceHandlerDelegate implementation="com.intellij.codeInsight.editorActions.IndentingBackspaceHandler" />
+    <backspaceHandlerDelegate implementation="com.intellij.codeInsight.editorActions.SimpleIndentingBackspaceHandler" />
+    <backspaceHandlerDelegate implementation="com.intellij.codeInsight.editorActions.SmartIndentingBackspaceHandler" />
 
     <codeInsight.linkHandler prefix="#inspection/" handlerClass="com.intellij.codeInsight.hint.InspectionDescriptionLinkHandler"/>
     <codeInsight.linkHandler prefix="#navigation/" handlerClass="com.intellij.codeInsight.hint.NavigationLinkHandler"/>
index 405316ff8468802524ae75850ba95c0611666c57..8907cc61f6339845bd4c19b33bdea8f977de3ad2 100644 (file)
   <action id="ResizeToolWindowDown">
     <keyboard-shortcut first-keystroke="control shift DOWN"/>
   </action>
+  <action id="MaximizeToolWindow">
+    <keyboard-shortcut first-keystroke="control shift QUOTE"/>
+  </action>
   <action id="HideSideWindows"/>
   <action id="ShowPopupMenu">
     <keyboard-shortcut first-keystroke="CONTEXT_MENU"/>
index fb3b31747ae97d5f022d80a039ddb003733bc5f3..4e2657b427ee9a5def5cf9b73f02fa6030b7d421 100644 (file)
           <action id="HideAllWindows" class="com.intellij.ide.actions.HideAllToolWindowsAction"/>
           <action id="CloseActiveTab" class="com.intellij.ide.actions.CloseActiveTabAction"/>
           <action id="JumpToLastWindow" class="com.intellij.ide.actions.JumpToLastWindowAction"/>
+          <action id="MaximizeToolWindow" class="com.intellij.openapi.wm.impl.MaximizeToolWindowAction"/>
           <separator/>
           <action id="TogglePinnedMode" class="com.intellij.ide.actions.TogglePinnedModeAction"/>
           <action id="ToggleDockMode" class="com.intellij.ide.actions.ToggleDockModeAction"/>
index 2f0ee4cebc01b19c79e5348bda0fb92dd843c934..9409cf539c7b788624170e5c0175f31349330e37 100644 (file)
@@ -86,6 +86,20 @@ public class FoldingTest extends AbstractEditorTest {
     });
   }
 
+  public void testIntersectsWithRegionFarInStorageOrder() {
+    myModel.runBatchFoldingOperation(new Runnable() {
+      @Override
+      public void run() {
+        FoldRegion region = myModel.addFoldRegion(0, 10, ".");
+        assertNotNull(region);
+        region = myModel.addFoldRegion(1, 5, ".");
+        assertNotNull(region);
+        region = myModel.addFoldRegion(6, 11, ".");
+        assertNull(region);
+      }
+    });
+  }
+
   public void testAddEmptyRegion() {
     FoldRegion region = null;
     try {
index 6fe898693d65f3ea9f02accc1bccde1642de416a..ba514c1c92b0135bc5ef0433338ca7947616f46f 100644 (file)
@@ -1419,6 +1419,8 @@ public class StructuralSearchTest extends StructuralSearchTestCase {
     // typed var with instanceof
     assertEquals("typed instanceof",findMatchesCount(s65,s66),1);
 
+    assertEquals("don't throw exception on incomplete instanceof expression", findMatchesCount(s65, "'_T instanceof"), 2);
+
     // typed vars with arrays
     assertEquals("typed pattern with array",findMatchesCount(s23,s24_1),2);
 
index a2768f2e1851628d62f729941f0bd1f2b32bebb4..4ee338aba63c2b75944049bdc3327a8ae9899668 100644 (file)
@@ -654,6 +654,15 @@ public class Mock {
       return null;
     }
 
+    @Override
+    public boolean isMaximized(@NotNull ToolWindow wnd) {
+      return false;
+    }
+
+    @Override
+    public void setMaximized(@NotNull ToolWindow wnd, boolean maximized) {
+    }
+
     @Override
     public void notifyByBalloon(@NotNull final String toolWindowId, @NotNull final MessageType type, @NotNull final String htmlBody) {
     }
index 49d2d4ca20b2c1f143883dea608bd651bfb3c396..b2d6620e22a7d052552b3d4ba744ffbe0f0c693c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2014 JetBrains s.r.o.
+ * Copyright 2000-2011 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.
@@ -30,6 +30,10 @@ public abstract class AbstractInplaceIntroduceTest extends LightPlatformCodeInsi
   protected abstract String getBasePath();
 
   protected void doTestEscape() {
+    doTestEscape(null);
+  }
+
+  protected void doTestEscape(Pass<AbstractInplaceIntroducer> pass) {
     String name = getTestName(true);
     configureByFile(getBasePath() + name + getExtension());
     final boolean enabled = getEditor().getSettings().isVariableInplaceRenameEnabled();
@@ -37,7 +41,10 @@ public abstract class AbstractInplaceIntroduceTest extends LightPlatformCodeInsi
       TemplateManagerImpl.setTemplateTesting(getProject(), getTestRootDisposable());
       getEditor().getSettings().setVariableInplaceRenameEnabled(true);
 
-      invokeRefactoring();
+      final AbstractInplaceIntroducer introducer = invokeRefactoring();
+      if (pass != null) {
+        pass.pass(introducer);
+      }
       TemplateState state = TemplateManagerImpl.getTemplateState(getEditor());
       assert state != null;
       state.gotoEnd(true);
index dc0e25bfae3c90aca52fc183c2d996cb8ff95315..51b906eb32ef972bafff8dcf2930ef44a43b5c96 100644 (file)
@@ -180,7 +180,6 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig
   @Override
   public VirtualFile copyFileToProject(@NotNull @NonNls final String sourceFilePath, @NotNull @NonNls final String targetPath) {
     final String testDataPath = getTestDataPath();
-    assert testDataPath != null : "test data path not specified";
 
     File fromFile = new File(testDataPath + "/" + sourceFilePath);
     if (!fromFile.exists()) {
@@ -236,7 +235,6 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig
   @Override
   public VirtualFile copyDirectoryToProject(@NotNull @NonNls final String sourceFilePath, @NotNull @NonNls final String targetPath) {
     final String testDataPath = getTestDataPath();
-    assert testDataPath != null : "test data path not specified";
 
     final File fromFile = new File(testDataPath + "/" + sourceFilePath);
     if (myTempDirFixture instanceof LightTempDirTestFixtureImpl) {
@@ -1276,7 +1274,7 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig
 
       @Override
       public boolean isToolEnabled(HighlightDisplayKey key, PsiElement element) {
-        return key != null && key.toString() != null && myAvailableTools.containsKey(key.toString()) && !myDisabledInspections.contains(key.toString());
+        return key != null && myAvailableTools.containsKey(key.toString()) && !myDisabledInspections.contains(key.toString());
       }
 
       @Override
index cdf476e7b393bbc669fc4d5bcdb1d1459deef114..028a6898ad22115ba6e6f13b5514ad9853ca2ad3 100644 (file)
@@ -27,23 +27,19 @@ import com.intellij.openapi.vcs.VcsException;
 import com.intellij.openapi.vcs.annotate.AnnotationProvider;
 import com.intellij.openapi.vcs.ex.ProjectLevelVcsManagerEx;
 import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
-import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.openapi.wm.ToolWindow;
 import com.intellij.openapi.wm.ToolWindowId;
 import com.intellij.openapi.wm.ToolWindowManager;
-import com.intellij.ui.TabbedPaneWrapper;
 import com.intellij.ui.content.Content;
 import com.intellij.ui.content.ContentFactory;
 import com.intellij.ui.content.ContentManager;
-import com.intellij.ui.tabs.TabInfo;
-import com.intellij.ui.tabs.impl.JBTabsImpl;
+import com.intellij.ui.content.TabbedContent;
+import com.intellij.ui.content.impl.TabbedContentImpl;
 import com.intellij.util.BufferedListConsumer;
 import com.intellij.util.Consumer;
 import com.intellij.util.ContentsUtil;
-import com.intellij.util.ui.UIUtil;
 import org.jetbrains.annotations.NotNull;
 
-import javax.swing.*;
 import java.util.List;
 
 /**
@@ -143,7 +139,7 @@ public class FileHistorySessionPartner implements VcsAppendableHistorySessionPar
         if (Registry.is("vcs.merge.toolwindows")) {
           Content history = null;
           for (Content content : toolWindow.getContentManager().getContents()) {
-            if ("History".equals(content.getTabName())) {
+            if (content.getTabName().startsWith("History: ")) {
               history = content;
               break;
             }
@@ -151,31 +147,11 @@ public class FileHistorySessionPartner implements VcsAppendableHistorySessionPar
 
           if (history == null) {
             final Disposable disposable = Disposer.newDisposable();
-            final TabbedPaneWrapper pane = new TabbedPaneWrapper(disposable);
-            final JComponent component = pane.getComponent();
-            component.putClientProperty("TabbedPaneWrapper", pane);
-            history = ContentFactory.SERVICE.getInstance().createContent(component, "History", true);
+            history = new TabbedContentImpl(myFileHistoryPanel, myFileHistoryPanel.getVirtualFile().getName(), true, "History: ");
             ContentsUtil.addOrReplaceContent(contentManager, history, true);
             Disposer.register(history, disposable);
-          }
-
-          final TabbedPaneWrapper wrapper = (TabbedPaneWrapper)history.getComponent().getClientProperty("TabbedPaneWrapper");
-          final JBTabsImpl tabs = UIUtil.findComponentOfType(history.getComponent(), JBTabsImpl.class);
-          assert tabs != null;
-          boolean alreadyContainsHistory = false;
-          final VirtualFile file = myFileHistoryPanel.getVirtualFile();
-          for (TabInfo info : tabs.getTabs()) {
-            final FileHistoryPanelImpl panel = UIUtil.findComponentOfType(info.getComponent(), FileHistoryPanelImpl.class);
-            if (panel != null && file.equals(panel.getVirtualFile())) {
-              alreadyContainsHistory = true;
-              info.setComponent(myFileHistoryPanel);
-              tabs.select(info, true);
-              break;
-            }
-          }
-          if (!alreadyContainsHistory) {
-            wrapper.addTab(myPath.getName(), myFileHistoryPanel);
-            wrapper.setSelectedIndex(wrapper.getTabCount() - 1, true);
+          } else {
+            ((TabbedContent)history).addContent(myFileHistoryPanel, myFileHistoryPanel.getVirtualFile().getName(), true);
           }
         } else {
           Content content = ContentFactory.SERVICE.getInstance().createContent(myFileHistoryPanel, actionName, true);
index 7be1211ec70c0b09662d4267dfdd967551be2e2f..834d2c62ec5575565d15e6beebc811834608c8d9 100644 (file)
@@ -279,7 +279,7 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable {
   }
 
   private void commitNotFound(@NotNull String commitHash) {
-    if (getFilters().isEmpty()) {
+    if (myMainFrame.getFilterUi().getFilters().isEmpty()) {
       showMessage(MessageType.WARNING, "Commit " + commitHash + " not found");
     }
     else {
@@ -315,7 +315,7 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable {
   }
 
   public void applyFiltersAndUpdateUi() {
-    myFilterer.onFiltersChange(getFilters());
+    myFilterer.onFiltersChange(myMainFrame.getFilterUi().getFilters());
   }
 
   @NotNull
@@ -323,11 +323,6 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable {
     return myFilterer;
   }
 
-  @NotNull
-  public VcsLogFilterCollection getFilters() {
-    return myMainFrame.getFilterUi().getFilters();
-  }
-
   public VcsLogGraphTable getTable() {
     return myMainFrame.getGraphTable();
   }
index c70595bf7d1b87f03320c7b1cbc84568e0af9b1f..c66a075c387841f7e754ff2ec4416e1013fff38e 100644 (file)
@@ -32,19 +32,33 @@ import java.util.*;
 
 public class BranchFilterPopupComponent extends MultipleValueFilterPopupComponent<VcsLogBranchFilter> {
 
-  @NotNull private final VcsLogUiProperties myUiProperties;
+  public BranchFilterPopupComponent(@NotNull VcsLogUiProperties uiProperties,
+                                    @NotNull FilterModel<VcsLogBranchFilter> filterModel) {
+    super("Branch", uiProperties, filterModel);
+  }
 
-  @NotNull private VcsLogDataPack myDataPack;
+  @NotNull
+  @Override
+  protected String getText(@NotNull VcsLogBranchFilter filter) {
+    return displayableText(filter.getBranchNames());
+  }
 
-  public BranchFilterPopupComponent(@NotNull VcsLogClassicFilterUi filterUi, @NotNull VcsLogDataPack dataPack,
-                                    @NotNull VcsLogUiProperties uiProperties) {
-    super(filterUi, "Branch");
-    myDataPack = dataPack;
-    myUiProperties = uiProperties;
+  @Nullable
+  @Override
+  protected String getToolTip(@NotNull VcsLogBranchFilter filter) {
+    return tooltip(filter.getBranchNames());
   }
 
-  void updateDataPack(@NotNull VcsLogDataPack dataPack) {
-    myDataPack = dataPack;
+  @NotNull
+  @Override
+  protected VcsLogBranchFilter createFilter(@NotNull Collection<String> values) {
+    return new VcsLogBranchFilterImpl(values);
+  }
+
+  @Override
+  @NotNull
+  protected Collection<String> getValues(@Nullable VcsLogBranchFilter filter) {
+    return filter == null ? Collections.<String>emptySet() : filter.getBranchNames();
   }
 
   @Override
@@ -54,7 +68,7 @@ public class BranchFilterPopupComponent extends MultipleValueFilterPopupComponen
     actionGroup.add(createAllAction());
     actionGroup.add(createSelectMultipleValuesAction());
 
-    actionGroup.add(constructActionGroup(myDataPack, createRecentItemsActionGroup(), new Function<String, AnAction>() {
+    actionGroup.add(constructActionGroup(myFilterModel.getDataPack(), createRecentItemsActionGroup(), new Function<String, AnAction>() {
       @Override
       public AnAction fun(String name) {
         return createPredefinedValueAction(Collections.singleton(name));
@@ -151,15 +165,6 @@ public class BranchFilterPopupComponent extends MultipleValueFilterPopupComponen
     }
   }
 
-  @Nullable
-  @Override
-  protected VcsLogBranchFilter getFilter() {
-    if (getSelectedValues() == null) {
-      return null;
-    }
-    return new VcsLogBranchFilterImpl(getSelectedValues());
-  }
-
   @NotNull
   @Override
   protected List<List<String>> getRecentValuesFromSettings() {
@@ -176,7 +181,7 @@ public class BranchFilterPopupComponent extends MultipleValueFilterPopupComponen
   @NotNull
   @Override
   protected List<String> getAllValues() {
-    return ContainerUtil.map(myDataPack.getRefs().getBranches(), new Function<VcsRef, String>() {
+    return ContainerUtil.map(myFilterModel.getDataPack().getRefs().getBranches(), new Function<VcsRef, String>() {
       @Override
       public String fun(VcsRef ref) {
         return ref.getName();
index 855b9266f12a5a234ec455874ec4c7d28d7759b3..dc6c2c8b042c7b66eb5d9b5d5d4a0b92be7114a7 100644 (file)
@@ -22,6 +22,7 @@ import com.intellij.openapi.project.DumbAwareAction;
 import com.intellij.openapi.ui.DialogBuilder;
 import com.intellij.openapi.ui.DialogWrapper;
 import com.intellij.openapi.vcs.versionBrowser.DateFilterComponent;
+import com.intellij.util.ObjectUtils;
 import com.intellij.util.text.DateFormatUtil;
 import com.intellij.vcs.log.VcsLogDateFilter;
 import com.intellij.vcs.log.data.VcsLogDateFilterImpl;
@@ -33,11 +34,30 @@ import java.util.Date;
 
 class DateFilterPopupComponent extends FilterPopupComponent<VcsLogDateFilter> {
 
-  private Date myAfter;
-  private Date myBefore;
+  public DateFilterPopupComponent(FilterModel<VcsLogDateFilter> filterModel) {
+    super("Date", filterModel);
+  }
+
+  @NotNull
+  @Override
+  protected String getText(@NotNull VcsLogDateFilter filter) {
+    Date after = filter.getAfter();
+    Date before = filter.getBefore();
+    if (after != null && before != null) {
+      return DateFormatUtil.formatDate(after) + "-" + DateFormatUtil.formatDate(before);
+    }
+    else if (after != null) {
+      return "Since " + DateFormatUtil.formatDate(after);
+    }
+    else {
+      return "Until " + DateFormatUtil.formatDate(ObjectUtils.assertNotNull(before));
+    }
+  }
 
-  DateFilterPopupComponent(@NotNull VcsLogClassicFilterUi filterUi) {
-    super(filterUi, "Date");
+  @Nullable
+  @Override
+  protected String getToolTip(@NotNull VcsLogDateFilter filter) {
+    return null;
   }
 
   @Override
@@ -49,48 +69,24 @@ class DateFilterPopupComponent extends FilterPopupComponent<VcsLogDateFilter> {
     cal.add(Calendar.DAY_OF_YEAR, -6);
     Date oneWeekBefore = cal.getTime();
 
-    DumbAwareAction allAction = new DumbAwareAction(ALL) {
-      @Override
-      public void actionPerformed(AnActionEvent e) {
-        myAfter = null;
-        myBefore = null;
-        applyFilters();
-        setValue(ALL);
-      }
-    };
-    return new DefaultActionGroup(allAction,
+    return new DefaultActionGroup(createAllAction(),
                                   new DateAction(oneDayBefore, "Last 24 hours"),
                                   new DateAction(oneWeekBefore, "Last 7 days"),
                                   new SelectAction());
   }
 
-  @Nullable
-  @Override
-  protected VcsLogDateFilter getFilter() {
-    return myAfter == null && myBefore == null ? null : new VcsLogDateFilterImpl(myAfter, myBefore);
-  }
-
-  private void setOnlyAfter(Date after) {
-    myAfter = after;
-    myBefore = null;
-  }
-
   private class DateAction extends DumbAwareAction {
 
-    private final Date mySince;
-    private final String myText;
+    @NotNull private final Date mySince;
 
-    DateAction(Date since, String text) {
+    DateAction(@NotNull Date since, @NotNull String text) {
       super(text);
       mySince = since;
-      myText = text;
     }
 
     @Override
-    public void actionPerformed(AnActionEvent e) {
-      setValue(myText);
-      setOnlyAfter(mySince);
-      applyFilters();
+    public void actionPerformed(@NotNull AnActionEvent e) {
+      myFilterModel.setFilter(new VcsLogDateFilterImpl(mySince, null));
     }
   }
 
@@ -101,13 +97,16 @@ class DateFilterPopupComponent extends FilterPopupComponent<VcsLogDateFilter> {
     }
 
     @Override
-    public void actionPerformed(AnActionEvent e) {
+    public void actionPerformed(@NotNull AnActionEvent e) {
       final DateFilterComponent dateComponent = new DateFilterComponent(false, DateFormatUtil.getDateFormat().getDelegate());
-      if (myBefore != null) {
-        dateComponent.setBefore(myBefore.getTime());
-      }
-      if (myAfter != null) {
-        dateComponent.setAfter(myAfter.getTime());
+      VcsLogDateFilter currentFilter = myFilterModel.getFilter();
+      if (currentFilter != null) {
+        if (currentFilter.getBefore() != null) {
+          dateComponent.setBefore(currentFilter.getBefore().getTime());
+        }
+        if (currentFilter.getAfter() != null) {
+          dateComponent.setAfter(currentFilter.getAfter().getTime());
+        }
       }
 
       DialogBuilder db = new DialogBuilder(DateFilterPopupComponent.this);
@@ -118,20 +117,8 @@ class DateFilterPopupComponent extends FilterPopupComponent<VcsLogDateFilter> {
       if (DialogWrapper.OK_EXIT_CODE == db.show()) {
         long after = dateComponent.getAfter();
         long before = dateComponent.getBefore();
-        myAfter = after > 0 ? new Date(after) : null;
-        myBefore = before > 0 ? new Date(before) : null;
-
-        if (myAfter != null && myBefore != null) {
-          setValue(DateFormatUtil.formatDate(after) + "-" + DateFormatUtil.formatDate(before));
-        }
-        else if (myAfter != null) {
-          setValue("After " + DateFormatUtil.formatDate(after));
-        }
-        else {
-          setValue("Before " + DateFormatUtil.formatDate(before));
-        }
-
-        applyFilters();
+        VcsLogDateFilter filter = new VcsLogDateFilterImpl(after > 0 ? new Date(after) : null, before > 0 ? new Date(before) : null);
+        myFilterModel.setFilter(filter);
       }
     }
   }
diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/FilterModel.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/FilterModel.java
new file mode 100644 (file)
index 0000000..4d32cea
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.vcs.log.ui.filter;
+
+import com.intellij.openapi.util.Computable;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.vcs.log.VcsLogDataPack;
+import com.intellij.vcs.log.VcsLogFilter;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+
+class FilterModel<Filter extends VcsLogFilter> {
+
+  @NotNull private final Computable<VcsLogDataPack> myDataPackProvider;
+  @NotNull private final Collection<Runnable> mySetFilterListeners = ContainerUtil.newArrayList();
+
+  @Nullable private Filter myFilter;
+
+  FilterModel(@NotNull Computable<VcsLogDataPack> provider) {
+    myDataPackProvider = provider;
+  }
+
+  void setFilter(@Nullable Filter filter) {
+    myFilter = filter;
+    for (Runnable listener : mySetFilterListeners) {
+      listener.run();
+    }
+  }
+
+  @Nullable
+  Filter getFilter() {
+    return myFilter;
+  }
+
+  @NotNull
+  VcsLogDataPack getDataPack() {
+    return myDataPackProvider.compute();
+  }
+
+  void addSetFilterListener(@NotNull Runnable runnable) {
+    mySetFilterListeners.add(runnable);
+  }
+
+}
index c30ebb546af04b5f6c54f7131274d9c5780848c6..00283950b9c261f9768281662fd0b89b4b68a084 100644 (file)
@@ -52,12 +52,19 @@ abstract class FilterPopupComponent<Filter extends VcsLogFilter> extends JPanel
 
   @NotNull private final JLabel myFilterNameLabel;
   @NotNull private final JLabel myFilterValueLabel;
-  @NotNull private final VcsLogClassicFilterUi myFilterUi;
+  @NotNull protected final FilterModel<Filter> myFilterModel;
 
-  FilterPopupComponent(@NotNull VcsLogClassicFilterUi filterUi, @NotNull String filterName) {
-    myFilterUi = filterUi;
+  FilterPopupComponent(@NotNull String filterName,
+                       @NotNull FilterModel<Filter> filterModel) {
+    myFilterModel = filterModel;
     myFilterNameLabel = new JLabel(filterName + ": ");
-    myFilterValueLabel = new JLabel(ALL);
+    myFilterValueLabel = new JLabel() {
+      @Override
+      public String getText() {
+        Filter filter = myFilterModel.getFilter();
+        return filter == null ? ALL : FilterPopupComponent.this.getText(filter);
+      }
+    };
     setDefaultForeground();
     setFocusable(true);
     setBorder(UNFOCUSED_BORDER);
@@ -68,12 +75,35 @@ abstract class FilterPopupComponent<Filter extends VcsLogFilter> extends JPanel
     add(Box.createHorizontalStrut(GAP_BEFORE_ARROW));
     add(new JLabel(AllIcons.Ide.Statusbar_arrows));
 
+    updateLabelOnFilterChange();
     showPopupMenuOnClick();
     showPopupMenuFromKeyboard();
     indicateHovering();
     indicateFocusing();
   }
 
+  private void updateLabelOnFilterChange() {
+    myFilterModel.addSetFilterListener(new Runnable() {
+      @Override
+      public void run() {
+        myFilterValueLabel.revalidate();
+        myFilterValueLabel.repaint();
+      }
+    });
+  }
+
+  @NotNull
+  protected abstract String getText(@NotNull Filter filter);
+
+  @Nullable
+  protected abstract String getToolTip(@NotNull Filter filter);
+
+  @Override
+  public String getToolTipText() {
+    Filter filter = myFilterModel.getFilter();
+    return filter == null ? null : getToolTip(filter);
+  }
+
   private static Border createFocusedBorder() {
     return BorderFactory.createCompoundBorder(new RoundedLineBorder(UIUtil.getHeaderActiveColor(), 10, BORDER_SIZE),
                                               INNER_MARGIN_BORDER);
@@ -84,52 +114,29 @@ abstract class FilterPopupComponent<Filter extends VcsLogFilter> extends JPanel
                                               INNER_MARGIN_BORDER);
   }
 
+
   /**
    * Create popup actions available under this filter.
    */
   protected abstract ActionGroup createActionGroup();
 
-  /**
-   * Return the filter currently selected via this component or null if no filters are selected via this component.
-   */
-  @Nullable
-  protected abstract Filter getFilter();
-
-  protected void setValue(@NotNull String newValue) {
-    setValue(newValue, newValue);
-  }
-
-  protected void setValue(@NotNull String value, @NotNull String tooltip) {
-    myFilterValueLabel.setText(value);
-    setToolTipText(tooltip.equals(value) ? null : tooltip);
-  }
-
-  @NotNull
-  public String getValue() {
-    return myFilterValueLabel.getText();
-  }
-
-  protected void applyFilters() {
-    myFilterUi.applyFilters();
-  }
-
   /**
    * Returns the special action that indicates that no filtering is selected in this component.
    */
   @NotNull
   protected AnAction createAllAction() {
-    return new AllAction(this);
+    return new AllAction();
   }
 
   private void indicateFocusing() {
     addFocusListener(new FocusAdapter() {
       @Override
-      public void focusGained(FocusEvent e) {
+      public void focusGained(@NotNull FocusEvent e) {
         setBorder(FOCUSED_BORDER);
       }
 
       @Override
-      public void focusLost(FocusEvent e) {
+      public void focusLost(@NotNull FocusEvent e) {
         setBorder(UNFOCUSED_BORDER);
       }
     });
@@ -138,7 +145,7 @@ abstract class FilterPopupComponent<Filter extends VcsLogFilter> extends JPanel
   private void showPopupMenuFromKeyboard() {
     addKeyListener(new KeyAdapter() {
       @Override
-      public void keyPressed(KeyEvent e) {
+      public void keyPressed(@NotNull KeyEvent e) {
         if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_DOWN) {
           showPopupMenu();
         }
@@ -159,12 +166,12 @@ abstract class FilterPopupComponent<Filter extends VcsLogFilter> extends JPanel
   private void indicateHovering() {
     addMouseListener(new MouseAdapter() {
       @Override
-      public void mouseEntered(MouseEvent e) {
+      public void mouseEntered(@NotNull MouseEvent e) {
         setOnHoverForeground();
       }
 
       @Override
-      public void mouseExited(MouseEvent e) {
+      public void mouseExited(@NotNull MouseEvent e) {
         setDefaultForeground();
       }
     });
@@ -188,28 +195,15 @@ abstract class FilterPopupComponent<Filter extends VcsLogFilter> extends JPanel
     popup.showUnderneathOf(this);
   }
 
-  private static class AllAction extends SetValueAction {
-    AllAction(@NotNull FilterPopupComponent component) {
-      super(ALL, component);
-    }
-  }
-
-  protected static class SetValueAction extends DumbAwareAction {
+  private class AllAction extends DumbAwareAction {
 
-    private final String myValue;
-    private final FilterPopupComponent myFilterComponent;
-
-    SetValueAction(String value, FilterPopupComponent filterComponent) {
-      super(value);
-      myValue = value;
-      myFilterComponent = filterComponent;
+    AllAction() {
+      super(ALL);
     }
 
     @Override
-    public void actionPerformed(AnActionEvent e) {
-      myFilterComponent.setValue(myValue);
-      myFilterComponent.applyFilters();
+    public void actionPerformed(@NotNull AnActionEvent e) {
+      myFilterModel.setFilter(null);
     }
   }
-
 }
index eab6cbc6f4444600b2292c51edd060e6110b98c0..24a8abc7c52de10a3dcf704c1ec79c11f6557cc3 100644 (file)
@@ -84,7 +84,7 @@ class MultilinePopupBuilder {
     popup.setMinimumSize(new Dimension(200, 90));
     AnAction okAction = new DumbAwareAction() {
       @Override
-      public void actionPerformed(AnActionEvent e) {
+      public void actionPerformed(@NotNull AnActionEvent e) {
         unregisterCustomShortcutSet(popup.getContent());
         popup.closeOk(e.getInputEvent());
       }
@@ -131,6 +131,7 @@ class MultilinePopupBuilder {
       return lastPosition;
     }
 
+    @SuppressWarnings("StringToUpperCaseOrToLowerCaseWithoutLocale")
     @Override
     protected void addCompletionVariants(@NotNull String text, int offset, @NotNull String prefix,
                                          @NotNull CompletionResultSet result) {
index 40bb39d2ec835c256137e5deea4ededddf083684..2f5b520d698e0e1c4e2e2cbec9b49ef290a79443 100644 (file)
@@ -26,6 +26,7 @@ import com.intellij.openapi.ui.popup.JBPopupAdapter;
 import com.intellij.openapi.ui.popup.LightweightWindowEvent;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.vcs.log.VcsLogFilter;
+import com.intellij.vcs.log.data.VcsLogUiProperties;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -36,12 +37,18 @@ abstract class MultipleValueFilterPopupComponent<Filter extends VcsLogFilter> ex
 
   private static final int MAX_FILTER_VALUE_LENGTH = 30;
 
-  @Nullable private Collection<String> mySelectedValues;
+  @NotNull protected final VcsLogUiProperties myUiProperties;
 
-  MultipleValueFilterPopupComponent(@NotNull VcsLogClassicFilterUi filterUi, @NotNull String filterName) {
-    super(filterUi, filterName);
+  MultipleValueFilterPopupComponent(@NotNull String filterName,
+                                    @NotNull VcsLogUiProperties uiProperties,
+                                    @NotNull FilterModel<Filter> filterModel) {
+    super(filterName, filterModel);
+    myUiProperties = uiProperties;
   }
 
+  @NotNull
+  protected abstract Collection<String> getValues(@Nullable Filter filter);
+
   @NotNull
   protected abstract List<List<String>> getRecentValuesFromSettings();
 
@@ -50,10 +57,8 @@ abstract class MultipleValueFilterPopupComponent<Filter extends VcsLogFilter> ex
   @NotNull
   protected abstract List<String> getAllValues();
 
-  @Nullable
-  protected Collection<String> getSelectedValues() {
-    return mySelectedValues;
-  }
+  @NotNull
+  protected abstract Filter createFilter(@NotNull Collection<String> values);
 
   @NotNull
   protected ActionGroup createRecentItemsActionGroup() {
@@ -69,15 +74,6 @@ abstract class MultipleValueFilterPopupComponent<Filter extends VcsLogFilter> ex
     return group;
   }
 
-  void apply(@Nullable Collection<String> values, @NotNull String text, @NotNull String tooltip) {
-    mySelectedValues = values;
-    applyFilters();
-    setValue(text, tooltip);
-    if (values != null) {
-      rememberValuesInSettings(values);
-    }
-  }
-
   @NotNull
   static String displayableText(@NotNull Collection<String> values) {
     if (values.size() == 1) {
@@ -91,17 +87,6 @@ abstract class MultipleValueFilterPopupComponent<Filter extends VcsLogFilter> ex
     return StringUtil.join(values, ", ");
   }
 
-  @Override
-  @NotNull
-  protected AnAction createAllAction() {
-    return new DumbAwareAction(ALL) {
-      @Override
-      public void actionPerformed(AnActionEvent e) {
-        apply(null, ALL, ALL);
-      }
-    };
-  }
-
   @NotNull
   protected AnAction createPredefinedValueAction(@NotNull Collection<String> values) {
     return new PredefinedValueAction(values);
@@ -122,8 +107,9 @@ abstract class MultipleValueFilterPopupComponent<Filter extends VcsLogFilter> ex
     }
 
     @Override
-    public void actionPerformed(AnActionEvent e) {
-      apply(myValues, displayableText(myValues), tooltip(myValues));
+    public void actionPerformed(@NotNull AnActionEvent e) {
+      myFilterModel.setFilter(createFilter(myValues));
+      rememberValuesInSettings(myValues);
     }
   }
 
@@ -137,13 +123,14 @@ abstract class MultipleValueFilterPopupComponent<Filter extends VcsLogFilter> ex
     }
 
     @Override
-    public void actionPerformed(AnActionEvent e) {
+    public void actionPerformed(@NotNull AnActionEvent e) {
       Project project = e.getProject();
       if (project == null) {
         return;
       }
 
-      final MultilinePopupBuilder popupBuilder = new MultilinePopupBuilder(project, myVariants, getPopupText(mySelectedValues));
+      Filter filter = myFilterModel.getFilter();
+      final MultilinePopupBuilder popupBuilder = new MultilinePopupBuilder(project, myVariants, getPopupText(getValues(filter)));
       JBPopup popup = popupBuilder.createPopup();
       popup.addListener(new JBPopupAdapter() {
         @Override
@@ -151,10 +138,11 @@ abstract class MultipleValueFilterPopupComponent<Filter extends VcsLogFilter> ex
           if (event.isOk()) {
             Collection<String> selectedValues = popupBuilder.getSelectedValues();
             if (selectedValues.isEmpty()) {
-              apply(null, ALL, ALL);
+              myFilterModel.setFilter(null);
             }
             else {
-              apply(selectedValues, displayableText(selectedValues), tooltip(selectedValues));
+              myFilterModel.setFilter(createFilter(selectedValues));
+              rememberValuesInSettings(selectedValues);
             }
           }
         }
index 3140d647795f3fb1008069db55df4fc9bd761161..b4d90e8ca94a3c00a3c4d8c3d829a041c58779f1 100644 (file)
@@ -17,6 +17,7 @@ package com.intellij.vcs.log.ui.filter;
 
 import com.intellij.openapi.actionSystem.ActionGroup;
 import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
 import com.intellij.openapi.actionSystem.DefaultActionGroup;
 import com.intellij.openapi.project.DumbAwareAction;
 import com.intellij.openapi.project.Project;
@@ -24,54 +25,61 @@ import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.util.Function;
 import com.intellij.util.containers.ContainerUtil;
+import com.intellij.vcs.log.VcsLogDataPack;
 import com.intellij.vcs.log.VcsLogStructureFilter;
 import com.intellij.vcs.log.data.VcsLogStructureFilterImpl;
 import com.intellij.vcs.log.ui.VcsStructureChooser;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
+import java.util.*;
 
 class StructureFilterPopupComponent extends FilterPopupComponent<VcsLogStructureFilter> {
 
-  public static final int FILTER_LABEL_LENGTH = 20;
-  @NotNull private final Collection<VirtualFile> myRoots;
-  @NotNull private final Collection<VirtualFile> myFiles;
+  private static final int FILTER_LABEL_LENGTH = 20;
 
-  public StructureFilterPopupComponent(@NotNull VcsLogClassicFilterUi filterUi, @NotNull Collection<VirtualFile> roots) {
-    super(filterUi, "Structure");
-    myRoots = roots;
-    myFiles = ContainerUtil.newArrayList();
+  public StructureFilterPopupComponent(@NotNull FilterModel<VcsLogStructureFilter> filterModel) {
+    super("Structure", filterModel);
   }
 
+  @NotNull
   @Override
-  protected ActionGroup createActionGroup() {
-    return new DefaultActionGroup(createAllAction(), new SelectAction());
-  }
-
-  @Nullable
-  @Override
-  protected VcsLogStructureFilter getFilter() {
-    return getValue() == ALL || myFiles.isEmpty() ? null : new VcsLogStructureFilterImpl(myFiles, myRoots);
-  }
-
-  private void setValue(@NotNull Collection<VirtualFile> files) {
+  protected String getText(@NotNull VcsLogStructureFilter filter) {
+    Collection<VirtualFile> files = getAllFiles(myFilterModel.getDataPack(), filter);
     if (files.size() == 0) {
-      setValue(ALL);
+      return ALL;
     }
     else if (files.size() == 1) {
       VirtualFile file = files.iterator().next();
-      setValue(StringUtil.shortenPathWithEllipsis(file.getPresentableUrl(), FILTER_LABEL_LENGTH), getTooltip(files));
+      return StringUtil.shortenPathWithEllipsis(file.getPresentableUrl(), FILTER_LABEL_LENGTH);
     }
     else {
-      setValue(files.size() + " items", getTooltip(files));
+      return files.size() + " items";
+    }
+  }
+
+  @Nullable
+  @Override
+  protected String getToolTip(@NotNull VcsLogStructureFilter filter) {
+    return getToolTip(getAllFiles(myFilterModel.getDataPack(), filter));
+  }
+
+  @NotNull
+  private static Collection<VirtualFile> getAllFiles(@NotNull VcsLogDataPack dataPack, @NotNull VcsLogStructureFilter filter) {
+    Collection<VirtualFile> result = ContainerUtil.newArrayList();
+    for (VirtualFile root : dataPack.getLogProviders().keySet()) {
+      result.addAll(filter.getFiles(root));
     }
+    return result;
+  }
+
+  @Override
+  protected ActionGroup createActionGroup() {
+    return new DefaultActionGroup(createAllAction(), new SelectAction());
   }
 
   @NotNull
-  private static String getTooltip(@NotNull Collection<VirtualFile> files) {
+  private static String getToolTip(@NotNull Collection<VirtualFile> files) {
     List<VirtualFile> filesToDisplay = new ArrayList<VirtualFile>(files);
     if (files.size() > 10) {
       filesToDisplay = filesToDisplay.subList(0, 10);
@@ -94,19 +102,17 @@ class StructureFilterPopupComponent extends FilterPopupComponent<VcsLogStructure
     }
 
     @Override
-    public void actionPerformed(AnActionEvent e) {
-      Project project = e.getProject();
-      assert project != null;
-      VcsStructureChooser chooser = new VcsStructureChooser(project, "Select Files or Folders to Filter", myFiles,
-                                                            new ArrayList<VirtualFile>(myRoots));
+    public void actionPerformed(@NotNull AnActionEvent e) {
+      Project project = e.getRequiredData(CommonDataKeys.PROJECT);
+      VcsLogDataPack dataPack = myFilterModel.getDataPack();
+      VcsLogStructureFilter filter = myFilterModel.getFilter();
+      Collection<VirtualFile> files = filter == null ? Collections.<VirtualFile>emptySet() : getAllFiles(dataPack, filter);
+      Set<VirtualFile> roots = dataPack.getLogProviders().keySet();
+      VcsStructureChooser chooser = new VcsStructureChooser(project, "Select Files or Folders to Filter", files,
+                                                            new ArrayList<VirtualFile>(roots));
       if (chooser.showAndGet()) {
-        myFiles.clear();
-        myFiles.addAll(chooser.getSelectedFiles());
-        setValue(myFiles);
-        applyFilters();
+        myFilterModel.setFilter(new VcsLogStructureFilterImpl(chooser.getSelectedFiles(), roots));
       }
     }
-
   }
-
 }
index 368b426efc1080db212d004251464d74a4f7c74a..8451e549c7fc2f708a4a75099b19e5092fe4f948 100644 (file)
@@ -38,16 +38,29 @@ class UserFilterPopupComponent extends MultipleValueFilterPopupComponent<VcsLogU
 
   private static final String ME = "me";
 
-  private final VcsLogDataHolder myDataHolder;
-  private final VcsLogUiProperties myUiProperties;
+  @NotNull private final VcsLogDataHolder myDataHolder;
+  @NotNull private final VcsLogUiProperties myUiProperties;
 
-  UserFilterPopupComponent(@NotNull VcsLogClassicFilterUi filterUi, @NotNull VcsLogDataHolder dataHolder,
-                           @NotNull VcsLogUiProperties uiProperties) {
-    super(filterUi, "User");
+  UserFilterPopupComponent(@NotNull VcsLogUiProperties uiProperties,
+                           @NotNull VcsLogDataHolder dataHolder,
+                           @NotNull FilterModel<VcsLogUserFilter> filterModel) {
+    super("User", uiProperties, filterModel);
     myDataHolder = dataHolder;
     myUiProperties = uiProperties;
   }
 
+  @NotNull
+  @Override
+  protected String getText(@NotNull VcsLogUserFilter filter) {
+    return displayableText(getValues(filter));
+  }
+
+  @Nullable
+  @Override
+  protected String getToolTip(@NotNull VcsLogUserFilter filter) {
+    return tooltip(getValues(filter));
+  }
+
   @Override
   protected ActionGroup createActionGroup() {
     DefaultActionGroup group = new DefaultActionGroup();
@@ -62,17 +75,21 @@ class UserFilterPopupComponent extends MultipleValueFilterPopupComponent<VcsLogU
 
   @NotNull
   @Override
-  protected List<List<String>> getRecentValuesFromSettings() {
-    return myUiProperties.getRecentlyFilteredUserGroups();
+  protected Collection<String> getValues(@Nullable VcsLogUserFilter filter) {
+    if (filter == null) {
+      return Collections.emptySet();
+    }
+    Set<String> result = ContainerUtil.newHashSet();
+    for (VirtualFile root : myFilterModel.getDataPack().getLogProviders().keySet()) {
+      result.addAll(filter.getUserNames(root));
+    }
+    return result;
   }
 
-  @Nullable
+  @NotNull
   @Override
-  protected VcsLogUserFilter getFilter() {
-    if (getSelectedValues() == null) {
-      return null;
-    }
-    return new VcsLogUserFilterImpl(getSelectedValues(), myDataHolder.getCurrentUser());
+  protected List<List<String>> getRecentValuesFromSettings() {
+    return myUiProperties.getRecentlyFilteredUserGroups();
   }
 
   @Override
@@ -91,6 +108,12 @@ class UserFilterPopupComponent extends MultipleValueFilterPopupComponent<VcsLogU
     });
   }
 
+  @NotNull
+  @Override
+  protected VcsLogUserFilter createFilter(@NotNull Collection<String> values) {
+    return new VcsLogUserFilterImpl(values, myDataHolder.getCurrentUser());
+  }
+
   private static class VcsLogUserFilterImpl implements VcsLogUserFilter {
 
     @NotNull private final Collection<String> myUsers;
@@ -119,6 +142,7 @@ class UserFilterPopupComponent extends MultipleValueFilterPopupComponent<VcsLogU
     @Override
     public boolean matches(@NotNull final VcsCommitMetadata commit) {
       return ContainerUtil.exists(getUserNames(commit.getRoot()), new Condition<String>() {
+        @SuppressWarnings("StringToUpperCaseOrToLowerCaseWithoutLocale")
         @Override
         public boolean value(String user) {
           String lowerUser = user.toLowerCase();
index 8bc71682ad7083064690c61aa56e6bc81cb66425..44a1d2508338430258d406db77edf01d19064ea1 100644 (file)
@@ -20,11 +20,13 @@ import com.intellij.openapi.actionSystem.AnActionEvent;
 import com.intellij.openapi.actionSystem.DefaultActionGroup;
 import com.intellij.openapi.actionSystem.Presentation;
 import com.intellij.openapi.actionSystem.ex.CustomComponentAction;
-import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.NotNullComputable;
 import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.ui.SearchTextField;
+import com.intellij.ui.DocumentAdapter;
 import com.intellij.ui.SearchTextFieldWithStoredHistory;
 import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.ui.UIUtil;
@@ -35,91 +37,129 @@ import com.intellij.vcs.log.impl.VcsLogFilterCollectionImpl;
 import com.intellij.vcs.log.impl.VcsLogHashFilterImpl;
 import com.intellij.vcs.log.ui.VcsLogUiImpl;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import java.awt.*;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
-import java.util.Arrays;
-import java.util.Collection;
 import java.util.List;
 
 /**
  */
 public class VcsLogClassicFilterUi implements VcsLogFilterUi {
 
-  private static final Logger LOG = Logger.getInstance(VcsLogClassicFilterUi.class);
   private static final String HASH_PATTERN = "[a-fA-F0-9]{7,}";
 
-  @NotNull private final SearchTextField myTextFilter;
   @NotNull private final VcsLogUiImpl myUi;
-  @NotNull private final DefaultActionGroup myActionGroup;
 
-  @NotNull private final BranchFilterPopupComponent myBranchFilterComponent;
-  @NotNull private final UserFilterPopupComponent myUserFilterComponent;
-  @NotNull private final DateFilterPopupComponent myDateFilterComponent;
-  @NotNull private final StructureFilterPopupComponent myStructureFilterComponent;
+  @NotNull private final VcsLogDataHolder myLogDataHolder;
+  @NotNull private final VcsLogUiProperties myUiProperties;
 
-  public VcsLogClassicFilterUi(@NotNull VcsLogUiImpl ui, @NotNull VcsLogDataHolder logDataHolder, @NotNull VcsLogUiProperties uiProperties,
+  @NotNull private VcsLogDataPack myDataPack;
+
+  @NotNull private final FilterModel<VcsLogBranchFilter> myBranchFilterModel;
+  @NotNull private final FilterModel<VcsLogUserFilter> myUserFilterModel;
+  @NotNull private final FilterModel<VcsLogDateFilter> myDateFilterModel;
+  @NotNull private final FilterModel<VcsLogStructureFilter> myStructureFilterModel;
+  @NotNull private final FilterModel<VcsLogTextFilter> myTextFilterModel;
+
+  public VcsLogClassicFilterUi(@NotNull VcsLogUiImpl ui,
+                               @NotNull VcsLogDataHolder logDataHolder,
+                               @NotNull VcsLogUiProperties uiProperties,
                                @NotNull VcsLogDataPack initialDataPack) {
     myUi = ui;
+    myLogDataHolder = logDataHolder;
+    myUiProperties = uiProperties;
+    myDataPack = initialDataPack;
 
-    myTextFilter = new SearchTextFieldWithStoredHistory("Vcs.Log.Text.Filter.History") {
+    NotNullComputable<VcsLogDataPack> dataPackGetter = new NotNullComputable<VcsLogDataPack>() {
+      @NotNull
       @Override
-      protected void onFieldCleared() {
-        applyFilters();
+      public VcsLogDataPack compute() {
+        return myDataPack;
       }
     };
-    myTextFilter.getTextEditor().addActionListener(new ActionListener() {
-      @Override
-      public void actionPerformed(ActionEvent e) {
-        applyFilters();
-        myTextFilter.addCurrentTextToHistory();
-      }
-    });
-
-    myBranchFilterComponent = new BranchFilterPopupComponent(this, initialDataPack, uiProperties);
-    myUserFilterComponent = new UserFilterPopupComponent(this, logDataHolder, uiProperties);
-    myDateFilterComponent  = new DateFilterPopupComponent(this);
-    myStructureFilterComponent = new StructureFilterPopupComponent(this, logDataHolder.getRoots());
-
-    myActionGroup = new DefaultActionGroup();
-    myActionGroup.add(new TextFilterComponent(myTextFilter));
-    myActionGroup.add(new FilterActionComponent(myBranchFilterComponent));
-    myActionGroup.add(new FilterActionComponent(myUserFilterComponent));
-    myActionGroup.add(new FilterActionComponent(myDateFilterComponent));
-    myActionGroup.add(new FilterActionComponent(myStructureFilterComponent));
+    myBranchFilterModel = new FilterModel<VcsLogBranchFilter>(dataPackGetter);
+    myUserFilterModel = new FilterModel<VcsLogUserFilter>(dataPackGetter);
+    myDateFilterModel = new FilterModel<VcsLogDateFilter>(dataPackGetter);
+    myStructureFilterModel = new FilterModel<VcsLogStructureFilter>(dataPackGetter);
+    myTextFilterModel = new FilterModel<VcsLogTextFilter>(dataPackGetter);
+
+    updateUiOnFilterChange();
+  }
+
+  private void updateUiOnFilterChange() {
+    FilterModel[] models = {myBranchFilterModel, myUserFilterModel, myDateFilterModel, myStructureFilterModel, myTextFilterModel};
+    for (FilterModel<?> model : models) {
+      model.addSetFilterListener(new Runnable() {
+        @Override
+        public void run() {
+          myUi.applyFiltersAndUpdateUi();
+        }
+      });
+    }
   }
 
   public void updateDataPack(@NotNull VcsLogDataPack dataPack) {
-    myBranchFilterComponent.updateDataPack(dataPack);
+    myDataPack = dataPack;
   }
 
   /**
    * Returns filter components which will be added to the Log toolbar.
    */
   @NotNull
-  public ActionGroup getActionGroup() {
-    return myActionGroup;
-  }
-
-  @NotNull
-  public List<JComponent> getComponents() {
-    return Arrays.<JComponent>asList(myTextFilter.getTextEditor(), myBranchFilterComponent, myUserFilterComponent,
-                                     myDateFilterComponent, myStructureFilterComponent);
+  public ActionGroup createActionGroup() {
+    DefaultActionGroup actionGroup = new DefaultActionGroup();
+    actionGroup.add(new TextFilterComponent(myTextFilterModel));
+    actionGroup.add(new FilterActionComponent(new Computable<JComponent>() {
+      @Override
+      public JComponent compute() {
+        return new BranchFilterPopupComponent(myUiProperties, myBranchFilterModel);
+      }
+    }));
+    actionGroup.add(new FilterActionComponent(new Computable<JComponent>() {
+      @Override
+      public JComponent compute() {
+        return new UserFilterPopupComponent(myUiProperties, myLogDataHolder, myUserFilterModel);
+      }
+    }));
+    actionGroup.add(new FilterActionComponent(new Computable<JComponent>() {
+      @Override
+      public JComponent compute() {
+        return new DateFilterPopupComponent(myDateFilterModel);
+      }
+    }));
+    actionGroup.add(new FilterActionComponent(new Computable<JComponent>() {
+      @Override
+      public JComponent compute() {
+        return new StructureFilterPopupComponent(myStructureFilterModel);
+      }
+    }));
+    return actionGroup;
   }
 
   @NotNull
   @Override
   public VcsLogFilterCollection getFilters() {
-    Pair<VcsLogTextFilter, VcsLogHashFilter> filtersFromText = getFiltersFromTextArea(myTextFilter.getText().trim());
-    return new VcsLogFilterCollectionImpl(myBranchFilterComponent.getFilter(), myUserFilterComponent.getFilter(),
-                                          filtersFromText.second, myDateFilterComponent.getFilter(),
-                                          filtersFromText.first, myStructureFilterComponent.getFilter());
+    ApplicationManager.getApplication().assertIsDispatchThread();
+    Pair<VcsLogTextFilter, VcsLogHashFilter> filtersFromText = getFiltersFromTextArea(myTextFilterModel.getFilter());
+    return new VcsLogFilterCollectionImpl(myBranchFilterModel.getFilter(),
+                                          myUserFilterModel.getFilter(),
+                                          filtersFromText.second,
+                                          myDateFilterModel.getFilter(),
+                                          filtersFromText.first,
+                                          myStructureFilterModel.getFilter());
   }
 
   @NotNull
-  private static Pair<VcsLogTextFilter, VcsLogHashFilter> getFiltersFromTextArea(@NotNull String text) {
-    if (text.isEmpty()) {
+  private static Pair<VcsLogTextFilter, VcsLogHashFilter> getFiltersFromTextArea(@Nullable VcsLogTextFilter filter) {
+    if (filter == null) {
+      return Pair.empty();
+    }
+    String text = filter.getText().trim();
+    if (StringUtil.isEmptyOrSpaces(text)) {
       return Pair.empty();
     }
     List<String> hashes = ContainerUtil.newArrayList();
@@ -147,23 +187,21 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi {
 
   @Override
   public void setFilter(@NotNull VcsLogFilter filter) {
+    ApplicationManager.getApplication().assertIsDispatchThread();
     if (filter instanceof VcsLogBranchFilter) {
-      Collection<String> values = ((VcsLogBranchFilter)filter).getBranchNames();
-      myBranchFilterComponent.apply(values, MultipleValueFilterPopupComponent.displayableText(values),
-                                    MultipleValueFilterPopupComponent.tooltip(values));
+      myBranchFilterModel.setFilter((VcsLogBranchFilter)filter);
+      JComponent toolbar = myUi.getMainFrame().getToolbar();
+      toolbar.revalidate();
+      toolbar.repaint();
     }
   }
 
-  void applyFilters() {
-    myUi.applyFiltersAndUpdateUi();
-  }
-
   private static class TextFilterComponent extends DumbAwareAction implements CustomComponentAction {
 
-    private final SearchTextField mySearchField;
+    private final FilterModel<VcsLogTextFilter> myFilterModel;
 
-    TextFilterComponent(SearchTextField searchField) {
-      mySearchField = searchField;
+    public TextFilterComponent(FilterModel<VcsLogTextFilter> filterModel) {
+      myFilterModel = filterModel;
     }
 
     @Override
@@ -172,30 +210,59 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi {
       JLabel filterCaption = new JLabel("Filter:");
       filterCaption.setForeground(UIUtil.isUnderDarcula() ? UIUtil.getLabelForeground() : UIUtil.getInactiveTextColor());
       panel.add(filterCaption);
-      panel.add(mySearchField);
+      panel.add(createSearchField());
       return panel;
     }
 
+    private Component createSearchField() {
+      final SearchTextFieldWithStoredHistory textFilter = new SearchTextFieldWithStoredHistory("Vcs.Log.Text.Filter.History") {
+        @Override
+        protected void onFieldCleared() {
+          myFilterModel.setFilter(null);
+        }
+      };
+      textFilter.getTextEditor().addActionListener(new ActionListener() {
+        @Override
+        public void actionPerformed(@NotNull ActionEvent e) {
+          myFilterModel.setFilter(new VcsLogTextFilterImpl(textFilter.getText()));
+          textFilter.addCurrentTextToHistory();
+        }
+      });
+      resetFilterOnTextClear(textFilter);
+      return textFilter;
+    }
+
+    private void resetFilterOnTextClear(@NotNull SearchTextFieldWithStoredHistory textFilter) {
+      textFilter.addDocumentListener(new DocumentAdapter() {
+        @Override
+        protected void textChanged(DocumentEvent e) {
+          if (e.getDocument().getLength() == 0) {
+            myFilterModel.setFilter(null);
+          }
+        }
+      });
+    }
+
     @Override
-    public void actionPerformed(AnActionEvent e) {
+    public void actionPerformed(@NotNull AnActionEvent e) {
     }
   }
 
   private static class FilterActionComponent extends DumbAwareAction implements CustomComponentAction {
-    private final FilterPopupComponent myComponent;
 
-    public FilterActionComponent(FilterPopupComponent component) {
-      myComponent = component;
+    @NotNull private final Computable<JComponent> myComponentCreator;
+
+    public FilterActionComponent(@NotNull Computable<JComponent> componentCreator) {
+      myComponentCreator = componentCreator;
     }
 
     @Override
     public JComponent createCustomComponent(Presentation presentation) {
-      return myComponent;
+      return myComponentCreator.compute();
     }
 
     @Override
-    public void actionPerformed(AnActionEvent e) {
+    public void actionPerformed(@NotNull AnActionEvent e) {
     }
   }
-
 }
index 45f05ad6646a82dc2b2ef54e484a588ad6cbb6fd..484e7c4b4ad3690062bb6eedf0059891fc21a6c7 100644 (file)
@@ -215,7 +215,7 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider {
     toolbarGroup.add(ActionManager.getInstance().getAction(VcsLogUiImpl.TOOLBAR_ACTION_GROUP));
 
     DefaultActionGroup mainGroup = new DefaultActionGroup();
-    mainGroup.add(myFilterUi.getActionGroup());
+    mainGroup.add(myFilterUi.createActionGroup());
     mainGroup.addSeparator();
     mainGroup.add(toolbarGroup);
     ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.CHANGES_VIEW_TOOLBAR, mainGroup, true);
@@ -264,7 +264,8 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider {
     }
   }
 
-  public Component getToolbar() {
+  @NotNull
+  public JComponent getToolbar() {
     return myToolbar;
   }
 
@@ -382,8 +383,7 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider {
     @NotNull
     @Override
     protected List<Component> getOrderedComponents() {
-      return ContainerUtil.concat(Arrays.<Component>asList(myGraphTable, myChangesBrowser.getPreferredFocusedComponent()),
-                                  myFilterUi.getComponents());
+      return Arrays.<Component>asList(myGraphTable, myChangesBrowser.getPreferredFocusedComponent());
     }
   }
 
diff --git a/platform/xdebugger-api/src/com/intellij/xdebugger/frame/XInlineSourcePosition.java b/platform/xdebugger-api/src/com/intellij/xdebugger/frame/XInlineSourcePosition.java
new file mode 100644 (file)
index 0000000..ff9bc06
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.xdebugger.frame;
+
+/**
+ * @author egor
+ */
+public interface XInlineSourcePosition extends XNavigatable {
+}
index 87abf3da3b2128dc3956bfb317d5d1936c1f274d..9b223851ee03479c9691cd15a5939f053ca0394d 100644 (file)
@@ -18,5 +18,5 @@ package com.intellij.xdebugger.frame;
 /**
  * @author Konstantin Bulenkov
  */
-public interface XNearestSourcePosition extends XNavigatable {
+public interface XNearestSourcePosition extends XInlineSourcePosition {
 }
index feb62d3c03dffe83fc044dcd34e1aa6f84c7db9c..303293d5a3ab4f5c4e6478367df3a2f507c323bb 100644 (file)
@@ -37,7 +37,6 @@ import com.intellij.util.containers.Convertor;
 import com.intellij.util.containers.TransferToEDTQueue;
 import com.intellij.util.ui.TextTransferable;
 import com.intellij.util.ui.UIUtil;
-import com.intellij.util.ui.tree.TreeModelAdapter;
 import com.intellij.xdebugger.XSourcePosition;
 import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider;
 import com.intellij.xdebugger.frame.XDebuggerTreeNodeHyperlink;
@@ -49,7 +48,6 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
-import javax.swing.event.TreeModelEvent;
 import javax.swing.tree.DefaultTreeModel;
 import javax.swing.tree.TreeNode;
 import javax.swing.tree.TreePath;
@@ -155,27 +153,6 @@ public class XDebuggerTree extends DnDAwareTree implements DataProvider, Disposa
     myEditorsProvider = editorsProvider;
     mySourcePosition = sourcePosition;
     myTreeModel = new DefaultTreeModel(null);
-    myTreeModel.addTreeModelListener(new TreeModelAdapter() {
-      @Override
-      public void treeNodesChanged(TreeModelEvent e) {
-        updateEditor();
-      }
-
-      @Override
-      public void treeNodesInserted(TreeModelEvent e) {
-        updateEditor();
-      }
-
-      @Override
-      public void treeNodesRemoved(TreeModelEvent e) {
-        updateEditor();
-      }
-
-      @Override
-      public void treeStructureChanged(TreeModelEvent e) {
-        updateEditor();
-      }
-    });
     setModel(myTreeModel);
     setCellRenderer(new XDebuggerTreeRenderer());
     new TreeLinkMouseListener(new XDebuggerTreeRenderer()) {
@@ -226,7 +203,7 @@ public class XDebuggerTree extends DnDAwareTree implements DataProvider, Disposa
     setTransferHandler(DEFAULT_TRANSFER_HANDLER);
   }
 
-  private void updateEditor() {
+  public void updateEditor() {
     myAlarm.cancelAndRequest();
   }
   
index 6f911b28cd6863f4319942684f8a9c41f2e48d59..992371f8ff7edca9a115c467ae5365c79db49823 100644 (file)
@@ -18,6 +18,7 @@ package com.intellij.xdebugger.impl.ui.tree.nodes;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.editor.Document;
 import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.util.Comparing;
 import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.registry.Registry;
 import com.intellij.openapi.util.text.StringUtil;
@@ -137,8 +138,8 @@ public class XValueNodeImpl extends XValueContainerNode<XValue> implements XValu
     try {
       final XDebugSession session = XDebugView.getSession(getTree());
       if (session != null) {
-        final XSourcePosition position = session.getCurrentPosition();
-        if (position != null) {
+        final XSourcePosition debuggerPosition = session.getCurrentPosition();
+        if (debuggerPosition != null) {
           final XInlineDebuggerDataCallback callback = new XInlineDebuggerDataCallback() {
             @Override
             public void computed(@NotNull VirtualFile file, @NotNull Document document, int line) {
@@ -156,11 +157,12 @@ public class XValueNodeImpl extends XValueContainerNode<XValue> implements XValu
               if (old != null) {
                 presentations.addAll(old);
               }
+              myTree.updateEditor();
             }
           };
 
           if (getValueContainer().computeInlineDebuggerData(callback) == ThreeState.UNSURE) {
-            class ValueDeclaration implements XNavigatable {
+            class ValueDeclaration implements XInlineSourcePosition {
               @Override
               public void setSourcePosition(@Nullable XSourcePosition sourcePosition) {
                 final Map<Pair<VirtualFile, Integer>, Set<XValueNodeImpl>> map =
@@ -168,6 +170,7 @@ public class XValueNodeImpl extends XValueContainerNode<XValue> implements XValu
                 final Map<VirtualFile, Long> timestamps = myTree.getProject().getUserData(XVariablesView.DEBUG_VARIABLES_TIMESTAMPS);
                 if (map == null || timestamps == null || sourcePosition == null) return;
                 VirtualFile file = sourcePosition.getFile();
+                if (!Comparing.equal(debuggerPosition.getFile(), sourcePosition.getFile())) return;
                 final Document doc = FileDocumentManager.getInstance().getDocument(file);
                 if (doc == null) return;
                 int line = sourcePosition.getLine();