diff --git a/build.txt b/build.txt index 9a5431c4a6a7..09d78e99910d 100644 --- a/build.txt +++ b/build.txt @@ -1 +1 @@ -102.SNAPSHOT +104.SNAPSHOT diff --git a/build/scripts/dist.gant b/build/scripts/dist.gant index 9b022fba90cc..689a3b95098a 100644 --- a/build/scripts/dist.gant +++ b/build/scripts/dist.gant @@ -64,7 +64,7 @@ def wire_build_date(String buildNumber) { target('default': 'The default target') { depends(compile) layoutAll( - buildNumber: "IC-102.SNAPSHOT", + buildNumber: "IC-104.SNAPSHOT", version: "IdeaX", system_selector: "IntelliJIdeaCE10", jdk_req: "1.6*", diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeArgumentsFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeArgumentsFix.java index 87fbe3ccf8f6..f0940a84f3ed 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeArgumentsFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeArgumentsFix.java @@ -20,6 +20,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.util.PsiUtil; +import com.intellij.refactoring.util.RefactoringUtil; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; @@ -72,11 +73,23 @@ public class AddTypeArgumentsFix extends MethodArgumentFix { final PsiElementFactory factory = JavaPsiFacade.getInstance(expression.getProject()).getElementFactory(); PsiMethodCallExpression copy = (PsiMethodCallExpression)expression.copy(); - final PsiReferenceParameterList parameterList = copy.getMethodExpression().getParameterList(); + final PsiReferenceExpression methodExpression = copy.getMethodExpression(); + final PsiReferenceParameterList parameterList = methodExpression.getParameterList(); LOG.assertTrue(parameterList != null); for (PsiType mapping : mappings) { parameterList.add(factory.createTypeElement(mapping)); } + if (methodExpression.getQualifierExpression() == null) { + final PsiExpression qualifierExpression; + final PsiClass containingClass = method.getContainingClass(); + LOG.assertTrue(containingClass != null); + if (method.hasModifierProperty(PsiModifier.STATIC)) { + qualifierExpression = factory.createReferenceExpression(containingClass); + } else { + qualifierExpression = RefactoringUtil.createThisExpression(method.getManager(), null); + } + methodExpression.setQualifierExpression(qualifierExpression); + } return copy; } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ConvertReturnStatementsVisitor.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ConvertReturnStatementsVisitor.java index 3d888e854ad4..8003afcc7ace 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ConvertReturnStatementsVisitor.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ConvertReturnStatementsVisitor.java @@ -71,7 +71,7 @@ public class ConvertReturnStatementsVisitor implements ReturnStatementsVisitor { final String value = generateValue(returnStatement); latestReplaced = (PsiReturnStatement) myFactory.createStatementFromText("return " + value+";", returnStatement.getParent()); - returnStatement.replace(latestReplaced); + latestReplaced = (PsiReturnStatement)returnStatement.replace(latestReplaced); } return latestReplaced; diff --git a/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/PlainEnterProcessor.java b/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/PlainEnterProcessor.java index 20ef10f7f27b..b8d06069838e 100644 --- a/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/PlainEnterProcessor.java +++ b/java/java-impl/src/com/intellij/codeInsight/editorActions/smartEnter/PlainEnterProcessor.java @@ -71,6 +71,10 @@ public class PlainEnterProcessor implements EnterProcessor { else if (element instanceof PsiDoWhileStatement) { body = ((PsiDoWhileStatement)element).getBody(); } + else if (element instanceof PsiMethod) { + PsiCodeBlock methodBody = ((PsiMethod)element).getBody(); + if (methodBody != null) return methodBody; + } return body instanceof PsiBlockStatement ? ((PsiBlockStatement)body).getCodeBlock() : null; } diff --git a/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java b/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java index 464e6761a0eb..5e851f65886b 100644 --- a/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java +++ b/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java @@ -215,7 +215,8 @@ public class JavaLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSett " x--;\n" + " } while (x > 10) \n" + " }\n" + - " public static void test() { \n" + + " public static void test() \n" + + " throws Exception { \n" + " foo.foo().bar(\"arg1\", \n" + " \"arg2\"); \n" + " } \n" + diff --git a/java/java-impl/src/com/intellij/psi/filters/getters/ClassLiteralGetter.java b/java/java-impl/src/com/intellij/psi/filters/getters/ClassLiteralGetter.java index 1ad4c520cbc6..747f64254254 100644 --- a/java/java-impl/src/com/intellij/psi/filters/getters/ClassLiteralGetter.java +++ b/java/java-impl/src/com/intellij/psi/filters/getters/ClassLiteralGetter.java @@ -58,14 +58,15 @@ public class ClassLiteralGetter extends CompletionProvider shortNameCondition, final PsiType classParameter, CompletionResultSet result) { @@ -79,7 +80,7 @@ public class ClassLiteralGetter extends CompletionProvider) +} diff --git a/java/java-tests/testData/codeInsight/completeStatement/IDEA25139_after.java b/java/java-tests/testData/codeInsight/completeStatement/IDEA25139_after.java new file mode 100644 index 000000000000..54aedb8277b7 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completeStatement/IDEA25139_after.java @@ -0,0 +1,8 @@ + +class Foo { + @Test + public void testValueOf() + { + + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArguments/afterUnqualified.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArguments/afterUnqualified.java new file mode 100644 index 000000000000..2e7cbdc95880 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArguments/afterUnqualified.java @@ -0,0 +1,7 @@ +// "Add explicit type arguments" "true" +import java.util.*; +class Test { + List f() { return new ArrayList(); } + void g(List a) {} + void someMethod() { g(this.f()); } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArguments/afterUnqualifiedStatic.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArguments/afterUnqualifiedStatic.java new file mode 100644 index 000000000000..6ca0a0b28769 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArguments/afterUnqualifiedStatic.java @@ -0,0 +1,7 @@ +// "Add explicit type arguments" "true" +import java.util.*; +class Test { + static List f() { return new ArrayList(); } + void g(List a) {} + void someMethod() { g(Test.f()); } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArguments/beforeUnqualified.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArguments/beforeUnqualified.java new file mode 100644 index 000000000000..8ed862474c9f --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArguments/beforeUnqualified.java @@ -0,0 +1,7 @@ +// "Add explicit type arguments" "true" +import java.util.*; +class Test { + List f() { return new ArrayList(); } + void g(List a) {} + void someMethod() { g(f()); } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArguments/beforeUnqualifiedStatic.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArguments/beforeUnqualifiedStatic.java new file mode 100644 index 000000000000..66bc051cd302 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeArguments/beforeUnqualifiedStatic.java @@ -0,0 +1,7 @@ +// "Add explicit type arguments" "true" +import java.util.*; +class Test { + static List f() { return new ArrayList(); } + void g(List a) {} + void someMethod() { g(f()); } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/inlineLocal/StaticImported.java b/java/java-tests/testData/refactoring/inlineLocal/StaticImported.java new file mode 100644 index 000000000000..eea23b87fda6 --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineLocal/StaticImported.java @@ -0,0 +1,23 @@ +import java.util.ArrayList; +import java.util.List; + +import static Statics._emptyList; + +public class Statics { + + public static List _emptyList() { + return new ArrayList(); + } +} + + +class Foo { + public static void main(String[] args) { + List v1 = _emptyList(); + doSomething(v1); + } + + public static void doSomething(List list) { + + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/inlineLocal/StaticImported.java.after b/java/java-tests/testData/refactoring/inlineLocal/StaticImported.java.after new file mode 100644 index 000000000000..d40a49eb49d4 --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineLocal/StaticImported.java.after @@ -0,0 +1,22 @@ +import java.util.ArrayList; +import java.util.List; + +import static Statics._emptyList; + +public class Statics { + + public static List _emptyList() { + return new ArrayList(); + } +} + + +class Foo { + public static void main(String[] args) { + doSomething(Statics._emptyList()); + } + + public static void doSomething(List list) { + + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/CompleteStatementTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/CompleteStatementTest.java index 52134e7078d2..d63d98fe8239 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/CompleteStatementTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/CompleteStatementTest.java @@ -184,7 +184,15 @@ public class CompleteStatementTest extends EditorActionTestCase { public void testIDEADEV40479() throws Exception { doTest(); } - public void testIDEADEV13019() throws Exception { + public void testIDEADEV13019() throws Exception { + doTestBracesNextLineStyle(); + } + + public void testIDEA25139() throws Exception { + doTestBracesNextLineStyle(); + } + + private void doTestBracesNextLineStyle() throws Exception { CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject()); settings.BRACE_STYLE = CodeStyleSettings.NEXT_LINE; settings.METHOD_BRACE_STYLE = CodeStyleSettings.NEXT_LINE; diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterAlignmentTest.java b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterAlignmentTest.java index df01c1730213..962298ef0b6d 100644 --- a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterAlignmentTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterAlignmentTest.java @@ -251,4 +251,26 @@ public class JavaFormatterAlignmentTest extends AbstractJavaFormatterTest { "}" ); } + + public void testAlignThrowsKeyword() throws Exception { + // Inspired by IDEA-63820 + + getSettings().ALIGN_THROWS_KEYWORD = true; + doClassTest( + "public void test()\n" + + " throws Exception {}", + "public void test()\n" + + "throws Exception {\n" + + "}" + ); + + getSettings().ALIGN_THROWS_KEYWORD = false; + doClassTest( + "public void test()\n" + + " throws Exception {}", + "public void test()\n" + + " throws Exception {\n" + + "}" + ); + } } diff --git a/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineLocalTest.java b/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineLocalTest.java index 8d469b31166e..9f20be7086c9 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineLocalTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineLocalTest.java @@ -123,6 +123,10 @@ public class InlineLocalTest extends LightCodeInsightTestCase { doTest(true); } + public void testStaticImported() throws Exception { + doTest(true); + } + private void doTest(final boolean inlineDef, String conflictMessage) throws Exception { try { doTest(inlineDef); diff --git a/platform/lang-api/src/com/intellij/facet/frameworks/LibrariesDownloadAssistant.java b/platform/lang-api/src/com/intellij/facet/frameworks/LibrariesDownloadAssistant.java index 5d48f92fae26..31d194d21ea9 100644 --- a/platform/lang-api/src/com/intellij/facet/frameworks/LibrariesDownloadAssistant.java +++ b/platform/lang-api/src/com/intellij/facet/frameworks/LibrariesDownloadAssistant.java @@ -56,7 +56,7 @@ public class LibrariesDownloadAssistant { LOG.error(e); } catch (IOException e) { - LOG.error(e); + // no route to host, unknown host, etc. } } diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java b/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java index 698cd3ad5a7f..44b1baa91379 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java @@ -299,6 +299,7 @@ public class CommonCodeStyleSettings { public boolean ALIGN_MULTILINE_ASSIGNMENT = false; public boolean ALIGN_MULTILINE_TERNARY_OPERATION = false; public boolean ALIGN_MULTILINE_THROWS_LIST = false; + public boolean ALIGN_THROWS_KEYWORD = false; public boolean ALIGN_MULTILINE_EXTENDS_LIST = false; public boolean ALIGN_MULTILINE_METHOD_BRACKETS = false; diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/WrappingAndBracesPanel.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/WrappingAndBracesPanel.java index eff6430a43be..038e7608b7c5 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/WrappingAndBracesPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/WrappingAndBracesPanel.java @@ -49,6 +49,7 @@ public class WrappingAndBracesPanel extends OptionTableWithPreviewPanel { addOption("THROWS_LIST_WRAP", WRAPPING_THROWS_LIST, WRAP_OPTIONS, WRAP_VALUES); addOption("ALIGN_MULTILINE_THROWS_LIST", ApplicationBundle.message("wrapping.align.when.multiline"), WRAPPING_THROWS_LIST); + addOption("ALIGN_THROWS_KEYWORD", ApplicationBundle.message("wrapping.align.throws.keyword"), WRAPPING_THROWS_LIST); addOption("THROWS_KEYWORD_WRAP", WRAPPING_THROWS_KEYWORD, WRAP_OPTIONS_FOR_SINGLETON, WRAP_VALUES_FOR_SINGLETON); addOption("METHOD_PARAMETERS_WRAP", WRAPPING_METHOD_PARAMETERS, WRAP_OPTIONS, WRAP_VALUES); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/quickFix/FileReferenceQuickFixProvider.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/quickFix/FileReferenceQuickFixProvider.java index 47a98cf0bc8d..a19edcdf6196 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/quickFix/FileReferenceQuickFixProvider.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/quickFix/FileReferenceQuickFixProvider.java @@ -24,6 +24,8 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.fileTypes.UnknownFileType; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleUtil; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; @@ -33,6 +35,8 @@ import com.intellij.psi.PsiNamedElement; import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReference; import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet; import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Arrays; import java.util.Collection; @@ -70,12 +74,22 @@ public class FileReferenceQuickFixProvider { return Collections.emptyList(); } + PsiElement element = reference.getElement(); + Module module = element != null ? ModuleUtil.findModuleForPsiElement(element) : null; + for (PsiFileSystemItem defaultContext : defaultContexts) { if (defaultContext != null) { final VirtualFile virtualFile = defaultContext.getVirtualFile(); if (virtualFile != null && defaultContext.isDirectory() && virtualFile.isInLocalFileSystem()) { - context = defaultContext; - break; + if (context == null) { + context = defaultContext; + } + else if (module != null && module == getModuleForContext(defaultContext)) { + // fixes IDEA-64156 + // todo: fix it on PsiFileReferenceHelper level in 10.X + context = defaultContext; + break; + } } } } @@ -134,4 +148,10 @@ public class FileReferenceQuickFixProvider { return Arrays.asList(action); } + + @Nullable + private static Module getModuleForContext(@NotNull PsiFileSystemItem context) { + VirtualFile file = context.getVirtualFile(); + return file != null ? ModuleUtil.findModuleForFile(file, context.getProject()) : null; + } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/LookupManager.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/LookupManager.java index 3aec15a4091e..3c085a317604 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/LookupManager.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/LookupManager.java @@ -55,6 +55,8 @@ public abstract class LookupManager { public abstract Lookup showLookup(Editor editor, @NotNull LookupElement[] items, String prefix, @NotNull LookupArranger arranger); public abstract void hideActiveLookup(); + + @Nullable public abstract Lookup getActiveLookup(); @NonNls public static final String PROP_ACTIVE_LOOKUP = "activeLookup"; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java index bff687ebe99b..26baa6ad318d 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java @@ -543,6 +543,7 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable { final PsiFile file = getPsiFile(); if (file != null && !WriteCommandAction.ensureFilesWritable(myProject, Arrays.asList(file))) { + fireItemSelected(null, completionChar); return; } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/actions/ChooseItemReplaceAction.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/actions/ChooseItemReplaceAction.java index 25c2163aeeb8..d1d4690cce1a 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/actions/ChooseItemReplaceAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/actions/ChooseItemReplaceAction.java @@ -19,7 +19,6 @@ package com.intellij.codeInsight.lookup.impl.actions; import com.intellij.codeInsight.completion.CodeCompletionFeatures; import com.intellij.codeInsight.completion.CompletionProcess; import com.intellij.codeInsight.completion.CompletionService; -import com.intellij.codeInsight.completion.CompletionUtil; import com.intellij.codeInsight.lookup.Lookup; import com.intellij.codeInsight.lookup.LookupManager; import com.intellij.codeInsight.lookup.impl.LookupImpl; @@ -31,6 +30,7 @@ import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.actionSystem.EditorAction; import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler; +import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; @@ -74,9 +74,10 @@ public class ChooseItemReplaceAction extends EditorAction { final Editor editor = lookup.getEditor(); PsiDocumentManager.getInstance(file.getProject()).commitDocument(editor.getDocument()); - final int offset = editor.getCaretModel().getOffset(); - final String prefix = CompletionUtil.findJavaIdentifierPrefix(file, offset); - final TemplateImpl template = LiveTemplateCompletionContributor.findApplicableTemplate(file, offset, prefix); + final int end = editor.getCaretModel().getOffset(); + final int start = lookup.getLookupStart(); + final String prefix = editor.getDocument().getText(TextRange.create(start, end)); + final TemplateImpl template = LiveTemplateCompletionContributor.findApplicableTemplate(file, end, prefix); return template != null && shortcutChar == TemplateSettings.getInstance().getShortcutChar(template); } diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java b/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java index 58f7d6fd1d92..e99fca8fb896 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.java @@ -96,7 +96,7 @@ public class ExecutionManagerImpl extends ExecutionManager implements ProjectCom return handlers.toArray(new ProcessHandler[handlers.size()]); } - public void compileAndRun(final Runnable startRunnable, final RunProfile configuration, final RunProfileState state, @Nullable final Runnable onCancelRunnable) { + public void compileAndRun(final Runnable startRunnable, final RunProfile configuration, final @Nullable RunProfileState state, @Nullable final Runnable onCancelRunnable) { if (configuration instanceof RunConfiguration) { final RunConfiguration runConfiguration = (RunConfiguration)configuration; final RunManagerImpl runManager = RunManagerImpl.getInstanceImpl(myProject); @@ -112,7 +112,7 @@ public class ExecutionManagerImpl extends ExecutionManager implements ProjectCom if (!activeProviders.isEmpty()) { ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { public void run() { - ConfigurationPerRunnerSettings configurationSettings = state.getConfigurationSettings(); + ConfigurationPerRunnerSettings configurationSettings = state != null ? state.getConfigurationSettings() : null; DataContext projectContext = SimpleDataContext.getProjectContext(myProject); final DataContext dataContext = configurationSettings != null ? SimpleDataContext 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 2337b752c294..d8aae724b221 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java @@ -57,6 +57,7 @@ import com.intellij.ui.components.labels.LinkListener; import com.intellij.util.ArrayUtil; import com.intellij.util.Consumer; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.text.BadLocationException; @@ -106,10 +107,14 @@ class FindDialog extends DialogWrapper { private JRadioButton myRbCustomScope; private ScopeChooserCombo myScopeCombo; + private LivePreviewController myLivePreviewController; + + public FindDialog(Project project, FindModel model, Runnable myOkHandler){ super(project, true); myProject = project; myModel = model; + this.myOkHandler = myOkHandler; if (myModel.isReplaceState()){ @@ -133,6 +138,7 @@ class FindDialog extends DialogWrapper { setOKButtonIcon(IconLoader.getIcon("/actions/find.png")); init(); initByModel(); + myLivePreviewController = new LivePreviewController(this, new LivePreview(myProject), getContentPane()); } @Override @@ -141,6 +147,9 @@ class FindDialog extends DialogWrapper { e.getKey().removeDocumentListener(e.getValue()); } myComboBoxListeners.clear(); + if (myLivePreviewController != null) { + myLivePreviewController.cleanUp(); + } super.dispose(); } @@ -274,6 +283,22 @@ class FindDialog extends DialogWrapper { validateFindButton(); } + public FindModel getModel() { + return myModel; + } + + @Nullable + public FindModel getCurrentModel() { + FindModel validateModel = (FindModel)myModel.clone(); + applyTo(validateModel, false); + + + if (getValidationInfo(validateModel) == null) { + return validateModel; + } + return null; + } + private static int getCaretPosition(JComboBox comboBox) { Component editorComponent = comboBox.getEditor().getEditorComponent(); if (editorComponent instanceof JTextField){ @@ -293,7 +318,6 @@ class FindDialog extends DialogWrapper { private void validateFindButton() { final String toFind = getStringToFind(); - if (toFind == null || toFind.length() == 0){ setOKStatus(false); return; @@ -416,76 +440,130 @@ class FindDialog extends DialogWrapper { private void doOKAction(boolean findAll) { FindModel validateModel = (FindModel)myModel.clone(); - applyTo(validateModel); - validateModel.setFindAll(findAll); - if (validateModel.getDirectoryName() != null) { - PsiDirectory directory = FindInProjectUtil.getPsiDirectory(validateModel, myProject); - if (directory == null) { + applyTo(validateModel, findAll); + + ValidationInfo validationInfo = getValidationInfo(validateModel); + + if (validationInfo == null) { + + myModel.copyFrom(validateModel); + updateFindSettings(); + + super.doOKAction(); + myOkHandler.run(); + } else { + String message = validationInfo.message; + if (message != null) { Messages.showMessageDialog( myProject, - FindBundle.message("find.directory.not.found.error", validateModel.getDirectoryName()), + message, CommonBundle.getErrorTitle(), Messages.getErrorIcon() ); - return; } } + } - if (validateModel.isRegularExpressions()) { - String toFind = validateModel.getStringToFind(); - try { - Pattern pattern = Pattern.compile(toFind, validateModel.isCaseSensitive() ? Pattern.MULTILINE : Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); - if (pattern.matcher("").matches() && !toFind.endsWith("$") && !toFind.startsWith("^")) { - throw new PatternSyntaxException(FindBundle.message("find.empty.match.regular.expression.error"),toFind, -1); - } - } - catch(PatternSyntaxException e){ - Messages.showMessageDialog( - myProject, - FindBundle.message("find.invalid.regular.expression.error", toFind, e.getDescription()), - CommonBundle.getErrorTitle(), - Messages.getErrorIcon() - ); - return; - } + private void updateFindSettings() { + FindSettings findSettings = FindSettings.getInstance(); + findSettings.setCaseSensitive(myModel.isCaseSensitive()); + if (myModel.isReplaceState()) { + findSettings.setPreserveCase(myModel.isPreserveCase()); } - validateModel.setFileFilter( null ); - FindSettings.getInstance().setFileMask(null); + findSettings.setWholeWordsOnly(myModel.isWholeWordsOnly()); + findSettings.setInStringLiteralsOnly(myModel.isInStringLiteralsOnly()); + findSettings.setInCommentsOnly(myModel.isInCommentsOnly()); - if (useFileFilter!=null && useFileFilter.isSelected() && - myFileFilter.getSelectedItem()!=null - ) { - final String mask = (String)myFileFilter.getSelectedItem(); + findSettings.setRegularExpressions(myModel.isRegularExpressions()); + if (!myModel.isMultipleFiles()){ + findSettings.setForward(myModel.isForward()); + findSettings.setFromCursor(myModel.isFromCursor()); - if (mask.length() > 0) { - try { - FindInProjectUtil.createFileMaskRegExp(mask); // verify that the regexp compiles - validateModel.setFileFilter(mask); - FindSettings.getInstance().setFileMask(mask); - } - catch (PatternSyntaxException ex) { - Messages.showMessageDialog(myProject, FindBundle.message("find.filter.invalid.file.mask.error", myFileFilter.getSelectedItem()), - CommonBundle.getErrorTitle(), Messages.getErrorIcon()); - return; - } + findSettings.setGlobal(myModel.isGlobal()); + } else{ + String directoryName = myModel.getDirectoryName(); + if (directoryName != null && !directoryName.isEmpty()) { + findSettings.setWithSubdirectories(myModel.isWithSubdirectories()); } - else { - Messages.showMessageDialog(myProject, FindBundle.message("find.filter.empty.file.mask.error"), CommonBundle.getErrorTitle(), - Messages.getErrorIcon()); - return; + else if (myRbModule.isSelected()) { + } + else if (myRbCustomScope.isSelected()) { + SearchScope selectedScope = myScopeCombo.getSelectedScope(); + String customScopeName = selectedScope == null ? null : selectedScope.getDisplayName(); + findSettings.setCustomScope(customScopeName); } } if (myCbToSkipResultsWhenOneUsage != null){ - FindSettings.getInstance().setSkipResultsWithOneUsage( + findSettings.setSkipResultsWithOneUsage( isSkipResultsWhenOneUsage() ); } - myModel.copyFrom(validateModel); - super.doOKAction(); - myOkHandler.run(); + findSettings.setFileMask(myModel.getFileFilter()); + } + + @Override + protected boolean postponeValidation() { + return true; + } + + private ValidationInfo getValidationInfo(FindModel model) { + if (myRbDirectory != null && myRbDirectory.isEnabled() && myRbDirectory.isSelected()) { + PsiDirectory directory = FindInProjectUtil.getPsiDirectory(model, myProject); + if (directory == null) { + return new ValidationInfo(FindBundle.message("find.directory.not.found.error", getDirectory()), myDirectoryComboBox); + } + } + + String toFind = (String)myInputComboBox.getSelectedItem(); + if (toFind != null && toFind.isEmpty()) { + return new ValidationInfo("String to find is empty", myInputComboBox); + } + + if (myCbRegularExpressions != null && myCbRegularExpressions.isEnabled()) { + try { + boolean isCaseSensitive = myCbCaseSensitive != null && myCbCaseSensitive.isEnabled(); + Pattern pattern = + Pattern.compile(toFind, isCaseSensitive ? Pattern.MULTILINE : Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); + if (pattern.matcher("").matches() && !toFind.endsWith("$") && !toFind.startsWith("^")) { + return new ValidationInfo(FindBundle.message("find.empty.match.regular.expression.error"), myInputComboBox); + } + } + catch (PatternSyntaxException e) { + return new ValidationInfo(FindBundle.message("find.invalid.regular.expression.error", toFind, e.getDescription()), myInputComboBox); + } + } + + final String mask = myFileFilter == null ? null : (String)myFileFilter.getSelectedItem(); + + if (mask != null) { + if (mask.length() == 0) { + return new ValidationInfo(FindBundle.message("find.filter.empty.file.mask.error"), myFileFilter); + } + else { + try { + FindInProjectUtil.createFileMaskRegExp(mask); // verify that the regexp compiles + } + catch (PatternSyntaxException ex) { + return new ValidationInfo(FindBundle.message("find.filter.invalid.file.mask.error", mask), myFileFilter); + } + } + } + return null; + } + + @Override + protected ValidationInfo doValidate() { + FindModel validateModel = (FindModel)myModel.clone(); + applyTo(validateModel, false); + + ValidationInfo result = getValidationInfo(validateModel); + + setOKStatus(result == null); + + return result; } public void doHelpAction() { @@ -768,7 +846,7 @@ class FindDialog extends DialogWrapper { public void consume(final VirtualFile[] files) { if (files.length != 0) { myDirectoryComboBox.setSelectedItem(files[0].getPresentableUrl()); - validateFindButton(); + //validateFindButton(); } } }); @@ -845,10 +923,12 @@ class FindDialog extends DialogWrapper { } private String getStringToFind() { - return (String)myInputComboBox.getEditor().getItem(); + String string = (String)myInputComboBox.getEditor().getItem(); + return string == null ? "" : string; } private String getStringToReplace() { - return (String)myReplaceComboBox.getEditor().getItem(); + String item = (String)myReplaceComboBox.getEditor().getItem(); + return item == null ? "" : item; } private String getDirectory() { @@ -886,27 +966,26 @@ class FindDialog extends DialogWrapper { } } - private void applyTo(FindModel model) { - FindSettings findSettings = FindSettings.getInstance(); + + + private void applyTo(FindModel model, boolean findAll) { + model.setCaseSensitive(myCbCaseSensitive.isSelected()); - findSettings.setCaseSensitive(myCbCaseSensitive.isSelected()); if (model.isReplaceState()) { model.setPreserveCase(myCbPreserveCase.isSelected()); - findSettings.setPreserveCase(myCbPreserveCase.isSelected()); } model.setWholeWordsOnly(myCbWholeWordsOnly.isSelected()); - findSettings.setWholeWordsOnly(myCbWholeWordsOnly.isSelected()); model.setInStringLiteralsOnly(myCbInStringLiteralsOnly.isSelected()); - findSettings.setInStringLiteralsOnly(myCbInStringLiteralsOnly.isSelected()); model.setInCommentsOnly(myCbInCommentsOnly.isSelected()); - findSettings.setInCommentsOnly(myCbInCommentsOnly.isSelected()); model.setRegularExpressions(myCbRegularExpressions.isSelected()); - findSettings.setRegularExpressions(myCbRegularExpressions.isSelected()); - model.setStringToFind(getStringToFind()); + String stringToFind = getStringToFind(); + if (stringToFind.length() > 0) { + model.setStringToFind(stringToFind); + } if (model.isReplaceState()){ model.setPromptOnReplace(true); @@ -920,11 +999,8 @@ class FindDialog extends DialogWrapper { if (!model.isMultipleFiles()){ model.setForward(myRbForward.isSelected()); - findSettings.setForward(myRbForward.isSelected()); model.setFromCursor(myRbFromCursor.isSelected()); - findSettings.setFromCursor(myRbFromCursor.isSelected()); model.setGlobal(myRbGlobal.isSelected()); - findSettings.setGlobal(myRbGlobal.isSelected()); } else{ if (myCbToOpenInNewTab != null){ @@ -942,7 +1018,6 @@ class FindDialog extends DialogWrapper { String directory = getDirectory(); model.setDirectoryName(directory == null ? "" : directory); model.setWithSubdirectories(myCbWithSubdirectories.isSelected()); - findSettings.setWithSubdirectories(myCbWithSubdirectories.isSelected()); } else if (myRbModule.isSelected()) { model.setModuleName((String)myModuleComboBox.getSelectedItem()); @@ -953,9 +1028,17 @@ class FindDialog extends DialogWrapper { model.setCustomScopeName(customScopeName); model.setCustomScope(selectedScope == null ? null : selectedScope); model.setCustomScope(true); - findSettings.setCustomScope(customScopeName); } } + + model.setFindAll(findAll); + + String mask = null; + if (useFileFilter!=null && useFileFilter.isSelected()) { + mask = (String)myFileFilter.getSelectedItem(); + } + model.setFileFilter(mask); + } @@ -1061,7 +1144,7 @@ class FindDialog extends DialogWrapper { setStringsToComboBox(FindSettings.getInstance().getRecentReplaceStrings(), myReplaceComboBox, myModel.getStringToReplace()); } updateControls(); - validateFindButton(); + } } diff --git a/platform/lang-impl/src/com/intellij/find/impl/LiveOccurrence.java b/platform/lang-impl/src/com/intellij/find/impl/LiveOccurrence.java new file mode 100644 index 000000000000..9297f07ecaeb --- /dev/null +++ b/platform/lang-impl/src/com/intellij/find/impl/LiveOccurrence.java @@ -0,0 +1,27 @@ +package com.intellij.find.impl; + +import com.intellij.openapi.util.TextRange; + +import java.util.ArrayList; +import java.util.Collection; + +public class LiveOccurrence { + private TextRange myPrimaryRange; + private Collection mySecondaryRanges = new ArrayList(); + + public TextRange getPrimaryRange() { + return myPrimaryRange; + } + + public Collection getSecondaryRanges() { + return mySecondaryRanges; + } + + public void setPrimaryRange(TextRange primaryRange) { + this.myPrimaryRange = primaryRange; + } + + public void setSecondaryRanges(Collection secondaryRanges) { + this.mySecondaryRanges = secondaryRanges; + } +} diff --git a/platform/lang-impl/src/com/intellij/find/impl/LivePreview.java b/platform/lang-impl/src/com/intellij/find/impl/LivePreview.java new file mode 100644 index 000000000000..3f70b91db5e1 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/find/impl/LivePreview.java @@ -0,0 +1,421 @@ +package com.intellij.find.impl; + + +import com.intellij.codeInsight.highlighting.HighlightManager; +import com.intellij.ide.IdeTooltipManager; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.ScrollType; +import com.intellij.openapi.editor.VisualPosition; +import com.intellij.openapi.editor.event.DocumentAdapter; +import com.intellij.openapi.editor.event.DocumentEvent; +import com.intellij.openapi.editor.event.VisibleAreaEvent; +import com.intellij.openapi.editor.event.VisibleAreaListener; +import com.intellij.openapi.editor.markup.CustomHighlighterRenderer; +import com.intellij.openapi.editor.markup.RangeHighlighter; +import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.fileEditor.TextEditor; +import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.Balloon; +import com.intellij.openapi.ui.popup.BalloonBuilder; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.awt.RelativePoint; +import com.intellij.util.Alarm; +import com.intellij.util.containers.HashSet; +import com.intellij.util.ui.PositionTracker; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.util.*; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +public class LivePreview extends DocumentAdapter { + + private final Collection myHighlighters = new HashSet(); + private RangeHighlighter myCursorHighlighter; + private final List myVisibleAreaListenersToRemove = new ArrayList(); + + public interface Delegate { + @NotNull + List performSearchInBackgroundInReadAction(Editor editor); + + @Nullable + String getReplacementPreviewText(Editor editor, LiveOccurrence liveOccurrence); + } + + private static final int USER_ACTIVITY_TRIGGERING_DELAY = 300; + + private static final TextAttributes OTHER_TARGETS_ATTRIBUTES = new TextAttributes(Color.BLACK, Color.GREEN, null, null, 0); + private static final TextAttributes MAIN_TARGET_ATTRIBUTES = new TextAttributes(Color.BLACK, Color.YELLOW, null, null, 0); + + private final Alarm myLivePreviewAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD); + + private final Project myProject; + private Editor myEditor; + + private Delegate myDelegate; + + private LiveOccurrence myCursor; + private List mySearchResults; + + private Balloon myReplacementBalloon; + + public LivePreview(Project project) { + this.myProject = project; + updateEditorReference(); + } + + public Delegate getDelegate() { + return myDelegate; + } + + public void setDelegate(Delegate delegate) { + this.myDelegate = delegate; + } + + @Nullable + public Editor updateEditorReference() { + if (myProject == null) return null; + FileEditorManagerEx instanceEx = FileEditorManagerEx.getInstanceEx(myProject); + if (instanceEx != null) { + VirtualFile currentFile = instanceEx.getCurrentFile(); + if (currentFile != null) { + FileEditor[] editors = instanceEx.getEditors(currentFile); + if (editors.length > 0) { + FileEditor fileEditor = editors[0]; + boolean focusedFound = false; + for (FileEditor e : editors) { + if (e instanceof TextEditor && ((TextEditor)e).getEditor().getContentComponent().hasFocus()) { + fileEditor = e; + focusedFound = true; + } + } + boolean needToUpdate = true; + if (!focusedFound) { + for (FileEditor e : editors) { + if (e instanceof TextEditor && ((TextEditor)e).getEditor() == myEditor) { + needToUpdate = false; + } + } + } + if (needToUpdate && fileEditor instanceof TextEditor) { + Editor editor1 = ((TextEditor) fileEditor).getEditor(); + if (editor1 != myEditor) { + cleanUp(); + } + if (myEditor != null) { + myEditor.getDocument().removeDocumentListener(this); + } + myEditor = editor1; + myEditor.getDocument().addDocumentListener(this); + } + } else { + myEditor = null; + } + } else { + myEditor = null; + } + } + return myEditor; + } + + @Override + public void documentChanged(DocumentEvent e) { + update(); + } + + public void update() { + myLivePreviewAlarm.cancelAllRequests(); + if (updateEditorReference() != null) { + myLivePreviewAlarm.addRequest(new Runnable() { + @Override + public void run() { + updateInBackground(); + } + }, USER_ACTIVITY_TRIGGERING_DELAY); + } + } + + public void cleanUp() { + if (myReplacementBalloon != null) { + myReplacementBalloon.hide(); + } + if (myEditor != null) { + for (VisibleAreaListener visibleAreaListener : myVisibleAreaListenersToRemove) { + myEditor.getScrollingModel().removeVisibleAreaListener(visibleAreaListener); + } + myVisibleAreaListenersToRemove.clear(); + for (RangeHighlighter h : myHighlighters) { + HighlightManager.getInstance(myProject).removeSegmentHighlighter(myEditor, h); + } + if (myCursorHighlighter != null) { + HighlightManager.getInstance(myProject).removeSegmentHighlighter(myEditor, myCursorHighlighter); + myCursorHighlighter = null; + } + } + } + + private void updateInBackground() { + if (myDelegate == null) return; + final TextRange oldCursorRange = myCursor != null ? myCursor.getPrimaryRange() : null; + mySearchResults = performSearchInBackground(); + Collections.sort(mySearchResults, new Comparator() { + @Override + public int compare(LiveOccurrence liveOccurrence, LiveOccurrence liveOccurence1) { + return liveOccurrence.getPrimaryRange().getStartOffset() - liveOccurence1.getPrimaryRange().getStartOffset(); + } + }); + if (mySearchResults != null) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + cleanUp(); + highlightUsages(oldCursorRange); + } + }); + } + } + + private List performSearchInBackground() { + final AtomicReference> occurrences = new AtomicReference>(); + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + occurrences.set(myDelegate.performSearchInBackgroundInReadAction(myEditor)); + } + }); + return occurrences.get(); + } + + private void highlightUsages(TextRange oldCursorRange) { + if (myEditor == null) return; + LiveOccurrence firstVisibleOccurrence = null; + LiveOccurrence firstOccurrence = null; + int offset = Integer.MAX_VALUE; + for (LiveOccurrence o : mySearchResults) { + if (insideVisibleArea(myEditor, o.getPrimaryRange())) { + if (firstVisibleOccurrence == null || o.getPrimaryRange().getStartOffset() < firstVisibleOccurrence.getPrimaryRange().getStartOffset()) { + firstVisibleOccurrence = o; + } + } + if (o.getPrimaryRange().getStartOffset() < offset) { + offset = o.getPrimaryRange().getStartOffset(); + firstOccurrence = o; + } + + for (TextRange textRange : o.getSecondaryRanges()) { + highlightRange(textRange, OTHER_TARGETS_ATTRIBUTES, myHighlighters); + } + highlightRange(o.getPrimaryRange(), MAIN_TARGET_ATTRIBUTES, myHighlighters); + } + + if (!tryToRepairOldCursor(oldCursorRange)) { + setCursor(firstVisibleOccurrence != null ? firstVisibleOccurrence : firstOccurrence); + } + } + + private boolean tryToRepairOldCursor(TextRange oldCursorRange) { + if (oldCursorRange == null) return false; + LiveOccurrence mayBeOldCursor = null; + for (LiveOccurrence searchResult : mySearchResults) { + if (searchResult.getPrimaryRange().intersects(oldCursorRange)) { + mayBeOldCursor = searchResult; + break; + } + } + if (mayBeOldCursor != null && insideVisibleArea(myEditor, mayBeOldCursor.getPrimaryRange())) { + setCursor(mayBeOldCursor); + return true; + } + return false; + } + + @Nullable + private LiveOccurrence prevOccurrence(LiveOccurrence o) { + if (mySearchResults == null) return null; + for (int i = 0; i < mySearchResults.size(); ++i) { + if (o == mySearchResults.get(i)) { + if (i > 0) { + return mySearchResults.get(i-1); + } + } + } + return null; + } + + @Nullable + private LiveOccurrence nextOccurrence(LiveOccurrence o) { + if (mySearchResults == null) return null; + boolean found = false; + for (LiveOccurrence occurrence : mySearchResults) { + if (found) { + return occurrence; + } + if (o == occurrence) { + found = true; + } + } + return null; + } + + public void prevOccurrence() { + LiveOccurrence prev = prevOccurrence(myCursor); + if (prev == null && !mySearchResults.isEmpty()) { + prev = mySearchResults.get(mySearchResults.size()-1); + } + moveCursorTo(prev); + } + + public void nextOccurrence() { + LiveOccurrence next = nextOccurrence(myCursor); + if (next == null && !mySearchResults.isEmpty()) { + next = mySearchResults.get(0); + } + moveCursorTo(next); + } + + public void moveCursorTo(LiveOccurrence next) { + if (next != null) { + setCursor(next); + } else { + showReplacementPreview(); + } + } + + private void setCursor(LiveOccurrence liveOccurrence) { + hideBalloon(); + myCursor = liveOccurrence; + + if (myCursorHighlighter != null) { + HighlightManager.getInstance(myProject).removeSegmentHighlighter(myEditor, myCursorHighlighter); + myCursorHighlighter = null; + } + if (myCursor != null) { + ArrayList dummy = new ArrayList(); + highlightRange(myCursor.getPrimaryRange(), new TextAttributes(null, null, null, null, 0), dummy); + if (!dummy.isEmpty()) { + myCursorHighlighter = dummy.get(0); + myCursorHighlighter.setCustomRenderer(new CustomHighlighterRenderer() { + @Override + public void paint(Editor editor, RangeHighlighter highlighter, Graphics g) { + Graphics2D g2d = (Graphics2D)g; + VisualPosition startVp = editor.offsetToVisualPosition(highlighter.getStartOffset()); + VisualPosition endVp = editor.offsetToVisualPosition(highlighter.getEndOffset()); + Point start = editor.visualPositionToXY(startVp); + Point end = editor.visualPositionToXY(endVp); + g2d.setColor(Color.YELLOW); + Point startP = new Point(start.x-5, start.y-5); + int lineHeight = editor.getLineHeight(); + Point endP = new Point(start.x-5, end.y+ lineHeight +10); + g2d.setPaint(new GradientPaint(startP, Color.YELLOW, endP, new Color(255, 200, 0))); + + g2d.fillRoundRect(startP.x, startP.y, + end.x - start.x+10, lineHeight +10, 10, 10); + g2d.setColor(Color.GRAY); + g2d.drawRoundRect(start.x - 5, start.y - 5, + end.x - start.x + 10, lineHeight + 10, 10, 10); + } + }); + } + + if (!insideVisibleArea(myEditor, myCursor.getPrimaryRange())) { + myEditor.getScrollingModel().scrollTo(myEditor.offsetToLogicalPosition(myCursor.getPrimaryRange().getStartOffset()), + ScrollType.CENTER); + myEditor.getScrollingModel().runActionOnScrollingFinished(new Runnable() { + @Override + public void run() { + showReplacementPreview(); + } + }); + } else { + showReplacementPreview(); + } + } + } + + private void showReplacementPreview() { + hideBalloon(); + if (myDelegate != null && myCursor != null) { + String replacementPreviewText = myDelegate.getReplacementPreviewText(myEditor, myCursor); + if (replacementPreviewText != null) { + + JLabel balloonContent = new JLabel(replacementPreviewText); + balloonContent.setForeground(Color.WHITE); + + BalloonBuilder balloonBuilder = JBPopupFactory.getInstance().createBalloonBuilder(balloonContent); + balloonBuilder.setFadeoutTime(0); + balloonBuilder.setFillColor(IdeTooltipManager.GRAPHITE_COLOR); + balloonBuilder.setAnimationCycle(0); + balloonBuilder.setHideOnClickOutside(false); + balloonBuilder.setHideOnKeyOutside(false); + balloonBuilder.setHideOnAction(false); + myReplacementBalloon = balloonBuilder.createBalloon(); + final int startOffset = myCursor.getPrimaryRange().getStartOffset(); + final int endOffset = myCursor.getPrimaryRange().getEndOffset(); + + myReplacementBalloon.show(new PositionTracker(myEditor.getContentComponent()) { + @Override + public RelativePoint recalculateLocation(final Balloon object) { + Point startPoint = myEditor.visualPositionToXY(myEditor.offsetToVisualPosition(startOffset)); + Point endPoint = myEditor.visualPositionToXY(myEditor.offsetToVisualPosition(endOffset)); + Point point = new Point((startPoint.x + endPoint.x)/2, startPoint.y); + if (!insideVisibleArea(myEditor, myCursor.getPrimaryRange())) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + object.hide(); + } + }); + + VisibleAreaListener visibleAreaListener = new VisibleAreaListener() { + @Override + public void visibleAreaChanged(VisibleAreaEvent e) { + if (insideVisibleArea(myEditor, myCursor.getPrimaryRange())) { + showReplacementPreview(); + final VisibleAreaListener visibleAreaListener = this; + myEditor.getScrollingModel().removeVisibleAreaListener(visibleAreaListener); + myVisibleAreaListenersToRemove.remove(visibleAreaListener); + } + } + }; + myEditor.getScrollingModel().addVisibleAreaListener(visibleAreaListener); + myVisibleAreaListenersToRemove.add(visibleAreaListener); + + } + return new RelativePoint(myEditor.getContentComponent(), point); + } + }, Balloon.Position.above); + } + } + } + + private void hideBalloon() { + if (myReplacementBalloon != null) { + myReplacementBalloon.hide(); + myReplacementBalloon = null; + } + } + + private void highlightRange(TextRange textRange, TextAttributes attributes, Collection highlighters) { + HighlightManager highlightManager = HighlightManager.getInstance(myProject); + if (highlightManager != null) { + highlightManager.addRangeHighlight(myEditor, + textRange.getStartOffset(), textRange.getEndOffset(), + attributes, true, highlighters); + } + } + + private static boolean insideVisibleArea(Editor e, TextRange r) { + Rectangle visibleArea = e.getScrollingModel().getVisibleArea(); + Point point = e.logicalPositionToXY(e.offsetToLogicalPosition(r.getStartOffset())); + + return visibleArea.contains(point); + } +} diff --git a/platform/lang-impl/src/com/intellij/find/impl/LivePreviewController.java b/platform/lang-impl/src/com/intellij/find/impl/LivePreviewController.java new file mode 100644 index 000000000000..b8d787f19919 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/find/impl/LivePreviewController.java @@ -0,0 +1,138 @@ +package com.intellij.find.impl; + +import com.intellij.find.FindManager; +import com.intellij.find.FindModel; +import com.intellij.find.FindResult; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.UserActivityListener; +import com.intellij.ui.UserActivityWatcher; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class LivePreviewController implements LivePreview.Delegate, UserActivityListener { + + private static final String EMPTY_STRING_DISPLAY_TEXT = ""; + private final FindDialog myFindDialog; + private final LivePreview myLivePreview; + private final UserActivityWatcher myWatcher = new MomentoUserActivityWatcher(); + private int myMatchesLimit = 100; + + public LivePreviewController(FindDialog findDialog, LivePreview livePreview, Component toWatch) { + myFindDialog = findDialog; + myLivePreview = livePreview; + myLivePreview.setDelegate(this); + myWatcher.register(toWatch); + myWatcher.addUserActivityListener(this); + myFindDialog.getWindow().addWindowFocusListener(new WindowFocusListener() { + @Override + public void windowGainedFocus(WindowEvent windowEvent) { + myLivePreview.update(); + } + + @Override + public void windowLostFocus(WindowEvent windowEvent) { + } + }); + + JComponent contentPane = (JComponent)findDialog.getContentPane(); + new AnAction() { + + @Override + public void actionPerformed(AnActionEvent e) { + myLivePreview.nextOccurrence(); + } + }.registerCustomShortcutSet(KeyEvent.VK_F3, 0, contentPane); + + new AnAction() { + + @Override + public void actionPerformed(AnActionEvent e) { + myLivePreview.prevOccurrence(); + } + }.registerCustomShortcutSet(KeyEvent.VK_F3, KeyEvent.SHIFT_MASK, contentPane); + } + + public void cleanUp() { + myWatcher.removeUserActivityListener(this); + myLivePreview.cleanUp(); + } + + public void setMatchesLimit(int matchesLimit) { + myMatchesLimit = matchesLimit; + } + + public int getMatchesLimit() { + return myMatchesLimit; + } + + @NotNull + @Override + public List performSearchInBackgroundInReadAction(Editor editor) { + ArrayList occurrences = new ArrayList(); + FindModel currentModel = myFindDialog.getCurrentModel(); + if (currentModel != null) { + int offset = 0; + VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(editor.getDocument()); + ArrayList results = new ArrayList(); + + while (true) { + FindManager findManager = FindManager.getInstance(editor.getProject()); + FindResult result = findManager.findString(editor.getDocument().getCharsSequence(), offset, currentModel, virtualFile); + if (!result.isStringFound()) break; + int newOffset = result.getEndOffset(); + if (offset == newOffset) break; + offset = newOffset; + results.add(result); + + if (results.size() > myMatchesLimit) break; + } + if (results.size() < myMatchesLimit) { + findResultsToOccurrences(results, occurrences); + } + } + return occurrences; + } + + @Override + public String getReplacementPreviewText(Editor editor, LiveOccurrence liveOccurrence) { + String foundString = editor.getDocument().getText(liveOccurrence.getPrimaryRange()); + String documentText = editor.getDocument().getText(); + FindModel currentModel = myFindDialog.getCurrentModel(); + String stringToReplace = null; + + if (currentModel != null) { + if (currentModel.isReplaceState()) { + FindManager findManager = FindManager.getInstance(editor.getProject()); + stringToReplace = findManager.getStringToReplace(foundString, currentModel, + liveOccurrence.getPrimaryRange().getStartOffset(), documentText); + if (stringToReplace != null && stringToReplace.isEmpty()) { + stringToReplace = EMPTY_STRING_DISPLAY_TEXT; + } + } + } + return stringToReplace; + } + + private static void findResultsToOccurrences(ArrayList results, Collection occurrences) { + for (FindResult r : results) { + LiveOccurrence occurrence = new LiveOccurrence(); + occurrence.setPrimaryRange(r); + occurrences.add(occurrence); + } + } + + @Override + public void stateChanged() { + myLivePreview.update(); + } +} diff --git a/platform/lang-impl/src/com/intellij/find/impl/MomentoUserActivityWatcher.java b/platform/lang-impl/src/com/intellij/find/impl/MomentoUserActivityWatcher.java new file mode 100644 index 000000000000..010c5fdbd46d --- /dev/null +++ b/platform/lang-impl/src/com/intellij/find/impl/MomentoUserActivityWatcher.java @@ -0,0 +1,31 @@ +package com.intellij.find.impl; + +import com.intellij.ui.UserActivityWatcher; + +import java.awt.*; +import java.util.HashSet; +import java.util.Set; + + +public class MomentoUserActivityWatcher extends UserActivityWatcher { + + Set components = new HashSet(); + + @Override + protected void processComponent(Component parentComponent) { + if (!components.contains(parentComponent)) { + super.processComponent(parentComponent); + components.add(parentComponent); + } + } + + @Override + protected void unprocessComponent(Component component) { + components.remove(component); + super.unprocessComponent(component); + } + + public boolean isWatched(Component c) { + return components.contains(c); + } +} diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/BaseProjectTreeBuilder.java b/platform/lang-impl/src/com/intellij/ide/projectView/BaseProjectTreeBuilder.java index c08e2e80381c..02edb5816ecf 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/BaseProjectTreeBuilder.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/BaseProjectTreeBuilder.java @@ -109,7 +109,9 @@ public abstract class BaseProjectTreeBuilder extends AbstractTreeBuilder { } protected final void expandNodeChildren(final DefaultMutableTreeNode node) { - Object element = ((NodeDescriptor)node.getUserObject()).getElement(); + final NodeDescriptor userObject = (NodeDescriptor)node.getUserObject(); + if (userObject == null) return; + Object element = userObject.getElement(); VirtualFile virtualFile = getFileToRefresh(element); super.expandNodeChildren(node); if (virtualFile != null) { diff --git a/platform/lang-impl/src/com/intellij/ide/scriptingContext/ScriptingLibraryMappings.java b/platform/lang-impl/src/com/intellij/ide/scriptingContext/ScriptingLibraryMappings.java index e3cdb712de0c..376cd7b3cf0d 100644 --- a/platform/lang-impl/src/com/intellij/ide/scriptingContext/ScriptingLibraryMappings.java +++ b/platform/lang-impl/src/com/intellij/ide/scriptingContext/ScriptingLibraryMappings.java @@ -375,6 +375,24 @@ public class ScriptingLibraryMappings extends LanguagePerFileMappings getSourceFiles() { + Set sourceFiles = new HashSet(); + for (ScriptingLibraryTable.LibraryModel libModel : myLibraries.values()) { + sourceFiles.addAll(libModel.getSourceFiles()); + } + return sourceFiles; + } + + @Override + public Set getCompactFiles() { + Set compactFiles = new HashSet(); + for (ScriptingLibraryTable.LibraryModel libModel : myLibraries.values()) { + compactFiles.addAll(libModel.getCompactFiles()); + } + return compactFiles; + } } /** @@ -400,6 +418,21 @@ public class ScriptingLibraryMappings extends LanguagePerFileMappings getLibraryFilesFor(VirtualFile srcFile) { + Set libFiles = new HashSet(); + collectLibraryFilesFor(srcFile, libFiles); + return libFiles; + } + + private void collectLibraryFilesFor(VirtualFile file, Set libFiles) { + if (file == null) return; + ScriptingLibraryTable.LibraryModel libraryModel = getMapping(file); + if (libraryModel != null) { + libFiles.addAll(libraryModel.getCompactFiles()); + libFiles.addAll(libraryModel.getSourceFiles()); + } + collectLibraryFilesFor(file.getParent(), libFiles); + } } diff --git a/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/EditLibraryDialog.java b/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/EditLibraryDialog.java index 5618ea8e8dc3..e50517cb219e 100644 --- a/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/EditLibraryDialog.java +++ b/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/EditLibraryDialog.java @@ -44,6 +44,7 @@ import java.awt.*; import java.awt.event.*; import java.util.ArrayList; import java.util.Arrays; +import java.util.Set; public class EditLibraryDialog extends DialogWrapper { @@ -290,11 +291,11 @@ public class EditLibraryDialog extends DialogWrapper { fireTableDataChanged(); } - public void setFiles(VirtualFile[] sourceFiles, VirtualFile[] compactFiles) { + public void setFiles(Set sourceFiles, Set compactFiles) { myFiles.clear(); - myFiles.addAll(Arrays.asList(sourceFiles)); - myFiles.addAll(Arrays.asList(compactFiles)); - myCompactFiles.addAll(Arrays.asList(compactFiles)); + myFiles.addAll(sourceFiles); + myFiles.addAll(compactFiles); + myCompactFiles.addAll(compactFiles); } public void setFileType(VirtualFile file, boolean isCompact) { diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/libraries/scripting/ScriptingLibrariesScope.java b/platform/lang-impl/src/com/intellij/openapi/roots/libraries/scripting/ScriptingLibrariesScope.java index 75d549c0dcbc..3775379d474f 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/libraries/scripting/ScriptingLibrariesScope.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/libraries/scripting/ScriptingLibrariesScope.java @@ -30,7 +30,7 @@ public class ScriptingLibrariesScope extends GlobalSearchScope { private GlobalSearchScope myBaseScope; private ScriptingIndexableSetContributor myContributor; - private Set myLibraryFiles; + protected Set myLibraryFiles; public ScriptingLibrariesScope(GlobalSearchScope baseScope, Class providerClass) { super(baseScope.getProject()); @@ -38,21 +38,16 @@ public class ScriptingLibrariesScope extends GlobalSearchScope { IndexableSetContributor contributor = IndexableSetContributor.EP_NAME.findExtension(providerClass); if (contributor instanceof ScriptingIndexableSetContributor) { myContributor = (ScriptingIndexableSetContributor)contributor; - updateLibraryFiles(); + setLibraryFiles(); } } - public void updateLibraryFiles() { + public void setLibraryFiles() { myLibraryFiles = myContributor.getLibraryFiles(myBaseScope.getProject()); } public boolean contains(VirtualFile file) { - return myBaseScope.contains(file) | contributorContains(file); - } - - private boolean contributorContains(VirtualFile file) { - if (myContributor == null) return false; - return myLibraryFiles.contains(file); + return myBaseScope.contains(file) | (myLibraryFiles == null ? false : myLibraryFiles.contains(file)); } @Override diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/libraries/scripting/ScriptingLibraryTable.java b/platform/lang-impl/src/com/intellij/openapi/roots/libraries/scripting/ScriptingLibraryTable.java index 77c20de37c41..a01a247226d9 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/libraries/scripting/ScriptingLibraryTable.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/libraries/scripting/ScriptingLibraryTable.java @@ -79,7 +79,7 @@ public class ScriptingLibraryTable { if (myCompactFilesCache == null) { myCompactFilesCache = new HashSet(); for (LibraryModel libraryModel : myLibraryModels) { - myCompactFilesCache.addAll(Arrays.asList(libraryModel.getCompactFiles())); + myCompactFilesCache.addAll(libraryModel.getCompactFiles()); } } return myCompactFilesCache.contains(file); @@ -149,8 +149,8 @@ public class ScriptingLibraryTable { public static class LibraryModel { private String myName; - private ArrayList mySourceFiles = new ArrayList(); - private ArrayList myCompactFiles = new ArrayList(); + private Set mySourceFiles = new HashSet(); + private Set myCompactFiles = new HashSet(); private ArrayList myDocUrls = new ArrayList(); public LibraryModel(String name, VirtualFile[] sourceFiles, VirtualFile[] compactFiles, String[] docUrls) { @@ -183,12 +183,12 @@ public class ScriptingLibraryTable { myDocUrls.addAll(Arrays.asList(docUrls)); } - public VirtualFile[] getSourceFiles() { - return mySourceFiles.toArray(new VirtualFile[mySourceFiles.size()]); + public Set getSourceFiles() { + return mySourceFiles; } - - public VirtualFile[] getCompactFiles() { - return myCompactFiles.toArray(new VirtualFile[myCompactFiles.size()]); + + public Set getCompactFiles() { + return myCompactFiles; } public String[] getDocUrls() { @@ -196,7 +196,7 @@ public class ScriptingLibraryTable { } @NotNull - public VirtualFile[] getFiles(OrderRootType rootType) { + public Set getFiles(OrderRootType rootType) { if (rootType == COMPACT_ROOT_TYPE) { return getCompactFiles(); } diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java index a0b1f5aff4c1..2fa225f058cb 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java @@ -929,9 +929,8 @@ public class SingleInspectionProfilePanel extends JPanel { if (myShareProfile != (mySelectedProfile.getProfileManager() == myProjectProfileManager)) return true; if (!Comparing.strEqual(myInitialProfile, mySelectedProfile.getName())) return true; if (descriptorsAreChanged()) { - return setSelectedProfileModified(true); + return true; } - setSelectedProfileModified(false); return false; } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/DefaultPomTargetDescriptionProvider.java b/platform/lang-impl/src/com/intellij/psi/impl/DefaultPomTargetDescriptionProvider.java index cd9bd8172a27..278bec95cd4e 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/DefaultPomTargetDescriptionProvider.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/DefaultPomTargetDescriptionProvider.java @@ -35,14 +35,19 @@ public class DefaultPomTargetDescriptionProvider extends PomDescriptionProvider if (element instanceof PsiElement) return null; if (location == UsageViewTypeLocation.INSTANCE) { - return TypeNameManager.getTypeName(element.getClass()); + return getTypeName(element); } if (location == UsageViewNodeTextLocation.INSTANCE) { - return TypeNameManager.getTypeName(element.getClass()) + " " + StringUtil.notNullize(element instanceof PomNamedTarget ? ((PomNamedTarget)element).getName() : null, "''"); + return getTypeName(element) + " " + StringUtil.notNullize(element instanceof PomNamedTarget ? ((PomNamedTarget)element).getName() : null, "''"); } if (location instanceof HighlightUsagesDescriptionLocation) { - return TypeNameManager.getTypeName(element.getClass()); + return getTypeName(element); } return null; } + + private static String getTypeName(PomTarget element) { + final String s = TypeNameManager._getTypeName(element.getClass()); + return s == null ? "Element" : s; + } } diff --git a/platform/lang-impl/src/com/intellij/refactoring/rename/PsiElementRenameHandler.java b/platform/lang-impl/src/com/intellij/refactoring/rename/PsiElementRenameHandler.java index a089ec7ba528..9f184dab1168 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/rename/PsiElementRenameHandler.java +++ b/platform/lang-impl/src/com/intellij/refactoring/rename/PsiElementRenameHandler.java @@ -99,10 +99,6 @@ public class PsiElementRenameHandler implements RenameHandler { if (element == null) return ""; if (!(element instanceof PsiFile) && CollectHighlightsUtil.isOutsideSourceRootJavaFile(element.getContainingFile())) return ""; - if (!element.isWritable()) { - return RefactoringBundle.getCannotRefactorMessage("This element cannot be renamed."); - } - boolean hasRenameProcessor = RenamePsiElementProcessor.forElement(element) != RenamePsiElementProcessor.DEFAULT; boolean hasWritableMetaData = element instanceof PsiMetaOwner && ((PsiMetaOwner)element).getMetaData() instanceof PsiWritableMetaData; @@ -110,9 +106,15 @@ public class PsiElementRenameHandler implements RenameHandler { return RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("error.wrong.caret.position.symbol.to.rename")); } - if (!PsiManager.getInstance(project).isInProject(element) && element.isPhysical()) { - return RefactoringBundle - .getCannotRefactorMessage(RefactoringBundle.message("error.out.of.project.element", UsageViewUtil.getType(element))); + if (!PsiManager.getInstance(project).isInProject(element)) { + if (element.isPhysical()) { + return RefactoringBundle + .getCannotRefactorMessage(RefactoringBundle.message("error.out.of.project.element", UsageViewUtil.getType(element))); + } + + if (!element.isWritable()) { + return RefactoringBundle.getCannotRefactorMessage("This element cannot be renamed."); + } } if (InjectedLanguageUtil.isInInjectedLanguagePrefixSuffix(element)) { diff --git a/platform/platform-api/src/com/intellij/ide/plugins/PluginManager.java b/platform/platform-api/src/com/intellij/ide/plugins/PluginManager.java index 1505d7073b69..2fc1343026e7 100644 --- a/platform/platform-api/src/com/intellij/ide/plugins/PluginManager.java +++ b/platform/platform-api/src/com/intellij/ide/plugins/PluginManager.java @@ -760,11 +760,11 @@ public class PluginManager { ourBuildNumber = BuildNumber.fromString(new String(FileUtil.loadFileText(buildTxtFile)).trim()); } else { - ourBuildNumber = BuildNumber.fromString("102.SNAPSHOT"); + ourBuildNumber = BuildNumber.fromString("104.SNAPSHOT"); } } catch (IOException e) { - ourBuildNumber = BuildNumber.fromString("102.SNAPSHOT"); + ourBuildNumber = BuildNumber.fromString("104.SNAPSHOT"); } } } diff --git a/platform/platform-api/src/com/intellij/notification/Notifications.java b/platform/platform-api/src/com/intellij/notification/Notifications.java index 2f71c57a27a5..e056d4ee7f37 100644 --- a/platform/platform-api/src/com/intellij/notification/Notifications.java +++ b/platform/platform-api/src/com/intellij/notification/Notifications.java @@ -49,19 +49,36 @@ public interface Notifications { } public static void notify(@NotNull final Notification notification, @NotNull final NotificationDisplayType defaultDisplayType, @Nullable final Project project) { - if (project != null && !project.isInitialized()) { - StartupManager.getInstance(project).runWhenProjectIsInitialized(new Runnable() { - public void run() { - project.getMessageBus().syncPublisher(TOPIC).notify(notification, defaultDisplayType); - } - }); + if (project != null && !project.isDisposed()) { + if (!project.isInitialized()) { + StartupManager.getInstance(project).runWhenProjectIsInitialized(new Runnable() { + public void run() { + project.getMessageBus().syncPublisher(TOPIC).notify(notification, defaultDisplayType); + } + }); + } + else { + if (EventQueue.isDispatchThread()) { + project.getMessageBus().syncPublisher(TOPIC).notify(notification, defaultDisplayType); + } + else { + //noinspection SSBasedInspection + SwingUtilities.invokeLater(new Runnable() { + public void run() { + project.getMessageBus().syncPublisher(TOPIC).notify(notification, defaultDisplayType); + } + }); + } + } + return; } FrameStateManager.getInstance().getApplicationActive().doWhenDone(new Runnable() { @Override public void run() { - final MessageBus bus = project == null ? ApplicationManager.getApplication().getMessageBus() : (project.isDisposed() ? null : project.getMessageBus()); + final MessageBus bus = + project == null ? ApplicationManager.getApplication().getMessageBus() : (project.isDisposed() ? null : project.getMessageBus()); if (bus != null) { if (EventQueue.isDispatchThread()) { bus.syncPublisher(TOPIC).notify(notification, defaultDisplayType); diff --git a/platform/platform-api/src/com/intellij/openapi/ui/popup/BalloonBuilder.java b/platform/platform-api/src/com/intellij/openapi/ui/popup/BalloonBuilder.java index 17fec4ebed64..1bbaf6010828 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/popup/BalloonBuilder.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/popup/BalloonBuilder.java @@ -69,4 +69,8 @@ public interface BalloonBuilder { @NotNull BalloonBuilder setPositionChangeYShift(int positionChangeYShift); + + boolean isHideOnAction(); + + BalloonBuilder setHideOnAction(boolean hideOnAction); } \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/AbstractUnappliableEditorCustomization.java b/platform/platform-impl/src/com/intellij/AbstractUnappliableEditorCustomization.java deleted file mode 100644 index 65348be2e58f..000000000000 --- a/platform/platform-impl/src/com/intellij/AbstractUnappliableEditorCustomization.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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. - * 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; - -import com.intellij.openapi.editor.ex.EditorEx; -import com.intellij.ui.EditorCustomization; -import org.jetbrains.annotations.NotNull; - -/** - * Basic super class for {@link EditorCustomization editor customizations} that don't consider un-applying feature. - * - * @author Denis Zhdanov - * @since 1/14/11 12:52 PM - */ -public abstract class AbstractUnappliableEditorCustomization implements EditorCustomization { - - @Override - public void removeCustomization(@NotNull EditorEx editor, @NotNull Feature feature) { - // Do nothing - } -} diff --git a/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java b/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java index 9dcee560e5ea..4238707e2b61 100644 --- a/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java +++ b/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java @@ -53,6 +53,7 @@ import java.awt.event.MouseEvent; public class IdeTooltipManager implements ApplicationComponent, AWTEventListener { + public static final Color GRAPHITE_COLOR = new Color(100, 100, 100, 230); private RegistryValue myIsEnabled; private Component myCurrentComponent; @@ -304,7 +305,7 @@ public class IdeTooltipManager implements ApplicationComponent, AWTEventListener } public Color getTextBackground(boolean awtTooltip) { - return useGraphite(awtTooltip) ? new Color(100, 100, 100, 230) : UIUtil.getToolTipBackground(); + return useGraphite(awtTooltip) ? GRAPHITE_COLOR : UIUtil.getToolTipBackground(); } public String getUlImg(boolean awtTooltip) { diff --git a/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordSafePromptDialog.java b/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordSafePromptDialog.java index ccc4bcee5b4d..d83651858622 100644 --- a/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordSafePromptDialog.java +++ b/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordSafePromptDialog.java @@ -98,6 +98,11 @@ public class PasswordSafePromptDialog extends DialogWrapper { return myRoot; } + @Override + public JComponent getPreferredFocusedComponent() { + return myPasswordPasswordField; + } + /** * Ask password possibly asking password database first. The method could be invoked from any thread. If UI needs to be shown, * the method invokes {@link com.intellij.util.ui.UIUtil#invokeAndWaitIfNeeded(Runnable)} diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java index ecdaca94248f..a3f1a005670d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java @@ -165,9 +165,9 @@ public class EditorActionUtil { if (isCamel) { if (firstIsIdentifierPart && secondIsIdentifierPart && - (Character.isLowerCase(prev) && Character.isUpperCase(current) || + (isLowerCaseOrDigit(prev) && Character.isUpperCase(current) || prev == '_' && current != '_' || - Character.isUpperCase(prev) && Character.isUpperCase(current) && Character.isLowerCase(next))) { + Character.isUpperCase(prev) && Character.isUpperCase(current) && isLowerCaseOrDigit(next))) { return true; } } @@ -175,6 +175,10 @@ public class EditorActionUtil { return (Character.isWhitespace(prev) || firstIsIdentifierPart) && !Character.isWhitespace(current) && !secondIsIdentifierPart; } + + private static boolean isLowerCaseOrDigit(char c) { + return Character.isLowerCase(c) || Character.isDigit(c); + } public static boolean isWordEnd(CharSequence text, int offset, boolean isCamel) { char prev = offset > 0 ? text.charAt(offset - 1) : 0; diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java index bae51f8cb205..9d4e8afd9e36 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java @@ -111,6 +111,7 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener, public void moveToVisualPosition(@NotNull VisualPosition pos) { assertIsDispatchThread(); validateCallContext(); + myDesiredX = -1; int column = pos.column; int line = pos.line; @@ -213,7 +214,7 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener, int newLineNumber = visualCaret.line + lineShift; int newColumnNumber = visualCaret.column + columnShift; if (desiredX >= 0) { - newColumnNumber = myEditor.xyToVisualPosition(new Point(desiredX, newLineNumber * myEditor.getLineHeight())).column; + newColumnNumber = myEditor.xyToVisualPosition(new Point(desiredX, Math.max(0, newLineNumber) * myEditor.getLineHeight())).column; } Document document = myEditor.getDocument(); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index 13f4ed7e29b6..3887e3583e17 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -1015,7 +1015,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi charWidth = EditorUtil.charWidth(c, fontType, this); } - if (charWidth <= 0) { + if (charWidth < 0) { charWidth = spaceSize; } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SelectionModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SelectionModelImpl.java index 73bf8957b66a..98de2ccac0ea 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SelectionModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SelectionModelImpl.java @@ -311,7 +311,13 @@ public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentLi FoldRegion endFold = foldingModel.getCollapsedRegionAtOffset(endOffset); if (endFold != null) { - endOffset = endFold.getEndOffset(); + VisualPosition visualFoldStart = myEditor.offsetToVisualPosition(endFold.getStartOffset()); + // All visual positions that lay at collapsed fold region placeholder are mapped to the same offset. Hence, there are + // at least two distinct situations - selection end is located inside collapsed fold region placeholder and just before it. + // We want to expand selection to the fold region end at the former case and keep selection as-is at the latest one. + if (visualFoldStart.column < endPosition.column) { + endOffset = endFold.getEndOffset(); + } } int oldSelectionStart; @@ -583,6 +589,20 @@ public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentLi if (marker != null) { int startOffset = marker.getStartOffset(); int endOffset = marker.getEndOffset(); + if (caretOffset != startOffset && caretOffset != endOffset) { + // Try to check if current selection is tweaked by fold region. + FoldingModelEx foldingModel = myEditor.getFoldingModel(); + FoldRegion foldRegion = foldingModel.getCollapsedRegionAtOffset(caretOffset); + if (foldRegion != null) { + if (foldRegion.getStartOffset() == startOffset) { + return endOffset; + } + else if (foldRegion.getEndOffset() == endOffset) { + return startOffset; + } + } + } + if (caretOffset == endOffset) { return startOffset; } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/FileTextFieldImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/FileTextFieldImpl.java index ddda44542ffa..c1e7aa6858b0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/FileTextFieldImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/FileTextFieldImpl.java @@ -51,6 +51,7 @@ import javax.swing.text.Document; import javax.swing.text.JTextComponent; import java.awt.*; import java.awt.event.*; +import java.io.File; import java.util.*; import java.util.List; @@ -535,6 +536,7 @@ public abstract class FileTextFieldImpl implements FileLookup, Disposable, FileT if (currentDiff == diff) { toPreselect = each; toPreselectFixed = true; + break; } } @@ -597,7 +599,10 @@ public abstract class FileTextFieldImpl implements FileLookup, Disposable, FileT if (typed == null) return null; LookupFile lastFound = myFinder.find(typed); if (lastFound == null) return null; - if (lastFound.exists()) return lastFound; + if (lastFound.exists()) { + if (typed.charAt(typed.length() - 1) != File.separatorChar) return lastFound.getParent(); + return lastFound; + } final String[] splits = myFinder.normalize(typed).split(myFileSpitRegExp); StringBuffer fullPath = new StringBuffer(); diff --git a/platform/platform-impl/src/com/intellij/platform/NewDirectoryProjectDialog.form b/platform/platform-impl/src/com/intellij/platform/NewDirectoryProjectDialog.form index 9aeb85636ba0..2c47a21e834b 100644 --- a/platform/platform-impl/src/com/intellij/platform/NewDirectoryProjectDialog.form +++ b/platform/platform-impl/src/com/intellij/platform/NewDirectoryProjectDialog.form @@ -17,7 +17,7 @@ - + diff --git a/platform/platform-impl/src/com/intellij/platform/NewDirectoryProjectDialog.java b/platform/platform-impl/src/com/intellij/platform/NewDirectoryProjectDialog.java index 0a46b047b627..47369f156cfd 100644 --- a/platform/platform-impl/src/com/intellij/platform/NewDirectoryProjectDialog.java +++ b/platform/platform-impl/src/com/intellij/platform/NewDirectoryProjectDialog.java @@ -49,6 +49,7 @@ public class NewDirectoryProjectDialog extends DialogWrapper { private JPanel myRootPane; private JComboBox myProjectTypeComboBox; private JPanel myProjectTypePanel; + private JLabel myLocationLabel; private String myBaseDir; private boolean myModifyingLocation = false; private boolean myModifyingProjectName = false; @@ -60,6 +61,8 @@ public class NewDirectoryProjectDialog extends DialogWrapper { setTitle("Create New Project"); init(); + myLocationLabel.setLabelFor(myLocationField.getChildComponent()); + myBaseDir = getBaseDir(); File projectName = FileUtil.findSequentNonexistentFile(new File(myBaseDir), "untitled", ""); myLocationField.setText(projectName.toString()); diff --git a/platform/platform-impl/src/com/intellij/ui/AbstractEditorCustomization.java b/platform/platform-impl/src/com/intellij/ui/AbstractEditorCustomization.java new file mode 100644 index 000000000000..1a477e8e7406 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/AbstractEditorCustomization.java @@ -0,0 +1,77 @@ +/* + * 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. + * 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; + +import com.intellij.openapi.editor.ex.EditorEx; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.EnumSet; +import java.util.Set; + +/** + * Base super class for {@link EditorCustomization editor customizations} that provide the following: + *
+ * 
    + *
  • + * Don't process {@link #addCustomization(EditorEx, Feature)} and {@link #removeCustomization(EditorEx, Feature)} if given feature + * is not supported by the current customization (supported features are defined at constructor); + *
  • + *
+ *
+ * + * @author Denis Zhdanov + * @since 1/24/11 3:56 PM + */ +public abstract class AbstractEditorCustomization implements EditorCustomization { + + private final Set myFeatures = EnumSet.noneOf(Feature.class); + + protected AbstractEditorCustomization(@NotNull Feature... features) { + myFeatures.addAll(Arrays.asList(features)); + } + + @Override + public Set getSupportedFeatures() { + return myFeatures; + } + + @Override + public void addCustomization(@NotNull EditorEx editor, @NotNull Feature feature) { + if (!myFeatures.contains(feature)) { + return; + } + doProcessCustomization(editor, feature, true); + } + + @Override + public void removeCustomization(@NotNull EditorEx editor, @NotNull Feature feature) { + if (!myFeatures.contains(feature)) { + return; + } + doProcessCustomization(editor, feature, false); + } + + /** + * Template method for sub-classes to process target feature applying/removal and being sure that given feature + * is supported by the current customization. + * + * @param editor target editor to apply the given feature + * @param feature target feature to apply to the given editor + * @param apply flag the identifies if given feature should be applied/removed from the given editor + */ + protected abstract void doProcessCustomization(@NotNull EditorEx editor, @NotNull Feature feature, boolean apply); +} diff --git a/platform/platform-impl/src/com/intellij/ui/AdditionalPageAtBottomEditorCustomization.java b/platform/platform-impl/src/com/intellij/ui/AdditionalPageAtBottomEditorCustomization.java index 2aedf8499e1a..c4c8abce75ac 100644 --- a/platform/platform-impl/src/com/intellij/ui/AdditionalPageAtBottomEditorCustomization.java +++ b/platform/platform-impl/src/com/intellij/ui/AdditionalPageAtBottomEditorCustomization.java @@ -18,29 +18,20 @@ package com.intellij.ui; import com.intellij.openapi.editor.ex.EditorEx; import org.jetbrains.annotations.NotNull; -import java.util.EnumSet; -import java.util.Set; - /** * {@link EditorCustomization} for {@link EditorCustomization.Feature#ADDITIONAL_PAGE_AT_BOTTOM}. * * @author Denis Zhdanov * @since 1/21/11 4:06 PM */ -public class AdditionalPageAtBottomEditorCustomization implements EditorCustomization { +public class AdditionalPageAtBottomEditorCustomization extends AbstractEditorCustomization { - @Override - public Set getSupportedFeatures() { - return EnumSet.of(Feature.ADDITIONAL_PAGE_AT_BOTTOM); + public AdditionalPageAtBottomEditorCustomization() { + super(Feature.ADDITIONAL_PAGE_AT_BOTTOM); } @Override - public void addCustomization(@NotNull EditorEx editor, @NotNull Feature feature) { - editor.getSettings().setAdditionalPageAtBottom(true); - } - - @Override - public void removeCustomization(@NotNull EditorEx editor, @NotNull Feature feature) { - editor.getSettings().setAdditionalPageAtBottom(false); + protected void doProcessCustomization(@NotNull EditorEx editor, @NotNull Feature feature, boolean apply) { + editor.getSettings().setAdditionalPageAtBottom(apply); } } diff --git a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java index c7f115096577..8cba5a58fec3 100644 --- a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java @@ -197,6 +197,7 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi private final JComponent myContent; private final boolean myHideOnMouse; private final boolean myHideOnKey; + private boolean myHideOnAction; private final boolean myEnableCloseButton; private final Icon myCloseButton = IconLoader.getIcon("/general/balloonClose.png"); @@ -205,6 +206,7 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi Color fillColor, boolean hideOnMouse, boolean hideOnKey, + boolean hideOnAction, boolean showPointer, boolean enableCloseButton, long fadeoutTime, @@ -220,6 +222,7 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi myContent = content; myHideOnMouse = hideOnMouse; myHideOnKey = hideOnKey; + myHideOnAction = hideOnAction; myShowPointer = showPointer; myEnableCloseButton = enableCloseButton; myHideOnFrameResize = hideOnFrameResize; @@ -380,6 +383,14 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi Toolkit.getDefaultToolkit().addAWTEventListener(myAwtActivityListener, MouseEvent.MOUSE_EVENT_MASK | MouseEvent.MOUSE_MOTION_EVENT_MASK | KeyEvent.KEY_EVENT_MASK); + ActionManager.getInstance().addAnActionListener(new AnActionListener.Adapter() { + @Override + public void beforeActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) { + if (myHideOnAction) { + hide(); + } + } + }, this); } private Rectangle getRecForPosition(Position position, boolean adjust) { @@ -462,13 +473,13 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi } myAnimator = new Animator("Balloon", 10, myAnimationCycle, false, 0, 1, forward) { public void paintNow(final float frame, final float totalFrames, final float cycle) { - if (myComp.getParent() == null) return; + if (myComp == null || myComp.getParent() == null) return; myComp.setAlpha(frame / totalFrames); } @Override protected void paintCycleEnd() { - if (myComp.getParent() == null) return; + if (myComp == null || myComp.getParent() == null) return; if (isForward()) { myComp.clear(); @@ -1230,7 +1241,7 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi //pane.setBorder(new LineBorder(Color.blue)); - balloon.set(new BalloonImpl(new JLabel("FUCK"), Color.black, MessageType.ERROR.getPopupBackground(), true, true, true, true, 0, true, null, false, 500, 5, 0, 0)); + balloon.set(new BalloonImpl(new JLabel("FUCK"), Color.black, MessageType.ERROR.getPopupBackground(), true, true, true, true, true, 0, true, null, false, 500, 5, 0, 0)); balloon.get().setShowPointer(true); if (e.isShiftDown()) { diff --git a/platform/platform-impl/src/com/intellij/ui/HorizontalScrollBarEditorCustomization.java b/platform/platform-impl/src/com/intellij/ui/HorizontalScrollBarEditorCustomization.java index 14ed975ec6bd..e9654ccc96c1 100644 --- a/platform/platform-impl/src/com/intellij/ui/HorizontalScrollBarEditorCustomization.java +++ b/platform/platform-impl/src/com/intellij/ui/HorizontalScrollBarEditorCustomization.java @@ -18,29 +18,19 @@ package com.intellij.ui; import com.intellij.openapi.editor.ex.EditorEx; import org.jetbrains.annotations.NotNull; -import java.util.EnumSet; -import java.util.Set; - /** * @author irengrig * Date: 12/6/10 * Time: 10:18 AM */ -public class HorizontalScrollBarEditorCustomization implements EditorCustomization { - public Set getSupportedFeatures() { - return EnumSet.of(Feature.HORIZONTAL_SCROLLBAR); - } +public class HorizontalScrollBarEditorCustomization extends AbstractEditorCustomization { - public void addCustomization(@NotNull EditorEx editor, @NotNull Feature feature) { - if (Feature.HORIZONTAL_SCROLLBAR.equals(feature)) { - editor.setHorizontalScrollbarVisible(true); - } + public HorizontalScrollBarEditorCustomization() { + super(Feature.HORIZONTAL_SCROLLBAR); } @Override - public void removeCustomization(@NotNull EditorEx editor, @NotNull Feature feature) { - if (Feature.HORIZONTAL_SCROLLBAR.equals(feature)) { - editor.setHorizontalScrollbarVisible(false); - } + protected void doProcessCustomization(@NotNull EditorEx editor, @NotNull Feature feature, boolean apply) { + editor.setHorizontalScrollbarVisible(apply); } } diff --git a/platform/platform-impl/src/com/intellij/ui/OneLineEditorCustomization.java b/platform/platform-impl/src/com/intellij/ui/OneLineEditorCustomization.java index df719184a920..4d35f07a5993 100644 --- a/platform/platform-impl/src/com/intellij/ui/OneLineEditorCustomization.java +++ b/platform/platform-impl/src/com/intellij/ui/OneLineEditorCustomization.java @@ -15,24 +15,20 @@ */ package com.intellij.ui; -import com.intellij.AbstractUnappliableEditorCustomization; import com.intellij.openapi.editor.ex.EditorEx; import org.jetbrains.annotations.NotNull; -import java.util.EnumSet; -import java.util.Set; - /** * @author Kirill Likhodedov */ -public class OneLineEditorCustomization extends AbstractUnappliableEditorCustomization { - public Set getSupportedFeatures() { - return EnumSet.of(Feature.ONE_LINE); +public class OneLineEditorCustomization extends AbstractEditorCustomization { + + public OneLineEditorCustomization() { + super(Feature.ONE_LINE); } - public void addCustomization(@NotNull EditorEx editor, @NotNull Feature feature) { - if (Feature.ONE_LINE.equals(feature)) { - editor.setOneLineMode(true); - } + @Override + protected void doProcessCustomization(@NotNull EditorEx editor, @NotNull Feature feature, boolean apply) { + editor.setOneLineMode(apply); } } diff --git a/platform/platform-impl/src/com/intellij/ui/SoftWrapsEditorCustomization.java b/platform/platform-impl/src/com/intellij/ui/SoftWrapsEditorCustomization.java index 83d69140e5ad..0c36b4d4dc5c 100644 --- a/platform/platform-impl/src/com/intellij/ui/SoftWrapsEditorCustomization.java +++ b/platform/platform-impl/src/com/intellij/ui/SoftWrapsEditorCustomization.java @@ -15,13 +15,9 @@ */ package com.intellij.ui; -import com.intellij.AbstractUnappliableEditorCustomization; import com.intellij.openapi.editor.ex.EditorEx; import org.jetbrains.annotations.NotNull; -import java.util.EnumSet; -import java.util.Set; - /** * Editor customization that can make target editor soft wraps-aware. *

@@ -30,15 +26,14 @@ import java.util.Set; * @author Denis Zhdanov * @since Aug 20, 2010 4:54:48 PM */ -public class SoftWrapsEditorCustomization extends AbstractUnappliableEditorCustomization { +public class SoftWrapsEditorCustomization extends AbstractEditorCustomization { - @Override - public Set getSupportedFeatures() { - return EnumSet.of(Feature.SOFT_WRAP); + public SoftWrapsEditorCustomization() { + super(Feature.SOFT_WRAP); } @Override - public void addCustomization(@NotNull EditorEx editor, @NotNull Feature feature) { - editor.getSettings().setUseSoftWraps(true); + protected void doProcessCustomization(@NotNull EditorEx editor, @NotNull Feature feature, boolean apply) { + editor.getSettings().setUseSoftWraps(apply); } } diff --git a/platform/platform-impl/src/com/intellij/ui/popup/BalloonPopupBuilderImpl.java b/platform/platform-impl/src/com/intellij/ui/popup/BalloonPopupBuilderImpl.java index 34b814462429..8023a052d83d 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/BalloonPopupBuilderImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/BalloonPopupBuilderImpl.java @@ -46,11 +46,23 @@ public class BalloonPopupBuilderImpl implements BalloonBuilder { private int myCalloutShift; private int myPositionChangeXShift; private int myPositionChangeYShift; + private boolean myHideOnAction = true; public BalloonPopupBuilderImpl(@NotNull final JComponent content) { myContent = content; } + @Override + public boolean isHideOnAction() { + return myHideOnAction; + } + + @Override + public BalloonBuilder setHideOnAction(boolean hideOnAction) { + myHideOnAction = hideOnAction; + return this; + } + @NotNull public BalloonBuilder setPreferredPosition(final Balloon.Position position) { myPrefferedPosition = position; @@ -122,7 +134,7 @@ public class BalloonPopupBuilderImpl implements BalloonBuilder { @NotNull public Balloon createBalloon() { - return new BalloonImpl(myContent, myBorder, myFill, myHideOnMouseOutside, myHideOnKeyOutside, myShowCalllout, myCloseButtonEnabled, myFadeoutTime, myHideOnFrameResize, myClickHandler, myCloseOnClick, myAnimationCycle, + return new BalloonImpl(myContent, myBorder, myFill, myHideOnMouseOutside, myHideOnKeyOutside, myHideOnAction, myShowCalllout, myCloseButtonEnabled, myFadeoutTime, myHideOnFrameResize, myClickHandler, myCloseOnClick, myAnimationCycle, myCalloutShift, myPositionChangeXShift, myPositionChangeYShift); } @@ -145,4 +157,4 @@ public class BalloonPopupBuilderImpl implements BalloonBuilder { myCalloutShift = length; return this; } -} \ No newline at end of file +} diff --git a/platform/platform-resources-en/src/messages/ApplicationBundle.properties b/platform/platform-resources-en/src/messages/ApplicationBundle.properties index eeca36cec25f..fcdb403e126b 100644 --- a/platform/platform-resources-en/src/messages/ApplicationBundle.properties +++ b/platform/platform-resources-en/src/messages/ApplicationBundle.properties @@ -180,6 +180,7 @@ wrapping.extends.implements.list=Extends/implements list wrapping.extends.implements.keyword=Extends/implements keyword wrapping.throws.list=Throws list wrapping.throws.keyword=Throws keyword +wrapping.align.throws.keyword=Align 'throws' to method start wrapping.ternary.operation=Ternary operation wrapping.assignment.variables.groups=Fields/variables groups wrapping.assignment.statement=Assignment statement diff --git a/platform/platform-resources/src/idea/Keymap_Mac.xml b/platform/platform-resources/src/idea/Keymap_Mac.xml index e1d671d70a26..d50286d232b3 100644 --- a/platform/platform-resources/src/idea/Keymap_Mac.xml +++ b/platform/platform-resources/src/idea/Keymap_Mac.xml @@ -182,7 +182,10 @@ - + + + + diff --git a/platform/platform-resources/src/idea/Keymap_MacClassic.xml b/platform/platform-resources/src/idea/Keymap_MacClassic.xml index 23e39811608d..47a55edc08ed 100644 --- a/platform/platform-resources/src/idea/Keymap_MacClassic.xml +++ b/platform/platform-resources/src/idea/Keymap_MacClassic.xml @@ -209,6 +209,9 @@ + + + diff --git a/platform/testFramework/src/com/intellij/testFramework/LightPlatformCodeInsightTestCase.java b/platform/testFramework/src/com/intellij/testFramework/LightPlatformCodeInsightTestCase.java index 298b52ea9311..9f8487354b82 100644 --- a/platform/testFramework/src/com/intellij/testFramework/LightPlatformCodeInsightTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/LightPlatformCodeInsightTestCase.java @@ -492,6 +492,14 @@ public abstract class LightPlatformCodeInsightTestCase extends LightPlatformTest doAction(IdeActions.ACTION_EDITOR_PASTE); } + protected static void moveCaretToPreviousWordWithSelection() { + doAction("EditorPreviousWordWithSelection"); + } + + protected static void moveCaretToNextWordWithSelection() { + doAction("EditorNextWordWithSelection"); + } + protected static void lineComment() { new CommentByLineCommentHandler().invoke(getProject(), getEditor(), getFile()); } diff --git a/platform/util/src/com/intellij/openapi/util/BuildNumber.java b/platform/util/src/com/intellij/openapi/util/BuildNumber.java index 28d6c65167eb..fa0f6f0be6a6 100644 --- a/platform/util/src/com/intellij/openapi/util/BuildNumber.java +++ b/platform/util/src/com/intellij/openapi/util/BuildNumber.java @@ -26,7 +26,7 @@ public class BuildNumber implements Comparable { private final int myBaselineVersion; private final int myBuildNumber; private static final String BUILD_NUMBER = "__BUILD_NUMBER__"; - private static final int TOP_BASELINE_VERSION = 102; + private static final int TOP_BASELINE_VERSION = 104; public BuildNumber(String productCode, int baselineVersion, int buildNumber) { myProductCode = productCode; diff --git a/platform/util/src/com/intellij/util/ui/FormBuilder.java b/platform/util/src/com/intellij/util/ui/FormBuilder.java index f23113f5c9c1..f529f72bc7d6 100644 --- a/platform/util/src/com/intellij/util/ui/FormBuilder.java +++ b/platform/util/src/com/intellij/util/ui/FormBuilder.java @@ -26,9 +26,18 @@ public class FormBuilder { private int line = 0; private final JPanel panel; + private boolean vertical; + + /** + * @param vertical labels will be placed on their own rows + */ + public FormBuilder(final boolean vertical) { + this.vertical = vertical; + panel = new JPanel(new GridBagLayout()); + } public FormBuilder() { - panel = new JPanel(new GridBagLayout()); + this(false); } public FormBuilder addLabeledComponent(String labelText, JComponent component) { @@ -38,24 +47,49 @@ public class FormBuilder { GridBagConstraints c = new GridBagConstraints(); int verticalInset = line > 0 ? 10 : 0; - - c.gridx = 0; - c.gridy = line; - c.weightx = 0; - c.anchor = GridBagConstraints.EAST; - c.insets = new Insets(verticalInset, 0, 0, 5); - panel.add(label, c); + if (vertical) { + c.gridwidth = 1; - c.gridx = 1; - c.gridy = line; - c.fill = GridBagConstraints.HORIZONTAL; - c.anchor = GridBagConstraints.WEST; - c.weightx = 1; - c.insets = new Insets(verticalInset, 0, 0, 0); - panel.add(component, c); + c.gridx = 0; + c.gridy = line; + c.weightx = 1.0; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.WEST; + c.insets = new Insets(verticalInset, 0, 0, 5); - line++; + panel.add(label, c); + + c.gridx = 0; + c.gridy = line + 1; + c.weightx = 1.0; + c.fill = GridBagConstraints.HORIZONTAL; + c.anchor = GridBagConstraints.WEST; + c.insets = new Insets(0, 0, 0, 5); + + panel.add(component, c); + + line += 2; + } + else { + c.gridx = 0; + c.gridy = line; + c.weightx = 0; + c.anchor = GridBagConstraints.EAST; + c.insets = new Insets(verticalInset, 0, 0, 5); + + panel.add(label, c); + + c.gridx = 1; + c.gridy = line; + c.fill = GridBagConstraints.HORIZONTAL; + c.anchor = GridBagConstraints.WEST; + c.weightx = 1; + c.insets = new Insets(verticalInset, 0, 0, 0); + panel.add(component, c); + + line++; + } return this; } diff --git a/platform/xdebugger-api/src/com/intellij/xdebugger/XDebuggerManager.java b/platform/xdebugger-api/src/com/intellij/xdebugger/XDebuggerManager.java index bb9e8432e9e0..53336a09bb64 100644 --- a/platform/xdebugger-api/src/com/intellij/xdebugger/XDebuggerManager.java +++ b/platform/xdebugger-api/src/com/intellij/xdebugger/XDebuggerManager.java @@ -21,7 +21,6 @@ import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ProgramRunner; import com.intellij.execution.ui.ExecutionConsole; import com.intellij.execution.ui.RunContentDescriptor; -import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; import com.intellij.xdebugger.breakpoints.XBreakpointManager; import org.jetbrains.annotations.NotNull; @@ -71,4 +70,14 @@ public abstract class XDebuggerManager { @NotNull public abstract XDebugSession startSessionAndShowTab(@NotNull String sessionName, @Nullable RunContentDescriptor contentToReuse, @NotNull XDebugProcessStarter starter) throws ExecutionException; + + /** + * Start a new debugging session and open 'Debug' tool window + * @param sessionName title of 'Debug' tool window + * @param showToolWindowOnSuspendOnly if {@code true} 'Debug' tool window won't be shown until debug process is suspended on a breakpoint + */ + @NotNull + public abstract XDebugSession startSessionAndShowTab(@NotNull String sessionName, @Nullable RunContentDescriptor contentToReuse, + boolean showToolWindowOnSuspendOnly, + @NotNull XDebugProcessStarter starter) throws ExecutionException; } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java index b9124eb339f1..147c26d606b9 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java @@ -15,7 +15,9 @@ */ package com.intellij.xdebugger.impl; +import com.intellij.execution.ExecutionManager; import com.intellij.execution.configurations.RunProfile; +import com.intellij.execution.executors.DefaultDebugExecutor; import com.intellij.execution.filters.HyperlinkInfo; import com.intellij.execution.filters.OpenFileHyperlinkInfo; import com.intellij.execution.process.ProcessHandler; @@ -74,27 +76,30 @@ public class XDebugSessionImpl implements XDebugSession { private MyDependentBreakpointListener myDependentBreakpointListener; private String mySessionName; private XDebugSessionTab mySessionTab; + private XDebugSessionData mySessionData; private final EventDispatcher myDispatcher = EventDispatcher.create(XDebugSessionListener.class); private Project myProject; private @Nullable ExecutionEnvironment myEnvironment; private ProgramRunner myRunner; private boolean myStopped; private boolean myPauseActionSupported; + private boolean myShowTabOnSuspend; public XDebugSessionImpl(final @NotNull ExecutionEnvironment env, final @NotNull ProgramRunner runner, XDebuggerManagerImpl debuggerManager) { - this(env, runner, debuggerManager, env.getRunProfile().getName()); + this(env, runner, debuggerManager, env.getRunProfile().getName(), false); } public XDebugSessionImpl(final @Nullable ExecutionEnvironment env, final @Nullable ProgramRunner runner, XDebuggerManagerImpl debuggerManager, - final @NotNull String sessionName) { + final @NotNull String sessionName, final boolean showTabOnSuspend) { myEnvironment = env; myRunner = runner; mySessionName = sessionName; myDebuggerManager = debuggerManager; + myShowTabOnSuspend = showTabOnSuspend; myProject = debuggerManager.getProject(); ValueLookupManager.getInstance(myProject).startListening(); } @@ -106,16 +111,27 @@ public class XDebugSessionImpl implements XDebugSession { @NotNull public RunContentDescriptor getRunContentDescriptor() { - LOG.assertTrue(mySessionTab != null, "Call init() first!"); + assertSessionTabInitialized(); return mySessionTab.getRunContentDescriptor(); } + private void assertSessionTabInitialized() { + if (myShowTabOnSuspend) { + LOG.error("Debug tool window isn't shown yet because debug process isn't suspended"); + } + else { + LOG.assertTrue(mySessionTab != null, "Debug tool window not initialized yet!"); + } + } + public void setPauseActionSupported(final boolean isSupported) { myPauseActionSupported = isSupported; } public void rebuildViews() { - mySessionTab.rebuildViews(); + if (!myShowTabOnSuspend) { + mySessionTab.rebuildViews(); + } } @Nullable @@ -162,6 +178,7 @@ public class XDebugSessionImpl implements XDebugSession { public XDebugSessionTab init(final XDebugProcess process, @NotNull final XDebugSessionData sessionData) { LOG.assertTrue(myDebugProcess == null); myDebugProcess = process; + mySessionData = sessionData; XBreakpointManagerImpl breakpointManager = myDebuggerManager.getBreakpointManager(); XDependentBreakpointManager dependentBreakpointManager = breakpointManager.getDependentBreakpointManager(); @@ -173,8 +190,9 @@ public class XDebugSessionImpl implements XDebugSession { myDependentBreakpointListener = new MyDependentBreakpointListener(); dependentBreakpointManager.addListener(myDependentBreakpointListener); - initSessionTab(sessionData); - process.sessionInitialized(); + if (!myShowTabOnSuspend) { + initSessionTab(); + } return mySessionTab; } @@ -183,13 +201,14 @@ public class XDebugSessionImpl implements XDebugSession { return mySessionTab; } - private void initSessionTab(@NotNull XDebugSessionData sessionData) { + private void initSessionTab() { mySessionTab = new XDebugSessionTab(myProject, mySessionName); if (myEnvironment != null) { mySessionTab.setEnvironment(myEnvironment); } Disposer.register(myProject, mySessionTab); - mySessionTab.attachToSession(this, myRunner, myEnvironment, sessionData); + mySessionTab.attachToSession(this, myRunner, myEnvironment, mySessionData); + myDebugProcess.sessionInitialized(); } private void disableSlaveBreakpoints(final XDependentBreakpointManager dependentBreakpointManager) { @@ -205,6 +224,15 @@ public class XDebugSessionImpl implements XDebugSession { } } + public void showSessionTab() { + RunContentDescriptor descriptor = getRunContentDescriptor(); + ExecutionManager.getInstance(getProject()).getContentManager().showRunContent(DefaultDebugExecutor.getDebugExecutorInstance(), descriptor); + ProcessHandler handler = descriptor.getProcessHandler(); + if (handler != null) { + handler.startNotify(); + } + } + private static > XBreakpointType getBreakpointTypeClass(final XBreakpointHandler handler) { return XDebuggerUtil.getInstance().findBreakpointType(handler.getBreakpointTypeClass()); } @@ -483,9 +511,10 @@ public class XDebugSessionImpl implements XDebugSession { } private void printMessage(final String message, final String hyperLinkText, @Nullable final HyperlinkInfo info) { + assertSessionTabInitialized(); DebuggerUIUtil.invokeOnEventDispatch(new Runnable() { public void run() { - final ConsoleView consoleView = getConsoleView(); + final ConsoleView consoleView = (ConsoleView)mySessionTab.getConsole(); consoleView.print(message, ConsoleViewContentType.SYSTEM_OUTPUT); if (info != null) { consoleView.printHyperlink(hyperLinkText, info); @@ -498,10 +527,6 @@ public class XDebugSessionImpl implements XDebugSession { }); } - private ConsoleView getConsoleView() { - return (ConsoleView)mySessionTab.getConsole(); - } - public void positionReached(@NotNull final XSuspendContext suspendContext) { enableBreakpoints(); mySuspendContext = suspendContext; @@ -515,6 +540,11 @@ public class XDebugSessionImpl implements XDebugSession { } UIUtil.invokeLaterIfNeeded(new Runnable() { public void run() { + if (myShowTabOnSuspend) { + myShowTabOnSuspend = false; + initSessionTab(); + showSessionTab(); + } mySessionTab.toFront(); } }); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebuggerManagerImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebuggerManagerImpl.java index 6375eb31cb68..f07b6d15291c 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebuggerManagerImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebuggerManagerImpl.java @@ -16,7 +16,6 @@ package com.intellij.xdebugger.impl; import com.intellij.execution.ExecutionException; -import com.intellij.execution.ExecutionManager; import com.intellij.execution.Executor; import com.intellij.execution.executors.DefaultDebugExecutor; import com.intellij.execution.process.ProcessHandler; @@ -150,18 +149,24 @@ public class XDebuggerManagerImpl extends XDebuggerManager implements ProjectCom @NotNull public XDebugSession startSessionAndShowTab(@NotNull String sessionName, @Nullable RunContentDescriptor contentToReuse, - @NotNull XDebugProcessStarter starter) throws ExecutionException { - XDebugSession session = startSession(contentToReuse, starter, new XDebugSessionImpl(null, null, this, sessionName)); - RunContentDescriptor descriptor = session.getRunContentDescriptor(); - ExecutionManager.getInstance(myProject).getContentManager().showRunContent(DefaultDebugExecutor.getDebugExecutorInstance(), descriptor); - ProcessHandler handler = descriptor.getProcessHandler(); - if (handler != null) { - handler.startNotify(); + @NotNull XDebugProcessStarter starter) throws ExecutionException { + return startSessionAndShowTab(sessionName, contentToReuse, false, starter); + } + + @NotNull + @Override + public XDebugSession startSessionAndShowTab(@NotNull String sessionName, @Nullable RunContentDescriptor contentToReuse, + boolean showToolWindowOnSuspendOnly, + @NotNull XDebugProcessStarter starter) throws ExecutionException { + XDebugSessionImpl session = startSession(contentToReuse, starter, new XDebugSessionImpl(null, null, this, sessionName, + showToolWindowOnSuspendOnly)); + if (!showToolWindowOnSuspendOnly) { + session.showSessionTab(); } return session; } - private XDebugSession startSession(final RunContentDescriptor contentToReuse, final XDebugProcessStarter processStarter, + private XDebugSessionImpl startSession(final RunContentDescriptor contentToReuse, final XDebugProcessStarter processStarter, final XDebugSessionImpl session) throws ExecutionException { XDebugProcess process = processStarter.start(session); diff --git a/plugins/android/src/org/jetbrains/android/facet/AndroidFacetEditorTab.form b/plugins/android/src/org/jetbrains/android/facet/AndroidFacetEditorTab.form index 0dbb99230e14..b5e43601d7fe 100644 --- a/plugins/android/src/org/jetbrains/android/facet/AndroidFacetEditorTab.form +++ b/plugins/android/src/org/jetbrains/android/facet/AndroidFacetEditorTab.form @@ -34,7 +34,7 @@ - + diff --git a/plugins/copyright/src/com/maddyhome/idea/copyright/CopyrightManager.java b/plugins/copyright/src/com/maddyhome/idea/copyright/CopyrightManager.java index 7e8ecfbec398..ed4e2d42f114 100644 --- a/plugins/copyright/src/com/maddyhome/idea/copyright/CopyrightManager.java +++ b/plugins/copyright/src/com/maddyhome/idea/copyright/CopyrightManager.java @@ -17,6 +17,7 @@ package com.maddyhome.idea.copyright; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.components.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileEditor.FileEditorManager; @@ -102,7 +103,7 @@ public class CopyrightManager extends AbstractProjectComponent implements JDOMEx new UpdateCopyrightProcessor(myProject, module, file).run(); } } - }); + }, ModalityState.NON_MODAL); } } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/documentation/GroovyDocumentationProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/documentation/GroovyDocumentationProvider.java index 96a251590ffd..e8c77a325055 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/documentation/GroovyDocumentationProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/documentation/GroovyDocumentationProvider.java @@ -24,6 +24,7 @@ import com.intellij.lang.documentation.CodeDocumentationProvider; import com.intellij.lang.documentation.ExternalDocumentationProvider; import com.intellij.lang.java.JavaDocumentationProvider; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.impl.source.javadoc.PsiDocParamRef; import com.intellij.psi.javadoc.PsiDocComment; @@ -162,7 +163,8 @@ public class GroovyDocumentationProvider implements CodeDocumentationProvider, E if (originalElement instanceof GrReferenceExpression) { final PsiType inferredType = ((GrReferenceExpression)originalElement).getType(); if (inferredType != null) { - buffer.append("[inferred type] ").append(inferredType.getCanonicalText()); + buffer.append("[inferred type] "); + appendTypeString(buffer, inferredType); return; } } @@ -256,7 +258,7 @@ public class GroovyDocumentationProvider implements CodeDocumentationProvider, E private static void appendTypeString(StringBuffer buffer, PsiType type) { if (type != null) { - buffer.append(type.getCanonicalText()); + buffer.append(StringUtil.escapeXml(type.getCanonicalText())); } else { buffer.append(GrModifier.DEF); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/util/ParserUtils.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/util/ParserUtils.java index 30158fe5ab09..600353d0fce1 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/util/ParserUtils.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/util/ParserUtils.java @@ -178,20 +178,6 @@ public abstract class ParserUtils { marker.error(msg); } - public static void waitNextRCurly(PsiBuilder builder) { - int i = 0; - PsiBuilder.Marker em = builder.mark(); - while (!builder.eof() && !GroovyElementTypes.mRCURLY.equals(builder.getTokenType())) { - builder.advanceLexer(); - i++; - } - if (i > 0) { - em.error(GroovyBundle.message("rcurly.expected")); - } else { - em.drop(); - } - } - public static void advance(PsiBuilder builder, int count) { for (int i = 0; i < count; i++) { builder.getTokenText(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypesSemilattice.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypesSemilattice.java index c85d8486118f..74e665d30fef 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypesSemilattice.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/dataFlow/types/TypesSemilattice.java @@ -17,6 +17,7 @@ package org.jetbrains.plugins.groovy.lang.psi.dataFlow.types; import com.intellij.psi.PsiManager; import com.intellij.psi.PsiType; +import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.containers.HashMap; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.Semilattice; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; @@ -72,7 +73,7 @@ public class TypesSemilattice implements Semilattice> { if (t1 != null || t2 != null) return false; } else { - if (!t1.equals(t2)) return false; + if (!TypeConversionUtil.erasure(t1).equals(TypeConversionUtil.erasure(t2))) return false; } } return true; diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java index b9f3f55187f0..e2693ef2ab23 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.java @@ -12,6 +12,7 @@ import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.testFramework.IdeaTestUtil; import com.intellij.testFramework.LightProjectDescriptor; import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; @@ -288,6 +289,15 @@ public class GroovyHighlightingTest extends LightCodeInsightFixtureTestCase { public void testSOEInFieldDeclarations() {doTest();} + public void testVeryLongDfaWithComplexGenerics() { + IdeaTestUtil.assertTiming("", 10000, 1, new Runnable() { + @Override + public void run() { + doTest(new GroovyAssignabilityCheckInspection(), new UnusedDefInspection()); + } + }); + } + public void testWrongAnnotation() {doTest();} public void testAmbiguousMethods() { diff --git a/plugins/groovy/testdata/highlighting/VeryLongDfaWithComplexGenerics.groovy b/plugins/groovy/testdata/highlighting/VeryLongDfaWithComplexGenerics.groovy new file mode 100644 index 000000000000..a506027b33d0 --- /dev/null +++ b/plugins/groovy/testdata/highlighting/VeryLongDfaWithComplexGenerics.groovy @@ -0,0 +1,25 @@ +class TroubleCase { + + private Foo fooBar; + private Foo fooBaz; + + private void troubleMethod(boolean b) { + def icDao = (b?fooBaz:fooBar); + + for(Object x: new ArrayList()) { + } + + } +} + +public interface Foo {} +public class Bar implements Cloneable, Zoo {} +public interface Zoo, FM extends Hoo> {} +public interface Hoo {} +public class Baz implements Cloneable, Zoo {} +public class Goo {} +public class Too implements Hoo {} +public class Coo implements Serializable, Cloneable, Hoo {} +public class Woo implements Serializable, Cloneable, Hoo {} +public class Yoo implements Serializable, Cloneable, Hoo {} +public class Doo {} \ No newline at end of file diff --git a/plugins/hg4idea/resources/images/mercurial.png b/plugins/hg4idea/resources/images/mercurial.png deleted file mode 100644 index 7745acfee357..000000000000 Binary files a/plugins/hg4idea/resources/images/mercurial.png and /dev/null differ diff --git a/plugins/hg4idea/src/META-INF/plugin.xml b/plugins/hg4idea/src/META-INF/plugin.xml index a73077bb494c..ec09509d7c6c 100644 --- a/plugins/hg4idea/src/META-INF/plugin.xml +++ b/plugins/hg4idea/src/META-INF/plugin.xml @@ -4,7 +4,7 @@ Provides integration with Mercurial version control system. Supports Mercurial 1.3+. VCS Integration 10.0 - Victor Iacoban and Willem Verstraeten + Victor Iacoban and Willem Verstraeten com.intellij.modules.vcs @@ -28,7 +28,7 @@ - + diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/HgProjectConfigurable.java b/plugins/hg4idea/src/org/zmlx/hg4idea/HgProjectConfigurable.java index 51c2b3eae3fe..3417a4906c7c 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/HgProjectConfigurable.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/HgProjectConfigurable.java @@ -34,7 +34,7 @@ public class HgProjectConfigurable implements SearchableConfigurable { } public Icon getIcon() { - return HgVcs.MERCURIAL_ICON; + return null; } public String getHelpTopic() { diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java b/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java index 0e264a40c11f..82d8ef8272c2 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/HgVcs.java @@ -78,8 +78,6 @@ public class HgVcs extends AbstractVcs { public static final Topic OUTGOING_TOPIC = new Topic("hg4idea.outgoing", HgUpdater.class); - public static final Icon MERCURIAL_ICON = IconLoader.getIcon("/images/mercurial.png"); - private static final Icon INCOMING_ICON = IconLoader.getIcon("/actions/moveDown.png"); private static final Icon OUTGOING_ICON = IconLoader.getIcon("/actions/moveUp.png"); diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCommandService.java b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCommandService.java index fcf87cbcf151..e5ad4ec72fa2 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCommandService.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCommandService.java @@ -22,7 +22,11 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.vcsUtil.VcsUtil; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.Nullable; -import org.zmlx.hg4idea.*; +import org.zmlx.hg4idea.HgExecutableValidator; +import org.zmlx.hg4idea.HgGlobalSettings; +import org.zmlx.hg4idea.HgUtil; +import org.zmlx.hg4idea.HgVcs; +import org.zmlx.hg4idea.HgVcsMessages; import javax.swing.*; import java.awt.*; @@ -31,7 +35,9 @@ import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.net.Socket; +import java.net.URI; import java.nio.charset.Charset; +import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; @@ -155,15 +161,43 @@ public final class HgCommandService { // logging to the Version Control console (without extensions and configs) final String cmdString = String.format("%s %s %s", mySettings.isRunViaBash() ? "bash -c " + HgVcs.HG_EXECUTABLE_FILE_NAME : HgVcs.HG_EXECUTABLE_FILE_NAME, operation, - StringUtils.join(arguments, " ")); - myVcs.showMessageInConsole(cmdString, ConsoleViewContentType.USER_INPUT.getAttributes()); + StringUtils.join(maskAuthInfoFromUrl(arguments), " ")); + myVcs.showMessageInConsole(cmdString, ConsoleViewContentType.NORMAL_OUTPUT.getAttributes()); + LOG.info(cmdString); if (!silent) { myVcs.showMessageInConsole(result.getRawOutput(), ConsoleViewContentType.SYSTEM_OUTPUT.getAttributes()); + LOG.info(result.getRawOutput()); } myVcs.showMessageInConsole(result.getRawError(), ConsoleViewContentType.ERROR_OUTPUT.getAttributes()); + LOG.info(result.getRawError()); return result; + } + /** + * Strips possible authentication information from arguments passed to the command line + * to prevent private information appear in the VCS console or logs. + * @param arguments command line arguments. + * @return command line arguments which don't contain authentication information. + */ + private static List maskAuthInfoFromUrl(List arguments) { + if (arguments == null || arguments.isEmpty()) { + return arguments; + } + final List newArgs = new ArrayList(arguments.size()); + for (String arg : arguments) { + if (!arg.contains("@")) { // simple filter + newArgs.add(arg); + } else { + try { + final URI uri = new URI(arg); // parsing via URI methods, exception means it's not an URI + newArgs.add(uri.toString().replace(uri.getUserInfo(), ":")); + } catch (Throwable e) { + newArgs.add(arg); + } + } + } + return newArgs; } private void showError(Exception e) { diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgCheckoutProvider.java b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgCheckoutProvider.java index 876b55e3646a..b551f382045a 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgCheckoutProvider.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgCheckoutProvider.java @@ -19,10 +19,12 @@ import com.intellij.notification.Notification; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.CheckoutProvider; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; @@ -34,13 +36,21 @@ import org.zmlx.hg4idea.command.HgCloneCommand; import org.zmlx.hg4idea.command.HgCommandResult; import org.zmlx.hg4idea.ui.HgCloneDialog; +import java.io.BufferedReader; import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.net.URI; /** * Checkout provider for Mercurial */ public class HgCheckoutProvider implements CheckoutProvider { + private static final Logger LOG = Logger.getInstance(HgCheckoutProvider.class.getName()); + public void doCheckout(@NotNull final Project project, @Nullable final Listener listener) { ApplicationManager.getApplication().runWriteAction(new Runnable() { public void run() { @@ -62,30 +72,97 @@ public class HgCheckoutProvider implements CheckoutProvider { final String sourceRepositoryURL = dialog.getSourceRepositoryURL(); new Task.Backgroundable(project, HgVcsMessages.message("hg4idea.clone.progress", sourceRepositoryURL), true) { @Override public void run(@NotNull ProgressIndicator indicator) { + // clone HgCloneCommand clone = new HgCloneCommand(project); clone.setRepositoryURL(sourceRepositoryURL); clone.setDirectory(targetDir); - final HgCommandResult myCloneResult = clone.execute(); - if (myCloneResult == null) { - notifyError("Clone failed", "Clone failed due to unknown error", project); - } else if (myCloneResult.getExitValue() != 0) { - notifyError("Clone failed", "Clone from " + sourceRepositoryURL + " failed.

" + myCloneResult.getRawError(), project); - } else { - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - if (listener != null) { - listener.directoryCheckedOut(new File(dialog.getParentDirectory(), dialog.getDirectoryName())); - listener.checkoutCompleted(); + + // handle result + try { + final HgCommandResult myCloneResult = clone.execute(); + if (myCloneResult == null) { + notifyError("Clone failed", "Clone failed due to unknown error", project); + } else if (myCloneResult.getExitValue() != 0) { + notifyError("Clone failed", "Clone from " + sourceRepositoryURL + " failed.

" + myCloneResult.getRawError(), project); + } else { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + if (listener != null) { + listener.directoryCheckedOut(new File(dialog.getParentDirectory(), dialog.getDirectoryName())); + listener.checkoutCompleted(); + } } - } - }); + }); + } + } finally { + cleanupAuthDataFromHgrc(targetDir); } } }.queue(); } + /** + * Removes authentication data from the parent URL of a just cloned repository. + * @param targetDir directory where the hg project was cloned into. + */ + private static void cleanupAuthDataFromHgrc(String targetDir) { + File hgrc = new File(new File(targetDir, ".hg"), "hgrc"); + if (!hgrc.exists()) { + return; + } + + for (int i = 0; i < 3; i++) { // 3 attempts in case of an IOException + BufferedReader reader = null; + PrintWriter writer = null; + try { + // writing correct info into a temporary file + final File tempFile = FileUtil.createTempFile("hgrc", "temp"); + tempFile.deleteOnExit(); + reader = new BufferedReader(new FileReader(hgrc)); + writer = new PrintWriter(new FileWriter(tempFile)); + String line; + while ((line = reader.readLine()) != null) { + String parseLine = line.trim(); + if (parseLine.startsWith("default") && parseLine.contains("@")) { // looking for paths.default + int eqIdx = parseLine.indexOf('='); + parseLine = parseLine.substring(eqIdx+1).trim(); // getting value of paths.default + try { + final URI uri = new URI(parseLine); + final String urlWithoutAuthData = uri.toString().replace(uri.getUserInfo() + "@", ""); + writer.println("default = " + urlWithoutAuthData); + } catch (Throwable t) { // not URI => no sensitive data + writer.println(line); + } + } else { + writer.println(line); + } + } + + // substituting files + if (!tempFile.renameTo(hgrc)) { // this may fail in case of different FSs + FileUtil.copy(tempFile, hgrc); + tempFile.delete(); + } + return; + } catch (IOException e) { + LOG.info(e); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + continue; + } + } + if (writer != null) { + writer.close(); + } + } + } + } + private static void notifyError(String title, String description, Project project) { Notifications.Bus.notify(new Notification(HgVcs.NOTIFICATION_GROUP_ID, title, description, NotificationType.ERROR), project); } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/update/HgUpdateEnvironment.java b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/update/HgUpdateEnvironment.java index 016675c58c99..061335e36aa8 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/update/HgUpdateEnvironment.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/update/HgUpdateEnvironment.java @@ -17,12 +17,15 @@ import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.Ref; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.ProjectLevelVcsManager; import com.intellij.openapi.vcs.VcsException; -import com.intellij.openapi.vcs.update.*; +import com.intellij.openapi.vcs.update.SequentialUpdatesContext; +import com.intellij.openapi.vcs.update.UpdateEnvironment; +import com.intellij.openapi.vcs.update.UpdateSession; +import com.intellij.openapi.vcs.update.UpdateSessionAdapter; +import com.intellij.openapi.vcs.update.UpdatedFiles; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; @@ -102,7 +105,7 @@ public class HgUpdateEnvironment implements UpdateEnvironment { } public Icon getIcon() { - return IconLoader.getIcon("/images/mercurial.png"); + return null; } public String getHelpTopic() { diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgCurrentBranchStatus.java b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgCurrentBranchStatus.java index e787d7604ada..4a05db7c3cee 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgCurrentBranchStatus.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgCurrentBranchStatus.java @@ -12,7 +12,6 @@ // limitations under the License. package org.zmlx.hg4idea.ui; -import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.wm.CustomStatusBarWidget; import com.intellij.openapi.wm.StatusBar; import org.apache.commons.lang.StringUtils; @@ -25,10 +24,9 @@ import javax.swing.*; import java.util.List; public class HgCurrentBranchStatus extends JLabel implements CustomStatusBarWidget { - private static final Icon MERCURIAL_ICON = IconLoader.getIcon("/images/mercurial.png"); public HgCurrentBranchStatus() { - super(MERCURIAL_ICON, SwingConstants.TRAILING); + super("", SwingConstants.TRAILING); setVisible(false); } diff --git a/plugins/spellchecker/src/com/intellij/spellchecker/ui/SpellCheckingEditorCustomization.java b/plugins/spellchecker/src/com/intellij/spellchecker/ui/SpellCheckingEditorCustomization.java index 8d009ed94c43..bcc2e3efd2d5 100644 --- a/plugins/spellchecker/src/com/intellij/spellchecker/ui/SpellCheckingEditorCustomization.java +++ b/plugins/spellchecker/src/com/intellij/spellchecker/ui/SpellCheckingEditorCustomization.java @@ -31,12 +31,14 @@ import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.spellchecker.inspections.SpellCheckerInspectionToolProvider; -import com.intellij.ui.EditorCustomization; +import com.intellij.ui.AbstractEditorCustomization; import com.intellij.util.containers.HashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; /** * Makes current editor to have spell checking turned on all the time. @@ -46,7 +48,7 @@ import java.util.*; * @author Denis Zhdanov * @since Aug 20, 2010 3:54:42 PM */ -public class SpellCheckingEditorCustomization implements EditorCustomization { +public class SpellCheckingEditorCustomization extends AbstractEditorCustomization { /** * Holds custom inspection profile wrapper. @@ -60,6 +62,10 @@ public class SpellCheckingEditorCustomization implements EditorCustomization { @Nullable private static final InspectionProfileWrapper INSPECTION_PROFILE_WRAPPER = initProvider(); + public SpellCheckingEditorCustomization() { + super(Feature.SPELL_CHECK); + } + @SuppressWarnings("unchecked") @Nullable private static InspectionProfileWrapper initProvider() { @@ -116,12 +122,7 @@ public class SpellCheckingEditorCustomization implements EditorCustomization { } @Override - public Set getSupportedFeatures() { - return EnumSet.of(Feature.SPELL_CHECK); - } - - @Override - public void addCustomization(@NotNull EditorEx editor, @NotNull Feature feature) { + protected void doProcessCustomization(@NotNull EditorEx editor, @NotNull Feature feature, boolean apply) { if (INSPECTION_PROFILE_WRAPPER == null) { return; } @@ -135,27 +136,16 @@ public class SpellCheckingEditorCustomization implements EditorCustomization { if (file == null) { return; } - file.putUserData(InspectionProfileWrapper.KEY, INSPECTION_PROFILE_WRAPPER); - editor.putUserData(IntentionManager.SHOW_INTENTION_OPTIONS_KEY, false); - updateRepresentation(project, file); - } - - @Override - public void removeCustomization(@NotNull EditorEx editor, @NotNull Feature feature) { - Project project = editor.getProject(); - if (project == null) { - return; + + if (apply) { + file.putUserData(InspectionProfileWrapper.KEY, INSPECTION_PROFILE_WRAPPER); + editor.putUserData(IntentionManager.SHOW_INTENTION_OPTIONS_KEY, false); + } + else { + file.putUserData(InspectionProfileWrapper.KEY, null); } - PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()); - if (file == null) { - return; - } - file.putUserData(InspectionProfileWrapper.KEY, null); - updateRepresentation(project, file); - } - - private static void updateRepresentation(@NotNull Project project, @NotNull PsiFile file) { + // Update representation. DaemonCodeAnalyzer analyzer = DaemonCodeAnalyzer.getInstance(project); if (analyzer != null) { analyzer.restart(file); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java index 7b0bdc05fb5b..757ed9891529 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFileSystemListener.java @@ -628,7 +628,7 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp if (value != VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY) { final AbstractVcsHelper vcsHelper = AbstractVcsHelper.getInstance(project); final Collection filesToProcess = promptAboutAddition(vcs, addedVFiles, value, vcsHelper); - if (filesToProcess != null) { + if (filesToProcess != null && !filesToProcess.isEmpty()) { final List exceptions = new ArrayList(); runInBackground(project, "Adding files to Subversion", createAdditionRunnable(project, vcs, copyFromMap, filesToProcess, exceptions)); @@ -758,7 +758,7 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp final AbstractVcsHelper vcsHelper = AbstractVcsHelper.getInstance(project); Collection filesToProcess; filesToProcess = promptAboutDeletion(deletedFiles, vcs, value, vcsHelper); - if (filesToProcess != null) { + if (filesToProcess != null && !filesToProcess.isEmpty()) { List exceptions = new ArrayList(); runInBackground(project, "Deleting files from Subversion", createDeleteRunnable(project, vcs, filesToProcess, exceptions)); if (!exceptions.isEmpty()) { @@ -845,6 +845,8 @@ public class SvnFileSystemListener extends CommandAdapter implements LocalFileOp } private void processMovedFiles(final Project project) { + if (myMovedFiles.isEmpty()) return; + final Runnable runnable = new Runnable() { public void run() { for (Iterator iterator = myMovedFiles.iterator(); iterator.hasNext();) { diff --git a/xml/impl/src/com/intellij/lang/xml/XMLExternalAnnotator.java b/xml/impl/src/com/intellij/lang/xml/XMLExternalAnnotator.java index 6a8a4ec0d21d..c43886ecc2eb 100644 --- a/xml/impl/src/com/intellij/lang/xml/XMLExternalAnnotator.java +++ b/xml/impl/src/com/intellij/lang/xml/XMLExternalAnnotator.java @@ -33,8 +33,7 @@ import com.intellij.xml.util.XmlTagUtil; /** * @author ven */ -public class XMLExternalAnnotator implements ExternalAnnotator, Validator.ValidationHost { - private AnnotationHolder myHolder; +public class XMLExternalAnnotator implements ExternalAnnotator { public void annotate(PsiFile file, AnnotationHolder holder) { if (!(file instanceof XmlFile)) return; @@ -44,64 +43,68 @@ public class XMLExternalAnnotator implements ExternalAnnotator, Validator.Valida XmlNSDescriptor nsDescriptor = rootTag == null ? null : rootTag.getNSDescriptor(rootTag.getNamespace(), false); if (nsDescriptor instanceof Validator && !HtmlUtil.isHtml5Document(document)) { - myHolder = holder; - try { //noinspection unchecked - ((Validator)nsDescriptor).validate(document, this); - } - finally { - myHolder = null; - } + ((Validator)nsDescriptor).validate(document, new MyHost(holder)); } } - private static final ErrorType[] types = ErrorType.values(); - - public void addMessage(PsiElement context, String message, int type) { - addMessage(context, message, types[type]); - } - - public void addMessage(final PsiElement context, final String message, final ErrorType type, final IntentionAction... fixes) { - if (message != null && message.length() > 0) { - if (context instanceof XmlTag) { - addMessagesForTag((XmlTag)context, message, type, fixes); - } - else { - if (type == Validator.ValidationHost.ErrorType.ERROR) { - appendFixes(myHolder.createErrorAnnotation(context, message), fixes); - } else { - appendFixes(myHolder.createWarningAnnotation(context, message), fixes); - } - } - } - } - - private void addMessagesForTag(XmlTag tag, String message, ErrorType type, IntentionAction... actions) { - XmlToken childByRole = XmlTagUtil.getStartTagNameElement(tag); - - addMessagesForTreeChild(childByRole, type, message, actions); - - childByRole = XmlTagUtil.getEndTagNameElement(tag); - addMessagesForTreeChild(childByRole, type, message, actions); - } - - private void addMessagesForTreeChild(final XmlToken childByRole, final ErrorType type, final String message, IntentionAction... actions) { - if (childByRole != null) { - Annotation annotation; - if (type == ErrorType.ERROR) { - annotation = myHolder.createErrorAnnotation(childByRole, message); - } - else { - annotation = myHolder.createWarningAnnotation(childByRole, message); - } - - appendFixes(annotation, actions); - } - } + private static final Validator.ValidationHost.ErrorType[] types = Validator.ValidationHost.ErrorType.values(); private static void appendFixes(final Annotation annotation, final IntentionAction... actions) { if (actions != null) { for(IntentionAction action:actions) annotation.registerFix(action); } } + + private static class MyHost implements Validator.ValidationHost { + + private final AnnotationHolder myHolder; + + public MyHost(AnnotationHolder holder) { + + myHolder = holder; + } + + public void addMessage(PsiElement context, String message, int type) { + addMessage(context, message, types[type]); + } + + public void addMessage(final PsiElement context, final String message, final ErrorType type, final IntentionAction... fixes) { + if (message != null && message.length() > 0) { + if (context instanceof XmlTag) { + addMessagesForTag((XmlTag)context, message, type, fixes); + } + else { + if (type == Validator.ValidationHost.ErrorType.ERROR) { + appendFixes(myHolder.createErrorAnnotation(context, message), fixes); + } else { + appendFixes(myHolder.createWarningAnnotation(context, message), fixes); + } + } + } + } + + private void addMessagesForTag(XmlTag tag, String message, ErrorType type, IntentionAction... actions) { + XmlToken childByRole = XmlTagUtil.getStartTagNameElement(tag); + + addMessagesForTreeChild(childByRole, type, message, actions); + + childByRole = XmlTagUtil.getEndTagNameElement(tag); + addMessagesForTreeChild(childByRole, type, message, actions); + } + + private void addMessagesForTreeChild(final XmlToken childByRole, final ErrorType type, final String message, IntentionAction... actions) { + if (childByRole != null) { + Annotation annotation; + if (type == ErrorType.ERROR) { + annotation = myHolder.createErrorAnnotation(childByRole, message); + } + else { + annotation = myHolder.createWarningAnnotation(childByRole, message); + } + + appendFixes(annotation, actions); + } + } + } } diff --git a/xml/impl/src/com/intellij/psi/impl/source/html/ScriptSupportUtil.java b/xml/impl/src/com/intellij/psi/impl/source/html/ScriptSupportUtil.java index 2c03f0e80dcb..73aa583f7b2c 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/html/ScriptSupportUtil.java +++ b/xml/impl/src/com/intellij/psi/impl/source/html/ScriptSupportUtil.java @@ -22,6 +22,8 @@ import com.intellij.psi.ResolveState; import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceUtil; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.search.PsiElementProcessor; +import com.intellij.psi.util.CachedValue; +import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.xml.*; import com.intellij.xml.XmlElementDescriptor; import com.intellij.xml.util.HtmlUtil; @@ -35,7 +37,7 @@ import java.util.List; * @author Maxim.Mossienko */ public class ScriptSupportUtil { - private static final Key CachedScriptTagsKey = Key.create("script tags"); + private static final Key> CachedScriptTagsKey = Key.create("script tags"); private static final ThreadLocal ProcessingDeclarationsFlag = new ThreadLocal(); private static final @NonNls String SCRIPT_TAG = "script"; @@ -46,61 +48,71 @@ public class ScriptSupportUtil { element.putUserData(CachedScriptTagsKey,null); } - public static boolean processDeclarations(XmlFile element, PsiScopeProcessor processor, ResolveState state, PsiElement lastParent, PsiElement place) { - XmlTag[] myCachedScriptTags = element.getUserData(CachedScriptTagsKey); - + public static boolean processDeclarations(final XmlFile element, + PsiScopeProcessor processor, + ResolveState state, + PsiElement lastParent, + PsiElement place) { + CachedValue myCachedScriptTags = element.getUserData(CachedScriptTagsKey); if (myCachedScriptTags == null) { - final List scriptTags = new ArrayList(); - final XmlDocument document = HtmlUtil.getRealXmlDocument(element.getDocument()); + myCachedScriptTags = element.getManager().getCachedValuesManager().createCachedValue(new CachedValueProvider() { + @Override + public Result compute() { + final List scriptTags = new ArrayList(); + final XmlDocument document = HtmlUtil.getRealXmlDocument(element.getDocument()); - if (document != null) { - XmlUtil.processXmlElements(document, - new PsiElementProcessor() { - public boolean execute(final PsiElement element) { - if (element instanceof XmlTag) { - final XmlTag tag = (XmlTag)element; + if (document != null) { + PsiElementProcessor psiElementProcessor = new PsiElementProcessor() { + public boolean execute(final PsiElement element) { + if (element instanceof XmlTag) { + final XmlTag tag = (XmlTag)element; - if (SCRIPT_TAG.equalsIgnoreCase(tag.getName())) { - final XmlElementDescriptor descriptor = tag.getDescriptor(); - if (descriptor != null && SCRIPT_TAG.equals(descriptor.getName())) { - scriptTags.add(tag); + if (SCRIPT_TAG.equalsIgnoreCase(tag.getName())) { + final XmlElementDescriptor descriptor = tag.getDescriptor(); + if (descriptor != null && SCRIPT_TAG.equals(descriptor.getName())) { + scriptTags.add(tag); + } + } } + return true; } - } - return true; + }; + XmlUtil.processXmlElements(document,psiElementProcessor, true); } - }, true); - } - myCachedScriptTags = scriptTags.toArray(new XmlTag[scriptTags.size()]); + return new Result(scriptTags.toArray(new XmlTag[scriptTags.size()]), element); + } + }, false); element.putUserData(CachedScriptTagsKey, myCachedScriptTags); } if (ProcessingDeclarationsFlag.get() != null) return true; - + try { ProcessingDeclarationsFlag.set(""); - for (XmlTag tag : myCachedScriptTags) { + for (XmlTag tag : myCachedScriptTags.getValue()) { final XmlTagChild[] children = tag.getValue().getChildren(); for (XmlTagChild child : children) { if (!child.processDeclarations(processor, state, null, place)) return false; } - if(tag.getAttributeValue("src") != null) { + if (tag.getAttributeValue("src") != null) { final XmlAttribute attribute = tag.getAttribute("src", null); if (attribute != null) { final PsiFile psiFile = FileReferenceUtil.findFile(attribute.getValueElement()); if (psiFile != null && psiFile.isValid()) { - if(!psiFile.processDeclarations(processor, state, null, place)) + if (!psiFile.processDeclarations(processor, state, null, place)) { return false; + } } } } } - } finally { + } + finally { ProcessingDeclarationsFlag.set(null); } diff --git a/xml/impl/src/com/intellij/psi/impl/source/xml/XmlEntityRefImpl.java b/xml/impl/src/com/intellij/psi/impl/source/xml/XmlEntityRefImpl.java index 3771b58326d8..31246794c167 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/xml/XmlEntityRefImpl.java +++ b/xml/impl/src/com/intellij/psi/impl/source/xml/XmlEntityRefImpl.java @@ -66,16 +66,19 @@ public class XmlEntityRefImpl extends XmlElementImpl implements XmlEntityRef { return cachedValue != null ? cachedValue.getValue():null; } - public static void cacheParticularEntity(PsiFile file, final XmlEntityDecl decl) { + public static void cacheParticularEntity(PsiFile file, XmlEntityDecl decl) { synchronized(PsiLock.LOCK) { final Map> cachingMap = getCachingMap(file); final String name = decl.getName(); if (cachingMap.containsKey(name)) return; + final SmartPsiElementPointer declPointer = SmartPointerManager.getInstance(file.getProject()).createSmartPsiElementPointer(decl); + cachingMap.put( name, CachedValuesManager.getManager(file.getProject()).createCachedValue(new CachedValueProvider() { public Result compute() { - if (decl.isValid() && name.equals(decl.getName())) - return new Result(decl,decl); + PsiElement declElement = declPointer.getElement(); + if (declElement instanceof XmlEntityDecl && declElement.isValid() && name.equals(((XmlEntityDecl)declElement).getName())) + return new Result((XmlEntityDecl)declElement, declElement); cachingMap.put(name,null); return new Result(null,null); } diff --git a/xml/impl/src/com/intellij/xml/util/AnchorReference.java b/xml/impl/src/com/intellij/xml/util/AnchorReference.java index 0b7d24be0a11..a68fd43bfb45 100644 --- a/xml/impl/src/com/intellij/xml/util/AnchorReference.java +++ b/xml/impl/src/com/intellij/xml/util/AnchorReference.java @@ -122,29 +122,7 @@ class AnchorReference implements PsiReference, EmptyResolveMessageProvider { if (file != null) { CachedValue> value = file.getUserData(ourCachedIdsKey); if (value == null) { - value = CachedValuesManager.getManager(file.getProject()).createCachedValue(new CachedValueProvider>() { - public Result> compute() { - final Map resultMap = new HashMap(); - XmlDocument document = HtmlUtil.getRealXmlDocument(file.getDocument()); - final XmlTag rootTag = document != null ? document.getRootTag():null; - - if (rootTag != null) { - processXmlElements(rootTag, - new PsiElementProcessor() { - public boolean execute(final PsiElement element) { - final String anchorValue = element instanceof XmlTag ? getAnchorValue((XmlTag)element):null; - - if (anchorValue!=null) { - resultMap.put(anchorValue, (XmlTag)element); - } - return true; - } - } - ); - } - return new Result>(resultMap, file); - } - }, false); + value = CachedValuesManager.getManager(file.getProject()).createCachedValue(new MapCachedValueProvider(file), false); file.putUserData(ourCachedIdsKey, value); } @@ -239,4 +217,35 @@ class AnchorReference implements PsiReference, EmptyResolveMessageProvider { XmlBundle.message("cannot.resolve.anchor", myAnchor) : XmlBundle.message("cannot.resolve.anchor.in.file", myAnchor, xmlFile.getName()); } + + // separate static class to avoid memory leak via this$0 + private static class MapCachedValueProvider implements CachedValueProvider> { + private final XmlFile myFile; + + public MapCachedValueProvider(XmlFile file) { + myFile = file; + } + + public Result> compute() { + final Map resultMap = new HashMap(); + XmlDocument document = HtmlUtil.getRealXmlDocument(myFile.getDocument()); + final XmlTag rootTag = document != null ? document.getRootTag():null; + + if (rootTag != null) { + processXmlElements(rootTag, + new PsiElementProcessor() { + public boolean execute(final PsiElement element) { + final String anchorValue = element instanceof XmlTag ? getAnchorValue((XmlTag)element):null; + + if (anchorValue!=null) { + resultMap.put(anchorValue, (XmlTag)element); + } + return true; + } + } + ); + } + return new Result>(resultMap, myFile); + } + } }