diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java index 76da573725a8..e1fdc3840f2a 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java @@ -1173,7 +1173,7 @@ public class GenericsHighlightUtil { public static HighlightInfo checkEnumMustNotBeLocal(final PsiClass aClass) { if (!aClass.isEnum()) return null; PsiElement parent = aClass.getParent(); - if (!(parent instanceof PsiClass || parent instanceof PsiFile)) { + if (!(parent instanceof PsiClass || parent instanceof PsiFile || parent instanceof PsiClassLevelDeclarationStatement)) { String description = JavaErrorMessages.message("local.enum"); TextRange textRange = HighlightNamesUtil.getClassDeclarationTextRange(aClass); return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaUtilImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaUtilImpl.java index e35aa3aa80a5..22d347ee1ecb 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaUtilImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaUtilImpl.java @@ -20,6 +20,7 @@ */ package com.intellij.codeInspection.reference; +import com.intellij.codeInsight.ExceptionUtil; import com.intellij.codeInspection.InspectionsBundle; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; @@ -30,6 +31,9 @@ import com.intellij.util.VisibilityUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; +import java.util.Collections; + public class RefJavaUtilImpl extends RefJavaUtil{ @Override @@ -114,6 +118,29 @@ public class RefJavaUtilImpl extends RefJavaUtil{ final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(aClass); if (interfaceMethod != null) { refFrom.addReference(refFrom.getRefManager().getReference(interfaceMethod), interfaceMethod, psiFrom, false, true, null); + + PsiElement body = null; + PsiElement topElement = null; + if (expression instanceof PsiLambdaExpression) { + body = ((PsiLambdaExpression)expression).getBody(); + topElement = expression; + } + else { + final PsiElement resolve = ((PsiMethodReferenceExpression)expression).resolve(); + if (resolve instanceof PsiMethod) { + body = ((PsiMethod)resolve).getBody(); + topElement = resolve; + } + } + + final Collection exceptionTypes = body != null ? ExceptionUtil.collectUnhandledExceptions(body, topElement, false) + : Collections.emptyList(); + RefElement refResolved = refFrom.getRefManager().getReference(interfaceMethod); + if (refResolved instanceof RefMethodImpl) { + for (final PsiClassType exceptionType : exceptionTypes) { + ((RefMethodImpl)refResolved).updateThrowsList(exceptionType); + } + } } } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaGenerateMemberCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaGenerateMemberCompletionContributor.java index 8ae9622f3440..e636ab4fc9b4 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaGenerateMemberCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaGenerateMemberCompletionContributor.java @@ -18,6 +18,7 @@ package com.intellij.codeInsight.completion; import com.intellij.codeInsight.generation.*; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.codeInspection.ex.GlobalInspectionContextBase; import com.intellij.icons.AllIcons; import com.intellij.openapi.util.Iconable; import com.intellij.openapi.util.Key; @@ -30,10 +31,7 @@ import com.intellij.util.VisibilityUtil; import com.intellij.util.containers.ContainerUtil; import javax.swing.*; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Set; +import java.util.*; import static com.intellij.patterns.PlatformPatterns.psiElement; @@ -137,6 +135,17 @@ public class JavaGenerateMemberCompletionContributor { List> newInfos = GenerateMembersUtil .insertMembersAtOffset(context.getFile(), context.getStartOffset(), infos); if (!newInfos.isEmpty()) { + final List elements = new ArrayList(); + for (GenerationInfo member : newInfos) { + if (!(member instanceof TemplateGenerationInfo)) { + final PsiMember psiMember = member.getPsiMember(); + if (psiMember != null) { + elements.add(psiMember); + } + } + } + + GlobalInspectionContextBase.cleanupElements(context.getProject(), null, elements.toArray(new PsiElement[elements.size()])); newInfos.get(0).positionCaret(context.getEditor(), true); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddExceptionToCatchFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddExceptionToCatchFix.java index 5d51949c1d6b..cc43f962fc19 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddExceptionToCatchFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddExceptionToCatchFix.java @@ -171,8 +171,8 @@ public class AddExceptionToCatchFix extends BaseIntentionAction { if (element == null) return null; @SuppressWarnings({"unchecked"}) - final PsiElement parent = PsiTreeUtil.getParentOfType(element, PsiTryStatement.class, PsiMethod.class); - if (parent == null || parent instanceof PsiMethod) return null; + final PsiElement parent = PsiTreeUtil.getParentOfType(element, PsiTryStatement.class, PsiMethod.class, PsiFunctionalExpression.class); + if (parent == null || parent instanceof PsiMethod || parent instanceof PsiFunctionalExpression) return null; final PsiTryStatement statement = (PsiTryStatement) parent; final PsiCodeBlock tryBlock = statement.getTryBlock(); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/GeneralizeCatchFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/GeneralizeCatchFix.java index 64ad43808b86..2f7c34e47886 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/GeneralizeCatchFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/GeneralizeCatchFix.java @@ -63,7 +63,7 @@ public class GeneralizeCatchFix implements IntentionAction { myTryStatement = (PsiTryStatement)element.getParent(); break; } - if (element instanceof PsiMethod || (element instanceof PsiClass && !(element instanceof PsiAnonymousClass))) break; + if (element instanceof PsiMethod || element instanceof PsiFunctionalExpression || (element instanceof PsiClass && !(element instanceof PsiAnonymousClass))) break; element = element.getParent(); } if (myTryStatement == null) return false; diff --git a/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.java b/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.java index 4c60a453eafa..1245f04103cd 100644 --- a/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.java +++ b/java/java-impl/src/com/intellij/unscramble/UnscrambleDialog.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -314,6 +314,10 @@ public class UnscrambleDialog extends DialogWrapper { builder.append(trimSuffix(line)).append("\n"); continue; } + if (line.startsWith("at breakpoint")) { // possible thread status mixed with "at ..." + builder.append(" ").append(trimSuffix(line)); + continue; + } if (!first && mustHaveNewLineBefore(line)) { builder.append("\n"); if (line.startsWith("\"")) builder.append("\n"); // Additional line break for thread names diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addCatchBlock/afterInsideLambdaTryInside.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addCatchBlock/afterInsideLambdaTryInside.java new file mode 100644 index 000000000000..0bad23e6f11a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addCatchBlock/afterInsideLambdaTryInside.java @@ -0,0 +1,25 @@ +// "Add 'catch' clause(s)" "true" +import java.io.IOException; +import java.util.function.Supplier; + +class C { + static Object get() throws Exception { + return null; + } + + void method() { + try { + Supplier lambda1 = () -> { + try { + return C.get(); + } catch (IOException e) { + throw new RuntimeException(); + } catch (Exception e) { + e.printStackTrace(); + } + }; + } catch( Exception e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addCatchBlock/beforeInsideLambda.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addCatchBlock/beforeInsideLambda.java new file mode 100644 index 000000000000..08b71cbd8571 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addCatchBlock/beforeInsideLambda.java @@ -0,0 +1,16 @@ +// "Add 'catch' clause(s)" "false" +import java.util.function.Supplier; + +class C { + static Object get() throws Exception { + return null; + } + + void method() { + try { + Supplier lambda1 = () -> C.get(); + } catch( Exception e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addCatchBlock/beforeInsideLambdaTryInside.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addCatchBlock/beforeInsideLambdaTryInside.java new file mode 100644 index 000000000000..857e16005aa8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addCatchBlock/beforeInsideLambdaTryInside.java @@ -0,0 +1,23 @@ +// "Add 'catch' clause(s)" "true" +import java.io.IOException; +import java.util.function.Supplier; + +class C { + static Object get() throws Exception { + return null; + } + + void method() { + try { + Supplier lambda1 = () -> { + try { + return C.get(); + } catch (IOException e) { + throw new RuntimeException(); + } + }; + } catch( Exception e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addCatchBlock/beforeInsideMethodRef.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addCatchBlock/beforeInsideMethodRef.java new file mode 100644 index 000000000000..300d6bdc806c --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addCatchBlock/beforeInsideMethodRef.java @@ -0,0 +1,16 @@ +// "Add 'catch' clause(s)" "false" +import java.util.function.Supplier; + +class C { + static Object get() throws Exception { + return null; + } + + void method() { + try { + Supplier lambda1 = C::get; + } catch( Exception e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/generalizeCatch/beforeInsideLambda.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/generalizeCatch/beforeInsideLambda.java new file mode 100644 index 000000000000..97db7ea2a88f --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/generalizeCatch/beforeInsideLambda.java @@ -0,0 +1,16 @@ +// "Generalize catch for 'java.lang.Exception' to 'java.lang.Exception'" "false" +import java.util.function.Supplier; + +class C { + static Object get() throws Exception { + return null; + } + + void method() { + try { + Supplier lambda1 = () -> C.get(); + } catch( Exception e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/generalizeCatch/beforeInsideMethodRef.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/generalizeCatch/beforeInsideMethodRef.java new file mode 100644 index 000000000000..1bc024dd7453 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/generalizeCatch/beforeInsideMethodRef.java @@ -0,0 +1,16 @@ +// "Generalize catch for 'java.lang.Exception' to 'java.lang.Exception'" "false" +import java.util.function.Supplier; + +class C { + static Object get() throws Exception { + return null; + } + + void method() { + try { + Supplier lambda1 = C::get; + } catch( Exception e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/redundantThrow/ThrownClausesInFunctionalExpressions/expected.xml b/java/java-tests/testData/inspection/redundantThrow/ThrownClausesInFunctionalExpressions/expected.xml new file mode 100644 index 000000000000..d7687ccb6f07 --- /dev/null +++ b/java/java-tests/testData/inspection/redundantThrow/ThrownClausesInFunctionalExpressions/expected.xml @@ -0,0 +1,9 @@ + + + + Foo.java + 21 + ObjectStreamException + + + diff --git a/java/java-tests/testData/inspection/redundantThrow/ThrownClausesInFunctionalExpressions/src/Foo.java b/java/java-tests/testData/inspection/redundantThrow/ThrownClausesInFunctionalExpressions/src/Foo.java new file mode 100644 index 000000000000..78316c54cfb5 --- /dev/null +++ b/java/java-tests/testData/inspection/redundantThrow/ThrownClausesInFunctionalExpressions/src/Foo.java @@ -0,0 +1,23 @@ +import java.io.*; + +class ExceptionTest { + + MyFunction method() { + return () -> { + throw new EOFException(); + }; + } + + MyFunction method1() { + return this::e; + } + + private void e() throws FileNotFoundException { + throw new FileNotFoundException(); + } + + @FunctionalInterface + private interface MyFunction { + void call() throws FileNotFoundException, EOFException, ObjectStreamException; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy index 3bf3f6fbe428..dafb0ea9445b 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy @@ -27,6 +27,7 @@ import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiMethod import com.intellij.psi.codeStyle.CodeStyleSettingsManager import com.intellij.psi.codeStyle.CommonCodeStyleSettings +import com.siyeh.ig.style.UnqualifiedFieldAccessInspection public class NormalCompletionTest extends LightFixtureCompletionTestCase { @Override @@ -1484,5 +1485,21 @@ class Bar { myFixture.assertPreferredCompletionItems(0, "xcreateZoo", "xcreateElephant"); } + public void "test code cleanup during completion generation"() { + myFixture.configureByText "a.java", "class Foo {int i; ge}" + def inspection = new UnqualifiedFieldAccessInspection() + try { + myFixture.enableInspections(inspection) + myFixture.complete(CompletionType.BASIC) + myFixture.checkResult '''class Foo {int i; + public int getI() { + return this.i; + } +}''' + } + finally { + myFixture.disableInspections(inspection) + } + } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/AddExceptionToCatchTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/AddExceptionToCatchTest.java index 4ef4c493e09d..e55cced09985 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/AddExceptionToCatchTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/AddExceptionToCatchTest.java @@ -1,5 +1,8 @@ package com.intellij.codeInsight.daemon.quickFix; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.testFramework.IdeaTestUtil; + public class AddExceptionToCatchTest extends LightQuickFixParameterizedTestCase { public void test() throws Exception { doAllTests(); @@ -9,4 +12,9 @@ public class AddExceptionToCatchTest extends LightQuickFixParameterizedTestCase protected String getBasePath() { return "/codeInsight/daemonCodeAnalyzer/quickFix/addCatchBlock"; } + + @Override + protected Sdk getProjectJDK() { + return IdeaTestUtil.getMockJdk18(); + } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/GeneralizeCatchTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/GeneralizeCatchTest.java index ac0c5b4ae09e..90299b35923c 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/GeneralizeCatchTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/GeneralizeCatchTest.java @@ -1,5 +1,8 @@ package com.intellij.codeInsight.daemon.quickFix; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.testFramework.IdeaTestUtil; + public class GeneralizeCatchTest extends LightQuickFixParameterizedTestCase { public void test() throws Exception { doAllTests(); @@ -9,4 +12,9 @@ public class GeneralizeCatchTest extends LightQuickFixParameterizedTestCase { protected String getBasePath() { return "/codeInsight/daemonCodeAnalyzer/quickFix/generalizeCatch"; } + + @Override + protected Sdk getProjectJDK() { + return IdeaTestUtil.getMockJdk18(); + } } diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/RedundantThrowTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/RedundantThrowTest.java index c1017d242538..61dc1d9a5600 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/RedundantThrowTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/RedundantThrowTest.java @@ -17,6 +17,10 @@ package com.intellij.codeInspection; import com.intellij.JavaTestUtil; import com.intellij.codeInspection.unneededThrows.RedundantThrows; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.LanguageLevelProjectExtension; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.testFramework.IdeaTestUtil; import com.intellij.testFramework.InspectionTestCase; public class RedundantThrowTest extends InspectionTestCase { @@ -60,4 +64,15 @@ public class RedundantThrowTest extends InspectionTestCase { public void testSelfCall() throws Exception { doTest(); } + + public void testThrownClausesInFunctionalExpressions() throws Exception { + doTest(); + } + + @Override + protected Sdk getTestProjectSdk() { + Sdk sdk = IdeaTestUtil.getMockJdk17(); + LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(LanguageLevel.JDK_1_8); + return sdk; + } } diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextBase.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextBase.java index 4372a5944a2f..b284780ae278 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextBase.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextBase.java @@ -438,7 +438,7 @@ public class GlobalInspectionContextBase extends UserDataHolderBase implements G }; Application application = ApplicationManager.getApplication(); - if (application.isWriteAccessAllowed()) { + if (application.isWriteAccessAllowed() && !application.isUnitTestMode()) { application.invokeLater(cleanupRunnable); } else { diff --git a/platform/lang-api/src/com/intellij/execution/util/ListTableWithButtons.java b/platform/lang-api/src/com/intellij/execution/util/ListTableWithButtons.java index 78f833591ebc..22cbf612d5fd 100644 --- a/platform/lang-api/src/com/intellij/execution/util/ListTableWithButtons.java +++ b/platform/lang-api/src/com/intellij/execution/util/ListTableWithButtons.java @@ -27,6 +27,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableCellRenderer; +import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.Collections; @@ -40,6 +41,7 @@ public abstract class ListTableWithButtons extends Observable { private final List myElements = ContainerUtil.newArrayList(); private final JPanel myPanel; private final TableView myTableView; + private final CommonActionsPanel myActionsPanel; private boolean myIsEnabled = true; protected ListTableWithButtons() { @@ -55,6 +57,7 @@ public abstract class ListTableWithButtons extends Observable { final int column = myTableView.getEditingColumn(); final int row = myTableView.getEditingRow(); if (e.getModifiers() == 0 && (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_TAB)) { + e.consume(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { @@ -69,6 +72,7 @@ public abstract class ListTableWithButtons extends Observable { nextRow = 0; } } + myTableView.scrollRectToVisible(myTableView.getCellRect(nextRow, nextColumn, true)); myTableView.editCellAt(nextRow, nextColumn); } }); @@ -79,19 +83,24 @@ public abstract class ListTableWithButtons extends Observable { } }; myTableView.setRowHeight(new JTextField().getPreferredSize().height); + myTableView.setIntercellSpacing(new Dimension(0, 0)); + myTableView.setStriped(true); + myTableView.getTableViewModel().setSortable(false); - myPanel = ToolbarDecorator.createDecorator(myTableView) + ToolbarDecorator decorator = ToolbarDecorator.createDecorator(myTableView); + myPanel = decorator .setAddAction(new AnActionButtonRunnable() { @Override public void run(AnActionButton button) { - if (!myElements.isEmpty() && isEmpty(myElements.get(myElements.size() - 1))) return; myTableView.stopEditing(); setModified(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - myElements.add(createElement()); - myTableView.getTableViewModel().setItems(myElements); + if (myElements.isEmpty() || !isEmpty(myElements.get(myElements.size() - 1))) { + myElements.add(createElement()); + myTableView.getTableViewModel().setItems(myElements); + } myTableView.scrollRectToVisible(myTableView.getCellRect(myElements.size() - 1, 0, true)); myTableView.getComponent().editCellAt(myElements.size() - 1, 0); } @@ -105,6 +114,7 @@ public abstract class ListTableWithButtons extends Observable { T selected = getSelection(); if (selected != null) { int selectedIndex = myElements.indexOf(selected); + myTableView.scrollRectToVisible(myTableView.getCellRect(selectedIndex, 0, true)); myElements.remove(selected); myTableView.getTableViewModel().setItems(myElements); @@ -133,6 +143,7 @@ public abstract class ListTableWithButtons extends Observable { } }); + myActionsPanel = decorator.getActionsPanel(); myTableView.getComponent().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); } @@ -157,6 +168,10 @@ public abstract class ListTableWithButtons extends Observable { return myPanel; } + public CommonActionsPanel getActionsPanel() { + return myActionsPanel; + } + public void setEnabled() { myTableView.getComponent().setEnabled(true); myIsEnabled = true; diff --git a/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java b/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java index 747cea82df93..f30da0b39b4b 100644 --- a/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java +++ b/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java @@ -450,6 +450,9 @@ public class EditorSearchComponent extends EditorHeaderComponent implements Data else { nothingToSearchFor(); } + if (mySearchField instanceof JTextArea) { + UIUtil.adjustRows((JTextArea)mySearchField, 2, 6); + } } public boolean isRegexp() { @@ -637,6 +640,9 @@ public class EditorSearchComponent extends EditorHeaderComponent implements Data private void replaceFieldDocumentChanged() { setMatchesLimit(LivePreviewController.MATCHES_LIMIT); myFindModel.setStringToReplace(myReplaceField.getText()); + if (myReplaceField instanceof JTextArea) { + UIUtil.adjustRows((JTextArea)myReplaceField, 2, 6); + } } private boolean canReplaceCurrent() { @@ -697,12 +703,17 @@ public class EditorSearchComponent extends EditorHeaderComponent implements Data super.paintBorder(g); paintBorderOfTextField(g); } + + @Override + public Dimension getPreferredSize() { + return super.getPreferredSize(); + } }; ((JTextArea)editorTextField).setColumns(25); - ((JTextArea)editorTextField).setRows(3); + ((JTextArea)editorTextField).setRows(2); final JScrollPane scrollPane = new JBScrollPane(editorTextField, - ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, - ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); myLeftComponent.add(scrollPane, constraint); componentRef.set(scrollPane); } diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java b/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java index d44e4a25cb17..6db6bfe0e4db 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java @@ -60,6 +60,7 @@ import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.table.JBTable; import com.intellij.usageView.UsageInfo; import com.intellij.usages.*; +import com.intellij.usages.impl.UsagePreviewPanel; import com.intellij.util.ArrayUtil; import com.intellij.util.Consumer; import com.intellij.util.Processor; @@ -70,14 +71,14 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellRenderer; import java.awt.*; import java.awt.event.*; -import java.util.Arrays; -import java.util.HashMap; +import java.util.*; import java.util.List; -import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; @@ -123,7 +124,9 @@ public class FindDialog extends DialogWrapper { private static boolean myPreviewResultsTabWasSelected; private static final int RESULTS_PREVIEW_TAB_INDEX = 1; + private Splitter myPreviewSplitter; private JBTable myResultsPreviewTable; + private UsagePreviewPanel myUsagePreviewPanel; private TabbedPane myContent; private volatile ProgressIndicatorBase myResultsPreviewSearchProgress; @@ -173,6 +176,7 @@ public class FindDialog extends DialogWrapper { @Override protected void dispose() { finishPreviousPreviewSearch(); + if (myUsagePreviewPanel != null) Disposer.dispose(myUsagePreviewPanel); for(Map.Entry e: myComboBoxListeners.entrySet()) { e.getKey().removeDocumentListener(e.getValue()); } @@ -489,8 +493,29 @@ public class FindDialog extends DialogWrapper { } }; table.setShowColumns(false); + table.setShowGrid(false); new NavigateToSourceListener().installOn(table); + + Splitter previewSplitter = new Splitter(true, 0.5f, 0.1f, 0.9f); + myUsagePreviewPanel = new UsagePreviewPanel(myProject, new UsageViewPresentation()); myResultsPreviewTable = table; + myResultsPreviewTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + if (e.getValueIsAdjusting()) return; + int index = myResultsPreviewTable.getSelectionModel().getLeadSelectionIndex(); + if (index != -1) { + UsageInfo usageInfo = ((UsageInfo2UsageAdapter)myResultsPreviewTable.getModel().getValueAt(index, 0)).getUsageInfo(); + myUsagePreviewPanel.updateLayout(Collections.singletonList(usageInfo)); + } + else { + myUsagePreviewPanel.updateLayout(null); + } + } + }); + previewSplitter.setFirstComponent(new JBScrollPane(myResultsPreviewTable)); + previewSplitter.setSecondComponent(myUsagePreviewPanel.createComponent()); + myPreviewSplitter = previewSplitter; } } else { @@ -513,10 +538,10 @@ public class FindDialog extends DialogWrapper { resultsOptionPanel.add(myCbToOpenInNewTab); } - if (myResultsPreviewTable != null) { + if (myPreviewSplitter != null) { TabbedPane pane = new TabbedPaneImpl(SwingConstants.TOP); pane.insertTab("Options", null, optionsPanel, null, 0); - pane.insertTab("Preview", null, new JBScrollPane(myResultsPreviewTable), null, RESULTS_PREVIEW_TAB_INDEX); + pane.insertTab("Preview", null, myPreviewSplitter, null, RESULTS_PREVIEW_TAB_INDEX); myContent = pane; if (myPreviewResultsTabWasSelected) myContent.setSelectedIndex(RESULTS_PREVIEW_TAB_INDEX); @@ -1389,36 +1414,46 @@ public class FindDialog extends DialogWrapper { } private static class UsageTableCellRenderer extends JPanel implements TableCellRenderer { - private SimpleColoredComponent myUsageRenderer = new SimpleColoredComponent(); - private SimpleColoredComponent myFileAndLineNumber = new SimpleColoredComponent(); + private ColoredTableCellRenderer myUsageRenderer = new ColoredTableCellRenderer() { + @Override + protected void customizeCellRenderer(JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) { + if (value instanceof UsageInfo2UsageAdapter) { + TextChunk[] text = ((UsageInfo2UsageAdapter)value).getPresentation().getText(); + + // skip line number / file info + for (int i = 1; i < text.length; ++i) { + TextChunk textChunk = text[i]; + myUsageRenderer.append(textChunk.getText(), textChunk.getSimpleAttributesIgnoreBackground()); + } + } + setBorder(null); + } + }; + private ColoredTableCellRenderer myFileAndLineNumber = new ColoredTableCellRenderer() { + @Override + protected void customizeCellRenderer(JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) { + if (value instanceof UsageInfo2UsageAdapter) { + TextChunk[] text = ((UsageInfo2UsageAdapter)value).getPresentation().getText(); + // line number / file info + append(((UsageInfo2UsageAdapter)value).getFile().getName() + " " + text[0].getText(), SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES); + } + setBorder(null); + } + }; UsageTableCellRenderer() { setLayout(new BorderLayout()); + add(myUsageRenderer, BorderLayout.WEST); add(myFileAndLineNumber, BorderLayout.EAST); } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - myUsageRenderer.clear(); - myFileAndLineNumber.clear(); - setBackground(isSelected && hasFocus ? UIUtil.getTableSelectionBackground() : UIUtil.getTableBackground()); + setBackground(UIUtil.getTableBackground(isSelected)); + myUsageRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + myFileAndLineNumber.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - if (value instanceof UsageInfo2UsageAdapter) { - UsageInfo2UsageAdapter usageAdapter = (UsageInfo2UsageAdapter)value; - UsagePresentation presentation = usageAdapter.getPresentation(); - TextChunk[] text = presentation.getText(); - - // put line number / file info at the right - for (int i = 1; i < text.length; ++i) { - TextChunk textChunk = text[i]; - SimpleTextAttributes simples = textChunk.getSimpleAttributesIgnoreBackground(); - myUsageRenderer.append(textChunk.getText(), simples); - } - - myFileAndLineNumber.append(usageAdapter.getFile().getName() + " " + text[0].getText(), - SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES); - } return this; } } diff --git a/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java b/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java index ee1e5dcf2695..60dcc2ea2d06 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -158,6 +158,7 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA private JBList myList; private JCheckBox myNonProjectCheckBox; private AnActionEvent myActionEvent; + private Set myDisabledActions = new HashSet(); private Component myContextComponent; private CalcThread myCalcThread; private static AtomicBoolean ourShiftIsPressed = new AtomicBoolean(false); @@ -1040,6 +1041,7 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA } }; SearchEverywherePsiRenderer myFileRenderer = new SearchEverywherePsiRenderer(myList); + ListCellRenderer myActionsRenderer = new GotoActionModel.GotoActionListCellRenderer(Function.TO_STRING); private String myLocationString; private DefaultPsiElementCellRenderer myPsiRenderer = new DefaultPsiElementCellRenderer() { @@ -1080,6 +1082,8 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA } else if (value instanceof PsiElement) { myFileRenderer.setPatternMatcher(matcher); cmp = myFileRenderer.getListCellRendererComponent(list, value, index, isSelected, isSelected); + } else if (value instanceof GotoActionModel.ActionWrapper) { + cmp = myActionsRenderer.getListCellRendererComponent(list, new GotoActionModel.MatchedValue(((GotoActionModel.ActionWrapper)value), pattern), index, isSelected, isSelected); } else { cmp = super.getListCellRendererComponent(list, value, index, isSelected, isSelected); final JPanel p = new JPanel(new BorderLayout()); @@ -1464,7 +1468,10 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA result.add(object); } } else if (actions && !isToolWindowAction(object) && isActionValue(object)) { - result.add(object); + AnAction action = object instanceof AnAction ? ((AnAction)object) : ((GotoActionModel.ActionWrapper)object).getAction(); + if (isEnabled(action)) { + result.add(object); + } } return result.size() <= max; } @@ -1905,19 +1912,9 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA public void run() { if (isCanceled()) return; - for (Object element : new ArrayList(elements)) { if (element instanceof AnAction) { - final AnAction action = (AnAction)element; - final AnActionEvent e = new AnActionEvent(myActionEvent.getInputEvent(), - myActionEvent.getDataContext(), - myActionEvent.getPlace(), - action.getTemplatePresentation(), - myActionEvent.getActionManager(), - myActionEvent.getModifiers()); - ActionUtil.performDumbAwareUpdate(action, e, false); - final Presentation presentation = e.getPresentation(); - if (!presentation.isEnabled() || !presentation.isVisible() || StringUtil.isEmpty(presentation.getText())) { + if (!isEnabled((AnAction)element)) { elements.remove(element); } if (isCanceled()) return; @@ -1933,6 +1930,29 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA } } + protected boolean isEnabled(final AnAction action) { + if (myDisabledActions.contains(action)) return false; + final AnActionEvent e = new AnActionEvent(myActionEvent.getInputEvent(), + myActionEvent.getDataContext(), + myActionEvent.getPlace(), + action.getTemplatePresentation(), + myActionEvent.getActionManager(), + myActionEvent.getModifiers()); + + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + ActionUtil.performDumbAwareUpdate(action, e, false); + } + }); + final Presentation presentation = e.getPresentation(); + final boolean enabled = presentation.isEnabled() && presentation.isVisible() && !StringUtil.isEmpty(presentation.getText()); + if (!enabled) { + myDisabledActions.add(action); + } + return enabled; + } + private synchronized void checkModelsUpToDate() { if (myClassModel == null) { myClassModel = new GotoClassModel2(project); @@ -2165,6 +2185,7 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA myEditor = null; myFileEditor = null; myStructureModel = null; + myDisabledActions.clear(); } } } diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoActionModel.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoActionModel.java index 3a9001e005ac..84fb10920923 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoActionModel.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoActionModel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,6 +41,7 @@ import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.OnOffButton; import com.intellij.ui.speedSearch.SpeedSearchUtil; import com.intellij.util.ArrayUtil; +import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ContainerUtilRt; import com.intellij.util.ui.EmptyIcon; @@ -144,7 +145,7 @@ public class GotoActionModel implements ChooseByNameModel, CustomMatcherModel, C @NotNull public final Comparable value; @NotNull final String pattern; - MatchedValue(@NotNull Comparable value, @NotNull String pattern) { + public MatchedValue(@NotNull Comparable value, @NotNull String pattern) { this.value = value; this.pattern = pattern; } @@ -208,121 +209,12 @@ public class GotoActionModel implements ChooseByNameModel, CustomMatcherModel, C @Override public ListCellRenderer getListCellRenderer() { - return new DefaultListCellRenderer() { + return new GotoActionListCellRenderer(new Function() { @Override - public Component getListCellRendererComponent(@NotNull final JList list, - final Object matchedValue, - final int index, final boolean isSelected, final boolean cellHasFocus) { - final JPanel panel = new JPanel(new BorderLayout()); - panel.setBorder(IdeBorderFactory.createEmptyBorder(2)); - panel.setOpaque(true); - Color bg = UIUtil.getListBackground(isSelected); - panel.setBackground(bg); - - if (matchedValue instanceof String) { //... - final JBLabel label = new JBLabel((String)matchedValue); - label.setIcon(EMPTY_ICON); - panel.add(label, BorderLayout.WEST); - return panel; - } - - Color groupFg = isSelected ? UIUtil.getListSelectionForeground() : UIUtil.getLabelDisabledForeground(); - - final Object value = ((MatchedValue) matchedValue).value; - String pattern = ((MatchedValue)matchedValue).pattern; - - SimpleColoredComponent nameComponent = new SimpleColoredComponent(); - nameComponent.setBackground(bg); - panel.add(nameComponent, BorderLayout.CENTER); - - if (value instanceof ActionWrapper) { - final ActionWrapper actionWithParentGroup = (ActionWrapper)value; - final AnAction anAction = actionWithParentGroup.getAction(); - final Presentation presentation = anAction.getTemplatePresentation(); - boolean toggle = anAction instanceof ToggleAction; - String groupName = actionWithParentGroup.getAction() instanceof ApplyIntentionAction ? null : actionWithParentGroup.getGroupName(); - final Color fg = defaultActionForeground(isSelected, actionWithParentGroup.getPresentation()); - panel.add(createIconLabel(presentation.getIcon()), BorderLayout.WEST); - appendWithColoredMatches(nameComponent, getName(presentation.getText(), groupName, toggle), pattern, fg, isSelected); - - final Shortcut shortcut = preferKeyboardShortcut(KeymapManager.getInstance().getActiveKeymap().getShortcuts(getActionId(anAction))); - if (shortcut != null) { - nameComponent.append(" (" + KeymapUtil.getShortcutText(shortcut) + ")", new SimpleTextAttributes(STYLE_PLAIN, groupFg)); - } - - if (toggle) { - final OnOffButton button = new OnOffButton(); - AnActionEvent event = new AnActionEvent(null, ((ActionWrapper)value).myDataContext, - ActionPlaces.UNKNOWN, new Presentation(), ActionManager.getInstance(), - 0); - button.setSelected(((ToggleAction)anAction).isSelected(event)); - panel.add(button, BorderLayout.EAST); - panel.setBorder(IdeBorderFactory.createEmptyBorder()); - } - else { - if (groupName != null) { - final JLabel groupLabel = new JLabel(groupName); - groupLabel.setBackground(bg); - groupLabel.setForeground(groupFg); - panel.add(groupLabel, BorderLayout.EAST); - } - } - } - else if (value instanceof OptionDescription) { - if (!isSelected && !(value instanceof BooleanOptionDescription)) { - Color descriptorBg = UIUtil.isUnderDarcula() ? ColorUtil.brighter(UIUtil.getListBackground(), 1) : LightColors.SLIGHTLY_GRAY; - panel.setBackground(descriptorBg); - nameComponent.setBackground(descriptorBg); - } - String hit = ((OptionDescription)value).getHit(); - if (hit == null) { - hit = ((OptionDescription)value).getOption(); - } - hit = StringUtil.unescapeXml(hit); - hit = hit.replace(" ", " "); // avoid extra spaces from mnemonics and xml conversion - String fullHit = hit; - hit = StringUtil.first(hit, 45, true); - - final Color fg = UIUtil.getListForeground(isSelected); - - appendWithColoredMatches(nameComponent, hit.trim(), pattern, fg, isSelected); - - panel.add(new JLabel(EMPTY_ICON), BorderLayout.WEST); - panel.setToolTipText(fullHit); - - if (value instanceof BooleanOptionDescription) { - final OnOffButton button = new OnOffButton(); - button.setSelected(((BooleanOptionDescription)value).isOptionEnabled()); - panel.add(button, BorderLayout.EAST); - panel.setBorder(IdeBorderFactory.createEmptyBorder()); - } - else { - final JLabel settingsLabel = new JLabel(getGroupName((OptionDescription)value)); - settingsLabel.setForeground(groupFg); - settingsLabel.setBackground(bg); - panel.add(settingsLabel, BorderLayout.EAST); - } - } - return panel; + public String fun(OptionDescription description) { + return getGroupName(description); } - - public String getName(String text, String groupName, boolean toggle) { - return toggle && StringUtil.isNotEmpty(groupName)? groupName + ": "+ text : text; - } - - private void appendWithColoredMatches(SimpleColoredComponent nameComponent, String name, String pattern, Color fg, boolean selected) { - final SimpleTextAttributes plain = new SimpleTextAttributes(STYLE_PLAIN, fg); - final SimpleTextAttributes highlighted = new SimpleTextAttributes(null, fg, null, STYLE_SEARCH_MATCH); - List fragments = ContainerUtil.newArrayList(); - if (selected) { - int matchStart = StringUtil.indexOfIgnoreCase(name, pattern, 0); - if (matchStart >= 0) { - fragments.add(TextRange.from(matchStart, pattern.length())); - } - } - SpeedSearchUtil.appendColoredFragments(nameComponent, name, fragments, plain, highlighted); - } - }; + }); } protected String getActionId(@NotNull final AnAction anAction) { @@ -721,4 +613,130 @@ public class GotoActionModel implements ChooseByNameModel, CustomMatcherModel, C return myAction.getTemplatePresentation().getText().hashCode(); } } + + public static class GotoActionListCellRenderer extends DefaultListCellRenderer { + private final Function myGroupNamer; + + public GotoActionListCellRenderer(Function groupNamer) { + myGroupNamer = groupNamer; + } + + @Override + public Component getListCellRendererComponent(@NotNull final JList list, + final Object matchedValue, + final int index, final boolean isSelected, final boolean cellHasFocus) { + final JPanel panel = new JPanel(new BorderLayout()); + panel.setBorder(IdeBorderFactory.createEmptyBorder(2)); + panel.setOpaque(true); + Color bg = UIUtil.getListBackground(isSelected); + panel.setBackground(bg); + + if (matchedValue instanceof String) { //... + final JBLabel label = new JBLabel((String)matchedValue); + label.setIcon(EMPTY_ICON); + panel.add(label, BorderLayout.WEST); + return panel; + } + + Color groupFg = isSelected ? UIUtil.getListSelectionForeground() : UIUtil.getLabelDisabledForeground(); + + final Object value = ((MatchedValue) matchedValue).value; + String pattern = ((MatchedValue)matchedValue).pattern; + + SimpleColoredComponent nameComponent = new SimpleColoredComponent(); + nameComponent.setBackground(bg); + panel.add(nameComponent, BorderLayout.CENTER); + + if (value instanceof ActionWrapper) { + final ActionWrapper actionWithParentGroup = (ActionWrapper)value; + final AnAction anAction = actionWithParentGroup.getAction(); + final Presentation presentation = anAction.getTemplatePresentation(); + boolean toggle = anAction instanceof ToggleAction; + String groupName = actionWithParentGroup.getAction() instanceof ApplyIntentionAction ? null : actionWithParentGroup.getGroupName(); + final Color fg = defaultActionForeground(isSelected, actionWithParentGroup.getPresentation()); + panel.add(createIconLabel(presentation.getIcon()), BorderLayout.WEST); + appendWithColoredMatches(nameComponent, getName(presentation.getText(), groupName, toggle), pattern, fg, isSelected); + + final Shortcut shortcut = preferKeyboardShortcut(KeymapManager.getInstance().getActiveKeymap().getShortcuts(ActionManager.getInstance().getId(anAction))); + if (shortcut != null) { + nameComponent.append(" (" + KeymapUtil.getShortcutText(shortcut) + ")", new SimpleTextAttributes(STYLE_PLAIN, groupFg)); + } + + if (toggle) { + final OnOffButton button = new OnOffButton(); + AnActionEvent event = new AnActionEvent(null, ((ActionWrapper)value).myDataContext, + ActionPlaces.UNKNOWN, new Presentation(), ActionManager.getInstance(), + 0); + button.setSelected(((ToggleAction)anAction).isSelected(event)); + panel.add(button, BorderLayout.EAST); + panel.setBorder(IdeBorderFactory.createEmptyBorder()); + } + else { + if (groupName != null) { + final JLabel groupLabel = new JLabel(groupName); + groupLabel.setBackground(bg); + groupLabel.setForeground(groupFg); + panel.add(groupLabel, BorderLayout.EAST); + } + } + } + else if (value instanceof OptionDescription) { + if (!isSelected && !(value instanceof BooleanOptionDescription)) { + Color descriptorBg = UIUtil.isUnderDarcula() ? ColorUtil.brighter(UIUtil.getListBackground(), 1) : LightColors.SLIGHTLY_GRAY; + panel.setBackground(descriptorBg); + nameComponent.setBackground(descriptorBg); + } + String hit = ((OptionDescription)value).getHit(); + if (hit == null) { + hit = ((OptionDescription)value).getOption(); + } + hit = StringUtil.unescapeXml(hit); + hit = hit.replace(" ", " "); // avoid extra spaces from mnemonics and xml conversion + String fullHit = hit; + hit = StringUtil.first(hit, 45, true); + + final Color fg = UIUtil.getListForeground(isSelected); + + appendWithColoredMatches(nameComponent, hit.trim(), pattern, fg, isSelected); + + panel.add(new JLabel(EMPTY_ICON), BorderLayout.WEST); + panel.setToolTipText(fullHit); + + if (value instanceof BooleanOptionDescription) { + final OnOffButton button = new OnOffButton(); + button.setSelected(((BooleanOptionDescription)value).isOptionEnabled()); + panel.add(button, BorderLayout.EAST); + panel.setBorder(IdeBorderFactory.createEmptyBorder()); + } + else { + final JLabel settingsLabel = new JLabel(myGroupNamer.fun((OptionDescription)value)); + settingsLabel.setForeground(groupFg); + settingsLabel.setBackground(bg); + panel.add(settingsLabel, BorderLayout.EAST); + } + } + return panel; + } + + public String getName(String text, String groupName, boolean toggle) { + return toggle && StringUtil.isNotEmpty(groupName)? groupName + ": "+ text : text; + } + + private static void appendWithColoredMatches(SimpleColoredComponent nameComponent, + String name, + String pattern, + Color fg, + boolean selected) { + final SimpleTextAttributes plain = new SimpleTextAttributes(STYLE_PLAIN, fg); + final SimpleTextAttributes highlighted = new SimpleTextAttributes(null, fg, null, STYLE_SEARCH_MATCH); + List fragments = ContainerUtil.newArrayList(); + if (selected) { + int matchStart = StringUtil.indexOfIgnoreCase(name, pattern, 0); + if (matchStart >= 0) { + fragments.add(TextRange.from(matchStart, pattern.length())); + } + } + SpeedSearchUtil.appendColoredFragments(nameComponent, name, fragments, plain, highlighted); + } + } } diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java index f81859417580..22f5eddf54c8 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java @@ -40,6 +40,8 @@ public interface IdeActions { @NonNls String ACTION_EDITOR_BACKSPACE = "EditorBackSpace"; @NonNls String ACTION_EDITOR_MOVE_CARET_LEFT_WITH_SELECTION = "EditorLeftWithSelection"; @NonNls String ACTION_EDITOR_MOVE_CARET_RIGHT_WITH_SELECTION = "EditorRightWithSelection"; + @NonNls String ACTION_EDITOR_MOVE_CARET_UP_WITH_SELECTION = "EditorUpWithSelection"; + @NonNls String ACTION_EDITOR_MOVE_CARET_DOWN_WITH_SELECTION = "EditorDownWithSelection"; @NonNls String ACTION_EDITOR_MOVE_CARET_UP = "EditorUp"; @NonNls String ACTION_EDITOR_MOVE_CARET_LEFT = "EditorLeft"; @NonNls String ACTION_EDITOR_MOVE_CARET_DOWN = "EditorDown"; diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaRadioButtonUI.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaRadioButtonUI.java index 4b1d9dfafaf9..e3fe9f582393 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaRadioButtonUI.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaRadioButtonUI.java @@ -117,7 +117,7 @@ public class DarculaRadioButtonUI extends MetalRadioButtonUI { if (b.isSelected()) { final boolean enabled = b.isEnabled(); g.setColor(UIManager.getColor(enabled ? "RadioButton.darcula.selectionEnabledShadowColor" : "RadioButton.darcula.selectionDisabledShadowColor"));// ? Gray._30 : Gray._60); - final int yOff = UIUtil.isUnderDarcula() ? 2 : JBUI.scale(1); + final int yOff = 2; g.fillOval(w/2 - rad/2, h/2 - rad/2 + yOff , rad, rad); g.setColor(UIManager.getColor(enabled ? "RadioButton.darcula.selectionEnabledColor" : "RadioButton.darcula.selectionDisabledColor")); //Gray._170 : Gray._120); g.fillOval(w/2 - rad/2, h/2 - rad/2 -1 + yOff, rad, rad); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java index fd49b3b5574d..233f0cc45621 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java @@ -248,6 +248,7 @@ public class CaretImpl extends UserDataHolderBase implements Caret { myEditor.getCaretModel().doWithCaretMerging(new Runnable() { @Override public void run() { + int oldOffset = myOffset; final int leadSelectionOffset = getLeadSelectionOffset(); final VisualPosition leadSelectionPosition = getLeadSelectionPosition(); EditorSettings editorSettings = myEditor.getSettings(); @@ -370,7 +371,7 @@ public class CaretImpl extends UserDataHolderBase implements Caret { else { int selectionStartToUse = leadSelectionOffset; VisualPosition selectionStartPositionToUse = leadSelectionPosition; - if (isUnknownDirection()) { + if (isUnknownDirection() || oldOffset > getSelectionStart() && oldOffset < getSelectionEnd()) { if (getOffset() > leadSelectionOffset ^ getSelectionStart() < getSelectionEnd()) { selectionStartToUse = getSelectionEnd(); selectionStartPositionToUse = getSelectionEndPosition(); diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/actions/EditorActionTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/editor/actions/EditorActionTest.java index a653da599d80..2545df56d20c 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/editor/actions/EditorActionTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/actions/EditorActionTest.java @@ -167,4 +167,24 @@ public class EditorActionTest extends AbstractEditorTest { executeAction(IdeActions.ACTION_EDITOR_DELETE_TO_WORD_END); checkResultByText("class Foo { String s = \"a\\b\"; }"); } + + public void testUpWithSelectionOnCaretInsideSelection() throws Exception { + initText("blah blah\n" + + "blah blah\n" + + "blah blah"); + executeAction(IdeActions.ACTION_EDITOR_MOVE_CARET_UP_WITH_SELECTION); + checkResultByText("blah blah\n" + + "blah blah\n" + + "blah blah"); + } + + public void testDownWithSelectionOnCaretInsideSelection() throws Exception { + initText("blah blah\n" + + "blah blah\n" + + "blah blah"); + executeAction(IdeActions.ACTION_EDITOR_MOVE_CARET_DOWN_WITH_SELECTION); + checkResultByText("blah blah\n" + + "blah blah\n" + + "blah blah"); + } } \ No newline at end of file diff --git a/platform/util/src/com/intellij/util/ui/ListTableModel.java b/platform/util/src/com/intellij/util/ui/ListTableModel.java index 8e01d89374cc..bdd04ec399ac 100644 --- a/platform/util/src/com/intellij/util/ui/ListTableModel.java +++ b/platform/util/src/com/intellij/util/ui/ListTableModel.java @@ -116,6 +116,7 @@ public class ListTableModel extends TableViewModel implements Editab if (rowIndex < myItems.size()) { myColumnInfos[columnIndex].setValue(getItem(rowIndex), aValue); } + fireTableCellUpdated(rowIndex, columnIndex); } /** diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index 02bed0eedf02..6af544af7755 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -3215,6 +3215,10 @@ public class UIUtil { textComponent.getActionMap().put("redoKeystroke", REDO_ACTION); } + public static void adjustRows(JTextArea area, int minRows, int maxRows) { + area.setRows(Math.max(minRows, Math.min(maxRows, area.getText().split("\n").length))); + } + public static void playSoundFromResource(final String resourceName) { final Class callerClass = ReflectionUtil.getGrandCallerClass(); if (callerClass == null) return; diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java index 80f78e885500..e32434c67177 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java @@ -947,12 +947,12 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper { if (myPatches.isEmpty() || (! myContainBasedChanges)) return; final List changes = getAllChanges(); Collections.sort(changes, myMyChangeComparator); - final List selectedChanges = myChangesTreeList.getSelectedChanges(); + List selectedChanges = myChangesTreeList.getSelectedChanges(); int selectedIdx = 0; final ArrayList diffRequestPresentables = new ArrayList(changes.size()); if (selectedChanges.isEmpty()) { - selectedChanges.addAll(changes); + selectedChanges = changes; } if (! selectedChanges.isEmpty()) { final FilePatchInProgress.PatchChange c = selectedChanges.get(0); diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLog.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLog.java index 29f63e6cdeb3..73b0c3bfdb54 100644 --- a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLog.java +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLog.java @@ -21,6 +21,7 @@ import org.jetbrains.annotations.Nullable; import java.awt.*; import java.util.Collection; import java.util.List; +import java.util.concurrent.Future; /** * Use this interface to access information available in the VCS Log. @@ -55,7 +56,8 @@ public interface VcsLog { /** * Selects the commit node defined by the given reference (commit hash, branch or tag). */ - void jumpToReference(String reference); + @NotNull + Future jumpToReference(String reference); /** * Returns the VCS log toolbar component. @@ -68,5 +70,4 @@ public interface VcsLog { */ @NotNull Collection getLogProviders(); - } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogImpl.java index e80032bbd9ba..0b685dffc3f1 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogImpl.java @@ -28,6 +28,7 @@ import org.jetbrains.annotations.Nullable; import java.awt.*; import java.util.Collection; import java.util.List; +import java.util.concurrent.Future; /** * @@ -84,8 +85,9 @@ public class VcsLogImpl implements VcsLog { return myUi.getDataPack().getRefsModel().getAllRefs(); } + @NotNull @Override - public void jumpToReference(final String reference) { + public Future jumpToReference(final String reference) { Collection references = getAllReferences(); VcsRef ref = ContainerUtil.find(references, new Condition() { @Override @@ -94,10 +96,10 @@ public class VcsLogImpl implements VcsLog { } }); if (ref != null) { - myUi.jumpToCommit(ref.getCommitHash()); + return myUi.jumpToCommit(ref.getCommitHash()); } else { - myUi.jumpToCommitByPartOfHash(reference); + return myUi.jumpToCommitByPartOfHash(reference); } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/FindPopupWithProgress.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/FindPopupWithProgress.java new file mode 100644 index 000000000000..0b46160165ad --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/FindPopupWithProgress.java @@ -0,0 +1,125 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.vcs.log.ui; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.JBPopup; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.ui.popup.JBPopupListener; +import com.intellij.openapi.ui.popup.LightweightWindowEvent; +import com.intellij.ui.components.JBTextField; +import com.intellij.util.Function; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.*; +import java.util.Collection; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +public class FindPopupWithProgress { + private static final Logger LOG = Logger.getInstance(FindPopupWithProgress.class); + + @NotNull private final TextFieldWithProgress myTextField; + @NotNull private final Function myFunction; + @NotNull private final JBPopup myPopup; + @Nullable private Future myFuture; + + public FindPopupWithProgress(@NotNull final Project project, + @NotNull Collection variants, + @NotNull Function function) { + myFunction = function; + myTextField = new TextFieldWithProgress(project, variants) { + @Override + public void onOk() { + if (myFuture == null) { + final Future future = myFunction.fun(getText().trim()); + myFuture = future; + showProgress(); + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + @Override + public void run() { + try { + future.get(); + ok(); + } + catch (CancellationException ex) { + cancel(); + } + catch (InterruptedException ex) { + cancel(); + } + catch (ExecutionException ex) { + LOG.error(ex); + cancel(); + } + } + }); + } + } + }; + + myPopup = JBPopupFactory.getInstance().createComponentPopupBuilder(myTextField, myTextField.getPreferableFocusComponent()) + .setCancelOnClickOutside(true).setCancelOnWindowDeactivation(true).setCancelKeyEnabled(true).setRequestFocus(true).createPopup(); + myPopup.addListener(new JBPopupListener.Adapter() { + @Override + public void onClosed(LightweightWindowEvent event) { + if (!event.isOk()) { + if (myFuture != null) { + myFuture.cancel(false); + myFuture = null; + } + } + } + }); + + final JBTextField field = new JBTextField(20); + final Dimension size = field.getPreferredSize(); + final Insets insets = myTextField.getBorder().getBorderInsets(myTextField); + size.height += 6 + insets.top + insets.bottom; + size.width += 4 + insets.left + insets.right; + myPopup.setSize(size); + } + + private void cancel() { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + if (myFuture != null) myFuture = null; + myTextField.hideProgress(); + myPopup.cancel(); + } + }); + } + + private void ok() { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + if (myFuture != null) myFuture = null; + myTextField.hideProgress(); + myPopup.closeOk(null); + } + }); + } + + public void showUnderneathOf(@NotNull Component anchor) { + myPopup.showUnderneathOf(anchor); + } +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/GoToRefAction.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/GoToRefAction.java index 8b117c72da26..12f6b65a0551 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/GoToRefAction.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/GoToRefAction.java @@ -18,9 +18,6 @@ package com.intellij.vcs.log.ui; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.popup.JBPopup; -import com.intellij.openapi.ui.popup.JBPopupListener; -import com.intellij.openapi.ui.popup.LightweightWindowEvent; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.VcsLog; @@ -28,6 +25,7 @@ import com.intellij.vcs.log.VcsLogDataKeys; import com.intellij.vcs.log.VcsRef; import java.util.Collection; +import java.util.concurrent.Future; public class GoToRefAction extends DumbAwareAction { @@ -45,16 +43,12 @@ public class GoToRefAction extends DumbAwareAction { return ref.getName(); } }); - final PopupWithTextFieldWithAutoCompletion textField = new PopupWithTextFieldWithAutoCompletion(project, refs); - JBPopup popup = textField.createPopup(); - popup.addListener(new JBPopupListener.Adapter() { - @Override - public void onClosed(LightweightWindowEvent event) { - if (event.isOk()) { - log.jumpToReference(textField.getText().trim()); - } - } - }); + FindPopupWithProgress popup = new FindPopupWithProgress(project, refs, new Function() { + @Override + public Future fun(String text) { + return log.jumpToReference(text); + } + }); popup.showUnderneathOf(log.getToolbar()); } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/PopupWithTextFieldWithAutoCompletion.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/PopupWithTextFieldWithAutoCompletion.java deleted file mode 100644 index fbfb71de3c04..000000000000 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/PopupWithTextFieldWithAutoCompletion.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2000-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.vcs.log.ui; - -import com.intellij.openapi.editor.ex.EditorEx; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.popup.JBPopup; -import com.intellij.openapi.ui.popup.JBPopupFactory; -import com.intellij.spellchecker.ui.SpellCheckingEditorCustomization; -import com.intellij.ui.TextFieldWithAutoCompletion; -import com.intellij.ui.components.JBTextField; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import javax.swing.border.EmptyBorder; -import java.awt.*; -import java.awt.event.KeyEvent; -import java.util.Collection; - -public class PopupWithTextFieldWithAutoCompletion extends TextFieldWithAutoCompletion { - - @Nullable private JBPopup myPopup; - - public PopupWithTextFieldWithAutoCompletion(@NotNull Project project, @NotNull Collection variants) { - super(project, new StringsCompletionProvider(variants, null), false, null); - setBorder(new EmptyBorder(3, 3, 3, 3)); - } - - public JBPopup createPopup() { - myPopup = JBPopupFactory.getInstance().createComponentPopupBuilder(this, this) - .setCancelOnClickOutside(true) - .setCancelOnWindowDeactivation(true) - .setCancelKeyEnabled(true) - .setRequestFocus(true) - .createPopup(); - - final JBTextField field = new JBTextField(20); - final Dimension size = field.getPreferredSize(); - final Insets insets = getBorder().getBorderInsets(this); - size.height+=6 + insets.top + insets.bottom; - size.width +=4 + insets.left + insets.right; - myPopup.setSize(size); - - return myPopup; - } - - @Override - protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) { - if (e.getKeyCode() == KeyEvent.VK_ENTER) { - if (myPopup != null) { - myPopup.closeOk(e); - } - return true; - } - else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { - if (myPopup != null) { - myPopup.cancel(e); - } - return true; - } - return false; - } - - @Override - protected EditorEx createEditor() { - // spell check is not needed - EditorEx editor = super.createEditor(); - SpellCheckingEditorCustomization.getInstance(false).customize(editor); - return editor; - } -} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/TextFieldWithProgress.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/TextFieldWithProgress.java new file mode 100644 index 000000000000..70901fe4caaa --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/TextFieldWithProgress.java @@ -0,0 +1,104 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.vcs.log.ui; + +import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.progress.PerformInBackgroundOption; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.Project; +import com.intellij.spellchecker.ui.SpellCheckingEditorCustomization; +import com.intellij.ui.IdeBorderFactory; +import com.intellij.ui.TextFieldWithAutoCompletion; +import com.intellij.util.ui.AsyncProcessIcon; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.util.Collection; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +public abstract class TextFieldWithProgress extends JPanel { + @NotNull private final TextFieldWithAutoCompletion myTextField; + @NotNull private final AsyncProcessIcon myProgressIcon; + + public TextFieldWithProgress(@NotNull Project project, @NotNull Collection variants) { + super(new BorderLayout()); + setBorder(IdeBorderFactory.createEmptyBorder(3)); + + myProgressIcon = new AsyncProcessIcon("Loading commits"); + myTextField = + new TextFieldWithAutoCompletion(project, new TextFieldWithAutoCompletion.StringsCompletionProvider(variants, null), false, + null) { + @Override + public void setBackground(Color bg) { + super.setBackground(bg); + myProgressIcon.setBackground(bg); + } + + @Override + protected EditorEx createEditor() { + // spell check is not needed + EditorEx editor = super.createEditor(); + SpellCheckingEditorCustomization.getInstance(false).customize(editor); + return editor; + } + + @Override + protected boolean processKeyBinding(KeyStroke ks, final KeyEvent e, int condition, boolean pressed) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + onOk(); + return true; + } + return false; + } + }; + myTextField.setBorder(IdeBorderFactory.createEmptyBorder()); + + myProgressIcon.setOpaque(true); + myProgressIcon.setBackground(myTextField.getBackground()); + + add(myTextField, BorderLayout.CENTER); + add(myProgressIcon, BorderLayout.EAST); + + hideProgress(); + } + + public JComponent getPreferableFocusComponent() { + return myTextField; + } + + public void showProgress() { + myTextField.setEnabled(false); + myProgressIcon.setVisible(true); + } + + public void hideProgress() { + myTextField.setEnabled(true); + myProgressIcon.setVisible(false); + } + + public String getText() { + return myTextField.getText(); + } + + public abstract void onOk(); +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUiImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUiImpl.java index 78ce9caeb2a1..440b3efd1a9a 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUiImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsLogUiImpl.java @@ -1,5 +1,6 @@ package com.intellij.vcs.log.ui; +import com.google.common.util.concurrent.SettableFuture; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; @@ -32,6 +33,7 @@ import javax.swing.table.TableModel; import java.awt.*; import java.util.ArrayList; import java.util.Collection; +import java.util.concurrent.Future; public class VcsLogUiImpl implements VcsLogUi, Disposable { @@ -192,22 +194,28 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable { return myUiProperties.isShowRootNames(); } - public void jumpToCommit(@NotNull Hash commitHash) { + @NotNull + public Future jumpToCommit(@NotNull Hash commitHash) { + SettableFuture future = SettableFuture.create(); jumpTo(commitHash, new PairFunction() { @Override public Integer fun(GraphTableModel model, Hash hash) { return model.getRowOfCommit(hash); } - }); + }, future); + return future; } - public void jumpToCommitByPartOfHash(@NotNull String commitHash) { + @NotNull + public Future jumpToCommitByPartOfHash(@NotNull String commitHash) { + SettableFuture future = SettableFuture.create(); jumpTo(commitHash, new PairFunction() { @Override public Integer fun(GraphTableModel model, String hash) { return model.getRowOfCommitByPartOfHash(hash); } - }); + }, future); + return future; } public void handleAnswer(@Nullable GraphAnswer answer, boolean dataCouldChange) { @@ -235,13 +243,15 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable { } } - private void jumpTo(@NotNull final T commitId, @NotNull final PairFunction rowGetter) { + private void jumpTo(@NotNull final T commitId, @NotNull final PairFunction rowGetter, @NotNull final SettableFuture future) { + if (future.isCancelled()) return; + GraphTableModel model = getModel(); if (model == null) { invokeOnChange(new Runnable() { @Override public void run() { - jumpTo(commitId, rowGetter); + jumpTo(commitId, rowGetter, future); } }); return; @@ -250,12 +260,13 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable { int row = rowGetter.fun(model, commitId); if (row >= 0) { myMainFrame.getGraphTable().jumpToRow(row); + future.set(true); } else if (model.canRequestMore()) { model.requestToLoadMore(new Runnable() { @Override public void run() { - jumpTo(commitId, rowGetter); + jumpTo(commitId, rowGetter, future); } }); } @@ -263,12 +274,13 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable { invokeOnChange(new Runnable() { @Override public void run() { - jumpTo(commitId, rowGetter); + jumpTo(commitId, rowGetter, future); } }); } else { commitNotFound(commitId.toString()); + future.set(false); } } diff --git a/plugins/git4idea/src/git4idea/history/wholeTree/SelectRevisionInGitLogAction.java b/plugins/git4idea/src/git4idea/history/wholeTree/SelectRevisionInGitLogAction.java index 1d7db804019b..0d4c66a44f8e 100644 --- a/plugins/git4idea/src/git4idea/history/wholeTree/SelectRevisionInGitLogAction.java +++ b/plugins/git4idea/src/git4idea/history/wholeTree/SelectRevisionInGitLogAction.java @@ -2,6 +2,11 @@ package git4idea.history.wholeTree; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.PerformInBackgroundOption; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; @@ -22,7 +27,12 @@ import git4idea.i18n.GitBundle; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + public class SelectRevisionInGitLogAction extends DumbAwareAction { + private static final Logger LOG = Logger.getInstance(SelectRevisionInGitLogAction.class); public SelectRevisionInGitLogAction() { super(GitBundle.getString("vcs.history.action.gitlog"), GitBundle.getString("vcs.history.action.gitlog"), null); @@ -30,7 +40,7 @@ public class SelectRevisionInGitLogAction extends DumbAwareAction { @Override public void actionPerformed(@NotNull AnActionEvent event) { - Project project = event.getRequiredData(CommonDataKeys.PROJECT); + final Project project = event.getRequiredData(CommonDataKeys.PROJECT); final VcsRevisionNumber revision = getRevisionNumber(event); if (revision == null) { return; @@ -60,7 +70,7 @@ public class SelectRevisionInGitLogAction extends DumbAwareAction { Runnable selectCommit = new Runnable() { @Override public void run() { - log.jumpToReference(revision.asString()); + jumpToRevisionUnderProgress(project, log, revision); } }; @@ -128,5 +138,25 @@ public class SelectRevisionInGitLogAction extends DumbAwareAction { return null; } - + private static void jumpToRevisionUnderProgress(@NotNull Project project, @NotNull VcsLog log, @NotNull VcsRevisionNumber revision) { + final Future future = log.jumpToReference(revision.asString()); + if (!future.isDone()) { + ProgressManager.getInstance().run(new Task.Backgroundable(project, "Searching for revision " + revision.asString(), false/*can not cancel*/, + PerformInBackgroundOption.ALWAYS_BACKGROUND) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + try { + future.get(); + } + catch (CancellationException ignored) { + } + catch (InterruptedException ignored) { + } + catch (ExecutionException e) { + LOG.error(e); + } + } + }); + } + } }