diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/ModuleCompileScope.java b/java/compiler/impl/src/com/intellij/compiler/impl/ModuleCompileScope.java index 3be5efc00fb0..b2485e8d8ea8 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/ModuleCompileScope.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/ModuleCompileScope.java @@ -33,6 +33,7 @@ import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.util.CommonProcessors; import org.jetbrains.annotations.NotNull; import java.util.HashMap; @@ -75,13 +76,7 @@ public class ModuleCompileScope extends FileIndexCompileScope { } private void buildScopeModulesSet(Module module) { - myScopeModules.add(module); - final Module[] dependencies = ModuleRootManager.getInstance(module).getDependencies(); - for (Module dependency : dependencies) { - if (!myScopeModules.contains(dependency)) { // may be in case of module circular dependencies - buildScopeModulesSet(dependency); - } - } + ModuleRootManager.getInstance(module).orderEntries().recursively().compileOnly().forEachModule(new CommonProcessors.CollectProcessor(myScopeModules)); } @NotNull diff --git a/java/compiler/impl/testSrc/com/intellij/compiler/BaseCompilerTestCase.java b/java/compiler/impl/testSrc/com/intellij/compiler/BaseCompilerTestCase.java index 40170c9b2605..5900ea2d6097 100644 --- a/java/compiler/impl/testSrc/com/intellij/compiler/BaseCompilerTestCase.java +++ b/java/compiler/impl/testSrc/com/intellij/compiler/BaseCompilerTestCase.java @@ -171,7 +171,15 @@ public abstract class BaseCompilerTestCase extends ModuleTestCase { } protected CompilationLog make(Module... modules) { - return make(getCompilerManager().createModulesCompileScope(modules, false), CompilerFilter.ALL); + return make(false, modules); + } + + protected CompilationLog makeWithDependencies(Module... modules) { + return make(true, modules); + } + + private CompilationLog make(boolean includeDependentModules, Module... modules) { + return make(getCompilerManager().createModulesCompileScope(modules, includeDependentModules), CompilerFilter.ALL); } protected CompilationLog recompile(Module... modules) { diff --git a/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTemplateList.java b/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTemplateList.java index 3c6d456b420f..57918b1843d1 100644 --- a/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTemplateList.java +++ b/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTemplateList.java @@ -25,8 +25,8 @@ import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.text.StringUtil; import com.intellij.platform.ProjectTemplate; import com.intellij.platform.templates.ArchivedProjectTemplate; -import com.intellij.ui.*; -import com.intellij.ui.SingleSelectionModel; +import com.intellij.ui.CollectionListModel; +import com.intellij.ui.IdeBorderFactory; import com.intellij.ui.components.JBList; import com.intellij.ui.popup.list.GroupedItemsListRenderer; import com.intellij.util.containers.ContainerUtil; @@ -100,7 +100,6 @@ public class ProjectTemplateList extends JPanel { } }; myList.setCellRenderer(renderer); - myList.setSelectionModel(new SingleSelectionModel()); myList.getSelectionModel().addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { @@ -136,7 +135,9 @@ public class ProjectTemplateList extends JPanel { int index = preserveSelection ? myList.getSelectedIndex() : -1; //noinspection unchecked myList.setModel(new CollectionListModel(list)); - myList.setSelectedIndex(index == -1 ? 0 : index); + if (myList.isEnabled()) { + myList.setSelectedIndex(index == -1 ? 0 : index); + } updateSelection(); } @@ -149,6 +150,12 @@ public class ProjectTemplateList extends JPanel { public void setEnabled(boolean enabled) { super.setEnabled(enabled); myList.setEnabled(enabled); + if (!enabled) { + myList.clearSelection(); + } + else { + myList.setSelectedIndex(0); + } myDescriptionPane.setEnabled(enabled); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index 1216b358b869..f386b646f1e4 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -553,6 +553,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh if ((!result.isAccessible() || !result.isStaticsScopeCorrect()) && !HighlightMethodUtil.isDummyConstructorCall(expression, myResolveHelper, list, referenceExpression) && + // this check is for fake expression from JspMethodCallImpl referenceExpression.getParent() == expression) { try { myHolder.add(HighlightMethodUtil.checkAmbiguousMethodCallArguments(referenceExpression, results, list, resolved, result, expression, myResolveHelper)); diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/AllClassesGetter.java b/java/java-impl/src/com/intellij/codeInsight/completion/AllClassesGetter.java index a5431b56082a..270d6f93f767 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/AllClassesGetter.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/AllClassesGetter.java @@ -22,20 +22,20 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; +import com.intellij.psi.impl.search.AllClassesSearchExecutor; import com.intellij.psi.impl.source.PostprocessReformattingAspect; import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.search.searches.AllClassesSearch; import com.intellij.util.Consumer; import com.intellij.util.IncorrectOperationException; import com.intellij.util.Processor; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; +import java.util.LinkedHashSet; import java.util.Set; /** @@ -184,18 +184,17 @@ public class AllClassesGetter { @NotNull Project project, @NotNull GlobalSearchScope scope, @NotNull Processor processor) { - AllClassesSearch.search(scope, project, new Condition() { + final Set names = new THashSet(10000); + AllClassesSearchExecutor.processClassNames(project, scope, new Consumer() { @Override - public boolean value(String s) { - return prefixMatcher.isStartMatch(s); + public void consume(String s) { + if (prefixMatcher.prefixMatches(s)) { + names.add(s); + } } - }).forEach(processor); - AllClassesSearch.search(scope, project, new Condition() { - @Override - public boolean value(String s) { - return prefixMatcher.prefixMatches(s); - } - }).forEach(processor); + }); + LinkedHashSet sorted = CompletionUtil.sortMatching(prefixMatcher, names); + AllClassesSearchExecutor.processClassesByNames(project, scope, sorted, processor); } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/PreferByKindWeigher.java b/java/java-impl/src/com/intellij/codeInsight/completion/PreferByKindWeigher.java index e8c828a6c4bc..2960bd797dea 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/PreferByKindWeigher.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/PreferByKindWeigher.java @@ -144,10 +144,8 @@ public class PreferByKindWeigher extends LookupElementWeigher { } } - if (myCompletionType == CompletionType.SMART) { - if (object instanceof PsiLocalVariable || object instanceof PsiParameter || object instanceof PsiThisExpression) { - return MyResult.localOrParameter; - } + if (object instanceof PsiLocalVariable || object instanceof PsiParameter || object instanceof PsiThisExpression) { + return MyResult.localOrParameter; } if (object instanceof String && item.getUserData(JavaCompletionUtil.SUPER_METHOD_PARAMETERS) == Boolean.TRUE) { diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/scope/JavaCompletionProcessor.java b/java/java-impl/src/com/intellij/codeInsight/completion/scope/JavaCompletionProcessor.java index fb7878dba26c..4e13baf779f2 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/scope/JavaCompletionProcessor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/scope/JavaCompletionProcessor.java @@ -25,6 +25,7 @@ import com.intellij.openapi.util.Key; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.filters.ElementFilter; +import com.intellij.psi.impl.light.LightMethodBuilder; import com.intellij.psi.impl.source.resolve.JavaResolveUtil; import com.intellij.psi.infos.CandidateInfo; import com.intellij.psi.scope.BaseScopeProcessor; @@ -100,14 +101,11 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme if (qualifier instanceof PsiSuperExpression) { final PsiJavaCodeReferenceElement qSuper = ((PsiSuperExpression)qualifier).getQualifier(); if (qSuper == null) { - myQualifierClass = JavaResolveUtil.getContextClass( myElement); + myQualifierClass = JavaResolveUtil.getContextClass(myElement); } else { final PsiElement target = qSuper.resolve(); myQualifierClass = target instanceof PsiClass ? (PsiClass)target : null; } - if (myQualifierClass != null) { - myQualifierType = JavaPsiFacade.getInstance(element.getProject()).getElementFactory().createType(myQualifierClass); - } } else if (qualifier != null) { setQualifierType(qualifier.getType()); @@ -117,8 +115,13 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme myQualifierClass = (PsiClass)target; } } + } else { + myQualifierClass = JavaResolveUtil.getContextClass(myElement); } } + if (myQualifierClass != null && myQualifierType == null) { + myQualifierType = JavaPsiFacade.getElementFactory(element.getProject()).createType(myQualifierClass); + } if (myOptions.checkInitialized) { myNonInitializedFields.addAll(getNonInitializedFields(element)); @@ -219,6 +222,19 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme return true; } + if (element instanceof PsiMethod) { + PsiMethod method = (PsiMethod)element; + if (PsiTypesUtil.isGetClass(method) && PsiUtil.isLanguageLevel5OrHigher(myElement)) { + PsiType patchedType = PsiTypesUtil.createJavaLangClassType(myElement, myQualifierType, false); + if (patchedType != null) { + element = new LightMethodBuilder(element.getManager(), method.getName()). + addModifier(PsiModifier.PUBLIC). + setMethodReturnType(patchedType). + setContainingClass(method.getContainingClass()); + } + } + } + if (satisfies(element, state) && isAccessible(element)) { CompletionElement element1 = new CompletionElement(element, state.get(PsiSubstitutor.KEY)); if (myResultNames.add(element1.getUniqueId())) { @@ -288,7 +304,9 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme if (!(element instanceof PsiMember)) return true; PsiMember member = (PsiMember)element; - return JavaPsiFacade.getInstance(element.getProject()).getResolveHelper().isAccessible(member, member.getModifierList(), myElement, myQualifierClass, myDeclarationHolder); + PsiClass accessObjectClass = member instanceof PsiClass ? null : myQualifierClass; + return JavaPsiFacade.getInstance(element.getProject()).getResolveHelper().isAccessible(member, member.getModifierList(), myElement, + accessObjectClass, myDeclarationHolder); } public void setCompletionElements(@NotNull Object[] elements) { diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplateCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplateCompletionContributor.java index b6a7b5f687ce..eb6b585f0ec1 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplateCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplateCompletionContributor.java @@ -21,34 +21,19 @@ import com.intellij.codeInsight.template.CustomLiveTemplate; import com.intellij.codeInsight.template.impl.TemplateManagerImpl; import com.intellij.codeInsight.template.postfix.templates.PostfixLiveTemplate; import com.intellij.openapi.editor.Editor; -import com.intellij.patterns.ElementPattern; -import com.intellij.psi.JavaTokenType; -import com.intellij.psi.PsiElement; +import com.intellij.patterns.PlatformPatterns; import com.intellij.psi.PsiFile; -import com.intellij.psi.impl.source.tree.ElementType; -import com.intellij.psi.tree.TokenSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import static com.intellij.patterns.PsiJavaPatterns.psiElement; -import static com.intellij.patterns.StandardPatterns.string; - public class PostfixTemplateCompletionContributor extends CompletionContributor { - private static final TokenSet SUITABLE_ELEMENTS = TokenSet.orSet(ElementType.KEYWORD_BIT_SET, - ElementType.LITERAL_BIT_SET, - TokenSet.create(JavaTokenType.IDENTIFIER)); - public PostfixTemplateCompletionContributor() { - extend(CompletionType.BASIC, identifierAfterDot(), new PostfixTemplatesCompletionProvider()); + extend(CompletionType.BASIC, PlatformPatterns.psiElement(), new PostfixTemplatesCompletionProvider()); } @Nullable - public static PostfixLiveTemplate getPostfixLiveTemplate(@NotNull PsiFile file, @NotNull Editor editor) { + public static PostfixLiveTemplate getPostfixLiveTemplate(@NotNull PsiFile file, @NotNull Editor editor) { PostfixLiveTemplate postfixLiveTemplate = CustomLiveTemplate.EP_NAME.findExtension(PostfixLiveTemplate.class); return postfixLiveTemplate != null && TemplateManagerImpl.isApplicable(postfixLiveTemplate, editor, file) ? postfixLiveTemplate : null; } - - private static ElementPattern identifierAfterDot() { - return psiElement().withElementType(SUITABLE_ELEMENTS).afterLeaf(psiElement().withText(string().contains("."))); - } } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplatesCompletionProvider.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplatesCompletionProvider.java index db253a6808b1..3e99fbf66db2 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplatesCompletionProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/completion/PostfixTemplatesCompletionProvider.java @@ -42,11 +42,12 @@ class PostfixTemplatesCompletionProvider extends CompletionProvider templates = Arrays.asList(PostfixTemplate.EP_NAME.getExtensions()); + + LanguageExtensionPoint[] extensions = new ExtensionPointName(LanguagePostfixTemplate.EP_NAME).getExtensions(); + + List templates = ContainerUtil.newArrayList(); + for (LanguageExtensionPoint extension : extensions) { + templates.addAll(((PostfixTemplateProvider)extension.getInstance()).getTemplates()); + } + ContainerUtil.sort(templates, new Comparator() { @Override public int compare(PostfixTemplate o1, PostfixTemplate o2) { @@ -184,7 +196,7 @@ public class PostfixTemplatesConfigurable implements SearchableConfigurable, Edi private static String shortcutToString(char shortcut) { if (shortcut == TemplateSettings.SPACE_CHAR) { return SPACE; - } + } if (shortcut == TemplateSettings.ENTER_CHAR) { return ENTER; } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/JavaPostfixTemplateProvider.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/JavaPostfixTemplateProvider.java new file mode 100644 index 000000000000..cb628dd2c276 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/JavaPostfixTemplateProvider.java @@ -0,0 +1,167 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.postfix.templates; + +import com.intellij.codeInsight.completion.CompletionInitializationContext; +import com.intellij.codeInsight.completion.JavaCompletionContributor; +import com.intellij.codeInsight.template.CustomTemplateCallback; +import com.intellij.codeInsight.template.postfix.util.Aliases; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.util.Ref; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.Set; + + +public class JavaPostfixTemplateProvider implements PostfixTemplateProvider { + + private static final Logger LOG = Logger.getInstance(JavaPostfixTemplateProvider.class); + + private final Map myMapTemplates; + + public JavaPostfixTemplateProvider() { + myMapTemplates = ContainerUtil.newHashMap(); + for (PostfixTemplate template : getInitializeTemplateSet()) { + register(template.getKey(), template); + Aliases aliases = template.getClass().getAnnotation(Aliases.class); + if (aliases != null) { + for (String key : aliases.value()) { + register(key, template); + } + } + } + } + + @NotNull + protected Set getInitializeTemplateSet() { + return ContainerUtil.newHashSet(new AssertStatementPostfixTemplate(), + new CastExpressionPostfixTemplate(), + new ElseStatementPostfixTemplate(), + new ForAscendingPostfixTemplate(), + new ForDescendingPostfixTemplate(), + new ForeachPostfixTemplate(), + new FormatPostfixTemplate(), + new IfStatementPostfixTemplate(), + new InstanceofExpressionPostfixTemplate(), + new IntroduceFieldPostfixTemplate(), + new IntroduceVariablePostfixTemplate(), + new IsNullCheckPostfixTemplate(), + new NotExpressionPostfixTemplate(), + new NotNullCheckPostfixTemplate(), + new ParenthesizedExpressionPostfixTemplate(), + new ReturnStatementPostfixTemplate(), + new SoutPostfixTemplate(), + new SwitchStatementPostfixTemplate(), + new SynchronizedStatementPostfixTemplate(), + new ThrowExceptionPostfixTemplate(), + new TryStatementPostfixTemplate(), + new TryWithResourcesPostfixTemplate(), + new WhileStatementPostfixTemplate()); + } + + @NotNull + @Override + public Set getTemplates() { + return ContainerUtil.newHashSet(myMapTemplates.values()); + } + + @NotNull + @Override + public Set getKeys() { + return myMapTemplates.keySet(); + } + + + @Nullable + @Override + public PostfixTemplate get(@Nullable String key) { + return myMapTemplates.get(key); + } + + @Override + public boolean isTerminalSymbol(char currentChar) { + return currentChar == '.' || currentChar == '!'; + } + + @NotNull + @Override + public PsiElement preExpand(@NotNull Editor editor, @NotNull PsiElement context, int offset, @NotNull final String key) { + + return addSemicolonIfNeeded(editor, editor.getDocument(), context, offset - key.length()); + } + + @NotNull + @Override + public PsiFile preCheck(@NotNull Editor editor, @NotNull PsiFile file, int currentOffset) { + Document document = file.getViewProvider().getDocument(); + assert document != null; + CharSequence sequence = document.getCharsSequence(); + StringBuilder fileContentWithoutKey = new StringBuilder(sequence); + if (isSemicolonNeeded(file, editor)) { + fileContentWithoutKey.insert(currentOffset, ';'); + file = PostfixLiveTemplate.copyFile(file, fileContentWithoutKey); + } + + return file; + } + + private void register(@NotNull String key, @NotNull PostfixTemplate template) { + PostfixTemplate registered = myMapTemplates.put(key, template); + if (registered != null) { + LOG.error("Can't register postfix template. Duplicated key: " + template.getKey()); + } + } + + @NotNull + private static PsiElement addSemicolonIfNeeded(@NotNull final Editor editor, + @NotNull final Document document, + @NotNull final PsiElement context, + final int offset) { + ApplicationManager.getApplication().assertIsDispatchThread(); + + final Ref newContext = Ref.create(context); + final PsiFile file = context.getContainingFile(); + if (isSemicolonNeeded(file, editor)) { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() { + public void run() { + document.insertString(offset, ";"); + PsiDocumentManager.getInstance(context.getProject()).commitDocument(document); + newContext.set(CustomTemplateCallback.getContext(file, offset - 1)); + } + }); + } + }); + } + return newContext.get(); + } + + private static boolean isSemicolonNeeded(@NotNull PsiFile file, @NotNull Editor editor) { + return JavaCompletionContributor.semicolonNeeded(editor, file, CompletionInitializationContext.calcStartOffset(editor)); + } +} diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/ClassInTopLevelPackageInspection.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/LanguagePostfixTemplate.java similarity index 54% rename from plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/ClassInTopLevelPackageInspection.java rename to java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/LanguagePostfixTemplate.java index 828b4c68d43f..d3780e1f3f12 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/ClassInTopLevelPackageInspection.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/LanguagePostfixTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.siyeh.ig.classlayout; +package com.intellij.codeInsight.template.postfix.templates; -import com.siyeh.ig.InspectionGadgetsFix; -import com.siyeh.ig.fixes.MoveClassFix; +import com.intellij.lang.LanguageExtension; -public class ClassInTopLevelPackageInspection extends ClassInTopLevelPackageInspectionBase { +public class LanguagePostfixTemplate extends LanguageExtension { + public static final LanguagePostfixTemplate INSTANCE = new LanguagePostfixTemplate(); + public static final String EP_NAME = "com.intellij.codeInsight.template.postfixTemplateProvider"; - @Override - protected InspectionGadgetsFix buildFix(Object... infos) { - return new MoveClassFix(); + private LanguagePostfixTemplate() { + super(EP_NAME); } -} \ No newline at end of file +} diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixLiveTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixLiveTemplate.java index d738fb8d227b..349eeda3ffb7 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixLiveTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixLiveTemplate.java @@ -15,17 +15,15 @@ */ package com.intellij.codeInsight.template.postfix.templates; -import com.intellij.codeInsight.completion.CompletionInitializationContext; -import com.intellij.codeInsight.completion.JavaCompletionContributor; +import com.google.common.collect.Sets; import com.intellij.codeInsight.template.CustomLiveTemplateBase; import com.intellij.codeInsight.template.CustomTemplateCallback; import com.intellij.codeInsight.template.impl.CustomLiveTemplateLookupElement; import com.intellij.codeInsight.template.impl.TemplateSettings; import com.intellij.codeInsight.template.postfix.completion.PostfixTemplateLookupElement; import com.intellij.codeInsight.template.postfix.settings.PostfixTemplatesSettings; -import com.intellij.codeInsight.template.postfix.util.Aliases; import com.intellij.featureStatistics.FeatureUsageTracker; -import com.intellij.lang.java.JavaLanguage; +import com.intellij.lang.Language; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.undo.UndoConstants; @@ -33,7 +31,6 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; @@ -42,71 +39,53 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileFactory; import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; +import java.util.Collections; import java.util.Set; public class PostfixLiveTemplate extends CustomLiveTemplateBase { public static final String POSTFIX_TEMPLATE_ID = "POSTFIX_TEMPLATE_ID"; - private static final Logger LOG = Logger.getInstance(PostfixLiveTemplate.class); - private final HashMap myTemplates = ContainerUtil.newHashMap(); + private static final LanguagePostfixTemplate templates = LanguagePostfixTemplate.INSTANCE; - public PostfixLiveTemplate() { - for (PostfixTemplate template : PostfixTemplate.EP_NAME.getExtensions()) { - register(template.getKey(), template); - Aliases aliases = template.getClass().getAnnotation(Aliases.class); - if (aliases != null) { - for (String key : aliases.value()) { - register(key, template); - } + @NotNull + public Set getAllTemplateKeys(PsiFile file, int offset) { + Set keys = Sets.newHashSet(); + Language language = PsiUtilCore.getLanguageAtOffset(file, offset); + + for (PostfixTemplateProvider provider : templates.allForLanguage(language)) { + keys.addAll(provider.getKeys()); + } + return keys; + } + + public boolean hasNotEmptyKey(PsiFile file, int offset) { + Language language = PsiUtilCore.getLanguageAtOffset(file, offset); + for (PostfixTemplateProvider provider : templates.allForLanguage(language)) { + if (StringUtil + .isNotEmpty(computeTemplateKeyWithoutContextChecking(provider, file.getText(), offset + 1))) { + return true; } } - } - - private void register(@NotNull String key, @NotNull PostfixTemplate template) { - PostfixTemplate registered = myTemplates.put(key, template); - if (registered != null) { - LOG.error("Can't register postfix template. Duplicated key: " + template.getKey()); - } - } - - @Nullable - @Override - public String computeTemplateKey(@NotNull CustomTemplateCallback callback) { - Editor editor = callback.getEditor(); - String key = computeTemplateKeyWithoutContextChecking(editor.getDocument().getCharsSequence(), editor.getCaretModel().getOffset()); - if (key == null) return null; - return isApplicableTemplate(getTemplateByKey(key), key, callback.getContext().getContainingFile(), editor) ? key : null; - } - - @Nullable - @Override - public String computeTemplateKeyWithoutContextChecking(@NotNull CustomTemplateCallback callback) { - Editor editor = callback.getEditor(); - return computeTemplateKeyWithoutContextChecking(editor.getDocument().getCharsSequence(), editor.getCaretModel().getOffset()); - } - - @Override - public boolean supportsMultiCaret() { return false; } @Nullable - public String computeTemplateKeyWithoutContextChecking(@NotNull CharSequence documentContent, int currentOffset) { + public String computeTemplateKeyWithoutContextChecking(PostfixTemplateProvider provider, + @NotNull CharSequence documentContent, + int currentOffset) { int startOffset = currentOffset; if (documentContent.length() < startOffset) { return null; } + while (startOffset > 0) { char currentChar = documentContent.charAt(startOffset - 1); if (!Character.isJavaIdentifierPart(currentChar)) { - if (currentChar != '.' && currentChar != '!') { + if (!provider.isTerminalSymbol(currentChar)) { return null; } startOffset--; @@ -117,19 +96,82 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase { return String.valueOf(documentContent.subSequence(startOffset, currentOffset)); } + @Nullable + @Override + public String computeTemplateKey(@NotNull CustomTemplateCallback callback) { + Editor editor = callback.getEditor(); + CharSequence charsSequence = editor.getDocument().getCharsSequence(); + int offset = editor.getCaretModel().getOffset(); + Language language = getLanguage(callback); + for (PostfixTemplateProvider provider : templates.allForLanguage(language)) { + String key = computeTemplateKeyWithoutContextChecking(provider, charsSequence, offset); + if (key != null && isApplicableTemplate(provider, key, callback.getFile(), editor)) { + return key; + } + } + + return null; + } + + @Nullable + @Override + public String computeTemplateKeyWithoutContextChecking(@NotNull CustomTemplateCallback callback) { + Editor editor = callback.getEditor(); + return computeTemplateKeyWithoutContextChecking(callback.getFile(), editor, editor.getCaretModel().getOffset()); + } + + @Override + public boolean supportsMultiCaret() { + return false; + } + + @Nullable + public String computeTemplateKeyWithoutContextChecking(PsiFile file, Editor editor, int currentOffset) { + Language language = PsiUtilCore.getLanguageAtOffset(file, currentOffset); + for (PostfixTemplateProvider provider : templates.allForLanguage(language)) { + String key = computeTemplateKeyWithoutContextChecking(provider, editor.getDocument().getCharsSequence(), currentOffset); + if (key != null) return key; + } + return null; + } + @Override public void expand(@NotNull final String key, @NotNull final CustomTemplateCallback callback) { ApplicationManager.getApplication().assertIsDispatchThread(); - FeatureUsageTracker.getInstance().triggerFeatureUsed("editing.completion.postfix"); - final PostfixTemplate template = getTemplateByKey(key); + Editor editor = callback.getEditor(); + Language language = getLanguage(callback); + for (PostfixTemplateProvider provider : templates.allForLanguage(language)) { + PostfixTemplate postfixTemplate = provider.get(key); + if (postfixTemplate != null) { + expandForProvider(provider, key, callback); + return; + } + } + + // don't care about errors in multiCaret mode + if (editor.getCaretModel().getAllCarets().size() == 1) { + LOG.error("Template not found by key: " + key); + } + } + + private static Language getLanguage(CustomTemplateCallback callback) { + return PsiUtilCore.getLanguageAtOffset(callback.getFile(), callback.getEditor().getCaretModel().getOffset()); + } + + public void expandForProvider( + @NotNull PostfixTemplateProvider provider, + @NotNull final String key, + @NotNull final CustomTemplateCallback callback) { final Editor editor = callback.getEditor(); final PsiFile file = callback.getContext().getContainingFile(); - if (isApplicableTemplate(template, key, file, editor)) { + if (isApplicableTemplate(provider, key, file, editor)) { int currentOffset = editor.getCaretModel().getOffset(); PsiElement newContext = deleteTemplateKey(file, editor.getDocument(), currentOffset, key); - newContext = addSemicolonIfNeeded(editor, editor.getDocument(), newContext, currentOffset - key.length()); + newContext = provider.preExpand(editor, newContext, currentOffset, key); + PostfixTemplate template = provider.get(key); + assert template != null; expandTemplate(template, editor, newContext); } // don't care about errors in multiCaret mode @@ -141,11 +183,10 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase { @Override public boolean isApplicable(PsiFile file, int offset, boolean wrapping) { PostfixTemplatesSettings settings = PostfixTemplatesSettings.getInstance(); - if (wrapping || file == null || settings == null || !settings.isPostfixTemplatesEnabled() || - PsiUtilCore.getLanguageAtOffset(file, offset) != JavaLanguage.INSTANCE) { + if (wrapping || file == null || settings == null || !settings.isPostfixTemplatesEnabled()) { return false; } - return StringUtil.isNotEmpty(computeTemplateKeyWithoutContextChecking(file.getText(), offset + 1)); + return hasNotEmptyKey(file, offset); } @Override @@ -177,35 +218,46 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase { @NotNull @Override - public Collection getLookupElements(@NotNull PsiFile file, @NotNull Editor editor, int offset) { - String key = computeTemplateKeyWithoutContextChecking(editor.getDocument().getCharsSequence(), offset); + public Collection getLookupElements(@NotNull PsiFile file, + @NotNull Editor editor, + int offset) { + Collection result = ContainerUtil.newHashSet(); + Language language = PsiUtilCore.getLanguageAtOffset(file, editor.getCaretModel().getOffset()); + for (PostfixTemplateProvider provider : templates.allForLanguage(language)) { + result.addAll(getLookupElementsForProvider(provider, file, editor, offset)); + } + + return result; + } + + @NotNull + private Collection getLookupElementsForProvider( + @NotNull PostfixTemplateProvider provider, + @NotNull PsiFile file, + @NotNull Editor editor, + int offset) { + String key = computeTemplateKeyWithoutContextChecking(file, editor, offset); if (key != null && editor.getCaretModel().getCaretCount() == 1) { Collection result = ContainerUtil.newHashSet(); - Condition isApplicationTemplateFunction = createIsApplicationTemplateFunction(key, file, editor); - for (Map.Entry entry : myTemplates.entrySet()) { - PostfixTemplate postfixTemplate = entry.getValue(); + + Condition isApplicationTemplateFunction = createIsApplicationTemplateFunction(provider, key, file, editor); + for (String postfixKey : provider.getKeys()) { + PostfixTemplate postfixTemplate = provider.get(postfixKey); + assert postfixTemplate != null; if (isApplicationTemplateFunction.value(postfixTemplate)) { - result.add(new PostfixTemplateLookupElement(this, postfixTemplate, entry.getKey(), false)); + result.add(new PostfixTemplateLookupElement(this, postfixTemplate, postfixKey, false)); } } return result; } - return super.getLookupElements(file, editor, offset); + return Collections.emptyList(); } - @NotNull - public Set getAllTemplateKeys() { - return myTemplates.keySet(); - } - @Nullable - public PostfixTemplate getTemplateByKey(@NotNull String key) { - return myTemplates.get(key); - } - - private static void expandTemplate(@NotNull final PostfixTemplate template, - @NotNull final Editor editor, - @NotNull final PsiElement context) { + private static void expandTemplate( + @NotNull final PostfixTemplate template, + @NotNull final Editor editor, + @NotNull final PsiElement context) { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { @@ -218,56 +270,6 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase { }); } - @Contract("null, _, _, _ -> false") - private static boolean isApplicableTemplate(@Nullable PostfixTemplate template, @NotNull String key, @NotNull PsiFile file, @NotNull Editor editor) { - return createIsApplicationTemplateFunction(key, file, editor).value(template); - } - - private static Condition createIsApplicationTemplateFunction(@NotNull String key, @NotNull PsiFile file, @NotNull Editor editor) { - int currentOffset = editor.getCaretModel().getOffset(); - final int newOffset = currentOffset - key.length(); - CharSequence fileContent = editor.getDocument().getCharsSequence(); - - StringBuilder fileContentWithoutKey = new StringBuilder(); - fileContentWithoutKey.append(fileContent.subSequence(0, newOffset)); - fileContentWithoutKey.append(fileContent.subSequence(currentOffset, fileContent.length())); - PsiFile copyFile = copyFile(file, fileContentWithoutKey); - Document copyDocument = copyFile.getViewProvider().getDocument(); - if (copyDocument == null) { - //noinspection unchecked - return Condition.FALSE; - } - - if (isSemicolonNeeded(copyFile, editor)) { - fileContentWithoutKey.insert(newOffset, ';'); - copyFile = copyFile(file, fileContentWithoutKey); - copyDocument = copyFile.getViewProvider().getDocument(); - if (copyDocument == null) { - //noinspection unchecked - return Condition.FALSE; - } - } - - final PsiElement context = CustomTemplateCallback.getContext(copyFile, newOffset > 0 ? newOffset - 1 : newOffset); - final Document finalCopyDocument = copyDocument; - return new Condition() { - @Override - public boolean value(PostfixTemplate template) { - return template != null && template.isEnabled() && template.isApplicable(context, finalCopyDocument, newOffset); - } - }; - } - - @NotNull - private static PsiFile copyFile(@NotNull PsiFile file, @NotNull StringBuilder fileContentWithoutKey) { - final PsiFileFactory psiFileFactory = PsiFileFactory.getInstance(file.getProject()); - PsiFile copy = psiFileFactory.createFileFromText(file.getName(), file.getFileType(), fileContentWithoutKey); - VirtualFile vFile = copy.getVirtualFile(); - if (vFile != null) { - vFile.putUserData(UndoConstants.DONT_RECORD_UNDO, Boolean.TRUE); - } - return copy; - } @NotNull private static PsiElement deleteTemplateKey(@NotNull final PsiFile file, @@ -291,33 +293,58 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase { return CustomTemplateCallback.getContext(file, startOffset > 0 ? startOffset - 1 : startOffset); } - @NotNull - private static PsiElement addSemicolonIfNeeded(@NotNull final Editor editor, - @NotNull final Document document, - @NotNull final PsiElement context, - final int offset) { - ApplicationManager.getApplication().assertIsDispatchThread(); - - final Ref newContext = Ref.create(context); - final PsiFile file = context.getContainingFile(); - if (isSemicolonNeeded(file, editor)) { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() { - public void run() { - document.insertString(offset, ";"); - PsiDocumentManager.getInstance(context.getProject()).commitDocument(document); - newContext.set(CustomTemplateCallback.getContext(file, offset - 1)); - } - }); - } - }); + private static Condition createIsApplicationTemplateFunction( + @NotNull PostfixTemplateProvider provider, + @NotNull String key, + @NotNull PsiFile file, + @NotNull Editor editor) { + int currentOffset = editor.getCaretModel().getOffset(); + final int newOffset = currentOffset - key.length(); + CharSequence fileContent = editor.getDocument().getCharsSequence(); + StringBuilder fileContentWithoutKey = new StringBuilder(); + fileContentWithoutKey.append(fileContent.subSequence(0, newOffset)); + fileContentWithoutKey.append(fileContent.subSequence(currentOffset, fileContent.length())); + PsiFile copyFile = copyFile(file, fileContentWithoutKey); + Document copyDocument = copyFile.getViewProvider().getDocument(); + if (copyDocument == null) { + //noinspection unchecked + return Condition.FALSE; } - return newContext.get(); + + copyFile = provider.preCheck(editor, copyFile, newOffset); + copyDocument = copyFile.getViewProvider().getDocument(); + if (copyDocument == null) { + //noinspection unchecked + return Condition.FALSE; + } + + final PsiElement context = CustomTemplateCallback.getContext(copyFile, newOffset > 0 ? newOffset - 1 : newOffset); + final Document finalCopyDocument = copyDocument; + return new Condition() { + @Override + public boolean value(PostfixTemplate template) { + return template != null && template.isEnabled() && template.isApplicable(context, finalCopyDocument, newOffset); + } + }; } - private static boolean isSemicolonNeeded(@NotNull PsiFile file, @NotNull Editor editor) { - return JavaCompletionContributor.semicolonNeeded(editor, file, CompletionInitializationContext.calcStartOffset(editor)); + + @NotNull + public static PsiFile copyFile(@NotNull PsiFile file, @NotNull StringBuilder fileContentWithoutKey) { + final PsiFileFactory psiFileFactory = PsiFileFactory.getInstance(file.getProject()); + PsiFile copy = psiFileFactory.createFileFromText(file.getName(), file.getFileType(), fileContentWithoutKey); + VirtualFile vFile = copy.getVirtualFile(); + if (vFile != null) { + vFile.putUserData(UndoConstants.DONT_RECORD_UNDO, Boolean.TRUE); + } + return copy; + } + + public static boolean isApplicableTemplate( + @NotNull PostfixTemplateProvider provider, + @NotNull String key, + @NotNull PsiFile file, + @NotNull Editor editor) { + return createIsApplicationTemplateFunction(provider, key, file, editor).value(provider.get(key)); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplate.java index 80a68ab26650..4131dd907d32 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplate.java @@ -18,7 +18,6 @@ package com.intellij.codeInsight.template.postfix.templates; import com.intellij.codeInsight.template.postfix.settings.PostfixTemplatesSettings; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiExpression; import com.intellij.psi.PsiExpressionStatement; @@ -32,9 +31,6 @@ public abstract class PostfixTemplate { @NotNull private final String myDescription; @NotNull private final String myExample; - @NotNull - public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.postfixTemplate"); - protected PostfixTemplate(@NotNull String name, @NotNull String description, @NotNull String example) { this(name, "." + name, description, example); } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateProvider.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateProvider.java new file mode 100644 index 000000000000..b74beba50598 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateProvider.java @@ -0,0 +1,64 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.template.postfix.templates; + + +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Set; + +public interface PostfixTemplateProvider { + + /** + * Return all templates registered in the provider + */ + @NotNull + Set getTemplates(); + + /** + * Return all keys registered in the provider + */ + @NotNull + Set getKeys(); + + /** + * Return postfix template for key + */ + @Nullable + PostfixTemplate get(@Nullable String key); + + /** + * Check symbol can separate template keys + */ + boolean isTerminalSymbol(char currentChar); + + /** + * Prepare original file content for template expanding + */ + @NotNull + PsiElement preExpand(@NotNull Editor editor, @NotNull PsiElement context, int currentOffset, @NotNull String key); + + /** + * Do some actions with the file content before check applicable. + * Return new copy of file because we should not change original file + */ + @NotNull + PsiFile preCheck(@NotNull Editor editor, @NotNull PsiFile file, int currentOffset); +} diff --git a/java/java-impl/src/com/intellij/ide/fileTemplates/JavaCreateFromTemplateHandler.java b/java/java-impl/src/com/intellij/ide/fileTemplates/JavaCreateFromTemplateHandler.java index 26e4adfa0bbb..f511eac9320d 100644 --- a/java/java-impl/src/com/intellij/ide/fileTemplates/JavaCreateFromTemplateHandler.java +++ b/java/java-impl/src/com/intellij/ide/fileTemplates/JavaCreateFromTemplateHandler.java @@ -26,6 +26,7 @@ import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.impl.file.JavaDirectoryServiceImpl; import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; import java.util.Map; @@ -100,8 +101,9 @@ public class JavaCreateFromTemplateHandler implements CreateFromTemplateHandler return fileType.equals(StdFileTypes.JAVA) && !FileTemplateUtil.INTERNAL_PACKAGE_INFO_TEMPLATE_NAME.equals(template.getName()); } + @NotNull public PsiElement createFromTemplate(final Project project, final PsiDirectory directory, final String fileName, FileTemplate template, - String templateText, Map props) throws IncorrectOperationException { + String templateText, @NotNull Map props) throws IncorrectOperationException { String extension = template.getExtension(); PsiElement result = createClassOrInterface(project, directory, templateText, template.isReformatCode(), extension); hackAwayEmptyPackage((PsiJavaFile)result.getContainingFile(), template, props); diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/search/AllClassesSearchExecutor.java b/java/java-indexing-impl/src/com/intellij/psi/impl/search/AllClassesSearchExecutor.java index 09b60c173f9b..293d3350040b 100644 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/search/AllClassesSearchExecutor.java +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/search/AllClassesSearchExecutor.java @@ -30,6 +30,7 @@ import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.PsiShortNamesCache; import com.intellij.psi.search.SearchScope; import com.intellij.psi.search.searches.AllClassesSearch; +import com.intellij.util.Consumer; import com.intellij.util.Processor; import com.intellij.util.QueryExecutor; import com.intellij.util.indexing.IdFilter; @@ -57,39 +58,28 @@ public class AllClassesSearchExecutor implements QueryExecutor processor) { - Project project = parameters.getProject(); - final PsiShortNamesCache cache = PsiShortNamesCache.getInstance(project); - final ProgressIndicator indicator = ProgressIndicatorProvider.getGlobalProgressIndicator(); - final Set names = new THashSet(10000); - cache.processAllClassNames(new Processor() { - int i = 0; - + processClassNames(parameters.getProject(), scope, new Consumer() { @Override - public boolean process(String s) { - if (indicator != null && i++ % 512 == 0) { - indicator.checkCanceled(); - } + public void consume(String s) { if (parameters.nameMatches(s)) { names.add(s); } - return true; - } - }, scope, IdFilter.getProjectIdFilter(project, true)); - - if (indicator != null) { - indicator.checkCanceled(); - } - - List sorted = new ArrayList(names); - Collections.sort(sorted, new Comparator() { - @Override - public int compare(final String o1, final String o2) { - return o1.compareToIgnoreCase(o2); } }); - for (final String name : sorted) { + List sorted = new ArrayList(names); + Collections.sort(sorted, String.CASE_INSENSITIVE_ORDER); + + return processClassesByNames(parameters.getProject(), scope, sorted, processor); + } + + public static boolean processClassesByNames(Project project, + final GlobalSearchScope scope, + Collection names, + Processor processor) { + final PsiShortNamesCache cache = PsiShortNamesCache.getInstance(project); + for (final String name : names) { ProgressIndicatorProvider.checkCanceled(); final PsiClass[] classes = ApplicationManager.getApplication().runReadAction(new Computable() { @Override @@ -107,6 +97,28 @@ public class AllClassesSearchExecutor implements QueryExecutor consumer) { + final ProgressIndicator indicator = ProgressIndicatorProvider.getGlobalProgressIndicator(); + + PsiShortNamesCache.getInstance(project).processAllClassNames(new Processor() { + int i = 0; + + @Override + public boolean process(String s) { + if (indicator != null && i++ % 512 == 0) { + indicator.checkCanceled(); + } + consumer.consume(s); + return true; + } + }, scope, IdFilter.getProjectIdFilter(project, true)); + + if (indicator != null) { + indicator.checkCanceled(); + } + return project; + } + private static boolean processScopeRootForAllClasses(@NotNull final PsiElement scopeRoot, @NotNull final Processor processor) { final boolean[] stopped = {false}; diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiTypesUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiTypesUtil.java index c448926a5874..0330fd9be8d2 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiTypesUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiTypesUtil.java @@ -22,7 +22,6 @@ import com.intellij.openapi.util.Condition; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; -import com.intellij.util.containers.HashMap; import gnu.trove.THashMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -151,9 +150,7 @@ public class PsiTypesUtil { @Nullable Condition condition, @NotNull LanguageLevel languageLevel) { //JLS3 15.8.2 - if (languageLevel.isAtLeast(LanguageLevel.JDK_1_5) && - GET_CLASS_METHOD.equals(method.getName()) && - CommonClassNames.JAVA_LANG_OBJECT.equals(method.getContainingClass().getQualifiedName())) { + if (languageLevel.isAtLeast(LanguageLevel.JDK_1_5) && isGetClass(method)) { PsiExpression qualifier = methodExpression.getQualifierExpression(); PsiType qualifierType = null; final Project project = call.getProject(); @@ -169,18 +166,28 @@ public class PsiTypesUtil { qualifierType = JavaPsiFacade.getInstance(project).getElementFactory().createType((PsiClass)parent.getPsi()); } } - if (qualifierType != null) { - PsiClass javaLangClass = JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_LANG_CLASS, call.getResolveScope()); - if (javaLangClass != null && javaLangClass.getTypeParameters().length == 1) { - Map map = new HashMap(); - map.put(javaLangClass.getTypeParameters()[0], PsiWildcardType.createExtends(call.getManager(), qualifierType)); - PsiSubstitutor substitutor = JavaPsiFacade.getInstance(project).getElementFactory().createSubstitutor(map); - final PsiClassType classType = JavaPsiFacade.getInstance(project).getElementFactory() - .createType(javaLangClass, substitutor, languageLevel); - final PsiElement parent = call.getParent(); - return parent instanceof PsiReferenceExpression && parent.getParent() instanceof PsiMethodCallExpression || parent instanceof PsiExpressionList - ? PsiUtil.captureToplevelWildcards(classType, methodExpression) : classType; - } + PsiElement parent = call.getParent(); + boolean captureTopLevelWildcards = parent instanceof PsiReferenceExpression && parent.getParent() instanceof PsiMethodCallExpression || + parent instanceof PsiExpressionList; + return createJavaLangClassType(methodExpression, qualifierType, captureTopLevelWildcards); + } + return null; + } + + public static boolean isGetClass(PsiMethod method) { + return GET_CLASS_METHOD.equals(method.getName()) && CommonClassNames.JAVA_LANG_OBJECT.equals(method.getContainingClass().getQualifiedName()); + } + + @Nullable + public static PsiType createJavaLangClassType(@NotNull PsiElement context, @Nullable PsiType qualifierType, boolean captureTopLevelWildcards) { + if (qualifierType != null) { + JavaPsiFacade facade = JavaPsiFacade.getInstance(context.getProject()); + PsiClass javaLangClass = facade.findClass(CommonClassNames.JAVA_LANG_CLASS, context.getResolveScope()); + if (javaLangClass != null && javaLangClass.getTypeParameters().length == 1) { + PsiSubstitutor substitutor = PsiSubstitutor.EMPTY. + put(javaLangClass.getTypeParameters()[0], PsiWildcardType.createExtends(context.getManager(), qualifierType)); + final PsiClassType classType = facade.getElementFactory().createType(javaLangClass, substitutor, PsiUtil.getLanguageLevel(context)); + return captureTopLevelWildcards ? PsiUtil.captureToplevelWildcards(classType, context) : classType; } } return null; diff --git a/java/java-tests/testData/codeInsight/completion/smartType/GetClassWhenClassExpected-out.java b/java/java-tests/testData/codeInsight/completion/smartType/GetClassWhenClassExpected-out.java new file mode 100644 index 000000000000..39fbcc47c65b --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/GetClassWhenClassExpected-out.java @@ -0,0 +1,7 @@ +class A { +} +class B extends A { + void m() { + Class c = getClass(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/smartType/GetClassWhenClassExpected.java b/java/java-tests/testData/codeInsight/completion/smartType/GetClassWhenClassExpected.java new file mode 100644 index 000000000000..918046761375 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/GetClassWhenClassExpected.java @@ -0,0 +1,7 @@ +class A { +} +class B extends A { + void m() { + Class c = g + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy index d789ce2ad957..5910f178f2cf 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/JavaAutoPopupTest.groovy @@ -1624,4 +1624,15 @@ class Foo { def tabKeyPresentation = KeyEvent.getKeyText(TemplateSettings.TAB_CHAR as int) assert p.typeText == " [$tabKeyPresentation] " } + + public void "test autopopup after package completion"() { + myFixture.addClass("package foo.bar.goo; class Foo {}") + myFixture.configureByText "a.java", "class Foo { { foo.b } }" + myFixture.completeBasic() + assert myFixture.editor.document.text.contains('foo.bar. ') + joinAutopopup() + joinCompletion() + assert lookup + assert myFixture.lookupElementStrings == ['goo'] + } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java index 6779aacee87d..478cc941567c 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java @@ -1022,6 +1022,7 @@ public class SmartTypeCompletionTest extends LightFixtureCompletionTestCase { public void testDuplicateMembersFromSuperClass() throws Throwable { doTest(); } public void testInnerAfterNew() throws Throwable { doTest(); } public void testEverythingInStringConcatenation() throws Throwable { doTest(); } + public void testGetClassWhenClassExpected() { doTest(); } public void testMemberImportStatically() { configureByTestName(); diff --git a/platform/lang-impl/src/com/intellij/application/options/colors/SimpleEditorPreview.java b/platform/lang-impl/src/com/intellij/application/options/colors/SimpleEditorPreview.java index 6f54db66fbf8..22eca017d876 100644 --- a/platform/lang-impl/src/com/intellij/application/options/colors/SimpleEditorPreview.java +++ b/platform/lang-impl/src/com/intellij/application/options/colors/SimpleEditorPreview.java @@ -19,7 +19,10 @@ package com.intellij.application.options.colors; import com.intellij.application.options.colors.highlighting.HighlightData; import com.intellij.application.options.colors.highlighting.HighlightsExtractor; import com.intellij.ide.highlighter.HighlighterFactory; -import com.intellij.openapi.editor.*; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.EditorFactory; +import com.intellij.openapi.editor.LogicalPosition; +import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.editor.colors.CodeInsightColors; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.colors.TextAttributesKey; @@ -42,10 +45,7 @@ import javax.swing.*; import java.awt.*; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.Map; +import java.util.*; import java.util.List; public class SimpleEditorPreview implements PreviewPanel{ @@ -91,6 +91,10 @@ public class SimpleEditorPreview implements PreviewPanel{ } } + public EditorEx getEditor() { + return myEditor; + } + private void addMouseMotionListener(final Editor view, final SyntaxHighlighter highlighter, final HighlightData[] data, final boolean isBackgroundImportant) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java index 392e35ac5941..25f0bf27092b 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java @@ -430,7 +430,8 @@ public class CodeCompletionHandlerBase { // the insert handler may have started a live template with completion if (CompletionService.getCompletionService().getCurrentCompletion() == null && - !ApplicationManager.getApplication().isUnitTestMode()) { + // ...or scheduled another autopopup + !CompletionServiceImpl.isPhase(CompletionPhase.CommittingDocuments.class)) { CompletionServiceImpl.setCompletionPhase(hasModifiers? new CompletionPhase.InsertedSingleItem(indicator, restorePrefix) : CompletionPhase.NoCompletion); } checkNotSync(indicator, items); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/OffsetsElementSignatureProvider.java b/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/OffsetsElementSignatureProvider.java index cae4fe686078..5a9defd95d0c 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/OffsetsElementSignatureProvider.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/OffsetsElementSignatureProvider.java @@ -206,7 +206,7 @@ public class OffsetsElementSignatureProvider extends AbstractElementSignaturePro buffer.append(ELEMENT_TOKENS_SEPARATOR).append(index); PsiFile containingFile = element.getContainingFile(); if (containingFile != null && containingFile.getViewProvider().getLanguages().size() > 1) { - buffer.append(ELEMENT_TOKENS_SEPARATOR).append(element.getLanguage().getID()); + buffer.append(ELEMENT_TOKENS_SEPARATOR).append(containingFile.getLanguage().getID()); } return buffer.toString(); } 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 0402bba74698..8df95370fdaa 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 @@ -16,7 +16,6 @@ package com.intellij.codeInsight.lookup.impl; -import com.intellij.codeInsight.CodeInsightBundle; import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInsight.completion.CodeCompletionFeatures; import com.intellij.codeInsight.completion.CompletionLookupArranger; @@ -27,25 +26,19 @@ import com.intellij.codeInsight.hint.HintManager; import com.intellij.codeInsight.hint.HintManagerImpl; import com.intellij.codeInsight.lookup.*; import com.intellij.featureStatistics.FeatureUsageTracker; -import com.intellij.icons.AllIcons; -import com.intellij.ide.DataManager; import com.intellij.ide.IdeEventQueue; import com.intellij.ide.ui.UISettings; import com.intellij.lang.LangBundle; import com.intellij.openapi.Disposable; -import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.event.*; import com.intellij.openapi.editor.event.DocumentAdapter; -import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; -import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; @@ -57,20 +50,12 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.impl.DebugUtil; import com.intellij.ui.*; import com.intellij.ui.awt.RelativePoint; -import com.intellij.ui.components.JBLayeredPane; import com.intellij.ui.components.JBList; -import com.intellij.ui.components.JBScrollPane; -import com.intellij.ui.plaf.beg.BegPopupMenuBorder; import com.intellij.ui.popup.AbstractPopup; -import com.intellij.util.Alarm; import com.intellij.util.CollectConsumer; -import com.intellij.util.PlatformIcons; import com.intellij.util.containers.ConcurrentHashMap; import com.intellij.util.containers.ConcurrentWeakHashMap; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.ui.AbstractLayoutManager; -import com.intellij.util.ui.AsyncProcessIcon; -import com.intellij.util.ui.ButtonlessScrollBarUI; import com.intellij.util.ui.update.Activatable; import com.intellij.util.ui.update.UiNotifyConnector; import org.jetbrains.annotations.NotNull; @@ -78,13 +63,11 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.border.EmptyBorder; -import javax.swing.border.LineBorder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import java.awt.*; -import java.awt.event.*; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -120,8 +103,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable return myExtender; } }; - private final LookupCellRenderer myCellRenderer; - private Boolean myPositionedAbove = null; + final LookupCellRenderer myCellRenderer; private final List myListeners = ContainerUtil.createLockFreeCopyOnWriteList(); @@ -131,12 +113,10 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable private boolean myHidden = false; private boolean mySelectionTouched; private FocusDegree myFocusDegree = FocusDegree.FOCUSED; - private final AsyncProcessIcon myProcessIcon = new AsyncProcessIcon("Completion progress"); - private final JPanel myIconPanel = new JPanel(new BorderLayout()); private volatile boolean myCalculating; private final Advertiser myAdComponent; private volatile String myAdText; - private volatile int myLookupTextWidth = 50; + volatile int myLookupTextWidth = 50; private boolean myChangeGuard; private volatile LookupArranger myArranger; private LookupArranger myPresentableArranger; @@ -144,18 +124,11 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable ContainerUtil.identityStrategy()); private final Map myCustomFonts = new ConcurrentWeakHashMap( ContainerUtil.identityStrategy()); - private LookupHint myElementHint = null; - private final Alarm myHintAlarm = new Alarm(); - private final JLabel mySortingLabel = new JLabel(); - private final JScrollPane myScrollPane; - final LookupLayeredPane myLayeredPane = new LookupLayeredPane(); - private final JButton myScrollBarIncreaseButton; private boolean myStartCompletionWhenNothingMatches; - private boolean myResizePending; - private int myMaximumHeight = Integer.MAX_VALUE; + boolean myResizePending; private boolean myFinishing; - private boolean myUpdating; - private final ModalityState myModalityState; + boolean myUpdating; + private LookupUi myUi; public LookupImpl(Project project, Editor editor, @NotNull LookupArranger arranger) { super(new JPanel(new BorderLayout())); @@ -169,7 +142,6 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable myArranger = arranger; myPresentableArranger = arranger; - myIconPanel.setVisible(false); myCellRenderer = new LookupCellRenderer(this); myList.setCellRenderer(myCellRenderer); @@ -181,36 +153,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable myList.getExpandableItemsHandler(); - myScrollBarIncreaseButton = new JButton(); - myScrollBarIncreaseButton.setFocusable(false); - myScrollBarIncreaseButton.setRequestFocusEnabled(false); - - myScrollPane = new JBScrollPane(myList); - myScrollPane.setViewportBorder(new EmptyBorder(0, 0, 0, 0)); - myScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - myScrollPane.getVerticalScrollBar().setPreferredSize(new Dimension(13, -1)); - myScrollPane.getVerticalScrollBar().setUI(new ButtonlessScrollBarUI() { - @Override - protected JButton createIncreaseButton(int orientation) { - return myScrollBarIncreaseButton; - } - }); - getComponent().add(myLayeredPane, BorderLayout.CENTER); - - //IDEA-82111 - fixMouseCheaters(); - - myLayeredPane.mainPanel.add(myScrollPane, BorderLayout.CENTER); - myScrollPane.setBorder(null); - myAdComponent = new Advertiser(); - JComponent adComponent = myAdComponent.getAdComponent(); - adComponent.setBorder(new EmptyBorder(0, 1, 1, 2 + AllIcons.Ide.LookupRelevance.getIconWidth())); - myLayeredPane.mainPanel.add(adComponent, BorderLayout.SOUTH); - getComponent().setBorder(new BegPopupMenuBorder()); - - myIconPanel.setBackground(Color.LIGHT_GRAY); - myIconPanel.add(myProcessIcon); myOffsets = new LookupOffsets(editor); @@ -218,14 +161,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable addEmptyItem(model); updateListHeight(model); - addListeners(); - - mySortingLabel.setBorder(new LineBorder(new JBColor(Color.LIGHT_GRAY, JBColor.background()))); - mySortingLabel.setOpaque(true); - new ChangeLookupSorting().installOn(mySortingLabel); - updateSorting(); - myModalityState = ModalityState.stateForComponent(getComponent()); } private CollectionListModel getListModel() { @@ -233,34 +169,6 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable return (CollectionListModel)myList.getModel(); } - //Yes, it's possible to move focus to the hint. It's inconvenient, it doesn't make sense, but it's possible. - // This fix is for those jerks - private void fixMouseCheaters() { - getComponent().addFocusListener(new FocusAdapter() { - @Override - public void focusGained(FocusEvent e) { - final ActionCallback done = IdeFocusManager.getInstance(myProject).requestFocus(myEditor.getContentComponent(), true); - IdeFocusManager.getInstance(myProject).typeAheadUntil(done); - new Alarm(LookupImpl.this).addRequest(new Runnable() { - @Override - public void run() { - if (!done.isDone()) { - done.setDone(); - } - } - }, 300, myModalityState); - } - }); - } - - void updateSorting() { - final boolean lexi = UISettings.getInstance().SORT_LOOKUP_ELEMENTS_LEXICOGRAPHICALLY; - mySortingLabel.setIcon(lexi ? AllIcons.Ide.LookupAlphanumeric : AllIcons.Ide.LookupRelevance); - mySortingLabel.setToolTipText(lexi ? "Click to sort variants by relevance" : "Click to sort variants alphabetically"); - - resort(false); - } - public void setArranger(LookupArranger arranger) { myArranger = arranger; } @@ -284,22 +192,8 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable public void setCalculating(final boolean calculating) { myCalculating = calculating; - Runnable setVisible = new Runnable() { - @Override - public void run() { - myIconPanel.setVisible(myCalculating); - } - }; - if (myCalculating) { - new Alarm(this).addRequest(setVisible, 100, myModalityState); - } else { - setVisible.run(); - } - - if (calculating) { - myProcessIcon.resume(); - } else { - myProcessIcon.suspend(); + if (myUi != null) { + myUi.setCalculating(calculating); } } @@ -564,50 +458,6 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable return myMatchers.get(item); } - // in layered pane coordinate system. - private Rectangle calculatePosition() { - Dimension dim = getComponent().getPreferredSize(); - int lookupStart = getLookupStart(); - if (lookupStart < 0 || lookupStart > myEditor.getDocument().getTextLength()) { - LOG.error(lookupStart + "; offset=" + myEditor.getCaretModel().getOffset() + "; element=" + - getPsiElement()); - } - - LogicalPosition pos = myEditor.offsetToLogicalPosition(lookupStart); - Point location = myEditor.logicalPositionToXY(pos); - location.y += myEditor.getLineHeight(); - location.x -= myCellRenderer.getIconIndent() + getComponent().getInsets().left; - - SwingUtilities.convertPointToScreen(location, myEditor.getContentComponent()); - final Rectangle screenRectangle = ScreenUtil.getScreenRectangle(location); - - if (!isPositionedAboveCaret()) { - int shiftLow = screenRectangle.height - (location.y + dim.height); - myPositionedAbove = shiftLow < 0 && shiftLow < location.y - dim.height && location.y >= dim.height; - } - if (isPositionedAboveCaret()) { - location.y -= dim.height + myEditor.getLineHeight(); - if (pos.line == 0) { - location.y += 1; - //otherwise the lookup won't intersect with the editor and every editor's resize (e.g. after typing in console) will close the lookup - } - } - - if (!screenRectangle.contains(location)) { - location = ScreenUtil.findNearestPointOnBorder(screenRectangle, location); - } - - final JRootPane rootPane = myEditor.getComponent().getRootPane(); - if (rootPane == null) { - LOG.error(myEditor.isDisposed() + "; shown=" + myShown + "; disposed=" + myDisposed + "; editorShowing=" + myEditor.getContentComponent().isShowing()); - } - Rectangle candidate = new Rectangle(location, dim); - ScreenUtil.cropRectangleToFitTheScreen(candidate); - - SwingUtilities.convertPointFromScreen(location, rootPane.getLayeredPane()); - return new Rectangle(location.x, location.y, dim.width, candidate.height); - } - public void finishLookup(final char completionChar) { finishLookup(completionChar, (LookupElement)myList.getSelectedValue()); } @@ -794,7 +644,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable return false; } if (isVisible()) { - updateLookupLocation(); + HintManagerImpl.updateLocation(this, myEditor, myUi.calculatePosition().getLocation()); } checkValid(); return true; @@ -835,12 +685,9 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable myAdComponent.showRandomText(); - getComponent().setBorder(null); - updateScrollbarVisibility(); - - Rectangle bounds = calculatePosition(); - myMaximumHeight = bounds.height; - Point p = bounds.getLocation(); + myUi = new LookupUi(this, myAdComponent, myList, myProject); + myUi.setCalculating(myCalculating); + Point p = myUi.calculatePosition().getLocation(); HintManagerImpl.getInstanceImpl().showEditorHint(this, myEditor, p, HintManager.HIDE_BY_ESCAPE | HintManager.UPDATE_BY_SCROLLING, 0, false, HintManagerImpl.createHintHint(myEditor, p, this, HintManager.UNDER).setAwtTooltip(false)); @@ -923,8 +770,6 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable @Override public void valueChanged(ListSelectionEvent e){ - myHintAlarm.cancelAllRequests(); - final LookupElement item = getCurrentItem(); if (oldItem != item && !myList.isEmpty()) { // do not update on temporary model wipe fireCurrentItemChanged(item); @@ -933,9 +778,6 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable } oldItem = item; } - if (item != null) { - updateHint(item); - } } }); @@ -956,53 +798,6 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable return true; } }.installOn(myList); - - final Alarm alarm = new Alarm(this); - myScrollPane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() { - @Override - public void adjustmentValueChanged(AdjustmentEvent e) { - if (!myShown || myUpdating || myDisposed) return; - alarm.addRequest(new Runnable() { - @Override - public void run() { - refreshUi(false, false); - } - }, 300, myModalityState); - } - }); - } - - private void updateHint(@NotNull final LookupElement item) { - checkValid(); - if (myElementHint != null) { - myLayeredPane.remove(myElementHint); - myElementHint = null; - final JRootPane rootPane = getComponent().getRootPane(); - if (rootPane != null) { - rootPane.revalidate(); - rootPane.repaint(); - } - } - if (!isFocused()) { - return; - } - - final Collection actions = getActionsFor(item); - if (!actions.isEmpty()) { - myHintAlarm.addRequest(new Runnable() { - @Override - public void run() { - assert !myDisposed; - if (!ShowHideIntentionIconLookupAction.shouldShowLookupHint() || - ((CompletionExtender)myList.getExpandableItemsHandler()).isShowing()) { - return; - } - myElementHint = new LookupHint(); - myLayeredPane.add(myElementHint, 20, 0); - myLayeredPane.layoutHint(); - } - }, 500, myModalityState); - } } @Override @@ -1075,7 +870,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable } } - private void fireCurrentItemChanged(LookupElement item){ + void fireCurrentItemChanged(LookupElement item){ if (!myListeners.isEmpty()){ LookupEvent event = new LookupEvent(this, item, (char)0); for (LookupListener listener : myListeners) { @@ -1208,7 +1003,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable @Override public boolean isPositionedAboveCaret(){ - return myPositionedAbove != null && myPositionedAbove.booleanValue(); + return myUi != null && myUi.isPositionedAboveCaret(); } @Override @@ -1279,8 +1074,6 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable } myOffsets.disposeMarkers(); - Disposer.dispose(myProcessIcon); - Disposer.dispose(myHintAlarm); myDisposed = true; disposeTrace = DebugUtil.currentStackTrace() + "\n============"; //noinspection AssignmentToStaticFieldFromInstanceMethod @@ -1291,59 +1084,19 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable assert !myUpdating; myUpdating = true; try { - doRefreshUi(mayCheckReused, onExplicitAction); + final boolean reused = mayCheckReused && checkReused(); + boolean selectionVisible = isSelectionVisible(); + boolean itemsChanged = updateList(onExplicitAction, reused); + if (isVisible()) { + LOG.assertTrue(!ApplicationManager.getApplication().isUnitTestMode()); + myUi.refreshUi(selectionVisible, itemsChanged, reused, onExplicitAction); + } } finally { myUpdating = false; } } - private void doRefreshUi(boolean mayCheckReused, boolean onExplicitAction) { - final boolean reused = mayCheckReused && checkReused(); - - boolean selectionVisible = isSelectionVisible(); - - boolean itemsChanged = updateList(onExplicitAction, reused); - - if (isVisible()) { - LOG.assertTrue(!ApplicationManager.getApplication().isUnitTestMode()); - - if (myEditor.getComponent().getRootPane() == null) { - return; - } - - updateScrollbarVisibility(); - - if (myResizePending || itemsChanged) { - myMaximumHeight = Integer.MAX_VALUE; - } - Rectangle rectangle = calculatePosition(); - myMaximumHeight = rectangle.height; - - if (myResizePending || itemsChanged) { - myResizePending = false; - pack(); - } - HintManagerImpl.updateLocation(this, myEditor, rectangle.getLocation()); - - if (reused || selectionVisible || onExplicitAction) { - ensureSelectionVisible(false); - } - } - } - - private void updateLookupLocation() { - Rectangle rectangle = calculatePosition(); - myMaximumHeight = rectangle.height; - HintManagerImpl.updateLocation(this, myEditor, rectangle.getLocation()); - } - - private void updateScrollbarVisibility() { - boolean showSorting = isCompletion() && getListModel().getSize() >= 3; - mySortingLabel.setVisible(showSorting); - myScrollPane.setVerticalScrollBarPolicy(showSorting ? ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS : ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); - } - public void markReused() { myAdComponent.clearAdvertisements(); synchronized (myList) { @@ -1368,7 +1121,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable if (ApplicationManager.getApplication().isDispatchThread()) { runnable.run(); } else { - ApplicationManager.getApplication().invokeLater(runnable, myModalityState); + ApplicationManager.getApplication().invokeLater(runnable); } } @@ -1406,155 +1159,12 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable return true; } - private class LookupLayeredPane extends JBLayeredPane { - final JPanel mainPanel = new JPanel(new BorderLayout()); - - private LookupLayeredPane() { - add(mainPanel, 0, 0); - add(myIconPanel, 42, 0); - add(mySortingLabel, 10, 0); - - setLayout(new AbstractLayoutManager() { - @Override - public Dimension preferredLayoutSize(@Nullable Container parent) { - int maxCellWidth = myLookupTextWidth + myCellRenderer.getIconIndent(); - int scrollBarWidth = myScrollPane.getPreferredSize().width - myScrollPane.getViewport().getPreferredSize().width; - int listWidth = Math.min(scrollBarWidth + maxCellWidth, UISettings.getInstance().MAX_LOOKUP_WIDTH2); - - Dimension adSize = myAdComponent.getAdComponent().getPreferredSize(); - - int panelHeight = myList.getPreferredScrollableViewportSize().height + adSize.height; - if (getListModel().getSize() > myList.getVisibleRowCount() && myList.getVisibleRowCount() >= 5) { - panelHeight -= myList.getFixedCellHeight() / 2; - } - return new Dimension(Math.max(listWidth, adSize.width), Math.min(panelHeight, myMaximumHeight)); - } - - @Override - public void layoutContainer(Container parent) { - Dimension size = getSize(); - mainPanel.setSize(size); - mainPanel.validate(); - - if (!myResizePending) { - Dimension preferredSize = preferredLayoutSize(null); - if (preferredSize.width != size.width) { - UISettings.getInstance().MAX_LOOKUP_WIDTH2 = Math.max(500, size.width); - } - - int listHeight = myList.getLastVisibleIndex() - myList.getFirstVisibleIndex() + 1; - if (listHeight != getListModel().getSize() && listHeight != myList.getVisibleRowCount() && preferredSize.height != size.height) { - UISettings.getInstance().MAX_LOOKUP_LIST_HEIGHT = Math.max(5, listHeight); - } - } - - myList.setFixedCellWidth(myScrollPane.getViewport().getWidth()); - layoutStatusIcons(); - layoutHint(); - } - }); - } - - private void layoutStatusIcons() { - int adHeight = myAdComponent.getAdComponent().getPreferredSize().height; - Dimension buttonSize = adHeight > 0 || !mySortingLabel.isVisible() ? new Dimension(0, 0) : new Dimension( - AllIcons.Ide.LookupRelevance.getIconWidth(), AllIcons.Ide.LookupRelevance.getIconHeight()); - myScrollBarIncreaseButton.setPreferredSize(buttonSize); - myScrollBarIncreaseButton.setMinimumSize(buttonSize); - myScrollBarIncreaseButton.setMaximumSize(buttonSize); - JScrollBar scrollBar = myScrollPane.getVerticalScrollBar(); - scrollBar.revalidate(); - scrollBar.repaint(); - - final Dimension iconSize = myProcessIcon.getPreferredSize(); - myIconPanel.setBounds(getWidth() - iconSize.width - (scrollBar.isVisible() ? scrollBar.getWidth() : 0), 0, iconSize.width, iconSize.height); - - final Dimension sortSize = mySortingLabel.getPreferredSize(); - final Point sbLocation = SwingUtilities.convertPoint(scrollBar, 0, 0, myLayeredPane); - - final int sortHeight = Math.max(adHeight, mySortingLabel.getPreferredSize().height); - mySortingLabel.setBounds(sbLocation.x, getHeight() - sortHeight, sortSize.width, sortHeight); - } - - void layoutHint() { - if (myElementHint != null && getCurrentItem() != null) { - final Rectangle bounds = getCurrentItemBounds(); - myElementHint.setSize(myElementHint.getPreferredSize()); - - JScrollBar sb = myScrollPane.getVerticalScrollBar(); - int x = bounds.x + bounds.width - myElementHint.getWidth() + (sb.isVisible() ? sb.getWidth() : 0); - x = Math.min(x, getWidth() - myElementHint.getWidth()); - myElementHint.setLocation(new Point(x, bounds.y)); - } - } - } - - private class LookupHint extends JLabel { - private final Border INACTIVE_BORDER = BorderFactory.createEmptyBorder(2, 2, 2, 2); - private final Border ACTIVE_BORDER = BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(Color.BLACK, 1), BorderFactory.createEmptyBorder(1, 1, 1, 1)); - private LookupHint() { - setOpaque(false); - setBorder(INACTIVE_BORDER); - setIcon(AllIcons.Actions.IntentionBulb); - String acceleratorsText = KeymapUtil.getFirstKeyboardShortcutText( - ActionManager.getInstance().getAction(IdeActions.ACTION_SHOW_INTENTION_ACTIONS)); - if (acceleratorsText.length() > 0) { - setToolTipText(CodeInsightBundle.message("lightbulb.tooltip", acceleratorsText)); - } - - addMouseListener(new MouseAdapter() { - @Override - public void mouseEntered(MouseEvent e) { - setBorder(ACTIVE_BORDER); - } - - @Override - public void mouseExited(MouseEvent e) { - setBorder(INACTIVE_BORDER); - } - @Override - public void mousePressed(MouseEvent e) { - if (!e.isPopupTrigger() && e.getButton() == MouseEvent.BUTTON1) { - showElementActions(); - } - } - }); - } - } - public Map getRelevanceStrings() { synchronized (myList) { return myPresentableArranger.getRelevanceStrings(); } } - private class ChangeLookupSorting extends ClickListener { - - @Override - public boolean onClick(@NotNull MouseEvent e, int clickCount) { - DataContext context = DataManager.getInstance().getDataContext(mySortingLabel); - DefaultActionGroup group = new DefaultActionGroup(); - group.add(createSortingAction(true)); - group.add(createSortingAction(false)); - JBPopupFactory.getInstance().createActionGroupPopup("Change sorting", group, context, JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, false).showInBestPositionFor( - context); - return true; - } - - private AnAction createSortingAction(boolean checked) { - boolean currentSetting = UISettings.getInstance().SORT_LOOKUP_ELEMENTS_LEXICOGRAPHICALLY; - final boolean newSetting = checked ? currentSetting : !currentSetting; - return new AnAction(newSetting ? "Sort lexicographically" : "Sort by relevance", null, checked ? PlatformIcons.CHECK_ICON : null) { - @Override - public void actionPerformed(AnActionEvent e) { - FeatureUsageTracker.getInstance().triggerFeatureUsed(CodeCompletionFeatures.EDITING_COMPLETION_CHANGE_SORTING); - UISettings.getInstance().SORT_LOOKUP_ELEMENTS_LEXICOGRAPHICALLY = newSetting; - updateSorting(); - } - }; - } - } - public enum FocusDegree { FOCUSED, SEMI_FOCUSED, UNFOCUSED } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupUi.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupUi.java new file mode 100644 index 000000000000..2851eb35a6c3 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupUi.java @@ -0,0 +1,466 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.lookup.impl; + +import com.intellij.codeInsight.CodeInsightBundle; +import com.intellij.codeInsight.completion.CodeCompletionFeatures; +import com.intellij.codeInsight.completion.ShowHideIntentionIconLookupAction; +import com.intellij.codeInsight.hint.HintManagerImpl; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupElementAction; +import com.intellij.featureStatistics.FeatureUsageTracker; +import com.intellij.icons.AllIcons; +import com.intellij.ide.DataManager; +import com.intellij.ide.ui.UISettings; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.LogicalPosition; +import com.intellij.openapi.keymap.KeymapUtil; +import com.intellij.openapi.project.DumbAwareAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.util.ActionCallback; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.wm.IdeFocusManager; +import com.intellij.ui.ClickListener; +import com.intellij.ui.JBColor; +import com.intellij.ui.ScreenUtil; +import com.intellij.ui.components.JBLayeredPane; +import com.intellij.ui.components.JBList; +import com.intellij.ui.components.JBScrollPane; +import com.intellij.util.Alarm; +import com.intellij.util.PlatformIcons; +import com.intellij.util.ui.AbstractLayoutManager; +import com.intellij.util.ui.AsyncProcessIcon; +import com.intellij.util.ui.ButtonlessScrollBarUI; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import javax.swing.border.LineBorder; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import java.awt.*; +import java.awt.event.*; +import java.util.Collection; + +/** + * @author peter + */ +class LookupUi { + private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.lookup.impl.LookupUi"); + private final LookupImpl myLookup; + private final Advertiser myAdvertiser; + private final JBList myList; + private final Project myProject; + private final ModalityState myModalityState; + private final Alarm myHintAlarm = new Alarm(); + private final JLabel mySortingLabel = new JLabel(); + private final JScrollPane myScrollPane; + private final JButton myScrollBarIncreaseButton; + private final AsyncProcessIcon myProcessIcon = new AsyncProcessIcon("Completion progress"); + private final JPanel myIconPanel = new JPanel(new BorderLayout()); + private final LookupLayeredPane myLayeredPane = new LookupLayeredPane(); + + private LookupHint myElementHint = null; + private int myMaximumHeight = Integer.MAX_VALUE; + private Boolean myPositionedAbove = null; + + LookupUi(LookupImpl lookup, Advertiser advertiser, JBList list, Project project) { + myLookup = lookup; + myAdvertiser = advertiser; + myList = list; + myProject = project; + + myIconPanel.setVisible(false); + myIconPanel.setBackground(Color.LIGHT_GRAY); + myIconPanel.add(myProcessIcon); + + JComponent adComponent = advertiser.getAdComponent(); + adComponent.setBorder(new EmptyBorder(0, 1, 1, 2 + AllIcons.Ide.LookupRelevance.getIconWidth())); + myLayeredPane.mainPanel.add(adComponent, BorderLayout.SOUTH); + + myScrollBarIncreaseButton = new JButton(); + myScrollBarIncreaseButton.setFocusable(false); + myScrollBarIncreaseButton.setRequestFocusEnabled(false); + + myScrollPane = new JBScrollPane(lookup.getList()); + myScrollPane.setViewportBorder(new EmptyBorder(0, 0, 0, 0)); + myScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + myScrollPane.getVerticalScrollBar().setPreferredSize(new Dimension(13, -1)); + myScrollPane.getVerticalScrollBar().setUI(new ButtonlessScrollBarUI() { + @Override + protected JButton createIncreaseButton(int orientation) { + return myScrollBarIncreaseButton; + } + }); + lookup.getComponent().add(myLayeredPane, BorderLayout.CENTER); + + //IDEA-82111 + fixMouseCheaters(); + + myLayeredPane.mainPanel.add(myScrollPane, BorderLayout.CENTER); + myScrollPane.setBorder(null); + + mySortingLabel.setBorder(new LineBorder(new JBColor(Color.LIGHT_GRAY, JBColor.background()))); + mySortingLabel.setOpaque(true); + new ChangeLookupSorting().installOn(mySortingLabel); + updateSorting(); + myModalityState = ModalityState.stateForComponent(myLookup.getComponent()); + + addListeners(); + + updateScrollbarVisibility(); + + Disposer.register(lookup, myProcessIcon); + Disposer.register(lookup, myHintAlarm); + } + + private void addListeners() { + myList.addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + myHintAlarm.cancelAllRequests(); + + final LookupElement item = myLookup.getCurrentItem(); + if (item != null) { + updateHint(item); + } + } + }); + + final Alarm alarm = new Alarm(myLookup); + myScrollPane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() { + @Override + public void adjustmentValueChanged(AdjustmentEvent e) { + if (myLookup.myUpdating || myLookup.isLookupDisposed()) return; + alarm.addRequest(new Runnable() { + @Override + public void run() { + myLookup.refreshUi(false, false); + } + }, 300, myModalityState); + } + }); + } + + private void updateScrollbarVisibility() { + boolean showSorting = myLookup.isCompletion() && myList.getModel().getSize() >= 3; + mySortingLabel.setVisible(showSorting); + myScrollPane.setVerticalScrollBarPolicy(showSorting ? ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS : ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); + } + + private void updateHint(@NotNull final LookupElement item) { + myLookup.checkValid(); + if (myElementHint != null) { + myLayeredPane.remove(myElementHint); + myElementHint = null; + final JRootPane rootPane = myLookup.getComponent().getRootPane(); + if (rootPane != null) { + rootPane.revalidate(); + rootPane.repaint(); + } + } + if (!myLookup.isFocused()) { + return; + } + + final Collection actions = myLookup.getActionsFor(item); + if (!actions.isEmpty()) { + myHintAlarm.addRequest(new Runnable() { + @Override + public void run() { + if (!ShowHideIntentionIconLookupAction.shouldShowLookupHint() || + ((CompletionExtender)myList.getExpandableItemsHandler()).isShowing()) { + return; + } + myElementHint = new LookupHint(); + myLayeredPane.add(myElementHint, 20, 0); + myLayeredPane.layoutHint(); + } + }, 500, myModalityState); + } + } + + //Yes, it's possible to move focus to the hint. It's inconvenient, it doesn't make sense, but it's possible. + // This fix is for those jerks + private void fixMouseCheaters() { + myLookup.getComponent().addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + final ActionCallback done = IdeFocusManager.getInstance(myProject).requestFocus(myLookup.getEditor().getContentComponent(), true); + IdeFocusManager.getInstance(myProject).typeAheadUntil(done); + new Alarm(myLookup).addRequest(new Runnable() { + @Override + public void run() { + if (!done.isDone()) { + done.setDone(); + } + } + }, 300, myModalityState); + } + }); + } + + void setCalculating(final boolean calculating) { + Runnable setVisible = new Runnable() { + @Override + public void run() { + myIconPanel.setVisible(myLookup.isCalculating()); + } + }; + if (myLookup.isCalculating()) { + new Alarm(myLookup).addRequest(setVisible, 100, myModalityState); + } else { + setVisible.run(); + } + + if (calculating) { + myProcessIcon.resume(); + } else { + myProcessIcon.suspend(); + } + } + + private void updateSorting() { + final boolean lexi = UISettings.getInstance().SORT_LOOKUP_ELEMENTS_LEXICOGRAPHICALLY; + mySortingLabel.setIcon(lexi ? AllIcons.Ide.LookupAlphanumeric : AllIcons.Ide.LookupRelevance); + mySortingLabel.setToolTipText(lexi ? "Click to sort variants by relevance" : "Click to sort variants alphabetically"); + + myLookup.resort(false); + } + + void refreshUi(boolean selectionVisible, boolean itemsChanged, boolean reused, boolean onExplicitAction) { + if (myLookup.getEditor().getComponent().getRootPane() == null) { + return; + } + + updateScrollbarVisibility(); + + if (myLookup.myResizePending || itemsChanged) { + myMaximumHeight = Integer.MAX_VALUE; + } + Rectangle rectangle = calculatePosition(); + myMaximumHeight = rectangle.height; + + if (myLookup.myResizePending || itemsChanged) { + myLookup.myResizePending = false; + myLookup.pack(); + } + HintManagerImpl.updateLocation(myLookup, myLookup.getEditor(), rectangle.getLocation()); + + if (reused || selectionVisible || onExplicitAction) { + myLookup.ensureSelectionVisible(false); + } + } + + boolean isPositionedAboveCaret() { + return myPositionedAbove != null && myPositionedAbove.booleanValue(); + } + + // in layered pane coordinate system. + Rectangle calculatePosition() { + Dimension dim = myLookup.getComponent().getPreferredSize(); + int lookupStart = myLookup.getLookupStart(); + Editor editor = myLookup.getEditor(); + if (lookupStart < 0 || lookupStart > editor.getDocument().getTextLength()) { + LOG.error(lookupStart + "; offset=" + editor.getCaretModel().getOffset() + "; element=" + + myLookup.getPsiElement()); + } + + LogicalPosition pos = editor.offsetToLogicalPosition(lookupStart); + Point location = editor.logicalPositionToXY(pos); + location.y += editor.getLineHeight(); + location.x -= myLookup.myCellRenderer.getIconIndent() + myLookup.getComponent().getInsets().left; + + SwingUtilities.convertPointToScreen(location, editor.getContentComponent()); + final Rectangle screenRectangle = ScreenUtil.getScreenRectangle(location); + + if (!isPositionedAboveCaret()) { + int shiftLow = screenRectangle.height - (location.y + dim.height); + myPositionedAbove = shiftLow < 0 && shiftLow < location.y - dim.height && location.y >= dim.height; + } + if (isPositionedAboveCaret()) { + location.y -= dim.height + editor.getLineHeight(); + if (pos.line == 0) { + location.y += 1; + //otherwise the lookup won't intersect with the editor and every editor's resize (e.g. after typing in console) will close the lookup + } + } + + if (!screenRectangle.contains(location)) { + location = ScreenUtil.findNearestPointOnBorder(screenRectangle, location); + } + + final JRootPane rootPane = editor.getComponent().getRootPane(); + if (rootPane == null) { + LOG.error("editor.disposed=" + editor.isDisposed() + "; lookup.disposed=" + myLookup.isLookupDisposed() + "; editorShowing=" + editor.getContentComponent().isShowing()); + } + Rectangle candidate = new Rectangle(location, dim); + ScreenUtil.cropRectangleToFitTheScreen(candidate); + + SwingUtilities.convertPointFromScreen(location, rootPane.getLayeredPane()); + myMaximumHeight = candidate.height; + return new Rectangle(location.x, location.y, dim.width, candidate.height); + } + + private class LookupLayeredPane extends JBLayeredPane { + final JPanel mainPanel = new JPanel(new BorderLayout()); + + private LookupLayeredPane() { + add(mainPanel, 0, 0); + add(myIconPanel, 42, 0); + add(mySortingLabel, 10, 0); + + setLayout(new AbstractLayoutManager() { + @Override + public Dimension preferredLayoutSize(@Nullable Container parent) { + int maxCellWidth = myLookup.myLookupTextWidth + myLookup.myCellRenderer.getIconIndent(); + int scrollBarWidth = myScrollPane.getPreferredSize().width - myScrollPane.getViewport().getPreferredSize().width; + int listWidth = Math.min(scrollBarWidth + maxCellWidth, UISettings.getInstance().MAX_LOOKUP_WIDTH2); + + Dimension adSize = myAdvertiser.getAdComponent().getPreferredSize(); + + int panelHeight = myList.getPreferredScrollableViewportSize().height + adSize.height; + if (myList.getModel().getSize() > myList.getVisibleRowCount() && myList.getVisibleRowCount() >= 5) { + panelHeight -= myList.getFixedCellHeight() / 2; + } + return new Dimension(Math.max(listWidth, adSize.width), Math.min(panelHeight, myMaximumHeight)); + } + + @Override + public void layoutContainer(Container parent) { + Dimension size = getSize(); + mainPanel.setSize(size); + mainPanel.validate(); + + if (!myLookup.myResizePending) { + Dimension preferredSize = preferredLayoutSize(null); + if (preferredSize.width != size.width) { + UISettings.getInstance().MAX_LOOKUP_WIDTH2 = Math.max(500, size.width); + } + + int listHeight = myList.getLastVisibleIndex() - myList.getFirstVisibleIndex() + 1; + if (listHeight != myList.getModel().getSize() && listHeight != myList.getVisibleRowCount() && preferredSize.height != size.height) { + UISettings.getInstance().MAX_LOOKUP_LIST_HEIGHT = Math.max(5, listHeight); + } + } + + myList.setFixedCellWidth(myScrollPane.getViewport().getWidth()); + layoutStatusIcons(); + layoutHint(); + } + }); + } + + private void layoutStatusIcons() { + int adHeight = myAdvertiser.getAdComponent().getPreferredSize().height; + Dimension buttonSize = adHeight > 0 || !mySortingLabel.isVisible() ? new Dimension(0, 0) : new Dimension( + AllIcons.Ide.LookupRelevance.getIconWidth(), AllIcons.Ide.LookupRelevance.getIconHeight()); + myScrollBarIncreaseButton.setPreferredSize(buttonSize); + myScrollBarIncreaseButton.setMinimumSize(buttonSize); + myScrollBarIncreaseButton.setMaximumSize(buttonSize); + JScrollBar scrollBar = myScrollPane.getVerticalScrollBar(); + scrollBar.revalidate(); + scrollBar.repaint(); + + final Dimension iconSize = myProcessIcon.getPreferredSize(); + myIconPanel.setBounds(getWidth() - iconSize.width - (scrollBar.isVisible() ? scrollBar.getWidth() : 0), 0, iconSize.width, iconSize.height); + + final Dimension sortSize = mySortingLabel.getPreferredSize(); + final Point sbLocation = SwingUtilities.convertPoint(scrollBar, 0, 0, myLayeredPane); + + final int sortHeight = Math.max(adHeight, mySortingLabel.getPreferredSize().height); + mySortingLabel.setBounds(sbLocation.x, getHeight() - sortHeight, sortSize.width, sortHeight); + } + + void layoutHint() { + if (myElementHint != null && myLookup.getCurrentItem() != null) { + final Rectangle bounds = myLookup.getCurrentItemBounds(); + myElementHint.setSize(myElementHint.getPreferredSize()); + + JScrollBar sb = myScrollPane.getVerticalScrollBar(); + int x = bounds.x + bounds.width - myElementHint.getWidth() + (sb.isVisible() ? sb.getWidth() : 0); + x = Math.min(x, getWidth() - myElementHint.getWidth()); + myElementHint.setLocation(new Point(x, bounds.y)); + } + } + } + + private class LookupHint extends JLabel { + private final Border INACTIVE_BORDER = BorderFactory.createEmptyBorder(2, 2, 2, 2); + private final Border ACTIVE_BORDER = BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(Color.BLACK, 1), BorderFactory.createEmptyBorder(1, 1, 1, 1)); + private LookupHint() { + setOpaque(false); + setBorder(INACTIVE_BORDER); + setIcon(AllIcons.Actions.IntentionBulb); + String acceleratorsText = KeymapUtil.getFirstKeyboardShortcutText( + ActionManager.getInstance().getAction(IdeActions.ACTION_SHOW_INTENTION_ACTIONS)); + if (acceleratorsText.length() > 0) { + setToolTipText(CodeInsightBundle.message("lightbulb.tooltip", acceleratorsText)); + } + + addMouseListener(new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + setBorder(ACTIVE_BORDER); + } + + @Override + public void mouseExited(MouseEvent e) { + setBorder(INACTIVE_BORDER); + } + @Override + public void mousePressed(MouseEvent e) { + if (!e.isPopupTrigger() && e.getButton() == MouseEvent.BUTTON1) { + myLookup.showElementActions(); + } + } + }); + } + } + + private class ChangeLookupSorting extends ClickListener { + + @Override + public boolean onClick(@NotNull MouseEvent e, int clickCount) { + DataContext context = DataManager.getInstance().getDataContext(mySortingLabel); + DefaultActionGroup group = new DefaultActionGroup(); + group.add(createSortingAction(true)); + group.add(createSortingAction(false)); + JBPopupFactory.getInstance().createActionGroupPopup("Change sorting", group, context, JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, false).showInBestPositionFor( + context); + return true; + } + + private AnAction createSortingAction(boolean checked) { + boolean currentSetting = UISettings.getInstance().SORT_LOOKUP_ELEMENTS_LEXICOGRAPHICALLY; + final boolean newSetting = checked ? currentSetting : !currentSetting; + return new DumbAwareAction(newSetting ? "Sort lexicographically" : "Sort by relevance", null, checked ? PlatformIcons.CHECK_ICON : null) { + @Override + public void actionPerformed(AnActionEvent e) { + FeatureUsageTracker.getInstance().triggerFeatureUsed(CodeCompletionFeatures.EDITING_COMPLETION_CHANGE_SORTING); + UISettings.getInstance().SORT_LOOKUP_ELEMENTS_LEXICOGRAPHICALLY = newSetting; + updateSorting(); + } + }; + } + } +} diff --git a/platform/lang-impl/src/com/intellij/ide/fileTemplates/CreateFromTemplateHandler.java b/platform/lang-impl/src/com/intellij/ide/fileTemplates/CreateFromTemplateHandler.java index b0ceafd439d7..dc728e661ef0 100644 --- a/platform/lang-impl/src/com/intellij/ide/fileTemplates/CreateFromTemplateHandler.java +++ b/platform/lang-impl/src/com/intellij/ide/fileTemplates/CreateFromTemplateHandler.java @@ -21,6 +21,7 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; import java.util.Map; @@ -31,8 +32,10 @@ public interface CreateFromTemplateHandler { ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.createFromTemplateHandler"); boolean handlesTemplate(FileTemplate template); + + @NotNull PsiElement createFromTemplate(Project project, PsiDirectory directory, final String fileName, FileTemplate template, String templateText, - Map props) throws IncorrectOperationException; + @NotNull Map props) throws IncorrectOperationException; boolean canCreate(final PsiDirectory[] dirs); boolean isNameRequired(); diff --git a/platform/lang-impl/src/com/intellij/ide/fileTemplates/DefaultCreateFromTemplateHandler.java b/platform/lang-impl/src/com/intellij/ide/fileTemplates/DefaultCreateFromTemplateHandler.java index a97351fbebb8..d12e887f3371 100644 --- a/platform/lang-impl/src/com/intellij/ide/fileTemplates/DefaultCreateFromTemplateHandler.java +++ b/platform/lang-impl/src/com/intellij/ide/fileTemplates/DefaultCreateFromTemplateHandler.java @@ -27,6 +27,7 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileFactory; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; import java.util.Map; @@ -39,10 +40,11 @@ public class DefaultCreateFromTemplateHandler implements CreateFromTemplateHandl return true; } + @NotNull @Override public PsiElement createFromTemplate(final Project project, final PsiDirectory directory, String fileName, final FileTemplate template, final String templateText, - final Map props) throws IncorrectOperationException { + @NotNull final Map props) throws IncorrectOperationException { fileName = checkAppendExtension(fileName, template); if (FileTypeManager.getInstance().isFileIgnored(fileName)) { diff --git a/platform/lang-impl/src/com/intellij/lang/customFolding/NetBeansCustomFoldingProvider.java b/platform/lang-impl/src/com/intellij/lang/customFolding/NetBeansCustomFoldingProvider.java index 4ce8a5aff6f4..44f619f609cf 100644 --- a/platform/lang-impl/src/com/intellij/lang/customFolding/NetBeansCustomFoldingProvider.java +++ b/platform/lang-impl/src/com/intellij/lang/customFolding/NetBeansCustomFoldingProvider.java @@ -34,7 +34,7 @@ public class NetBeansCustomFoldingProvider extends CustomFoldingProvider { @Override public String getPlaceholderText(String elementText) { - String customText = elementText.replaceFirst(".*desc\\s*=\\s*\"(.*)\".*", "$1").trim(); + String customText = elementText.replaceFirst(".*desc\\s*=\\s*\"([^\"]*)\".*", "$1").trim(); return customText.isEmpty() ? "..." : customText; } diff --git a/platform/lang-impl/src/com/intellij/openapi/editor/richcopy/view/HtmlTransferableData.java b/platform/lang-impl/src/com/intellij/openapi/editor/richcopy/view/HtmlTransferableData.java index 0d64830b5041..c6224026bc11 100644 --- a/platform/lang-impl/src/com/intellij/openapi/editor/richcopy/view/HtmlTransferableData.java +++ b/platform/lang-impl/src/com/intellij/openapi/editor/richcopy/view/HtmlTransferableData.java @@ -89,7 +89,6 @@ public class HtmlTransferableData extends AbstractSyntaxAwareReaderTransferableD } finally { myResultBuffer = null; - myRawText = null; myColorRegistry = null; myFontNameRegistry = null; myColors.clear(); diff --git a/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java b/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java index 5f5fecfa8a6e..eb2936a058d5 100644 --- a/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java @@ -44,7 +44,8 @@ public class InstalledPackagesPanel extends JPanel { protected final JBTable myPackagesTable; private DefaultTableModel myPackagesTableModel; - protected PackageManagementService myPackageManagementService; + // can be accessed from any thread + protected volatile PackageManagementService myPackageManagementService; protected final Project myProject; protected final PackagesNotificationPanel myNotificationArea; protected final List> myPathChangedListeners = ContainerUtil.createLockFreeCopyOnWriteList(); @@ -424,7 +425,7 @@ public class InstalledPackagesPanel extends JPanel { final boolean shouldFetchLatestVersionsForOnlyInstalledPackages = shouldFetchLatestVersionsForOnlyInstalledPackages(); if (cache.isEmpty()) { if (!shouldFetchLatestVersionsForOnlyInstalledPackages) { - refreshLatestVersions(); + refreshLatestVersions(packageManagementService); } } UIUtil.invokeLaterIfNeeded(new Runnable() { @@ -514,28 +515,30 @@ public class InstalledPackagesPanel extends JPanel { return false; } - private void refreshLatestVersions() { + private void refreshLatestVersions(@NotNull final PackageManagementService packageManagementService) { final Application application = ApplicationManager.getApplication(); application.executeOnPooledThread(new Runnable() { @Override public void run() { - try { - List packages = myPackageManagementService.reloadAllPackages(); - final Map packageMap = buildNameToPackageMap(packages); - application.invokeLater(new Runnable() { - @Override - public void run() { - for (int i = 0; i != myPackagesTableModel.getRowCount(); ++i) { - final InstalledPackage pyPackage = (InstalledPackage)myPackagesTableModel.getValueAt(i, 0); - final RepoPackage repoPackage = packageMap.get(pyPackage.getName()); - myPackagesTableModel.setValueAt(repoPackage == null ? null : repoPackage.getLatestVersion(), i, 2); + if (packageManagementService == myPackageManagementService) { + try { + List packages = packageManagementService.reloadAllPackages(); + final Map packageMap = buildNameToPackageMap(packages); + application.invokeLater(new Runnable() { + @Override + public void run() { + for (int i = 0; i != myPackagesTableModel.getRowCount(); ++i) { + final InstalledPackage pyPackage = (InstalledPackage)myPackagesTableModel.getValueAt(i, 0); + final RepoPackage repoPackage = packageMap.get(pyPackage.getName()); + myPackagesTableModel.setValueAt(repoPackage == null ? null : repoPackage.getLatestVersion(), i, 2); + } + myPackagesTable.setPaintBusy(false); } - myPackagesTable.setPaintBusy(false); - } - }, ModalityState.stateForComponent(myPackagesTable)); - } - catch (IOException ignored) { - myPackagesTable.setPaintBusy(false); + }, ModalityState.stateForComponent(myPackagesTable)); + } + catch (IOException ignored) { + myPackagesTable.setPaintBusy(false); + } } } }); diff --git a/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java index 679674523d8f..0795c482f0f2 100644 --- a/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java @@ -22,7 +22,7 @@ import java.util.Map; * @author yole */ public class PackagesNotificationPanel { - private final JEditorPane myEditorPane = new JEditorPane(); + private final JEditorPane myEditorPane = new MyNotificationPane(); private final Project myProject; private final Map myLinkHandlers = new HashMap(); private String myErrorTitle; @@ -91,6 +91,10 @@ public class PackagesNotificationPanel { myLinkHandlers.put(key, handler); } + public void removeAllLinkHandlers() { + myLinkHandlers.clear(); + } + public JComponent getComponent() { return myEditorPane; } @@ -101,7 +105,8 @@ public class PackagesNotificationPanel { private void showContent(String text, final Color background) { myEditorPane.removeAll(); - myEditorPane.setText(UIUtil.toHtml(text)); + String htmlText = text.startsWith("") ? text : UIUtil.toHtml(text); + myEditorPane.setText(htmlText); myEditorPane.setBackground(background); myEditorPane.setVisible(true); myErrorTitle = null; @@ -125,4 +130,15 @@ public class PackagesNotificationPanel { public boolean hasLinkHandler(String key) { return myLinkHandlers.containsKey(key); } + + private static class MyNotificationPane extends JEditorPane { + @Override + public Dimension getPreferredSize() { + // This trick makes text component to carry text over to the next line + // iff the text line width exceeds parent's width + Dimension dimension = super.getPreferredSize(); + dimension.width = 0; + return dimension; + } + } } diff --git a/platform/platform-impl/src/com/intellij/ide/customize/AbstractCustomizeWizardStep.java b/platform/platform-impl/src/com/intellij/ide/customize/AbstractCustomizeWizardStep.java index 15a690a57233..5b57ca8d4787 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/AbstractCustomizeWizardStep.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/AbstractCustomizeWizardStep.java @@ -29,11 +29,11 @@ import java.awt.event.MouseEvent; public abstract class AbstractCustomizeWizardStep extends JPanel { protected static final int GAP = 20; - abstract String getTitle(); + protected abstract String getTitle(); - abstract String getHTMLHeader(); + protected abstract String getHTMLHeader(); - abstract String getHTMLFooter(); + protected abstract String getHTMLFooter(); private static Color getSelectionBackground() { return ColorUtil.mix(UIUtil.getListSelectionBackground(), UIUtil.getLabelBackground(), .75); @@ -46,18 +46,23 @@ public abstract class AbstractCustomizeWizardStep extends JPanel { return anchorButton.isSelected() ? getSelectionBackground() : super.getBackground(); } }; - panel.setOpaque(true); + panel.setOpaque(anchorButton.isSelected()); new ClickListener() { @Override public boolean onClick(@NotNull MouseEvent event, int clickCount) { anchorButton.setSelected(true); - action.run(); return true; } }.installOn(panel); anchorButton.addItemListener(new ItemListener() { + boolean curState = anchorButton.isSelected(); @Override public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED && curState != anchorButton.isSelected()) { + action.run(); + } + curState = anchorButton.isSelected(); + panel.setOpaque(curState); panel.repaint(); } }); @@ -67,4 +72,7 @@ public abstract class AbstractCustomizeWizardStep extends JPanel { Component getDefaultFocusedComponent() { return null; } + + public void beforeShown(boolean forward) { + } } diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java index 186c7bdecb20..d3e340e3b1e2 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeFeaturedPluginsStepPanel.java @@ -16,14 +16,22 @@ package com.intellij.ide.customize; import com.intellij.CommonBundle; +import com.intellij.icons.AllIcons; import com.intellij.ide.plugins.IdeaPluginDescriptor; import com.intellij.ide.plugins.PluginNode; import com.intellij.openapi.progress.util.ProgressIndicatorBase; +import com.intellij.openapi.ui.VerticalFlowLayout; import com.intellij.openapi.updateSettings.impl.PluginDownloader; +import com.intellij.ui.ColorUtil; +import com.intellij.ui.JBColor; +import com.intellij.ui.border.CustomLineBorder; import com.intellij.ui.components.JBScrollPane; +import com.intellij.ui.components.labels.LinkLabel; +import com.intellij.ui.components.labels.LinkListener; import com.intellij.util.ConcurrencyUtil; import javax.swing.*; +import javax.swing.border.CompoundBorder; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -32,22 +40,29 @@ import java.util.Map; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardStep { - private static ScheduledExecutorService ourService = new ScheduledThreadPoolExecutor(4, ConcurrencyUtil.newNamedThreadFactory("FeaturedPlugins", true, Thread.NORM_PRIORITY)); + private static final int COLS = 3; + private static ScheduledExecutorService ourService = new ScheduledThreadPoolExecutor(4, ConcurrencyUtil.newNamedThreadFactory( + "FeaturedPlugins", true, Thread.NORM_PRIORITY)); - public CustomizeFeaturedPluginsStepPanel() { - setLayout(new GridLayout(0, 3, GAP, GAP)); - JPanel gridPanel = new JPanel(new GridLayout(0, 3, GAP, GAP)); + public final AtomicBoolean myCanceled = new AtomicBoolean(false); + + + public CustomizeFeaturedPluginsStepPanel() throws OfflineException { + setLayout(new GridLayout(1, 1)); + JPanel gridPanel = new JPanel(new GridLayout(0, 3)); JBScrollPane scrollPane = new JBScrollPane(gridPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); scrollPane.getVerticalScrollBar().setUnitIncrement(10); Map config = PluginGroups.getInstance().getFeaturedPlugins(); + boolean isEmptyOrOffline = true; + List pluginsFromRepository = PluginGroups.getInstance().getPluginsFromRepository(); for (Map.Entry entry : config.entrySet()) { JPanel groupPanel = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); - gbc.insets = new Insets(0, 0, 10, 0); gbc.fill = GridBagConstraints.BOTH; gbc.anchor = GridBagConstraints.WEST; gbc.gridwidth = GridBagConstraints.REMAINDER; @@ -61,10 +76,10 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt final String description = s.substring(i + 1, j); final String pluginId = s.substring(j + 1); IdeaPluginDescriptor foundDescriptor = null; - List pluginsFromRepository = PluginGroups.getInstance().getPluginsFromRepository(); for (IdeaPluginDescriptor descriptor : pluginsFromRepository) { if (descriptor.getPluginId().getIdString().equals(pluginId)) { foundDescriptor = descriptor; + isEmptyOrOffline = false; break; } } @@ -88,13 +103,26 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt final JButton installButton = new JButton("Install"); final JProgressBar progressBar = new JProgressBar(0, 100); progressBar.setStringPainted(true); - buttonWrapper.add(installButton, "button"); - buttonWrapper.add(progressBar, "progress"); + JPanel progressPanel = new JPanel(new VerticalFlowLayout(true, false)); + progressPanel.add(progressBar); + final LinkLabel cancelLink = new LinkLabel("Cancel", AllIcons.Actions.Cancel); + JPanel linkWrapper = new JPanel(new FlowLayout(FlowLayout.CENTER)); + linkWrapper.add(cancelLink); + progressPanel.add(linkWrapper); + + JPanel buttonPanel = new JPanel(new VerticalFlowLayout()); + buttonPanel.add(installButton); + + buttonWrapper.add(buttonPanel, "button"); + buttonWrapper.add(progressPanel, "progress"); + wrapperLayout.show(buttonWrapper, "button"); final ProgressIndicatorBase indicator = new ProgressIndicatorBase(true) { + @Override public void start() { + myCanceled.set(false); super.start(); SwingUtilities.invokeLater(new Runnable() { @Override @@ -104,18 +132,15 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt }); } - @Override - public void setIndeterminate(boolean indeterminate) { - super.setIndeterminate(indeterminate); - } - @Override public void processFinish() { super.processFinish(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - progressBar.setString("Installed"); + wrapperLayout.show(buttonWrapper, "button"); + installButton.setEnabled(false); + installButton.setText("Installed"); } }); } @@ -135,11 +160,14 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt @Override public void cancel() { + stop(); + myCanceled.set(true); super.cancel(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { wrapperLayout.show(buttonWrapper, "button"); + progressBar.setValue(0); } }); } @@ -152,6 +180,7 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt @Override public void run() { try { + indicator.start(); PluginNode node = new PluginNode(descriptor.getPluginId()); node.setUrl(descriptor.getUrl()); PluginDownloader downloader = PluginDownloader.createDownloader(node); @@ -160,7 +189,9 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt indicator.processFinish(); } catch (Exception ignored) { - onFail(); + if (!myCanceled.get()) { + onFail(); + } } } @@ -169,28 +200,48 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt SwingUtilities.invokeLater(new Runnable() { @Override public void run() { + indicator.stop(); wrapperLayout.show(buttonWrapper, "progress"); progressBar.setString("Cannot download plugin"); } }); } }, 0, TimeUnit.SECONDS); - //PluginGroups.getInstance().setFeaturedPluginEnabled(pluginId, installButton.isSelected()); } }); + cancelLink.setListener(new LinkListener() { + @Override + public void linkSelected(LinkLabel aSource, Object aLinkData) { + indicator.cancel(); + } + }, null); + gbc.insets.bottom = -5; groupPanel.add(titleLabel, gbc); + gbc.insets.bottom = 10; groupPanel.add(topicLabel, gbc); groupPanel.add(descriptionLabel, gbc); gbc.weighty = 1; groupPanel.add(Box.createVerticalGlue(), gbc); gbc.weighty = 0; groupPanel.add(buttonWrapper, gbc); - gbc.weighty = 1; - groupPanel.add(Box.createVerticalGlue(), gbc); - groupPanel.setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP)); gridPanel.add(groupPanel); } - setLayout(new GridLayout(1, 1)); + int cursor = 0; + Component[] components = gridPanel.getComponents(); + int rowCount = components.length / COLS; + for (Component component : components) { + ((JComponent)component).setBorder( + new CompoundBorder(new CustomLineBorder(ColorUtil.withAlpha(JBColor.foreground(), .2), 0, 0, cursor / 3 < rowCount ? 1 : 0, + cursor % COLS != COLS - 1 ? 1 : 0) { + @Override + protected Color getColor() { + return ColorUtil.withAlpha(JBColor.foreground(), .2); + } + }, BorderFactory.createEmptyBorder(GAP, GAP, 0, GAP))); + cursor++; + } + + if (isEmptyOrOffline) throw new OfflineException(); add(scrollPane); } @@ -210,4 +261,6 @@ public class CustomizeFeaturedPluginsStepPanel extends AbstractCustomizeWizardSt public String getHTMLFooter() { return "New plugins can also be downloaded in " + CommonBundle.settingsTitle() + " | Plugins"; } + + static class OfflineException extends Exception {}; } diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java index 9b077846433b..be3519eedff3 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java @@ -70,7 +70,12 @@ public class CustomizeIDEWizardDialog extends DialogWrapper implements ActionLis mySteps.add(new CustomizeKeyboardSchemeStepPanel()); } mySteps.add(new CustomizePluginsStepPanel()); - mySteps.add(new CustomizeFeaturedPluginsStepPanel()); + try { + mySteps.add(new CustomizeFeaturedPluginsStepPanel()); + } + catch (CustomizeFeaturedPluginsStepPanel.OfflineException e) { + //skip featured step if we're offline + } } @Override diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizePluginsStepPanel.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizePluginsStepPanel.java index 65b2987c8526..055a1d67e2cf 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizePluginsStepPanel.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizePluginsStepPanel.java @@ -60,20 +60,19 @@ public class CustomizePluginsStepPanel extends AbstractCustomizeWizardStep imple add(scrollPane, MAIN); add(myCustomizePanel, CUSTOMIZE); - //PluginManager.loadDisabledPlugins(new File(PathManager.getConfigPath()).getPath(), myDisabledPluginIds); - //for (IdeaPluginDescriptor pluginDescriptor : myAllPlugins) { - // if (pluginDescriptor.getPluginId().getIdString().equals("com.intellij")) { - //// skip 'IDEA CORE' plugin - //continue; - //} - // //PluginManager.initClassLoader(PluginGroups.class.getClassLoader(), (IdeaPluginDescriptorImpl)pluginDescriptor); - //} Map> groups = PluginGroups.getInstance().getTree(); for (Map.Entry> entry : groups.entrySet()) { final String group = entry.getKey(); if (PluginGroups.CORE.equals(group)) continue; - JPanel groupPanel = new JPanel(new GridBagLayout()); + JPanel groupPanel = new JPanel(new GridBagLayout()) { + @Override + public Color getBackground() { + Color color = UIManager.getColor("Panel.background"); + return isGroupEnabled(group)? color : ColorUtil.darker(color, 1); + } + }; + gridPanel.setOpaque(true); GridBagConstraints gbc = new GridBagConstraints(); gbc.insets = new Insets(0, 0, 10, 0); gbc.fill = GridBagConstraints.BOTH; @@ -113,6 +112,7 @@ public class CustomizePluginsStepPanel extends AbstractCustomizeWizardStep imple } else { JPanel buttonsPanel = new JPanel(new GridLayout(1, 2, 10, 5)); + buttonsPanel.setOpaque(false); LinkLabel customizeButton = createLink(CUSTOMIZE_COMMAND + ":" + group, CUSTOMIZE_TEXT_PROVIDER); buttonsPanel.add(customizeButton); LinkLabel disableAllButton = createLink(SWITCH_COMMAND + ":" + group, getGroupSwitchTextProvider(group)); @@ -213,22 +213,20 @@ public class CustomizePluginsStepPanel extends AbstractCustomizeWizardStep imple return null; } - - - - - private class IdSetPanel extends JPanel { + private class IdSetPanel extends JPanel implements LinkListener { private JLabel myTitleLabel = new JLabel(); private JPanel myContentPanel = new JPanel(new GridLayout(0, 3, 5, 5)); private JButton mySaveButton = new JButton("Save Changes and Go Back"); + private String myGroup; private IdSetPanel() { setLayout(new VerticalFlowLayout(VerticalFlowLayout.TOP, 0, GAP, true, false)); add(myTitleLabel); add(myContentPanel); - JPanel buttonPanel = new JPanel(new BorderLayout()); - buttonPanel.add(mySaveButton, BorderLayout.WEST); - buttonPanel.add(Box.createHorizontalGlue(), BorderLayout.CENTER); + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 25, 5)); + buttonPanel.add(mySaveButton); + buttonPanel.add(new LinkLabel("Enable All", null, this, "enable")); + buttonPanel.add(new LinkLabel("Disable All", null, this, "disable")); add(buttonPanel); mySaveButton.addActionListener(new ActionListener() { @Override @@ -238,7 +236,19 @@ public class CustomizePluginsStepPanel extends AbstractCustomizeWizardStep imple }); } + @Override + public void linkSelected(LinkLabel aSource, String command) { + if (myGroup == null) return; + boolean enable = "enable".equals(command); + List idSets = PluginGroups.getInstance().getSets(myGroup); + for (IdSet set : idSets) { + PluginGroups.getInstance().setIdSetEnabled(set, enable); + } + CustomizePluginsStepPanel.this.repaint(); + } + void update(String group) { + myGroup = group; myTitleLabel.setText("

" + group + "

"); myContentPanel.removeAll(); List idSets = PluginGroups.getInstance().getSets(group); diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java index 6ac22dcfd271..91e2625c3a6b 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeUIThemeStepPanel.java @@ -26,12 +26,11 @@ import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.SystemInfo; import com.intellij.util.IconUtil; import com.intellij.util.PlatformUtils; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; import java.util.LinkedHashMap; import java.util.Map; @@ -101,13 +100,6 @@ public class CustomizeUIThemeStepPanel extends AbstractCustomizeWizardStep { }; label.setVerticalAlignment(SwingConstants.TOP); panel.add(label, BorderLayout.CENTER); - radioButton.addItemListener(new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { - if (e.getStateChange() != ItemEvent.SELECTED) return; - applyLaf(lafName, CustomizeUIThemeStepPanel.this); - } - }); group.add(radioButton); buttonsPanel.add(panel); @@ -161,6 +153,7 @@ public class CustomizeUIThemeStepPanel extends AbstractCustomizeWizardStep { } Window window = SwingUtilities.getWindowAncestor(component); if (window != null) { + window.setBackground(new Color(UIUtil.getPanelBackground().getRGB())); SwingUtilities.updateComponentTreeUI(window); } if (ApplicationManager.getApplication() != null) { @@ -187,7 +180,7 @@ public class CustomizeUIThemeStepPanel extends AbstractCustomizeWizardStep { @Nullable private static UIManager.LookAndFeelInfo getLookAndFeelInfo(String name) { - if (DEFAULT.equals(name)) return new UIManager.LookAndFeelInfo(DEFAULT, "apple.laf.AquaLookAndFeel"); + if (DEFAULT.equals(name)) return new UIManager.LookAndFeelInfo(DEFAULT, "com.apple.laf.AquaLookAndFeel"); if (DARCULA.equals(name)) return new UIManager.LookAndFeelInfo(DARCULA, DarculaLaf.class.getName()); if (INTELLIJ.equals(name)) return new UIManager.LookAndFeelInfo(INTELLIJ, IntelliJLaf.class.getName()); if (ALLOY.equals(name)) return new UIManager.LookAndFeelInfo(ALLOY, "com.incors.plaf.alloy.AlloyIdea"); diff --git a/platform/platform-impl/src/com/intellij/ide/customize/PluginGroups.java b/platform/platform-impl/src/com/intellij/ide/customize/PluginGroups.java index c649cc1d0842..e3af6525cda5 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/PluginGroups.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/PluginGroups.java @@ -32,7 +32,7 @@ import java.util.*; class PluginGroups { static final String CORE = "Core"; - private static final int MAX_DESCR_LENGTH = 80; + private static final int MAX_DESCR_LENGTH = 50; private static PluginGroups instance = null; @@ -62,7 +62,7 @@ class PluginGroups { myPluginsFromRepository.addAll(RepositoryHelper.loadPluginsFromRepository(null)); } catch (Exception e) { - e.printStackTrace(); + //OK, it's offline } PluginManager.loadDisabledPlugins(new File(PathManager.getConfigPath()).getPath(), myDisabledPluginIds); diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/DarculaLookAndFeelInfo.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/DarculaLookAndFeelInfo.java index cf8492750b9b..880ff1accb5a 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/DarculaLookAndFeelInfo.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/DarculaLookAndFeelInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ import javax.swing.*; * @author Konstantin Bulenkov */ public class DarculaLookAndFeelInfo extends UIManager.LookAndFeelInfo { - @NonNls public static final String CLASS_NAME = "idea.dark.laf.classname"; + @NonNls public static final String CLASS_NAME = DarculaLaf.class.getName(); public DarculaLookAndFeelInfo(){ super(IdeBundle.message("idea.dark.look.and.feel"), CLASS_NAME); } diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaProgressBarUI.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaProgressBarUI.java index 0c4025bcdeae..37f5ed91042e 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaProgressBarUI.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaProgressBarUI.java @@ -89,6 +89,9 @@ public class DarculaProgressBarUI extends BasicProgressBarUI { } area.subtract(new Area(new RoundRectangle2D.Double(0,0,w, h, 9,9))); ((Graphics2D)g).setPaint(c.getParent().getBackground()); + if (c.isOpaque()) { + ((Graphics2D)g).fill(area); + } g.drawRoundRect(1,1, w-3, h-3, 8,8); g.translate(0, -(c.getHeight() - h)/2); diff --git a/platform/platform-impl/src/com/intellij/platform/PlatformProjectOpenProcessor.java b/platform/platform-impl/src/com/intellij/platform/PlatformProjectOpenProcessor.java index 67d44770a1a2..4ffac2cb8eb9 100644 --- a/platform/platform-impl/src/com/intellij/platform/PlatformProjectOpenProcessor.java +++ b/platform/platform-impl/src/com/intellij/platform/PlatformProjectOpenProcessor.java @@ -27,9 +27,9 @@ import com.intellij.openapi.project.DumbAwareRunnable; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.project.ex.ProjectManagerEx; +import com.intellij.openapi.roots.ContentEntry; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.ModuleRootManager; -import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Disposer; @@ -199,6 +199,8 @@ public class PlatformProjectOpenProcessor extends ProjectOpenProcessor { @Override public void run() { ModifiableRootModel model = ModuleRootManager.getInstance(module).getModifiableModel(); + ContentEntry[] entries = model.getContentEntries(); + if (entries.length == 1) model.removeContentEntry(entries[0]); // remove custom content entry created for temp directory model.addContentEntry(virtualFile); model.commit(); } diff --git a/platform/script-debugger/backend/src/org/jetbrains/debugger/values/ValueType.java b/platform/script-debugger/backend/src/org/jetbrains/debugger/values/ValueType.java index bd24b5b971e8..06da6c1fdc5e 100644 --- a/platform/script-debugger/backend/src/org/jetbrains/debugger/values/ValueType.java +++ b/platform/script-debugger/backend/src/org/jetbrains/debugger/values/ValueType.java @@ -15,14 +15,7 @@ public enum ValueType { ARRAY, NODE, - /** - * undefined type. - */ UNDEFINED, - - /** - * null type. This is a bogus type that doesn't exist in JavaScript. - */ NULL; private static final ValueType[] VALUE_TYPES = ValueType.values(); diff --git a/platform/util/src/com/intellij/openapi/util/IconLoader.java b/platform/util/src/com/intellij/openapi/util/IconLoader.java index b611d7a51891..32584463a71e 100644 --- a/platform/util/src/com/intellij/openapi/util/IconLoader.java +++ b/platform/util/src/com/intellij/openapi/util/IconLoader.java @@ -24,6 +24,7 @@ import com.intellij.util.ReflectionUtil; import com.intellij.util.RetinaImage; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.WeakHashMap; +import com.intellij.util.ui.JBImageIcon; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -33,7 +34,6 @@ import javax.swing.*; import java.awt.*; import java.awt.image.BufferedImage; import java.awt.image.FilteredImageSource; -import java.awt.image.ImageObserver; import java.awt.image.ImageProducer; import java.lang.ref.Reference; import java.lang.reflect.Field; @@ -98,7 +98,7 @@ public final class IconLoader { @Deprecated public static Icon getIcon(@NotNull final Image image) { - return new MyImageIcon(image); + return new JBImageIcon(image); } public static void setUseDarkIcons(boolean useDarkIcons) { @@ -279,7 +279,7 @@ public final class IconLoader { Image img = createDisabled(image); if (UIUtil.isRetina()) img = RetinaImage.createFrom(img, 2, ImageLoader.ourComponent); - disabledIcon = new MyImageIcon(img); + disabledIcon = new JBImageIcon(img); ourIcon2DisabledIcon.put(icon, disabledIcon); } return disabledIcon; @@ -383,19 +383,6 @@ public final class IconLoader { } } - private static final class MyImageIcon extends ImageIcon { - public MyImageIcon(final Image image) { - super(image); - } - - @Override - public final synchronized void paintIcon(final Component c, final Graphics g, final int x, final int y) { - final ImageObserver observer = getImageObserver(); - - UIUtil.drawImage(g, getImage(), x, y, observer == null ? c : observer); - } - } - public abstract static class LazyIcon implements Icon { private boolean myWasComputed; private Icon myIcon; diff --git a/platform/util/src/com/intellij/util/ui/JBImageIcon.java b/platform/util/src/com/intellij/util/ui/JBImageIcon.java new file mode 100644 index 000000000000..8d8bc6e40b00 --- /dev/null +++ b/platform/util/src/com/intellij/util/ui/JBImageIcon.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.util.ui; + +import javax.swing.*; +import java.awt.*; +import java.awt.image.ImageObserver; + +/** + * HiDPI-aware image icon + * + * @author Konstantin Bulenkov + */ +public class JBImageIcon extends ImageIcon { + public JBImageIcon(Image image) { + super(image); + } + + @Override + public final synchronized void paintIcon(final Component c, final Graphics g, final int x, final int y) { + final ImageObserver observer = getImageObserver(); + + UIUtil.drawImage(g, getImage(), x, y, observer == null ? c : observer); + } +} diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/vfs/ContentRevisionVirtualFile.java b/platform/vcs-api/src/com/intellij/openapi/vcs/vfs/ContentRevisionVirtualFile.java index 6f5a84881cbd..71626fa32e61 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/vfs/ContentRevisionVirtualFile.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/vfs/ContentRevisionVirtualFile.java @@ -84,8 +84,7 @@ public class ContentRevisionVirtualFile extends AbstractVcsVirtualFile { myModificationStamp++; setRevision(myContentRevision.getRevisionNumber().asString()); - final ByteBuffer byteBuffer = getCharset().encode(content); - myContent = byteBuffer.compact().array(); + myContent = content.getBytes(getCharset()); ApplicationManager.getApplication().runWriteAction(new Runnable() { public void run() { vcsFileSystem.fireContentsChanged(this, ContentRevisionVirtualFile.this, 0); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/FilePathImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/FilePathImpl.java index 516508176fe4..5c8e0abac81f 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/FilePathImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/FilePathImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -62,8 +62,7 @@ public class FilePathImpl implements FilePath { } } - @NotNull - private static File fileFromVirtual(VirtualFile virtualParent, final VirtualFile child, @NotNull String name) { + private static File fileFromVirtual(VirtualFile virtualParent, final VirtualFile child, String name) { assert virtualParent != null || child != null; if (virtualParent != null) { return new File(virtualParent.getPath(), name); @@ -72,12 +71,12 @@ public class FilePathImpl implements FilePath { } @Heavy - public FilePathImpl(@NotNull VirtualFile virtualParent, @NotNull String name, final boolean isDirectory) { + public FilePathImpl(@NotNull VirtualFile virtualParent, String name, final boolean isDirectory) { this(virtualParent, name, isDirectory, null, false); } @Heavy - private FilePathImpl(@NotNull VirtualFile virtualParent, @NotNull String name, final boolean isDirectory, final boolean forDeleted) { + private FilePathImpl(@NotNull VirtualFile virtualParent, String name, final boolean isDirectory, final boolean forDeleted) { this(virtualParent, name, isDirectory, null, forDeleted); } @@ -96,15 +95,15 @@ public class FilePathImpl implements FilePath { this(virtualFile.getParent(), virtualFile.getName(), virtualFile.isDirectory(), virtualFile, false); } - @NotNull public FilePath createChild(final String subPath, final boolean isDirectory) { if (StringUtil.isEmptyOrSpaces(subPath)) return this; - VirtualFile virtualFile = getVirtualFile(); - if (virtualFile != null && subPath.indexOf('/') == -1 && subPath.indexOf('\\') == -1) { - return new FilePathImpl(virtualFile, subPath, isDirectory, true); + if (getVirtualFile() != null && subPath.indexOf('/') == -1 && subPath.indexOf('\\') == -1) { + return new FilePathImpl(getVirtualFile(), subPath, isDirectory, true); + } + else { + return new FilePathImpl(new File(getIOFile(), subPath), isDirectory); } - return new FilePathImpl(new File(getIOFile(), subPath), isDirectory); } public int hashCode() { @@ -115,13 +114,13 @@ public class FilePathImpl implements FilePath { if (!(o instanceof FilePath)) { return false; } - if (!isSpecialName(myName) && !isSpecialName(((FilePath)o).getName()) && - !Comparing.equal(myName, ((FilePath)o).getName())) { - return false; + else { + if (! isSpecialName(myName) && ! isSpecialName(((FilePath)o).getName()) && + ! Comparing.equal(myName, ((FilePath)o).getName())) return false; + return myFile.equals(((FilePath)o).getIOFile()); } - return myFile.equals(((FilePath)o).getIOFile()); } - + private static boolean isSpecialName(final String name) { return ".".equals(name) || "..".equals(name); } @@ -129,29 +128,31 @@ public class FilePathImpl implements FilePath { @Override public void refresh() { if (myLocal) { - VirtualFile virtualParent = getVirtualFileParent(); - myVirtualFile = virtualParent == null ? LocalFileSystem.getInstance().findFileByIoFile(myFile) : virtualParent.findChild(myName); + if (myVirtualParent == null) { + myVirtualFile = LocalFileSystem.getInstance().findFileByIoFile(myFile); + } + else { + myVirtualFile = myVirtualParent.findChild(myName); + } } } @Override public void hardRefresh() { - if (myLocal) { - VirtualFile virtualFile = getVirtualFile(); - if (virtualFile == null || !virtualFile.isValid()) { - myVirtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(myFile); - } + if (myLocal && (myVirtualFile == null || ! myVirtualFile.isValid())) { + myVirtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(myFile); } } - @NotNull @Override public String getPath() { - final VirtualFile virtualFile = getVirtualFile(); + final VirtualFile virtualFile = myVirtualFile; if (virtualFile != null && virtualFile.isValid()) { return virtualFile.getPath(); } - return myFile.getPath(); + else { + return myFile.getPath(); + } } public void setIsDirectory(boolean isDirectory) { @@ -160,17 +161,20 @@ public class FilePathImpl implements FilePath { @Override public boolean isDirectory() { - VirtualFile virtualFile = getVirtualFile(); - return virtualFile == null ? myIsDirectory : virtualFile.isDirectory(); + if (myVirtualFile == null) { + return myIsDirectory; + } + else { + return myVirtualFile.isDirectory(); + } } @Override - public boolean isUnder(@NotNull FilePath parent, boolean strict) { - VirtualFile virtualFile = getVirtualFile(); - if (virtualFile != null) { + public boolean isUnder(FilePath parent, boolean strict) { + if (myVirtualFile != null) { final VirtualFile parentFile = parent.getVirtualFile(); if (parentFile != null) { - return VfsUtilCore.isAncestor(parentFile, virtualFile, strict); + return VfsUtilCore.isAncestor(parentFile, myVirtualFile, strict); } } return FileUtil.isAncestor(parent.getIOFile(), getIOFile(), strict); @@ -178,9 +182,8 @@ public class FilePathImpl implements FilePath { @Override public FilePath getParentPath() { - VirtualFile virtualParent = getVirtualFileParent(); - if (virtualParent != null && virtualParent.getParent() != null) { - return new FilePathImpl(virtualParent); + if (myVirtualParent != null && myVirtualParent.isValid() && myVirtualParent.getParent() != null) { + return new FilePathImpl(myVirtualParent); } // can't use File.getParentPath() because the path may not correspond to an actual file on disk, @@ -197,25 +200,19 @@ public class FilePathImpl implements FilePath { @Override @Nullable public VirtualFile getVirtualFile() { - VirtualFile virtualFile = myVirtualFile; - if (virtualFile != null && !virtualFile.isValid()) { - myVirtualFile = virtualFile = null; + if (myVirtualFile != null && !myVirtualFile.isValid()) { + myVirtualFile = null; } - if (virtualFile == null) { - refresh(); - virtualFile = myVirtualFile; - } - return virtualFile; + return myVirtualFile; } @Override @Nullable public VirtualFile getVirtualFileParent() { - VirtualFile virtualParent = myVirtualParent; - if (virtualParent != null && !virtualParent.isValid()) { - myVirtualParent = virtualParent = null; + if (myVirtualParent != null && !myVirtualParent.isValid()) { + myVirtualParent = null; } - return virtualParent; + return myVirtualParent; } @Override @@ -224,7 +221,6 @@ public class FilePathImpl implements FilePath { return myFile; } - @NotNull @Override public String getName() { return myName; @@ -232,21 +228,21 @@ public class FilePathImpl implements FilePath { @Override public String getPresentableUrl() { - VirtualFile virtualFile = getVirtualFile(); - if (virtualFile == null || !virtualFile.isValid()) { + if (myVirtualFile == null || !myVirtualFile.isValid()) { return myFile.getAbsolutePath(); } - return virtualFile.getPresentableUrl(); + else { + return myVirtualFile.getPresentableUrl(); + } } @Override @Nullable public Document getDocument() { - VirtualFile virtualFile = getVirtualFile(); - if (virtualFile == null || virtualFile.getFileType().isBinary()) { + if (myVirtualFile == null || myVirtualFile.getFileType().isBinary()) { return null; } - return FileDocumentManager.getInstance().getDocument(virtualFile); + return FileDocumentManager.getInstance().getDocument(myVirtualFile); } @Override @@ -257,8 +253,7 @@ public class FilePathImpl implements FilePath { @Override public Charset getCharset(Project project) { // try to find existing virtual file - VirtualFile virtualFile = getVirtualFile(); - VirtualFile existing = virtualFile != null && virtualFile.isValid() ? virtualFile : null; + VirtualFile existing = myVirtualFile != null && myVirtualFile.isValid() ? myVirtualFile : null; if (existing == null) { LocalFileSystem lfs = LocalFileSystem.getInstance(); for (File f = myFile; f != null; f = f.getParentFile()) { @@ -283,8 +278,7 @@ public class FilePathImpl implements FilePath { @Override public FileType getFileType() { - VirtualFile virtualFile = getVirtualFile(); - return virtualFile != null ? virtualFile.getFileType() : FileTypeManager.getInstance().getFileTypeByFileName(myFile.getName()); + return myVirtualFile != null ? myVirtualFile.getFileType() : FileTypeManager.getInstance().getFileTypeByFileName(myFile.getName()); } public static FilePathImpl create(VirtualFile file) { @@ -322,52 +316,59 @@ public class FilePathImpl implements FilePath { if (virtualFileParent != null) { return new FilePathImpl(virtualFileParent, selectedFile.getName(), isDirectory, true); } - return new FilePathImpl(selectedFile, isDirectory); + else { + return new FilePathImpl(selectedFile, isDirectory); + } } - public static FilePath createOn(@NotNull String s) { + public static FilePath createOn(String s) { File ioFile = new File(s); final LocalFileSystem localFileSystem = LocalFileSystem.getInstance(); VirtualFile virtualFile = localFileSystem.findFileByIoFile(ioFile); if (virtualFile != null) { return new FilePathImpl(virtualFile); } - VirtualFile virtualFileParent = localFileSystem.findFileByIoFile(ioFile.getParentFile()); - if (virtualFileParent == null) return null; - return new FilePathImpl(virtualFileParent, ioFile.getName(), false); + else { + VirtualFile virtualFileParent = localFileSystem.findFileByIoFile(ioFile.getParentFile()); + if (virtualFileParent != null) { + return new FilePathImpl(virtualFileParent, ioFile.getName(), false); + } + else { + return null; + } + } } - private static final Constructor ourFileStringConstructor; - static { - // avoid filename normalization (IDEADEV-10548) - Constructor constructor = null; // new File(String, int) - try { - constructor = File.class.getDeclaredConstructor(String.class, int.class); - constructor.setAccessible(true); - } - catch (Exception ignored) { - } - ourFileStringConstructor = constructor; - } + private static Constructor ourFileStringConstructor; + private static boolean ourFileStringConstructorInitialized; @NotNull - public static FilePath createNonLocal(@NotNull String path, final boolean directory) { + public static FilePath createNonLocal(String path, final boolean directory) { path = path.replace('/', File.separatorChar); - File file = createIoFile(path); - return new FilePathImpl(file, directory, false); - } - - @NotNull - private static File createIoFile(@NotNull String path) { - if (ourFileStringConstructor != null) { + // avoid filename normalization (IDEADEV-10548) + if (!ourFileStringConstructorInitialized) { + ourFileStringConstructorInitialized = true; try { - return ourFileStringConstructor.newInstance(path, 1); + ourFileStringConstructor = File.class.getDeclaredConstructor(String.class, int.class); + ourFileStringConstructor.setAccessible(true); } catch (Exception ex) { - // reflection call failed, try regular call + ourFileStringConstructor = null; } } - return new File(path); + File file = null; + try { + if (ourFileStringConstructor != null) { + file = ourFileStringConstructor.newInstance(path, 1); + } + } + catch (Exception ex) { + // reflection call failed, try regular call + } + if (file == null) { + file = new File(path); + } + return new FilePathImpl(file, directory, false); } @Override diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/TreeModelBuilder.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/TreeModelBuilder.java index ba022cbc282c..48a17aeaa31a 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/TreeModelBuilder.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/TreeModelBuilder.java @@ -504,7 +504,8 @@ public class TreeModelBuilder { ChangesBrowserNode parentNode = myFoldersCache.get(parentPath.getKey()); if (parentNode == null) { - parentNode = ChangesBrowserNode.create(myProject, new FilePathImpl(new File(parentPath.getPath()), true)); + FilePathImpl filePath = parentPath.getVf() == null ? new FilePathImpl(new File(parentPath.getPath()), true) : new FilePathImpl(parentPath.getVf()); + parentNode = ChangesBrowserNode.create(myProject, filePath); ChangesBrowserNode grandPa = getParentNodeFor(parentPath, policy, rootNode); model.insertNodeInto(parentNode, grandPa, grandPa.getChildCount()); myFoldersCache.put(parentPath.getKey(), parentNode); diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml index a9bfaa130c66..2b47e3a3c33d 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml @@ -394,10 +394,6 @@ - diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ClassInTopLevelPackageInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ClassInTopLevelPackageInspectionBase.java deleted file mode 100644 index dec9e2e662d2..000000000000 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ClassInTopLevelPackageInspectionBase.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2000-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.siyeh.ig.classlayout; - -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiJavaFile; -import com.intellij.psi.util.FileTypeUtils; -import com.siyeh.InspectionGadgetsBundle; -import com.siyeh.ig.BaseInspection; -import com.siyeh.ig.BaseInspectionVisitor; -import com.siyeh.ig.psiutils.ClassUtils; -import org.jetbrains.annotations.NotNull; - -public class ClassInTopLevelPackageInspectionBase extends BaseInspection { - @Override - @NotNull - public String getID() { - return "ClassWithoutPackageStatement"; - } - - @Override - @NotNull - public String getDisplayName() { - return InspectionGadgetsBundle.message( - "class.in.top.level.package.display.name"); - } - - @Override - @NotNull - protected String buildErrorString(Object... infos) { - return InspectionGadgetsBundle.message( - "class.in.top.level.package.problem.descriptor"); - } - - @Override - protected boolean buildQuickFixesOnlyForOnTheFlyErrors() { - return true; - } - - @Override - public BaseInspectionVisitor buildVisitor() { - return new ClassInTopLevelPackageVisitor(); - } - - private static class ClassInTopLevelPackageVisitor - extends BaseInspectionVisitor { - - @Override - public void visitClass(@NotNull PsiClass aClass) { - // no call to super, so that it doesn't drill down to inner classes - if (FileTypeUtils.isInServerPageFile(aClass)) { - return; - } - if (ClassUtils.isInnerClass(aClass)) { - return; - } - final PsiFile file = aClass.getContainingFile(); - if (!(file instanceof PsiJavaFile)) { - return; - } - if (((PsiJavaFile)file).getPackageStatement() != null) { - return; - } - registerClassError(aClass); - } - } -} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/PointlessArithmeticExpressionInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/PointlessArithmeticExpressionInspection.java index 6b2f34b1aa47..bebf5a559d15 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/PointlessArithmeticExpressionInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/PointlessArithmeticExpressionInspection.java @@ -20,8 +20,8 @@ import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; -import com.intellij.psi.util.ConstantExpressionUtil; import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.PsiUtilCore; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; @@ -116,20 +116,19 @@ public class PointlessArithmeticExpressionInspection } else if (tokenType.equals(JavaTokenType.ASTERISK) && isZero(operand) || tokenType.equals(JavaTokenType.PERC) && (isOne(operand) || EquivalenceChecker.expressionsAreEquivalent(previousOperand, operand))) { - return PsiType.LONG.equals(polyadicExpression.getType()) ? "0L" : "0"; - } - else if (tokenType.equals(JavaTokenType.LE) || tokenType.equals(JavaTokenType.GE) || - tokenType.equals(JavaTokenType.LT) || tokenType.equals(JavaTokenType.GT)) { - return (tokenType.equals(JavaTokenType.LT) || tokenType.equals(JavaTokenType.GT)) ? "false" : "true"; + fromTarget = operands[0]; + untilTarget = operands[length - 1]; + replacement = PsiType.LONG.equals(polyadicExpression.getType()) ? "0L" : "0"; + break; } previousOperand = operand; } - return buildReplacementExpression(polyadicExpression, fromTarget, untilTarget, replacement).trim(); + return getText(polyadicExpression, fromTarget, untilTarget, replacement).trim(); } - public static String buildReplacementExpression(PsiPolyadicExpression expression, PsiElement fromTarget, PsiElement untilTarget, - String replacement) { + public static String getText(PsiPolyadicExpression expression, PsiElement fromTarget, PsiElement untilTarget, + @NotNull @NonNls String replacement) { final StringBuilder result = new StringBuilder(); boolean stop = false; for (PsiElement child : expression.getChildren()) { @@ -198,13 +197,10 @@ public class PointlessArithmeticExpressionInspection if (!arithmeticTokens.contains(expression.getOperationTokenType())) { return; } - if (ExpressionUtils.hasStringType(expression)) { + if (ExpressionUtils.hasStringType(expression) || PsiUtilCore.hasErrorElementChild(expression)) { return; } final PsiExpression[] operands = expression.getOperands(); - if (operands.length < 2) { - return; - } final IElementType tokenType = expression.getOperationTokenType(); final boolean isPointless; if (tokenType.equals(JavaTokenType.PLUS)) { @@ -222,14 +218,6 @@ public class PointlessArithmeticExpressionInspection else if (tokenType.equals(JavaTokenType.PERC)) { isPointless = modExpressionIsPointless(operands); } - else if (tokenType.equals(JavaTokenType.LE) || - tokenType.equals(JavaTokenType.GE) || - tokenType.equals(JavaTokenType.GT) || - tokenType.equals(JavaTokenType.LT)) { - final PsiExpression lhs = operands[0]; - final PsiExpression rhs = operands[1]; - isPointless = comparisonExpressionIsPointless(lhs, rhs, tokenType); - } else { isPointless = false; } @@ -292,45 +280,6 @@ public class PointlessArithmeticExpressionInspection } return false; } - - private boolean comparisonExpressionIsPointless( - PsiExpression lhs, PsiExpression rhs, IElementType comparison) { - if (PsiType.INT.equals(lhs.getType()) && - PsiType.INT.equals(rhs.getType())) { - return intComparisonIsPointless(lhs, rhs, comparison); - } - else if (PsiType.LONG.equals(lhs.getType()) && - PsiType.LONG.equals(rhs.getType())) { - return longComparisonIsPointless(lhs, rhs, comparison); - } - return false; - } - - private boolean intComparisonIsPointless( - PsiExpression lhs, PsiExpression rhs, IElementType comparison) { - if (isMaxInt(lhs) || isMinInt(rhs)) { - return JavaTokenType.GE.equals(comparison) || - JavaTokenType.LT.equals(comparison); - } - if (isMinInt(lhs) || isMaxInt(rhs)) { - return JavaTokenType.LE.equals(comparison) || - JavaTokenType.GT.equals(comparison); - } - return false; - } - - private boolean longComparisonIsPointless( - PsiExpression lhs, PsiExpression rhs, IElementType comparison) { - if (isMaxLong(lhs) || isMinLong(rhs)) { - return JavaTokenType.GE.equals(comparison) || - JavaTokenType.LT.equals(comparison); - } - if (isMinLong(lhs) || isMaxLong(rhs)) { - return JavaTokenType.LE.equals(comparison) || - JavaTokenType.GT.equals(comparison); - } - return false; - } } boolean isZero(PsiExpression expression) { @@ -346,32 +295,4 @@ public class PointlessArithmeticExpressionInspection } return ExpressionUtils.isOne(expression); } - - private static boolean isMinInt(PsiExpression expression) { - final Integer value = (Integer) - ConstantExpressionUtil.computeCastTo( - expression, PsiType.INT); - return value != null && value.intValue() == Integer.MIN_VALUE; - } - - private static boolean isMaxInt(PsiExpression expression) { - final Integer value = (Integer) - ConstantExpressionUtil.computeCastTo( - expression, PsiType.INT); - return value != null && value.intValue() == Integer.MAX_VALUE; - } - - private static boolean isMinLong(PsiExpression expression) { - final Long value = (Long) - ConstantExpressionUtil.computeCastTo( - expression, PsiType.LONG); - return value != null && value.longValue() == Long.MIN_VALUE; - } - - private static boolean isMaxLong(PsiExpression expression) { - final Long value = (Long) - ConstantExpressionUtil.computeCastTo( - expression, PsiType.LONG); - return value != null && value.longValue() == Long.MAX_VALUE; - } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/ClassInTopLevelPackage.html b/plugins/InspectionGadgets/src/inspectionDescriptions/ClassInTopLevelPackage.html deleted file mode 100644 index c6263f25c4d5..000000000000 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/ClassInTopLevelPackage.html +++ /dev/null @@ -1,8 +0,0 @@ - - -Reports any classes which do not contain package declarations. - -

- - - \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/pointless_arithmetic_expression/PointlessArithmeticExpression.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/pointless_arithmetic_expression/PointlessArithmeticExpression.java index d7577b1c9c79..019805adcf17 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/pointless_arithmetic_expression/PointlessArithmeticExpression.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/pointless_arithmetic_expression/PointlessArithmeticExpression.java @@ -123,4 +123,5 @@ class Expanded {{ System.out.println(u * 1); long g = 8L / 8L; long h = 9L * 0L; + int a = 8 * 0 * 8 * ; // don't warn }} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/pointless_arithmetic_expression/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/pointless_arithmetic_expression/expected.xml index 58e0898bdcdd..f6af12237ea0 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/pointless_arithmetic_expression/expected.xml +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/pointless_arithmetic_expression/expected.xml @@ -36,90 +36,6 @@ <code>j%1</code> can be replaced with '0' #loc - - PointlessArithmeticExpression.java - 30 - Pointless arithmetic expression - <code>k<=Integer.MAX_VALUE</code> can be replaced with 'true' #loc - - - - PointlessArithmeticExpression.java - 34 - Pointless arithmetic expression - <code>k>=Integer.MIN_VALUE</code> can be replaced with 'true' #loc - - - - PointlessArithmeticExpression.java - 38 - Pointless arithmetic expression - <code>k>Integer.MAX_VALUE</code> can be replaced with 'false' #loc - - - - PointlessArithmeticExpression.java - 42 - Pointless arithmetic expression - <code>k<Integer.MIN_VALUE</code> can be replaced with 'false' #loc - - - - PointlessArithmeticExpression.java - 46 - Pointless arithmetic expression - <code>Integer.MAX_VALUE >= k</code> can be replaced with 'true' #loc - - - - PointlessArithmeticExpression.java - 50 - Pointless arithmetic expression - <code>Integer.MIN_VALUE <= k</code> can be replaced with 'true' #loc - - - - PointlessArithmeticExpression.java - 54 - Pointless arithmetic expression - <code>Integer.MAX_VALUE < k</code> can be replaced with 'false' #loc - - - - PointlessArithmeticExpression.java - 58 - Pointless arithmetic expression - <code>Integer.MIN_VALUE > k</code> can be replaced with 'false' #loc - - - - PointlessArithmeticExpression.java - 70 - Pointless arithmetic expression - <code>i > Integer.MAX_VALUE</code> can be replaced with 'false' #loc - - - - PointlessArithmeticExpression.java - 76 - Pointless arithmetic expression - <code>i <= Integer.MAX_VALUE</code> can be replaced with 'true' #loc - - - - PointlessArithmeticExpression.java - 80 - Pointless arithmetic expression - <code>i >= Integer.MIN_VALUE</code> can be replaced with 'true' #loc - - - - PointlessArithmeticExpression.java - 83 - Pointless arithmetic expression - <code>i < Integer.MIN_VALUE</code> can be replaced with 'false' #loc - - PointlessArithmeticExpression.java 93 diff --git a/python/src/com/jetbrains/python/documentation/PyStructuredDocstringFormatter.java b/python/src/com/jetbrains/python/documentation/PyStructuredDocstringFormatter.java index 964d5b1715dc..b439a54f3012 100644 --- a/python/src/com/jetbrains/python/documentation/PyStructuredDocstringFormatter.java +++ b/python/src/com/jetbrains/python/documentation/PyStructuredDocstringFormatter.java @@ -15,6 +15,7 @@ */ package com.jetbrains.python.documentation; +import com.google.common.collect.Lists; import com.intellij.execution.process.ProcessOutput; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; @@ -50,7 +51,11 @@ public class PyStructuredDocstringFormatter { @Nullable public static List formatDocstring(@NotNull final PsiElement element, @NotNull final String docstring) { Module module = ModuleUtilCore.findModuleForPsiElement(element); - if (module == null) module = ModuleManager.getInstance(element.getProject()).getModules()[0]; + if (module == null) { + final Module[] modules = ModuleManager.getInstance(element.getProject()).getModules(); + if (modules.length == 0) return Lists.newArrayList(); + module = modules[0]; + } final PyDocumentationSettings documentationSettings = PyDocumentationSettings.getInstance(module); final List result = new ArrayList(); diff --git a/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java b/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java index e766bc00a7bf..bc46d71e8f78 100644 --- a/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java @@ -81,7 +81,10 @@ public class PyProtectedMemberInspection extends PyInspection { public void visitPyImportElement(PyImportElement node) { final PyStatement statement = node.getContainingImportStatement(); if (!(statement instanceof PyFromImportStatement)) return; - checkReference(node.getImportReferenceExpression(), ((PyFromImportStatement)statement).getImportSource()); + final PyReferenceExpression importReferenceExpression = node.getImportReferenceExpression(); + final PyReferenceExpression importSource = ((PyFromImportStatement)statement).getImportSource(); + if (importReferenceExpression != null && importSource != null) + checkReference(importReferenceExpression, importSource); } @Override @@ -91,7 +94,7 @@ public class PyProtectedMemberInspection extends PyInspection { checkReference(node, qualifier); } - private void checkReference(PyReferenceExpression node, PyExpression qualifier) { + private void checkReference(@NotNull final PyReferenceExpression node, @NotNull final PyExpression qualifier) { if (myTypeEvalContext.getType(qualifier) instanceof PyNamedTupleType) return; final String name = node.getName(); final List quickFixes = new ArrayList(); diff --git a/python/src/com/jetbrains/python/testing/nosetest/PythonNoseTestConfigurationProducer.java b/python/src/com/jetbrains/python/testing/nosetest/PythonNoseTestConfigurationProducer.java index b7dc704ff4e8..ddbd90623080 100644 --- a/python/src/com/jetbrains/python/testing/nosetest/PythonNoseTestConfigurationProducer.java +++ b/python/src/com/jetbrains/python/testing/nosetest/PythonNoseTestConfigurationProducer.java @@ -41,8 +41,7 @@ public class PythonNoseTestConfigurationProducer extends module = modules[0]; } final Sdk sdk = PythonSdkType.findPythonSdk(module); - return (TestRunnerService.getInstance(module).getProjectConfiguration().equals( - PythonTestConfigurationsModel.PYTHONS_NOSETEST_NAME) && sdk != null); + return (PythonTestConfigurationsModel.PYTHONS_NOSETEST_NAME.equals(TestRunnerService.getInstance(module).getProjectConfiguration()) && sdk != null); } @Override diff --git a/resources-en/src/inspectionDescriptions/WrongPackageStatement.html b/resources-en/src/inspectionDescriptions/WrongPackageStatement.html index 01fb18e9f597..78ff05272bfe 100644 --- a/resources-en/src/inspectionDescriptions/WrongPackageStatement.html +++ b/resources-en/src/inspectionDescriptions/WrongPackageStatement.html @@ -1,5 +1,6 @@ -Detects package statements that do not correspond to the project directory structure. +Detects package statements that do not correspond to the project directory structure + and reports classes without package statements. \ No newline at end of file diff --git a/resources/src/META-INF/PostfixTemplates.xml b/resources/src/META-INF/PostfixTemplates.xml index 90294e0d619c..2fe4cd381876 100644 --- a/resources/src/META-INF/PostfixTemplates.xml +++ b/resources/src/META-INF/PostfixTemplates.xml @@ -1,41 +1,23 @@ - + + + + - - - - - - - - - - - - - - - - - - - - - - - + - + - + \ No newline at end of file