diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java index 0d7a54633fec..54efeb29a62a 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java @@ -154,23 +154,25 @@ public class CompileDriver { myGenerationCompilerModuleToOutputDirMap = new HashMap, Pair>(); - final LocalFileSystem lfs = LocalFileSystem.getInstance(); - final IntermediateOutputCompiler[] generatingCompilers = CompilerManager.getInstance(myProject).getCompilers(IntermediateOutputCompiler.class, myCompilerFilter); - final Module[] allModules = ModuleManager.getInstance(myProject).getModules(); - final CompilerConfiguration config = CompilerConfiguration.getInstance(project); - for (Module module : allModules) { - for (IntermediateOutputCompiler compiler : generatingCompilers) { - final VirtualFile productionOutput = lookupVFile(lfs, CompilerPaths.getGenerationOutputPath(compiler, module, false)); - final VirtualFile testOutput = lookupVFile(lfs, CompilerPaths.getGenerationOutputPath(compiler, module, true)); - final Pair pair = new Pair(compiler, module); - final Pair outputs = new Pair(productionOutput, testOutput); - myGenerationCompilerModuleToOutputDirMap.put(pair, outputs); - } - if (config.isAnnotationProcessorsEnabled()) { - if (config.isAnnotationProcessingEnabled(module)) { - final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module); - if (path != null) { - lookupVFile(lfs, path); // ensure the file is created and added to VFS + if (!useCompileServer()) { + final LocalFileSystem lfs = LocalFileSystem.getInstance(); + final IntermediateOutputCompiler[] generatingCompilers = CompilerManager.getInstance(myProject).getCompilers(IntermediateOutputCompiler.class, myCompilerFilter); + final Module[] allModules = ModuleManager.getInstance(myProject).getModules(); + final CompilerConfiguration config = CompilerConfiguration.getInstance(project); + for (Module module : allModules) { + for (IntermediateOutputCompiler compiler : generatingCompilers) { + final VirtualFile productionOutput = lookupVFile(lfs, CompilerPaths.getGenerationOutputPath(compiler, module, false)); + final VirtualFile testOutput = lookupVFile(lfs, CompilerPaths.getGenerationOutputPath(compiler, module, true)); + final Pair pair = new Pair(compiler, module); + final Pair outputs = new Pair(productionOutput, testOutput); + myGenerationCompilerModuleToOutputDirMap.put(pair, outputs); + } + if (config.isAnnotationProcessorsEnabled()) { + if (config.isAnnotationProcessingEnabled(module)) { + final String path = CompilerPaths.getAnnotationProcessorsGenerationPath(module); + if (path != null) { + lookupVFile(lfs, path); // ensure the file is created and added to VFS + } } } } diff --git a/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteWindow.java b/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteWindow.java index f14a38242e12..18a485dc18e3 100644 --- a/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteWindow.java +++ b/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteWindow.java @@ -55,9 +55,11 @@ public class PaletteWindow extends JPanel implements DataProvider { private PaletteGroupHeader myLastFocusedGroup; @NonNls private static final String ourHelpID = "guiDesigner.uiTour.palette"; + private PaletteManager myPaletteManager; public PaletteWindow(Project project) { myProject = project; + myPaletteManager = PaletteManager.getInstance(myProject); myProviders = Extensions.getExtensions(PaletteItemProvider.EP_NAME, project); for(PaletteItemProvider provider: myProviders) { provider.addListener(myPropertyChangeListener); @@ -180,7 +182,7 @@ public class PaletteWindow extends JPanel implements DataProvider { group.getComponentList().clearSelection(); } ListSelectionEvent event = new ListSelectionEvent(this, -1, -1, false); - PaletteManager.getInstance(myProject).notifySelectionChanged(event); + myPaletteManager.notifySelectionChanged(event); } @Nullable public PaletteItem getActiveItem() { @@ -252,7 +254,7 @@ public class PaletteWindow extends JPanel implements DataProvider { break; } } - PaletteManager.getInstance(myProject).notifySelectionChanged(e); + myPaletteManager.notifySelectionChanged(e); } } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ProjectStructureElementRenderer.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ProjectStructureElementRenderer.java index 06bb5ec7731b..54037ea58236 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ProjectStructureElementRenderer.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ProjectStructureElementRenderer.java @@ -53,8 +53,7 @@ class ProjectStructureElementRenderer extends ColoredTreeCellRenderer { setToolTipText(null); setFont(UIUtil.getTreeFont()); - SimpleTextAttributes textAttributes = - selected && hasFocus ? SimpleTextAttributes.SELECTED_SIMPLE_CELL_ATTRIBUTES : SimpleTextAttributes.REGULAR_ATTRIBUTES; + SimpleTextAttributes textAttributes = SimpleTextAttributes.REGULAR_ATTRIBUTES; if (node.isDisplayInBold()) { textAttributes = SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES; } @@ -74,6 +73,13 @@ class ProjectStructureElementRenderer extends ColoredTreeCellRenderer { textAttributes = textAttributes.derive(style, foreground, null, waveColor); setToolTipText(problemsHolder.composeTooltipMessage()); } + + append(displayName, textAttributes); + String description = projectStructureElement.getDescription(); + if (description != null) { + append(" (" + description + ")", SimpleTextAttributes.GRAY_ATTRIBUTES, false); + } + return; } } append(displayName, textAttributes); diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureElement.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureElement.java index f9e866962e79..fed82781265d 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureElement.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureElement.java @@ -18,6 +18,11 @@ public abstract class ProjectStructureElement { public abstract String getPresentableName(); + @Nullable + public String getDescription() { + return null; + } + public abstract String getTypeName(); public abstract String getId(); diff --git a/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileContainerImpl.java b/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileContainerImpl.java index 6299468b345b..331db279750b 100644 --- a/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileContainerImpl.java +++ b/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileContainerImpl.java @@ -16,10 +16,11 @@ package com.intellij.util.descriptors.impl; +import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.MultiValuesMap; import com.intellij.openapi.vfs.*; -import com.intellij.openapi.Disposable; import com.intellij.util.EventDispatcher; import com.intellij.util.descriptors.*; import org.jetbrains.annotations.Nullable; @@ -135,6 +136,7 @@ public class ConfigFileContainerImpl implements ConfigFileContainer { } for (ConfigFileInfo configuration : newDescriptors) { final ConfigFileImpl configFile = new ConfigFileImpl(this, configuration); + Disposer.register(this, configFile); myConfigFiles.put(metaData, configFile); added.add(configFile); } @@ -142,6 +144,7 @@ public class ConfigFileContainerImpl implements ConfigFileContainer { for (ConfigFile descriptor : toDelete) { myConfigFiles.remove(descriptor.getMetaData(), descriptor); + Disposer.dispose(descriptor); } myCachedConfigFiles = null; diff --git a/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileImpl.java b/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileImpl.java index fcee66b35973..a64cc6c8a313 100644 --- a/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileImpl.java +++ b/java/idea-ui/src/com/intellij/util/descriptors/impl/ConfigFileImpl.java @@ -1,4 +1,3 @@ - package com.intellij.util.descriptors.impl; import com.intellij.openapi.project.Project; @@ -22,43 +21,38 @@ import org.jetbrains.annotations.Nullable; */ public class ConfigFileImpl implements ConfigFile { @NotNull private ConfigFileInfo myInfo; - private VirtualFilePointer myFilePointer; - private PsiFile myPsiFile; + private final VirtualFilePointer myFilePointer; + private volatile PsiFile myPsiFile; private final ConfigFileContainerImpl myContainer; private final Project myProject; private long myModificationCount; - private final Object myPsiFileLock = new Object(); - private final VirtualFilePointerListener myListener = new VirtualFilePointerListener() { - public void beforeValidityChanged(final VirtualFilePointer[] pointers) { - } - - public void validityChanged(final VirtualFilePointer[] pointers) { - synchronized (myPsiFileLock) { - myPsiFile = null; - } - onChange(); - } - }; public ConfigFileImpl(@NotNull final ConfigFileContainerImpl container, @NotNull final ConfigFileInfo configuration) { myContainer = container; myInfo = configuration; - setUrl(configuration.getUrl()); + final VirtualFilePointerManager pointerManager = VirtualFilePointerManager.getInstance(); + myFilePointer = pointerManager.create(configuration.getUrl(), this, new VirtualFilePointerListener() { + @Override + public void beforeValidityChanged(final VirtualFilePointer[] pointers) { + } + + @Override + public void validityChanged(final VirtualFilePointer[] pointers) { + myPsiFile = null; + onChange(); + } + }); + onChange(); Disposer.register(container, this); myProject = myContainer.getProject(); } - private void setUrl(String url) { - final VirtualFilePointerManager pointerManager = VirtualFilePointerManager.getInstance(); - myFilePointer = pointerManager.create(url, this, myListener); - onChange(); - } - private void onChange() { myModificationCount++; myContainer.fireDescriptorChanged(this); } + @Override public String getUrl() { return myFilePointer.getUrl(); } @@ -67,17 +61,16 @@ public class ConfigFileImpl implements ConfigFile { myInfo = info; } + @Override @Nullable public VirtualFile getVirtualFile() { return myFilePointer.getFile(); } + @Override @Nullable public PsiFile getPsiFile() { - PsiFile psiFile; - synchronized (myPsiFileLock) { - psiFile = myPsiFile; - } + PsiFile psiFile = myPsiFile; if (psiFile != null && psiFile.isValid()) { return psiFile; @@ -88,27 +81,29 @@ public class ConfigFileImpl implements ConfigFile { psiFile = PsiManager.getInstance(myProject).findFile(virtualFile); - synchronized (myPsiFileLock) { - myPsiFile = psiFile; - } + myPsiFile = psiFile; return psiFile; } + @Override @Nullable public XmlFile getXmlFile() { final PsiFile file = getPsiFile(); return file instanceof XmlFile ? (XmlFile)file : null; } + @Override public void dispose() { } + @Override @NotNull public ConfigFileInfo getInfo() { return myInfo; } + @Override public boolean isValid() { final PsiFile psiFile = getPsiFile(); if (psiFile == null || !psiFile.isValid()) { @@ -122,12 +117,14 @@ public class ConfigFileImpl implements ConfigFile { } + @Override @NotNull public ConfigFileMetaData getMetaData() { return myInfo.getMetaData(); } + @Override public long getModificationCount() { return myModificationCount; } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaNoVariantsDelegator.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaNoVariantsDelegator.java index 1cb0667bd7e8..669905ed006e 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaNoVariantsDelegator.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaNoVariantsDelegator.java @@ -112,9 +112,11 @@ public class JavaNoVariantsDelegator extends CompletionContributor { PsiType type = JavaCompletionUtil.getLookupElementType(base); if (type != null && !PsiType.VOID.equals(type)) { PsiReferenceExpression ref = ReferenceExpressionCompletionContributor.createMockReference(position, type, base); - for (final LookupElement item : JavaSmartCompletionContributor.completeReference(position, ref, filter, true, true, parameters, - result.getPrefixMatcher())) { - qualifiedCollector.addElement(new JavaChainLookupElement(base, item)); + if (ref != null) { + for (final LookupElement item : JavaSmartCompletionContributor.completeReference(position, ref, filter, true, true, parameters, + result.getPrefixMatcher())) { + qualifiedCollector.addElement(new JavaChainLookupElement(base, item)); + } } } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/ReferenceExpressionCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/ReferenceExpressionCompletionContributor.java index 5033f6a629cd..e1f28cd2b85a 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/ReferenceExpressionCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/ReferenceExpressionCompletionContributor.java @@ -25,8 +25,8 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Iconable; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.patterns.PsiJavaPatterns; import com.intellij.patterns.PsiMethodPattern; +import com.intellij.patterns.StandardPatterns; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.psi.codeStyle.JavaCodeStyleManager; @@ -46,7 +46,10 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Set; import static com.intellij.patterns.PsiJavaPatterns.psiElement; import static com.intellij.patterns.PsiJavaPatterns.psiMethod; @@ -57,7 +60,7 @@ import static com.intellij.patterns.PsiJavaPatterns.psiMethod; public class ReferenceExpressionCompletionContributor { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.ReferenceExpressionCompletionContributor"); private static final PsiMethodPattern OBJECT_METHOD_PATTERN = psiMethod().withName( - PsiJavaPatterns.string().oneOf("hashCode", "equals", "finalize", "wait", "notify", "notifyAll", "getClass", "clone", "toString")). + StandardPatterns.string().oneOf("hashCode", "equals", "finalize", "wait", "notify", "notifyAll", "getClass", "clone", "toString")). definedInClass(CommonClassNames.JAVA_LANG_OBJECT); private static final PrefixMatcher TRUE_MATCHER = new PrefixMatcher("") { @Override @@ -83,9 +86,9 @@ public class ReferenceExpressionCompletionContributor { } if (psiElement().inside( - PsiJavaPatterns.or( - psiElement(PsiAnnotationParameterList.class), - psiElement(PsiSwitchLabelStatement.class)) + StandardPatterns.or( + psiElement(PsiAnnotationParameterList.class), + psiElement(PsiSwitchLabelStatement.class)) ).accepts(element)) { return new ElementExtractorFilter(new AndFilter( new ClassFilter(PsiField.class), @@ -426,27 +429,37 @@ public class ReferenceExpressionCompletionContributor { PsiType qualifierType, final PsiType expectedType, JavaSmartCompletionParameters parameters) throws IncorrectOperationException { final PsiReferenceExpression mockRef = createMockReference(place, qualifierType, qualifierItem); + if (mockRef == null) { + return; + } final ElementFilter filter = getReferenceFilter(place, true); for (final LookupElement item : completeFinalReference(place, mockRef, filter, parameters)) { - if (shoudChain(place, qualifierType, expectedType, item)) { + if (shouldChain(place, qualifierType, expectedType, item)) { result.consume(new JavaChainLookupElement(qualifierItem, item)); } } } + @Nullable public static PsiReferenceExpression createMockReference(final PsiElement place, @NotNull PsiType qualifierType, LookupElement qualifierItem) { PsiElementFactory factory = JavaPsiFacade.getElementFactory(place.getProject()); if (qualifierItem.getObject() instanceof PsiClass) { - return (PsiReferenceExpression)factory - .createExpressionFromText(((PsiClass)qualifierItem.getObject()).getQualifiedName() + ".xxx", place); + try { + return (PsiReferenceExpression)factory + .createExpressionFromText(((PsiClass)qualifierItem.getObject()).getQualifiedName() + ".xxx", place); + } + catch (IncorrectOperationException e) { + LOG.info(e); + return null; + } } return (PsiReferenceExpression) factory.createExpressionFromText("xxx.xxx", JavaCompletionUtil .createContextWithXxxVariable(place, qualifierType)); } - private static boolean shoudChain(PsiElement element, PsiType qualifierType, PsiType expectedType, LookupElement item) { + private static boolean shouldChain(PsiElement element, PsiType qualifierType, PsiType expectedType, LookupElement item) { if (item.getObject() instanceof PsiMethod) { final PsiMethod method = (PsiMethod)item.getObject(); if (psiMethod().withName("toArray").withParameterCount(1) diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java index 31a4851df62c..d4dcd96dd43f 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java @@ -272,7 +272,7 @@ public class PsiImplUtil { return new PsiImmediateClassType(classClass, substitutor); } - public static PsiAnnotation findAnnotation(@NotNull PsiAnnotationOwner modifierList, @NotNull String qualifiedName) { + @Nullable public static PsiAnnotation findAnnotation(@NotNull PsiAnnotationOwner modifierList, @NotNull String qualifiedName) { final String shortName = StringUtil.getShortName(qualifiedName); PsiAnnotation[] annotations = modifierList.getAnnotations(); for (PsiAnnotation annotation : annotations) { @@ -368,7 +368,7 @@ public class PsiImplUtil { } else if (member.hasModifierProperty(PsiModifier.PRIVATE)) { PsiClass topClass = PsiUtil.getTopLevelClass(member); - return topClass != null ? new LocalSearchScope(topClass) : new LocalSearchScope(file); + return topClass != null ? new LocalSearchScope(topClass) : file != null ? new LocalSearchScope(file) : maximalUseScope; } else { if (file instanceof PsiJavaFile) { @@ -388,7 +388,7 @@ public class PsiImplUtil { return getServerPageFile(element) != null; } - public static ServerPageFile getServerPageFile(final PsiElement element) { + @Nullable public static ServerPageFile getServerPageFile(final PsiElement element) { final PsiFile psiFile = PsiUtilCore.getTemplateLanguageFile(element); return psiFile instanceof ServerPageFile ? (ServerPageFile)psiFile : null; } diff --git a/java/java-psi-impl/src/com/intellij/psi/scope/util/PsiScopesUtil.java b/java/java-psi-impl/src/com/intellij/psi/scope/util/PsiScopesUtil.java index d44097d038d4..962d7cb9256a 100644 --- a/java/java-psi-impl/src/com/intellij/psi/scope/util/PsiScopesUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/scope/util/PsiScopesUtil.java @@ -144,7 +144,7 @@ public class PsiScopesUtil { public static boolean resolveAndWalk(PsiScopeProcessor processor, PsiJavaCodeReferenceElement ref, - PsiElement maxScope, + @Nullable PsiElement maxScope, boolean incompleteCode) { final PsiElement qualifier = ref.getQualifier(); final PsiElement classNameElement = ref.getReferenceNameElement(); diff --git a/java/testFramework/src/com/intellij/codeInsight/daemon/DaemonAnalyzerTestCase.java b/java/testFramework/src/com/intellij/codeInsight/daemon/DaemonAnalyzerTestCase.java index 53f1c6b6583e..b3dc24ace4ae 100644 --- a/java/testFramework/src/com/intellij/codeInsight/daemon/DaemonAnalyzerTestCase.java +++ b/java/testFramework/src/com/intellij/codeInsight/daemon/DaemonAnalyzerTestCase.java @@ -109,7 +109,6 @@ public abstract class DaemonAnalyzerTestCase extends CodeInsightTestCase { @Override protected void setUp() throws Exception { super.setUp(); - ((VirtualFilePointerManagerImpl)VirtualFilePointerManager.getInstance()).cleanupForNextTest(); final LocalInspectionTool[] tools = configureLocalInspectionTools(); for (LocalInspectionTool tool : tools) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java index 4c4a3e7f78f4..099eb993c267 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java @@ -77,8 +77,9 @@ public abstract class ModuleLevelBuilder extends Builder { final HashSet affectedBeforeDif = new HashSet(allAffectedFiles); + final ModulesBasedFileFilter moduleBasedFilter = new ModulesBasedFileFilter(context, chunk); final boolean incremental = globalMappings.differentiate( - delta, removedPaths, filesToCompile, allCompiledFiles, allAffectedFiles + delta, removedPaths, filesToCompile, allCompiledFiles, allAffectedFiles, moduleBasedFilter ); if (LOG.isDebugEnabled()) { @@ -103,13 +104,13 @@ public abstract class ModuleLevelBuilder extends Builder { if (!newlyAffectedFiles.isEmpty()) { - if (LOG.isDebugEnabled()) { - final List> wrongFiles = checkAffectedFilesInCorrectModules(context, chunk, newlyAffectedFiles); + final List> wrongFiles = checkAffectedFilesInCorrectModules(context, newlyAffectedFiles, moduleBasedFilter); if (!wrongFiles.isEmpty()) { LOG.debug("Wrong affected files for module chunk " + chunk.getName() + ": "); for (Pair pair : wrongFiles) { - LOG.debug("\t[" + pair.second.getName() + "] " + pair.first.getPath()); + final String name = pair.second != null? pair.second.getName() : "null"; + LOG.debug("\t[" + name + "] " + pair.first.getPath()); } } } @@ -146,45 +147,20 @@ public abstract class ModuleLevelBuilder extends Builder { } } - - private static List> checkAffectedFilesInCorrectModules(CompileContext context, ModuleChunk currentChunk, Collection affected) { + private static List> checkAffectedFilesInCorrectModules(CompileContext context, Collection affected, ModulesBasedFileFilter moduleBasedFilter) { if (affected.isEmpty()) { return Collections.emptyList(); } - - final Set chunkModules = currentChunk.getModules(); - final Map> cache = new HashMap>(); final List> result = new ArrayList>(); - for (File file : affected) { - final RootDescriptor moduleAndRoot = context.getModuleAndRoot(file); - if (moduleAndRoot == null) { - continue; + if (!moduleBasedFilter.accept(file)) { + final RootDescriptor moduleAndRoot = context.getModuleAndRoot(file); + result.add(Pair.create(file, moduleAndRoot != null? moduleAndRoot.module : null)); } - final Module moduleOfFile = moduleAndRoot.module; - if (chunkModules.contains(moduleOfFile)) { - continue; - } - Set moduleOfFileWithDependencies = cache.get(moduleOfFile); - if (moduleOfFileWithDependencies == null) { - moduleOfFileWithDependencies = ProjectPaths.getModulesWithDependentsRecursively(moduleOfFile, true); - cache.put(moduleOfFile, moduleOfFileWithDependencies); - } - if (intersects(moduleOfFileWithDependencies, chunkModules)) { - continue; - } - result.add(Pair.create(file, moduleOfFile)); } return result; } - private static boolean intersects(Set set1, Set set2) { - if (set1.size() < set2.size()) { - return new HashSet(set1).removeAll(set2); - } - return new HashSet(set2).removeAll(set1); - } - private static boolean chunkContainsAffectedFiles(CompileContext context, ModuleChunk chunk, final Set affected) throws IOException { final Set chunkModules = new HashSet(chunk.getModules()); if (!chunkModules.isEmpty()) { @@ -221,4 +197,40 @@ public abstract class ModuleLevelBuilder extends Builder { return removed != null? removed : Collections.emptySet(); } + private static class ModulesBasedFileFilter implements Mappings.DependentFilesFilter{ + private final CompileContext myContext; + private final Set myChunkModules; + private final Map> myCache = new HashMap>(); + + private ModulesBasedFileFilter(CompileContext context, ModuleChunk chunk) { + myContext = context; + myChunkModules = chunk.getModules(); + } + + @Override + public boolean accept(File file) { + final RootDescriptor moduleAndRoot = myContext.getModuleAndRoot(file); + if (moduleAndRoot == null) { + return true; + } + final Module moduleOfFile = moduleAndRoot.module; + if (myChunkModules.contains(moduleOfFile)) { + return true; + } + Set moduleOfFileWithDependencies = myCache.get(moduleOfFile); + if (moduleOfFileWithDependencies == null) { + moduleOfFileWithDependencies = ProjectPaths.getModulesWithDependentsRecursively(moduleOfFile, true); + myCache.put(moduleOfFile, moduleOfFileWithDependencies); + } + return intersects(moduleOfFileWithDependencies, myChunkModules); + } + + private static boolean intersects(Set set1, Set set2) { + if (set1.size() < set2.size()) { + return new HashSet(set1).removeAll(set2); + } + return new HashSet(set2).removeAll(set1); + } + } + } diff --git a/jps/model/src/org/jetbrains/ether/dependencyView/Mappings.java b/jps/model/src/org/jetbrains/ether/dependencyView/Mappings.java index d6190d61d815..7ff64fdb13bc 100644 --- a/jps/model/src/org/jetbrains/ether/dependencyView/Mappings.java +++ b/jps/model/src/org/jetbrains/ether/dependencyView/Mappings.java @@ -806,7 +806,10 @@ public class Mappings { } } - private boolean incrementalDecision(final DependencyContext.S owner, final Proto member, final Collection affectedFiles) { + private boolean incrementalDecision(final DependencyContext.S owner, + final Proto member, + final Collection affectedFiles, + DependentFilesFilter filter) { final boolean isField = member instanceof FieldRepr; final Util self = new Util(this); @@ -842,19 +845,33 @@ public class Mappings { if (ClassRepr.getPackageName(myContext.getValue(className)).equals(packageName)) { final String f = myContext.getValue(fileName); - debug("Adding: ", f); - affectedFiles.add(new File(f)); + final File file = new File(f); + if (filter.accept(file)) { + debug("Adding: ", f); + affectedFiles.add(file); + } } } return true; } + public interface DependentFilesFilter { + DependentFilesFilter ALL_FILES = new DependentFilesFilter() { + @Override + public boolean accept(File file) { + return true; + } + }; + + boolean accept(File file); + } + public boolean differentiate(final Mappings delta, final Collection removed, final Collection filesToCompile, final Collection compiledFiles, - final Collection affectedFiles) { + final Collection affectedFiles, DependentFilesFilter filter) { synchronized (myLock) { debug("Begin of Differentiate:"); @@ -926,7 +943,7 @@ public class Mappings { if (it.isAnnotation() && it.policy == RetentionPolicy.SOURCE) { debug("Annotation, retention policy = SOURCE => a switch to non-incremental mode requested"); - if (!incrementalDecision(it.outerClassName, it, affectedFiles)) { + if (!incrementalDecision(it.outerClassName, it, affectedFiles, filter)) { debug("End of Differentiate, returning false"); return false; } @@ -970,7 +987,7 @@ public class Mappings { if (removedtargets.contains(ElementType.LOCAL_VARIABLE)) { debug("Removed target contains LOCAL_VARIABLE => a switch to non-incremental mode requested"); - if (!incrementalDecision(it.outerClassName, it, affectedFiles)) { + if (!incrementalDecision(it.outerClassName, it, affectedFiles, filter)) { debug("End of Differentiate, returning false"); return false; } @@ -1396,7 +1413,7 @@ public class Mappings { if ((f.access & Opcodes.ACC_PRIVATE) == 0 && (f.access & mask) == mask && f.hasValue()) { debug("Field had value and was (non-private) final static => a switch to non-incremental mode requested"); - if (!incrementalDecision(it.name, f, affectedFiles)) { + if (!incrementalDecision(it.name, f, affectedFiles, filter)) { debug("End of Differentiate, returning false"); return false; } @@ -1417,7 +1434,7 @@ public class Mappings { if ((field.access & Opcodes.ACC_PRIVATE) == 0 && (field.access & mask) == mask) { if ((d.base() & Difference.ACCESS) > 0 || (d.base() & Difference.VALUE) > 0) { debug("Inline field changed it's access or value => a switch to non-incremental mode requested"); - if (!incrementalDecision(it.name, field, affectedFiles)) { + if (!incrementalDecision(it.name, field, affectedFiles, filter)) { debug("End of Differentiate, returning false"); return false; } @@ -1633,40 +1650,46 @@ public class Mappings { if (delta.isDifferentiated()) { for (DependencyContext.S c : delta.getChangedClasses()) { - myClassToSubclasses.remove(c); - final Collection subClasses = delta.myClassToSubclasses.get(c); - if (subClasses != null) { - myClassToSubclasses.put(c, subClasses); + myClassToSubclasses.replace(c, subClasses); + } + else { + myClassToSubclasses.remove(c); } - myClassToSourceFile.remove(c); - final DependencyContext.S sourceFile = delta.myClassToSourceFile.get(c); - if (sourceFile != null) { myClassToSourceFile.put(c, sourceFile); } + else { + myClassToSourceFile.remove(c); + } } for (DependencyContext.S f : delta.getChangedFiles()) { - mySourceFileToClasses.remove(f); final Collection classes = delta.mySourceFileToClasses.get(f); if (classes != null) { - mySourceFileToClasses.put(f, classes); + mySourceFileToClasses.replace(f, classes); + } + else { + mySourceFileToClasses.remove(f); } - mySourceFileToUsages.remove(f); final Collection clusters = delta.mySourceFileToUsages.get(f); if (clusters != null) { - mySourceFileToUsages.put(f, clusters); + mySourceFileToUsages.replace(f, clusters); + } + else { + mySourceFileToUsages.remove(f); } - mySourceFileToAnnotationUsages.remove(f); final Collection usages = delta.mySourceFileToAnnotationUsages.get(f); if (usages != null) { - mySourceFileToAnnotationUsages.put(f, usages); + mySourceFileToAnnotationUsages.replace(f, usages); + } + else { + mySourceFileToAnnotationUsages.remove(f); } } } @@ -1711,8 +1734,7 @@ public class Mappings { changed |= past.addAll(now); if (changed) { - myClassToClassDependency.remove(aClass); - myClassToClassDependency.put(aClass, past); + myClassToClassDependency.replace(aClass, past); } } } diff --git a/jps/model/src/org/jetbrains/ether/dependencyView/MultiMaplet.java b/jps/model/src/org/jetbrains/ether/dependencyView/MultiMaplet.java index a7152b3684dc..15a2875086e3 100644 --- a/jps/model/src/org/jetbrains/ether/dependencyView/MultiMaplet.java +++ b/jps/model/src/org/jetbrains/ether/dependencyView/MultiMaplet.java @@ -30,6 +30,7 @@ interface MultiMaplet { Collection get(final K key); void put(final K key, final V value); void put(final K key, final Collection value); + void replace(final K key, final Collection value); void putAll(MultiMaplet m); void replaceAll(MultiMaplet m); void remove(final K key); diff --git a/jps/model/src/org/jetbrains/ether/dependencyView/PersistentMultiMaplet.java b/jps/model/src/org/jetbrains/ether/dependencyView/PersistentMultiMaplet.java index f1d288696244..e5497ab73994 100644 --- a/jps/model/src/org/jetbrains/ether/dependencyView/PersistentMultiMaplet.java +++ b/jps/model/src/org/jetbrains/ether/dependencyView/PersistentMultiMaplet.java @@ -66,6 +66,21 @@ class PersistentMultiMaplet implements MultiMaplet { } } + @Override + public void replace(K key, Collection value) { + try { + if (value == null) { + myMap.remove(key); + } + else { + myMap.put(key, value); + } + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + @Override public void put(final K key, final Collection value) { try { @@ -149,9 +164,7 @@ class PersistentMultiMaplet implements MultiMaplet { @Override public void replaceAll(MultiMaplet m) { for (Map.Entry> entry : m.entrySet()) { - final K key = entry.getKey(); - remove(key); - put(key, entry.getValue()); + replace(entry.getKey(), entry.getValue()); } } diff --git a/jps/model/src/org/jetbrains/ether/dependencyView/TransientMultiMaplet.java b/jps/model/src/org/jetbrains/ether/dependencyView/TransientMultiMaplet.java index 7a5f2b85eb90..71aafd8696c8 100644 --- a/jps/model/src/org/jetbrains/ether/dependencyView/TransientMultiMaplet.java +++ b/jps/model/src/org/jetbrains/ether/dependencyView/TransientMultiMaplet.java @@ -72,6 +72,16 @@ class TransientMultiMaplet implements MultiMaplet { } } + @Override + public void replace(K key, Collection value) { + if (value == null) { + myMap.remove(key); + } + else { + myMap.put(key, value); + } + } + @Override public void put(final K key, final V value) { final Collection x = constr.create(); @@ -111,8 +121,7 @@ class TransientMultiMaplet implements MultiMaplet { @Override public void replaceAll(MultiMaplet m) { for (Map.Entry> e : m.entrySet()) { - remove(e.getKey()); - put(e.getKey(), e.getValue()); + replace(e.getKey(), e.getValue()); } } diff --git a/jps/src/org/jetbrains/ether/ProjectWrapper.java b/jps/src/org/jetbrains/ether/ProjectWrapper.java index 01811ee5a439..05c22f89ada4 100644 --- a/jps/src/org/jetbrains/ether/ProjectWrapper.java +++ b/jps/src/org/jetbrains/ether/ProjectWrapper.java @@ -1163,7 +1163,8 @@ public class ProjectWrapper { final Collection affected = new HashSet(); - final boolean incremental = dependencyMapping.differentiate(delta, removed, files, compiled, affected); + final boolean incremental = dependencyMapping.differentiate(delta, removed, files, compiled, affected, + Mappings.DependentFilesFilter.ALL_FILES); for (File a : affected) { affectedFiles.add(FileUtil.toSystemIndependentName(a.getAbsolutePath())); diff --git a/platform/core-api/src/com/intellij/psi/PsiElement.java b/platform/core-api/src/com/intellij/psi/PsiElement.java index f26dbf06b44d..99bc7789c90c 100644 --- a/platform/core-api/src/com/intellij/psi/PsiElement.java +++ b/platform/core-api/src/com/intellij/psi/PsiElement.java @@ -269,7 +269,7 @@ public interface PsiElement extends UserDataHolder, Iconable { * @return the element which was actually added (either element or its copy). * @throws IncorrectOperationException if the modification is not supported or not possible for some reason. */ - PsiElement addBefore(@NotNull PsiElement element, PsiElement anchor) throws IncorrectOperationException; + PsiElement addBefore(@NotNull PsiElement element, @Nullable PsiElement anchor) throws IncorrectOperationException; /** * Adds a child to this PSI element, after the specified anchor element. @@ -279,7 +279,7 @@ public interface PsiElement extends UserDataHolder, Iconable { * @return the element which was actually added (either element or its copy). * @throws IncorrectOperationException if the modification is not supported or not possible for some reason. */ - PsiElement addAfter(@NotNull PsiElement element, PsiElement anchor) throws IncorrectOperationException; + PsiElement addAfter(@NotNull PsiElement element, @Nullable PsiElement anchor) throws IncorrectOperationException; /** * Checks if it is possible to add the specified element as a child to this element, diff --git a/platform/core-api/src/com/intellij/psi/search/LocalSearchScope.java b/platform/core-api/src/com/intellij/psi/search/LocalSearchScope.java index c092c70e2d10..9c751afbed35 100644 --- a/platform/core-api/src/com/intellij/psi/search/LocalSearchScope.java +++ b/platform/core-api/src/com/intellij/psi/search/LocalSearchScope.java @@ -41,11 +41,11 @@ public class LocalSearchScope extends SearchScope { public static final LocalSearchScope EMPTY = new LocalSearchScope(PsiElement.EMPTY_ARRAY); private String myDisplayName; - public LocalSearchScope(PsiElement scope) { + public LocalSearchScope(@NotNull PsiElement scope) { this(scope, null); } - public LocalSearchScope(PsiElement scope, String displayName) { + public LocalSearchScope(@NotNull PsiElement scope, @Nullable String displayName) { this(new PsiElement[]{scope}); myDisplayName = displayName; } @@ -54,16 +54,17 @@ public class LocalSearchScope extends SearchScope { this(scope, null); } - public LocalSearchScope(PsiElement[] scope, String displayName) { + public LocalSearchScope(PsiElement[] scope, @Nullable String displayName) { this(scope, displayName, false); } - public LocalSearchScope(final PsiElement[] scope, final String displayName, final boolean ignoreInjectedPsi) { + public LocalSearchScope(final PsiElement[] scope, @Nullable final String displayName, final boolean ignoreInjectedPsi) { myIgnoreInjectedPsi = ignoreInjectedPsi; myDisplayName = displayName; Set localScope = new LinkedHashSet(scope.length); for (final PsiElement element : scope) { + LOG.assertTrue(element != null, "null element"); LOG.assertTrue(element.getContainingFile() != null, element.getClass().getName()); if (element instanceof PsiFile) { List files = ((PsiFile)element).getViewProvider().getAllFiles(); diff --git a/platform/core-impl/src/com/intellij/openapi/editor/ex/DocumentBulkUpdateListener.java b/platform/core-impl/src/com/intellij/openapi/editor/ex/DocumentBulkUpdateListener.java index b85db01dc00b..999482c517df 100644 --- a/platform/core-impl/src/com/intellij/openapi/editor/ex/DocumentBulkUpdateListener.java +++ b/platform/core-impl/src/com/intellij/openapi/editor/ex/DocumentBulkUpdateListener.java @@ -24,7 +24,7 @@ import com.intellij.util.messages.Topic; import org.jetbrains.annotations.NotNull; public interface DocumentBulkUpdateListener { - Topic TOPIC = Topic.create("Bulk document change notifcation like reformat, etc.", DocumentBulkUpdateListener.class); + Topic TOPIC = Topic.create("Bulk document change notification like reformat, etc.", DocumentBulkUpdateListener.class); void updateStarted(@NotNull Document doc); void updateFinished(@NotNull Document doc); diff --git a/platform/icons/src/actions/checked_16x16.png b/platform/icons/src/actions/checked_16x16.png new file mode 100644 index 000000000000..d55c1fc133c9 Binary files /dev/null and b/platform/icons/src/actions/checked_16x16.png differ diff --git a/platform/icons/src/actions/checked_16x16_selected.png b/platform/icons/src/actions/checked_16x16_selected.png new file mode 100644 index 000000000000..2dfa1eda3c12 Binary files /dev/null and b/platform/icons/src/actions/checked_16x16_selected.png differ diff --git a/platform/lang-api/src/com/intellij/lang/folding/CustomFoldingBuilder.java b/platform/lang-api/src/com/intellij/lang/folding/CustomFoldingBuilder.java index cc6c34625ef8..689ad61035da 100644 --- a/platform/lang-api/src/com/intellij/lang/folding/CustomFoldingBuilder.java +++ b/platform/lang-api/src/com/intellij/lang/folding/CustomFoldingBuilder.java @@ -58,7 +58,7 @@ public abstract class CustomFoldingBuilder extends FoldingBuilderEx implements D private void addCustomFoldingRegionsRecursively(@Nullable FoldingStack foldingStack, @NotNull ASTNode node, - List descriptors, + @NotNull List descriptors, int currDepth) { FoldingStack localFoldingStack = isCustomFoldingRoot(node) || foldingStack == null ? new FoldingStack(node) : foldingStack; for (ASTNode child = node.getFirstChildNode(); child != null; child = child.getTreeNext()) { @@ -196,7 +196,7 @@ public abstract class CustomFoldingBuilder extends FoldingBuilderEx implements D } private static class FoldingStack extends Stack { - private ASTNode owner; + private final ASTNode owner; public FoldingStack(@NotNull ASTNode owner) { super(1); @@ -205,7 +205,7 @@ public abstract class CustomFoldingBuilder extends FoldingBuilderEx implements D @NotNull public ASTNode getOwner() { - return this.owner; + return owner; } } } diff --git a/platform/lang-impl/src/com/intellij/codeEditor/printing/TextPainter.java b/platform/lang-impl/src/com/intellij/codeEditor/printing/TextPainter.java index e36efc870d41..b57def2b36be 100644 --- a/platform/lang-impl/src/com/intellij/codeEditor/printing/TextPainter.java +++ b/platform/lang-impl/src/com/intellij/codeEditor/printing/TextPainter.java @@ -27,7 +27,6 @@ import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; @@ -93,8 +92,6 @@ public class TextPainter implements Printable { myFileName = fileName; mySegmentEnd = myDocument.getTextLength(); - PsiDocumentManager.getInstance(project).commitAllDocuments(); - myFileType = psiFile.getFileType(); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ExternalToolPass.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ExternalToolPass.java index 16e61e3fa520..df0cea32c0c2 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ExternalToolPass.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ExternalToolPass.java @@ -50,6 +50,8 @@ public class ExternalToolPass extends TextEditorHighlightingPass { private final Map myAnnotator2DataMap; + private final ExternalToolPassFactory myExternalToolPassFactory; + private static class MyData { final PsiFile myPsiRoot; final Object myCollectedInfo; @@ -61,7 +63,8 @@ public class ExternalToolPass extends TextEditorHighlightingPass { } } - public ExternalToolPass(@NotNull PsiFile file, + public ExternalToolPass(@NotNull ExternalToolPassFactory externalToolPassFactory, + @NotNull PsiFile file, @NotNull Editor editor, int startOffset, int endOffset) { @@ -72,6 +75,7 @@ public class ExternalToolPass extends TextEditorHighlightingPass { myAnnotationHolder = new AnnotationHolderImpl(new AnnotationSession(file)); myAnnotator2DataMap = new HashMap(); + myExternalToolPassFactory = externalToolPassFactory; } @Override @@ -159,7 +163,7 @@ public class ExternalToolPass extends TextEditorHighlightingPass { r.run(); } else { - ApplicationManager.getApplication().executeOnPooledThread(r); + myExternalToolPassFactory.scheduleExternalActivity(myFile, r); } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ExternalToolPassFactory.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ExternalToolPassFactory.java index 4dec3b2a23b6..f719f5e89cf4 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ExternalToolPassFactory.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ExternalToolPassFactory.java @@ -28,6 +28,8 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiFile; +import com.intellij.util.ui.update.MergingUpdateQueue; +import com.intellij.util.ui.update.Update; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -38,10 +40,15 @@ import java.util.List; * @author cdr */ public class ExternalToolPassFactory extends AbstractProjectComponent implements TextEditorHighlightingPassFactory { + private final MergingUpdateQueue myExternalActivitiesQueue; + public ExternalToolPassFactory(Project project, TextEditorHighlightingPassRegistrar highlightingPassRegistrar) { super(project); // start after PostHighlightingPass completion since it could report errors that can prevent us to run highlightingPassRegistrar.registerTextEditorHighlightingPass(this, new int[]{Pass.POST_UPDATE_ALL}, null, true, Pass.EXTERNAL_TOOLS); + + myExternalActivitiesQueue = new MergingUpdateQueue("ExternalActivitiesQueue", 300, true, MergingUpdateQueue.ANY_COMPONENT, project, + null, false); } @Override @@ -54,11 +61,11 @@ public class ExternalToolPassFactory extends AbstractProjectComponent implements @Override @Nullable public TextEditorHighlightingPass createHighlightingPass(@NotNull PsiFile file, @NotNull final Editor editor) { - TextRange textRange = file.getTextRange(); + TextRange textRange = FileStatusMap.getDirtyTextRange(editor, Pass.EXTERNAL_TOOLS) == null ? null : file.getTextRange(); if (textRange == null || !externalAnnotatorsDefined(file)) { return null; } - return new ExternalToolPass(file, editor, textRange.getStartOffset(), textRange.getEndOffset()); + return new ExternalToolPass(this, file, editor, textRange.getStartOffset(), textRange.getEndOffset()); } private static boolean externalAnnotatorsDefined(PsiFile file) { @@ -70,4 +77,13 @@ public class ExternalToolPassFactory extends AbstractProjectComponent implements } return false; } + + void scheduleExternalActivity(@NotNull PsiFile file, @NotNull final Runnable r) { + myExternalActivitiesQueue.queue(new Update(file) { + @Override + public void run() { + r.run(); + } + }); + } } 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 ef1ca8393cf9..9d2c76e76879 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 @@ -924,7 +924,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable @Override public boolean vetoesHiding() { - return myChangeGuard || myDisposed; + return myChangeGuard; } public boolean isAvailableToUser() { diff --git a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/GridCellImpl.java b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/GridCellImpl.java index fc6488f989db..ae9624772796 100644 --- a/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/GridCellImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/ui/layout/impl/GridCellImpl.java @@ -363,7 +363,9 @@ public class GridCellImpl implements GridCell { service.setSize(getDimensionKey(), size, myContext.getProject()); if (myContext.getWindow() != 0) { final Window frame = SwingUtilities.getWindowAncestor(myPlaceholder); - service.setLocation(getDimensionKey(), frame.getLocationOnScreen()); + if (frame != null) { + service.setLocation(getDimensionKey(), frame.getLocationOnScreen()); + } } } diff --git a/platform/lang-impl/src/com/intellij/ide/actions/GotoActionBase.java b/platform/lang-impl/src/com/intellij/ide/actions/GotoActionBase.java index dd3afc04bb4e..f7586f8ce300 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/GotoActionBase.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/GotoActionBase.java @@ -175,7 +175,8 @@ public abstract class GotoActionBase extends AnAction { @Override public void onClose() { ourLastStrings.put(myInAction, Pair.create(popup.getEnteredText(), popup.getSelectedIndex())); - if (startedAction.equals(myInAction)) { + //noinspection ConstantConditions + if (startedAction != null && startedAction.equals(myInAction)) { myInAction = null; } if (filter != null) { diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java index 08d9c320118f..9b63a06d17bc 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java @@ -253,8 +253,6 @@ public class ProjectViewImpl extends ProjectView implements PersistentStateCompo myViewContentPanel = new JPanel(); myPanel = new SimpleToolWindowPanel(true).setProvideQuickActions(false); myPanel.setContent(myViewContentPanel); - - myPanel.setBorder(new ToolWindow.Border(true, false, false, false)); } public String getName() { diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ContributorsBasedGotoByModel.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ContributorsBasedGotoByModel.java index db21ded54762..b02c3272bc89 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ContributorsBasedGotoByModel.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ContributorsBasedGotoByModel.java @@ -16,10 +16,13 @@ package com.intellij.ide.util.gotoByName; import com.intellij.concurrency.JobUtil; +import com.intellij.diagnostic.PluginException; +import com.intellij.ide.plugins.PluginManager; import com.intellij.ide.util.NavigationItemListCellRenderer; import com.intellij.navigation.ChooseByNameContributor; import com.intellij.navigation.NavigationItem; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.extensions.PluginId; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.DumbService; @@ -109,7 +112,13 @@ public abstract class ContributorsBasedGotoByModel implements ChooseByNameModel try { for (NavigationItem item : contributor.getItemsByName(name, pattern, myProject, checkBoxState)) { if (item == null) { - LOG.error("null item from contributor " + contributor + " for name " + name); + final PluginId pluginId = PluginManager.getPluginByClassName(contributor.getClass().getName()); + if (pluginId != null) { + LOG.error(new PluginException("null item from contributor " + contributor + " for name " + name, pluginId)); + } + else { + LOG.error("null item from contributor " + contributor + " for name " + name); + } continue; } diff --git a/platform/lang-impl/src/com/intellij/platform/ModuleAttachProcessor.java b/platform/lang-impl/src/com/intellij/platform/ModuleAttachProcessor.java index 90bd69e50224..e1768278e035 100644 --- a/platform/lang-impl/src/com/intellij/platform/ModuleAttachProcessor.java +++ b/platform/lang-impl/src/com/intellij/platform/ModuleAttachProcessor.java @@ -70,12 +70,15 @@ public class ModuleAttachProcessor extends ProjectAttachProcessor { token.finish(); } } - for(String file: projectDir.list()) { - if (FileUtil.getExtension(file).equals("iml")) { - VirtualFile imlFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(projectDir, file)); - if (imlFile != null) { - attachModule(project, imlFile, callback); - return true; + final String[] files = projectDir.list(); + if (files != null) { + for(String file: files) { + if (FileUtil.getExtension(file).equals("iml")) { + VirtualFile imlFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(projectDir, file)); + if (imlFile != null) { + attachModule(project, imlFile, callback); + return true; + } } } } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java index 84e455f565d3..2d97fc4052a7 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java @@ -31,6 +31,7 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.EditorFactory; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.event.DocumentListener; +import com.intellij.openapi.editor.ex.DocumentBulkUpdateListener; import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.FileDocumentManagerAdapter; @@ -79,7 +80,7 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec private final List myListeners = ContainerUtil.createEmptyCOWList(); private final SmartPointerManagerImpl mySmartPointerManager; - public PsiDocumentManagerImpl(@NotNull Project project, + public PsiDocumentManagerImpl(@NotNull final Project project, @NotNull PsiManager psiManager, @NotNull SmartPointerManager smartPointerManager, @NotNull EditorFactory editorFactory, @@ -105,6 +106,12 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec fireDocumentCreated(document, psiFile); } }); + bus.connect().subscribe(DocumentBulkUpdateListener.TOPIC, new DocumentBulkUpdateListener.Adapter() { + @Override + public void updateFinished(@NotNull Document doc) { + documentCommitThread.queueCommit(project, doc, "Bulk update finished"); + } + }); ApplicationManager.getApplication().addApplicationListener(new ApplicationAdapter() { @Override public void beforeWriteActionStart(Object action) { @@ -668,12 +675,13 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec commitNecessary = true; } + boolean fromRefresh = ApplicationManager.getApplication().hasWriteAction(ExternalChangeAction.class); if (commitNecessary) { myUncommittedDocuments.add(document); - myDocumentCommitThread.log("PDI: added to uncommitted", null, false, document, event, myUncommittedDocuments); - - myDocumentCommitThread.queueCommit(myProject, document, event); + if (!fromRefresh && !((DocumentEx)document).isInBulkUpdate()) { + myDocumentCommitThread.queueCommit(myProject, document, event); + } } // Consider that it's worth to perform complete re-parse instead of merge if the whole document text is replaced and @@ -683,7 +691,7 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec document.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, Boolean.TRUE); } - if (commitNecessary && ApplicationManager.getApplication().hasWriteAction(ExternalChangeAction.class)){ + if (commitNecessary && fromRefresh){ commitDocument(document); } // avoid documents piling up during batch processing diff --git a/platform/lvcs-impl/src/com/intellij/history/integration/ui/actions/ShowSelectionHistoryAction.java b/platform/lvcs-impl/src/com/intellij/history/integration/ui/actions/ShowSelectionHistoryAction.java index 90bafa107ef6..843795e24455 100644 --- a/platform/lvcs-impl/src/com/intellij/history/integration/ui/actions/ShowSelectionHistoryAction.java +++ b/platform/lvcs-impl/src/com/intellij/history/integration/ui/actions/ShowSelectionHistoryAction.java @@ -20,12 +20,14 @@ import com.intellij.history.core.LocalHistoryFacade; import com.intellij.history.integration.IdeaGateway; import com.intellij.history.integration.ui.views.SelectionHistoryDialog; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.actions.VcsContext; import com.intellij.openapi.vcs.actions.VcsContextWrapper; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.vcsUtil.VcsSelection; import com.intellij.vcsUtil.VcsSelectionUtil; +import org.jetbrains.annotations.Nullable; public class ShowSelectionHistoryAction extends ShowHistoryAction { @Override @@ -44,12 +46,23 @@ public class ShowSelectionHistoryAction extends ShowHistoryAction { return sel == null ? super.getText(e) : sel.getActionName(); } + @Override + public void update(AnActionEvent e) { + if (e.getData(PlatformDataKeys.EDITOR) == null) { + e.getPresentation().setVisible(false); + } + else { + super.update(e); + } + } + @Override protected boolean isEnabled(LocalHistoryFacade vcs, IdeaGateway gw, VirtualFile f, AnActionEvent e) { return super.isEnabled(vcs, gw, f, e) && !f.isDirectory() && getSelection(e) != null; } - private VcsSelection getSelection(AnActionEvent e) { + @Nullable + private static VcsSelection getSelection(AnActionEvent e) { VcsContext c = VcsContextWrapper.createCachedInstanceOn(e); return VcsSelectionUtil.getSelection(c); } diff --git a/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java b/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java index 62c78bbb0f6c..c7d0dcbacf19 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java @@ -462,7 +462,7 @@ public abstract class MasterDetailsComponent implements Configurable, DetailsCom boolean hasFocus) { if (value instanceof MyNode) { final MyNode node = ((MyNode)value); - setIcon(node.getConfigurable().getIcon(expanded)); + setIcon(node.getIcon(expanded)); final Font font = UIUtil.getTreeFont(); if (node.isDisplayInBold()) { setFont(font.deriveFont(Font.BOLD)); @@ -699,8 +699,8 @@ public abstract class MasterDetailsComponent implements Configurable, DetailsCom throw new ConfigurationException("Name should contain non-space characters"); } if (names.contains(name)) { - final NamedConfigurable selectedConfugurable = getSelectedConfugurable(); - if (selectedConfugurable == null || !Comparing.strEqual(selectedConfugurable.getDisplayName(), name)) { + final NamedConfigurable selectedConfigurable = getSelectedConfugurable(); + if (selectedConfigurable == null || !Comparing.strEqual(selectedConfigurable.getDisplayName(), name)) { selectNodeInTree(node); } throw new ConfigurationException(CommonBundle.message("smth.already.exist.error.message", prefix, name), title); @@ -836,6 +836,16 @@ public abstract class MasterDetailsComponent implements Configurable, DetailsCom public void setDisplayInBold(boolean displayInBold) { myDisplayInBold = displayInBold; } + + @Nullable + public Icon getIcon(boolean expanded) { + // thanks to invokeLater() in TreeUtil.showAndSelect(), we can get calls to getIcon() after the tree has been disposed + final NamedConfigurable configurable = getConfigurable(); + if (configurable != null) { + return configurable.getIcon(expanded); + } + return null; + } } @SuppressWarnings({"ConstantConditions"}) diff --git a/platform/platform-api/src/com/intellij/ui/ColoredTreeCellRenderer.java b/platform/platform-api/src/com/intellij/ui/ColoredTreeCellRenderer.java index 9a7a047a812b..98757ca2ff8f 100644 --- a/platform/platform-api/src/com/intellij/ui/ColoredTreeCellRenderer.java +++ b/platform/platform-api/src/com/intellij/ui/ColoredTreeCellRenderer.java @@ -56,7 +56,7 @@ public abstract class ColoredTreeCellRenderer extends SimpleColoredComponent imp clear(); mySelected = selected; - myFocused = tree.hasFocus(); + myFocused = isFocused(); // We paint background if and only if tree path is selected and tree has focus. // If path is selected and tree is not focused then we just paint focused border. @@ -104,7 +104,7 @@ public abstract class ColoredTreeCellRenderer extends SimpleColoredComponent imp super.setIconOpaque(false); } else { - super.setOpaque(myOpaque || selected && hasFocus || selected && tree.hasFocus()); // draw selection background even for non-opaque tree + super.setOpaque(myOpaque || selected && hasFocus || selected && isFocused()); // draw selection background even for non-opaque tree } if (tree.getUI() instanceof UIUtil.MacTreeUI) { @@ -121,6 +121,10 @@ public abstract class ColoredTreeCellRenderer extends SimpleColoredComponent imp return this; } + protected boolean isFocused() { + return myTree.hasFocus(); + } + public void setOpaque(boolean isOpaque) { myOpaque = isOpaque; super.setOpaque(isOpaque); diff --git a/platform/platform-api/src/com/intellij/ui/HtmlListCellRenderer.java b/platform/platform-api/src/com/intellij/ui/HtmlListCellRenderer.java index 179e6418c92d..f383b18eb0b6 100644 --- a/platform/platform-api/src/com/intellij/ui/HtmlListCellRenderer.java +++ b/platform/platform-api/src/com/intellij/ui/HtmlListCellRenderer.java @@ -90,6 +90,15 @@ public abstract class HtmlListCellRenderer extends ListCellRendererWrapper } } + public void append(SimpleColoredText text) { + int length = text.getTexts().size(); + for (int i = 0; i < length; i++) { + String fragment = text.getTexts().get(i); + SimpleTextAttributes attributes = text.getAttributes().get(i); + append(fragment, attributes); + } + } + private static void formatStyle(final StringBuilder builder, final SimpleTextAttributes attributes) { final Color fgColor = attributes.getFgColor(); final Color bgColor = attributes.getBgColor(); diff --git a/platform/platform-api/src/com/intellij/ui/RawCommandLineEditor.java b/platform/platform-api/src/com/intellij/ui/RawCommandLineEditor.java index f2af238df062..5b0296ecfd54 100644 --- a/platform/platform-api/src/com/intellij/ui/RawCommandLineEditor.java +++ b/platform/platform-api/src/com/intellij/ui/RawCommandLineEditor.java @@ -15,7 +15,9 @@ */ package com.intellij.ui; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileChooser.FileChooserDescriptor; +import com.intellij.openapi.ui.LabeledComponent; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.util.IconLoader; @@ -27,6 +29,8 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class RawCommandLineEditor extends JPanel { + private static final Logger LOG = Logger.getInstance("#com.intellij.ui.RawCommandLineEditor"); + private final TextFieldWithBrowseButton myTextField; private String myDialogCaption = ""; @@ -34,6 +38,14 @@ public class RawCommandLineEditor extends JPanel { super(new BorderLayout()); myTextField = new TextFieldWithBrowseButton(new ActionListener() { public void actionPerformed(ActionEvent e) { + if (myDialogCaption == null) { + Container parent = getParent(); + if (parent instanceof LabeledComponent) { + parent = parent.getParent(); + } + LOG.error("Did not call RawCommandLineEditor.setDialogCaption() in " + parent); + myDialogCaption = "Parameters"; + } Messages.showTextAreaDialog(myTextField.getTextField(), myDialogCaption, "EditParametersPopupWindow"); } }); diff --git a/platform/platform-api/src/com/intellij/ui/SimpleColoredText.java b/platform/platform-api/src/com/intellij/ui/SimpleColoredText.java index d568198030c0..a267e62a87d8 100644 --- a/platform/platform-api/src/com/intellij/ui/SimpleColoredText.java +++ b/platform/platform-api/src/com/intellij/ui/SimpleColoredText.java @@ -81,4 +81,15 @@ public class SimpleColoredText { public ArrayList getAttributes() { return myAttributes; } + + public SimpleColoredText derive(SimpleTextAttributes attributes, boolean override) { + SimpleColoredText result = new SimpleColoredText(); + for (int i = 0; i < myTexts.size(); i++) { + SimpleTextAttributes overridden = override + ? SimpleTextAttributes.merge(myAttributes.get(i), attributes) + : SimpleTextAttributes.merge(attributes, myAttributes.get(i)); + result.append(myTexts.get(i), overridden); + } + return result; + } } diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java index 45c466bf66e9..53ec6dcc98ab 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/JBTabsImpl.java @@ -337,7 +337,9 @@ public class JBTabsImpl extends JComponent BufferedImage img; if (cmp.isShowing()) { - img = new BufferedImage(cmp.getWidth(), cmp.getHeight(), BufferedImage.TYPE_INT_ARGB); + final int width = cmp.getWidth(); + final int height = cmp.getHeight(); + img = new BufferedImage(width > 0 ? width : 500, height > 0 ? height : 500, BufferedImage.TYPE_INT_ARGB); Graphics2D g = img.createGraphics(); cmp.paint(g); } else { diff --git a/platform/platform-impl/src/com/intellij/notification/EventLog.java b/platform/platform-impl/src/com/intellij/notification/EventLog.java index 2ac011e9a5b9..89e8189e62e0 100644 --- a/platform/platform-impl/src/com/intellij/notification/EventLog.java +++ b/platform/platform-impl/src/com/intellij/notification/EventLog.java @@ -36,6 +36,7 @@ import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; +import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.ui.SimpleToolWindowPanel; import com.intellij.openapi.ui.popup.Balloon; import com.intellij.openapi.util.*; @@ -403,7 +404,7 @@ public class EventLog implements Notifications { myProjectModel.addNotification(notification); - ApplicationManager.getApplication().invokeLater(new Runnable() { + StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new Runnable() { @Override public void run() { if (!ShutDownTracker.isShutdownHookRunning() && !myProject.isDisposed()) { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/highlighter/EditorHighlighterFactoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/highlighter/EditorHighlighterFactoryImpl.java index e53742e1e2f4..4c32efd347a6 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/highlighter/EditorHighlighterFactoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/highlighter/EditorHighlighterFactoryImpl.java @@ -16,6 +16,7 @@ package com.intellij.openapi.editor.highlighter; import com.intellij.lang.Language; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.ex.util.LexerEditorHighlighter; @@ -32,6 +33,8 @@ import org.jetbrains.annotations.Nullable; * @author yole */ public class EditorHighlighterFactoryImpl extends EditorHighlighterFactory { + private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.highlighter.EditorHighlighterFactoryImpl"); + @Override public EditorHighlighter createEditorHighlighter(SyntaxHighlighter highlighter, final EditorColorsScheme colors) { if (highlighter == null) highlighter = new PlainSyntaxHighlighter(); @@ -74,8 +77,16 @@ public class EditorHighlighterFactoryImpl extends EditorHighlighterFactory { final ContentBasedFileSubstitutor[] processors = Extensions.getExtensions(ContentBasedFileSubstitutor.EP_NAME); SyntaxHighlighter highlighter = null; for (ContentBasedFileSubstitutor processor : processors) { - if (processor.isApplicable(project, vFile) && processor instanceof ContentBasedClassFileProcessor) { - highlighter = ((ContentBasedClassFileProcessor) processor).createHighlighter(project, vFile); + boolean applicable; + try { + applicable = processor.isApplicable(project, vFile); + } + catch (Exception e) { + LOG.error(e); + continue; + } + if (applicable && processor instanceof ContentBasedClassFileProcessor) { + highlighter = ((ContentBasedClassFileProcessor)processor).createHighlighter(project, vFile); } } if (highlighter == null) { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index fb2f5a32ae26..b44e8d539581 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -5803,7 +5803,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi if (last != null && !(last instanceof EditorComponentImpl)) return; final Editor editor = getEditor(source); - if (action == MOVE && !editor.isViewer()) { + if (action == MOVE && !editor.isViewer() && myDraggedRange != null) { if (!FileDocumentManager.getInstance().requestWriting(editor.getDocument(), editor.getProject())) { return; } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java index fb4c6759109a..e56a68e89719 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java @@ -1647,6 +1647,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec if (newFilePair == null) continue; VirtualFile newFile = newFilePair.first; + if (newFile == null) continue; // already open if (eachWindow.findFileIndex(newFile) != -1) continue; diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/CheckForUpdateAction.java b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/CheckForUpdateAction.java index 719dd5276cd4..ce5ad9dacc10 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/CheckForUpdateAction.java +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/CheckForUpdateAction.java @@ -50,6 +50,7 @@ public class CheckForUpdateAction extends AnAction implements DumbAware { ProgressManager.getInstance().run(new Task.Modal(project, "Checking for updates", false) { @Override public void run(@NotNull ProgressIndicator indicator) { + indicator.setIndeterminate(true); final CheckForUpdateResult result = UpdateChecker.checkForUpdates(instance, PropertiesComponent.getInstance(), true ); diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerImpl.java index c1b580603cbd..fa98c84c896e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerImpl.java @@ -219,11 +219,13 @@ public class VirtualFilePointerImpl extends UserDataHolderBase implements Virtua disposed = true; final Pair pair = myFileAndUrl; - if (pair.first != null) { - ((VirtualFilePointerManagerImpl)VirtualFilePointerManager.getInstance()).clearPointerCaches(pair.first.getUrl(), myListener); + VirtualFile file = pair.first; + if (file != null) { + ((VirtualFilePointerManagerImpl)VirtualFilePointerManager.getInstance()).clearPointerCaches(file.getUrl(), myListener); } - if (pair.second != null) { - ((VirtualFilePointerManagerImpl)VirtualFilePointerManager.getInstance()).clearPointerCaches(pair.second, myListener); + String url = pair.second; + if (url != null && (file == null || !url.equals(file.getUrl()))) { + ((VirtualFilePointerManagerImpl)VirtualFilePointerManager.getInstance()).clearPointerCaches(url, myListener); } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerManagerImpl.java index 15ec1b998201..6b9ac8d5ab93 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/VirtualFilePointerManagerImpl.java @@ -72,7 +72,6 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp synchronized void clearPointerCaches(String url, VirtualFilePointerListener listener) { TreeMap urlToPointer = myUrlToPointerMaps.get(listener); - if (urlToPointer == null && ApplicationManager.getApplication().isUnitTestMode()) return; assert urlToPointer != null; urlToPointer.remove(VfsUtil.urlToPath(url)); if (urlToPointer.isEmpty()) { @@ -148,12 +147,6 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp return url.substring(separatorIndex + JarFileSystem.JAR_SEPARATOR.length()); } - @TestOnly - public synchronized void cleanupForNextTest() { - myUrlToPointerMaps.clear(); - myContainers.clear(); - } - /** * @see #create(String, com.intellij.openapi.Disposable, com.intellij.openapi.vfs.pointers.VirtualFilePointerListener) */ @@ -339,7 +332,7 @@ public class VirtualFilePointerManagerImpl extends VirtualFilePointerManager imp VirtualFilePointerListener listener = entry.getKey(); TreeMap map = entry.getValue(); for (VirtualFilePointerImpl pointer : map.values()) { - myUrlToPointerMaps.clear(); + //myUrlToPointerMaps.clear(); pointer.throwNotDisposedError("Not disposed pointer: listener="+listener); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeader.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeader.java index cc38413cf978..00988b058f35 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeader.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeader.java @@ -24,6 +24,7 @@ import com.intellij.openapi.actionSystem.impl.MenuItemPresentationFactory; import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.util.IconLoader; +import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowAnchor; import com.intellij.openapi.wm.ToolWindowType; @@ -430,7 +431,7 @@ public abstract class ToolWindowHeader extends JPanel implements Disposable { } }; - myButton.setHoveringEnabled(true); + myButton.setHoveringEnabled(!SystemInfo.isMac); setContent(myButton); setOpaque(false); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/PositionPanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/PositionPanel.java index cc6e397f8e86..e1f9760bb1f8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/PositionPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/PositionPanel.java @@ -128,8 +128,11 @@ public class PositionPanel extends EditorBasedWidget implements StatusBarWidget. public void caretPositionChanged(final CaretEvent e) { final Editor editor = e.getEditor(); Project project = editor.getProject(); - if (project != null && !project.isDisposed() && FileEditorManager.getInstance(project).getSelectedTextEditor() == e.getEditor()) { - updatePosition(editor); + if (project != null && !project.isDisposed()) { + final FileEditorManager fileEditorManager = FileEditorManager.getInstance(project); // returns null for default project + if (fileEditorManager != null && fileEditorManager.getSelectedTextEditor() == e.getEditor()) { + updatePosition(editor); + } } } diff --git a/platform/platform-impl/src/com/intellij/platform/NewDirectoryProjectDialog.java b/platform/platform-impl/src/com/intellij/platform/NewDirectoryProjectDialog.java index b40e9ca29530..d902cbe95204 100644 --- a/platform/platform-impl/src/com/intellij/platform/NewDirectoryProjectDialog.java +++ b/platform/platform-impl/src/com/intellij/platform/NewDirectoryProjectDialog.java @@ -103,6 +103,11 @@ public class NewDirectoryProjectDialog extends DialogWrapper { setErrorText("Project name can't be empty"); return; } + if (myLocationField.getText().indexOf('$') >= 0) { + setOKActionEnabled(false); + setErrorText("Project directory name must not contain the $ character"); + return; + } DirectoryProjectGenerator generator = getProjectGenerator(); if (generator != null) { String baseDirPath = myLocationField.getTextField().getText(); diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VirtualFilePointerTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VirtualFilePointerTest.java index 116a6c59557c..ec9f7b074671 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VirtualFilePointerTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VirtualFilePointerTest.java @@ -424,6 +424,39 @@ public class VirtualFilePointerTest extends PlatformLangTestCase { }); } + public void testDoubleDispose() throws IOException { + final File tempDir = createTempDirectory(); + final File file = new File(tempDir, "f1"); + boolean created = file.createNewFile(); + assertTrue(created); + + final VirtualFile[] vFile = new VirtualFile[1]; + final String url = VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, file.getCanonicalPath().replace(File.separatorChar, '/')); + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + vFile[0] = VirtualFileManager.getInstance().refreshAndFindFileByUrl(url); + } + }); + + Disposable disposable = Disposer.newDisposable(); + final VirtualFilePointer pointer = myVirtualFilePointerManager.create(vFile[0], disposable, new VirtualFilePointerListener() { + @Override + public void beforeValidityChanged(VirtualFilePointer[] pointers) { + } + + @Override + public void validityChanged(VirtualFilePointer[] pointers) { + } + }); + + + assertTrue(pointer.isValid()); + + Disposer.dispose(disposable); + assertFalse(pointer.isValid()); + } + public void testThreads() throws IOException, InterruptedException { final File ioTempDir = createTempDirectory(); final File ioPtrBase = new File(ioTempDir, "parent"); diff --git a/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java b/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java index 2a853d4ea32f..6eaef6b87234 100644 --- a/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java @@ -53,11 +53,9 @@ import com.intellij.openapi.util.EmptyRunnable; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.impl.VirtualFilePointerManagerImpl; import com.intellij.openapi.vfs.impl.local.LocalFileSystemImpl; import com.intellij.openapi.vfs.newvfs.impl.VirtualDirectoryImpl; import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS; -import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; @@ -339,10 +337,6 @@ public abstract class PlatformTestCase extends UsefulTestCase implements DataPro LocalHistoryImpl.getInstanceImpl().cleanupForNextTest(); - VirtualFilePointerManagerImpl virtualFilePointerManager = (VirtualFilePointerManagerImpl)VirtualFilePointerManager.getInstance(); - if (virtualFilePointerManager != null) { - virtualFilePointerManager.cleanupForNextTest(); - } PatchedWeakReference.clearAll(); } diff --git a/platform/util/src/com/intellij/openapi/util/Comparing.java b/platform/util/src/com/intellij/openapi/util/Comparing.java index 95f5f8ab166a..4860c7ab53f7 100644 --- a/platform/util/src/com/intellij/openapi/util/Comparing.java +++ b/platform/util/src/com/intellij/openapi/util/Comparing.java @@ -140,6 +140,10 @@ public class Comparing { return o1 < o2 ? -1 : o1 == o2 ? 0 : 1; } + public static int compare(boolean o1, boolean o2) { + return o1 == o2 ? 0 : o1 ? 1 : -1; + } + public static int compare(int o1, int o2) { return o1 < o2 ? -1 : o1 == o2 ? 0 : 1; } diff --git a/plugins/android/resources/messages/AndroidBundle.properties b/plugins/android/resources/messages/AndroidBundle.properties index ba66305c6f68..a5203a77da24 100644 --- a/plugins/android/resources/messages/AndroidBundle.properties +++ b/plugins/android/resources/messages/AndroidBundle.properties @@ -36,7 +36,8 @@ activity.class.not.specified.error=Activity class not specified not.activity.subclass.error={0} is not an Activity subclass no.facet.error=No Android facet found for {0} android.logcat.tab.title=Logcat -android.logcat.title=Android Logcat +android.adb.logs.tab.title=ADB logs +android.logcat.title=Android android.logcat.error.dialog.title=Android Logcat Error android.logcat.color.page.name=Android Logcat warning.level.title=Warning diff --git a/plugins/android/src/META-INF/plugin.xml b/plugins/android/src/META-INF/plugin.xml index 1cfc42b1a4fd..008774bdaad1 100644 --- a/plugins/android/src/META-INF/plugin.xml +++ b/plugins/android/src/META-INF/plugin.xml @@ -181,7 +181,7 @@ - { return ApplicationManager.getApplication().runReadAction(new Computable() { @Nullable public PsiClass compute() { - return c.getSuperClass(); + return c.isValid() ? c.getSuperClass() : null; } }); } diff --git a/plugins/android/src/org/jetbrains/android/dom/manifest/ManifestDomFileDescription.java b/plugins/android/src/org/jetbrains/android/dom/manifest/ManifestDomFileDescription.java index acea0d6bb5f5..641838ee575c 100644 --- a/plugins/android/src/org/jetbrains/android/dom/manifest/ManifestDomFileDescription.java +++ b/plugins/android/src/org/jetbrains/android/dom/manifest/ManifestDomFileDescription.java @@ -42,7 +42,7 @@ public class ManifestDomFileDescription extends DomFileDescription { return false; } final Module module = ModuleUtil.findModuleForPsiElement(file); - return module == null || AndroidFacet.getInstance(module) != null; + return module == null || !module.isDisposed() && AndroidFacet.getInstance(module) != null; } protected void initializeFileDescription() { diff --git a/plugins/android/src/org/jetbrains/android/logcat/AdbErrors.java b/plugins/android/src/org/jetbrains/android/logcat/AdbErrors.java new file mode 100644 index 000000000000..4fd6125e134d --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/logcat/AdbErrors.java @@ -0,0 +1,37 @@ +package org.jetbrains.android.logcat; + +import com.intellij.util.ArrayUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Eugene.Kudelevsky + */ +public class AdbErrors { + private AdbErrors() { + } + + private static volatile List ourAdbErrorList; + + @NotNull + public static synchronized String[] getErrors() { + return ourAdbErrorList != null ? ArrayUtil.toStringArray(ourAdbErrorList) : ArrayUtil.EMPTY_STRING_ARRAY; + } + + public static synchronized void clear() { + if (ourAdbErrorList != null) { + ourAdbErrorList.clear(); + } + } + + public static synchronized void reportError(@NotNull String message, @Nullable String tag) { + final String fullMessage = tag != null ? tag + ": " + message : message; + if (ourAdbErrorList == null) { + ourAdbErrorList = new ArrayList(); + } + ourAdbErrorList.add(fullMessage); + } +} diff --git a/plugins/android/src/org/jetbrains/android/logcat/AndroidLogcatToolWindowFactory.java b/plugins/android/src/org/jetbrains/android/logcat/AndroidLogcatToolWindowFactory.java index 82f5a22cdd84..389f0f05b1a6 100644 --- a/plugins/android/src/org/jetbrains/android/logcat/AndroidLogcatToolWindowFactory.java +++ b/plugins/android/src/org/jetbrains/android/logcat/AndroidLogcatToolWindowFactory.java @@ -16,8 +16,10 @@ package org.jetbrains.android.logcat; +import com.android.ddmlib.Log; import com.intellij.ProjectTopics; import com.intellij.execution.filters.HyperlinkInfo; +import com.intellij.execution.impl.ConsoleViewImpl; import com.intellij.execution.ui.ConsoleView; import com.intellij.execution.ui.ConsoleViewContentType; import com.intellij.facet.ProjectFacetManager; @@ -34,6 +36,7 @@ import com.intellij.openapi.wm.ex.ToolWindowManagerAdapter; import com.intellij.openapi.wm.ex.ToolWindowManagerEx; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentManager; +import com.intellij.ui.content.impl.ContentImpl; import com.intellij.util.messages.MessageBusConnection; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.maven.AndroidMavenUtil; @@ -90,12 +93,34 @@ public class AndroidLogcatToolWindowFactory implements ToolWindowFactory { JPanel contentPanel = view.getContentPanel(); final ContentManager contentManager = toolWindow.getContentManager(); - final Content content = contentManager.getFactory().createContent(contentPanel, null, false); - content.setDisposer(view); - content.setCloseable(false); - content.setPreferredFocusableComponent(contentPanel); - contentManager.addContent(content); - contentManager.setSelectedContent(content, true); + + final Content logcatContent = + contentManager.getFactory().createContent(contentPanel, AndroidBundle.message("android.logcat.tab.title"), false); + logcatContent.setDisposer(view); + logcatContent.setCloseable(false); + logcatContent.setPreferredFocusableComponent(contentPanel); + contentManager.addContent(logcatContent); + contentManager.setSelectedContent(logcatContent, true); + + final ConsoleView console = new ConsoleViewImpl(project, false); + final Content adbLogsContent = new ContentImpl(console.getComponent(), AndroidBundle.message("android.adb.logs.tab.title"), false); + adbLogsContent.setCloseable(false); + contentManager.addContent(adbLogsContent); + + //noinspection UnnecessaryFullyQualifiedName + com.android.ddmlib.Log.setLogOutput(new Log.ILogOutput() { + @Override + public void printLog(Log.LogLevel logLevel, String tag, String message) { + reportAdbLogMessage(logLevel, tag, message, console); + } + + @Override + public void printAndPromptLog(Log.LogLevel logLevel, String tag, String message) { + // todo: should we show dialog? + reportAdbLogMessage(logLevel, tag, message, console); + } + }); + ApplicationManager.getApplication().invokeLater(new Runnable() { public void run() { view.activate(); @@ -107,6 +132,48 @@ public class AndroidLogcatToolWindowFactory implements ToolWindowFactory { }); } + private static void reportAdbLogMessage(Log.LogLevel logLevel, String tag, String message, @NotNull ConsoleView consoleView) { + if (message == null) { + return; + } + if (logLevel == null) { + logLevel = Log.LogLevel.INFO; + } + + if (logLevel == Log.LogLevel.ERROR || logLevel == Log.LogLevel.ASSERT) { + AdbErrors.reportError(message, tag); + } + + final ConsoleViewContentType contentType = toConsoleViewContentType(logLevel); + if (contentType == null) { + return; + } + + final String fullMessage = tag != null ? tag + ": " + message : message; + consoleView.print(fullMessage + '\n', contentType); + } + + @Nullable + private static ConsoleViewContentType toConsoleViewContentType(@NotNull Log.LogLevel logLevel) { + switch (logLevel) { + case VERBOSE: + return null; + case DEBUG: + return null; + case INFO: + return ConsoleViewContentType.getConsoleViewType(AndroidLogcatConstants.INFO); + case WARN: + return ConsoleViewContentType.getConsoleViewType(AndroidLogcatConstants.WARNING); + case ERROR: + return ConsoleViewContentType.getConsoleViewType(AndroidLogcatConstants.ERROR); + case ASSERT: + return ConsoleViewContentType.getConsoleViewType(AndroidLogcatConstants.ASSERT); + default: + assert false : "Unknown log level " + logLevel; + } + return null; + } + private static void checkFacetAndSdk(Project project, AndroidLogcatToolWindowView view) { final List facets = ProjectFacetManager.getInstance(project).getFacets(AndroidFacet.ID); final ConsoleView console = view.getLogConsole().getConsole(); diff --git a/plugins/android/src/org/jetbrains/android/sdk/AndroidSdkData.java b/plugins/android/src/org/jetbrains/android/sdk/AndroidSdkData.java index c63650b914fb..616a78281a17 100644 --- a/plugins/android/src/org/jetbrains/android/sdk/AndroidSdkData.java +++ b/plugins/android/src/org/jetbrains/android/sdk/AndroidSdkData.java @@ -18,6 +18,7 @@ package org.jetbrains.android.sdk; import com.android.ddmlib.AndroidDebugBridge; import com.android.ddmlib.DdmPreferences; +import com.android.ddmlib.Log; import com.android.sdklib.*; import com.android.sdklib.internal.project.ProjectProperties; import com.intellij.CommonBundle; @@ -33,6 +34,7 @@ import com.intellij.reference.SoftReference; import com.intellij.util.containers.HashMap; import gnu.trove.TIntObjectHashMap; import org.jetbrains.android.actions.AndroidEnableAdbServiceAction; +import org.jetbrains.android.logcat.AdbErrors; import org.jetbrains.android.util.AndroidCommonUtils; import org.jetbrains.android.util.AndroidUtils; import org.jetbrains.android.util.BufferingFileWrapper; @@ -182,12 +184,15 @@ public class AndroidSdkData { return getLocation().hashCode(); } + @SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod") private boolean initializeDdmlib(@NotNull Project project) { ApplicationManager.getApplication().assertIsDispatchThread(); while (true) { final MyInitializeDdmlibTask task = new MyInitializeDdmlibTask(project); + AdbErrors.clear(); + Thread t = new Thread(new Runnable() { @Override public void run() { @@ -205,25 +210,33 @@ public class AndroidSdkData { boolean finished = task.isFinished(); - //noinspection AssignmentToStaticFieldFromInstanceMethod - myAdbCrashed = !finished; - if (task.isCanceled()) { + myAdbCrashed = !finished; forceInterrupt(t); return false; } - if (!finished) { - int result = Messages - .showOkCancelDialog(project, - "ADB not responding. Please, kill \"" + SdkConstants.FN_ADB + "\" process manually and click 'Retry'", - CommonBundle.getErrorTitle(), "&Retry", "&Cancel", Messages.getErrorIcon()); + myAdbCrashed = false; - if (result == 1) { + if (!finished) { + final String adbErrorString = combine(AdbErrors.getErrors()); + final int result = Messages.showDialog(project, "ADB not responding. You can wait more, or kill \"" + + SdkConstants.FN_ADB + + "\" process manually and click 'Restart'" + + (adbErrorString.length() > 0 ? "\nErrors from ADB:\n" + adbErrorString : ""), + CommonBundle.getErrorTitle(), new String[]{"&Wait more", "&Restart", "&Cancel"}, 0, + Messages.getErrorIcon()); + if (result == 2) { + // cancel + myAdbCrashed = true; forceInterrupt(t); return false; } - retryWas = true; + else if (result == 1) { + // restart + myAdbCrashed = true; + retryWas = true; + } } } @@ -236,6 +249,19 @@ public class AndroidSdkData { return true; } + @NotNull + private static String combine(@NotNull String[] strs) { + final StringBuilder builder = new StringBuilder(); + + for (String str : strs) { + if (builder.length() > 0) { + builder.append('\n'); + } + builder.append(str); + } + return builder.toString(); + } + @SuppressWarnings({"BusyWait"}) private static void forceInterrupt(Thread thread) { /* @@ -262,6 +288,7 @@ public class AndroidSdkData { synchronized (myDdmsLock) { if (!myDdmLibInitialized) { myDdmLibInitialized = true; + DdmPreferences.setLogLevel(Log.LogLevel.INFO.getStringValue()); DdmPreferences.setTimeOut(AndroidUtils.TIMEOUT); AndroidDebugBridge.init(AndroidEnableAdbServiceAction.isAdbServiceEnabled()); LOG.info("DDMLib initialized"); diff --git a/plugins/generate-tostring/src/org/jetbrains/generate/tostring/GenerateToStringUtils.java b/plugins/generate-tostring/src/org/jetbrains/generate/tostring/GenerateToStringUtils.java index e582eb2c336c..c74798537cb2 100644 --- a/plugins/generate-tostring/src/org/jetbrains/generate/tostring/GenerateToStringUtils.java +++ b/plugins/generate-tostring/src/org/jetbrains/generate/tostring/GenerateToStringUtils.java @@ -18,7 +18,6 @@ package org.jetbrains.generate.tostring; import com.intellij.codeInsight.generation.PsiElementClassMember; import com.intellij.codeInsight.generation.PsiFieldMember; import com.intellij.codeInsight.generation.PsiMethodMember; -import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; @@ -31,7 +30,6 @@ import org.jetbrains.generate.tostring.exception.GenerateCodeException; import org.jetbrains.generate.tostring.exception.PluginException; import org.jetbrains.generate.tostring.psi.PsiAdapter; -import java.io.File; import java.util.ArrayList; import java.util.List; @@ -142,9 +140,8 @@ public class GenerateToStringUtils { * @param e the caused exception. * @throws RuntimeException is thrown for severe exceptions */ - public static void handleExeption(Project project, Exception e) throws RuntimeException { - e.printStackTrace(); // must print stacktrace to see caused in IDEA log / console - log.error(e); + public static void handleException(Project project, Exception e) throws RuntimeException { + log.info(e); if (e instanceof GenerateCodeException) { // code generation error - display velocity errror in error dialog so user can identify problem quicker diff --git a/plugins/generate-tostring/src/org/jetbrains/generate/tostring/GenerateToStringWorker.java b/plugins/generate-tostring/src/org/jetbrains/generate/tostring/GenerateToStringWorker.java index 0eb91c0fd2a8..d2dd14f394ef 100644 --- a/plugins/generate-tostring/src/org/jetbrains/generate/tostring/GenerateToStringWorker.java +++ b/plugins/generate-tostring/src/org/jetbrains/generate/tostring/GenerateToStringWorker.java @@ -382,7 +382,7 @@ public class GenerateToStringWorker { new GenerateToStringWorker(clazz, editor, insertAtOverride).execute(selectedMemebers, template); } catch (Exception e) { - GenerateToStringUtils.handleExeption(clazz.getProject(), e); + GenerateToStringUtils.handleException(clazz.getProject(), e); } } }); diff --git a/plugins/git4idea/src/git4idea/actions/GitCompareWithBranchAction.java b/plugins/git4idea/src/git4idea/actions/GitCompareWithBranchAction.java index 45c32dbddf20..925cd756b38d 100644 --- a/plugins/git4idea/src/git4idea/actions/GitCompareWithBranchAction.java +++ b/plugins/git4idea/src/git4idea/actions/GitCompareWithBranchAction.java @@ -142,7 +142,11 @@ public class GitCompareWithBranchAction extends DumbAwareAction { if (repository == null || repository.isFresh() || noBranchesToCompare(repository)) { presentation.setEnabled(false); presentation.setVisible(true); + return; } + + presentation.setEnabled(true); + presentation.setVisible(true); } private static boolean noBranchesToCompare(@NotNull GitRepository repository) { diff --git a/plugins/git4idea/src/git4idea/actions/GitInit.java b/plugins/git4idea/src/git4idea/actions/GitInit.java index 78291cd44878..43aa23976177 100644 --- a/plugins/git4idea/src/git4idea/actions/GitInit.java +++ b/plugins/git4idea/src/git4idea/actions/GitInit.java @@ -15,6 +15,7 @@ */ package git4idea.actions; +import com.intellij.notification.NotificationType; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.components.ServiceManager; @@ -30,12 +31,12 @@ import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.ProjectLevelVcsManager; import com.intellij.openapi.vcs.VcsDirectoryMapping; -import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.vcsUtil.VcsFileUtil; import git4idea.GitUtil; import git4idea.GitVcs; import git4idea.commands.Git; +import git4idea.commands.GitCommandResult; import git4idea.i18n.GitBundle; import git4idea.util.GitUIUtil; import org.jetbrains.annotations.NotNull; @@ -78,13 +79,13 @@ public class GitInit extends DumbAwareAction { } } - GitVcs vcs = GitVcs.getInstance(project); Git git = ServiceManager.getService(Git.class); - try { - git.init(project, root); - } catch (VcsException ex) { - if (vcs == null || vcs.getExecutableValidator().isExecutableValid()) { - GitUIUtil.showOperationErrors(project, Collections.singleton(ex), "git init"); + GitVcs vcs = GitVcs.getInstance(project); + GitCommandResult result = git.init(project, root); + if (!result.success()) { + if (vcs != null && vcs.getExecutableValidator().isExecutableValid()) { + GitUIUtil.notify(GitVcs.IMPORTANT_ERROR_NOTIFICATION, project, "Git init failed", result.getErrorOutputAsHtmlString(), + NotificationType.ERROR, null); } return; } diff --git a/plugins/git4idea/src/git4idea/commands/Git.java b/plugins/git4idea/src/git4idea/commands/Git.java index b0e303723376..a4dfbb727e37 100644 --- a/plugins/git4idea/src/git4idea/commands/Git.java +++ b/plugins/git4idea/src/git4idea/commands/Git.java @@ -33,7 +33,7 @@ import java.util.Set; */ public interface Git { - void init(Project project, VirtualFile root) throws VcsException; + GitCommandResult init(@NotNull Project project, @NotNull VirtualFile root, @NotNull GitLineHandlerListener... listeners); @NotNull Set untrackedFiles(@NotNull Project project, diff --git a/plugins/git4idea/src/git4idea/commands/GitImpl.java b/plugins/git4idea/src/git4idea/commands/GitImpl.java index 56dfb31eb1d6..f610113b1cbe 100644 --- a/plugins/git4idea/src/git4idea/commands/GitImpl.java +++ b/plugins/git4idea/src/git4idea/commands/GitImpl.java @@ -50,17 +50,15 @@ public class GitImpl implements Git { /** * Calls 'git init' on the specified directory. - * // TODO use common format */ @Override - public void init(Project project, VirtualFile root) throws VcsException { - GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.INIT); - h.setSilent(false); - h.setNoSSH(true); - h.run(); - if (!h.errors().isEmpty()) { - throw h.errors().get(0); + public GitCommandResult init(@NotNull Project project, @NotNull VirtualFile root, @NotNull GitLineHandlerListener... listeners) { + GitLineHandler h = new GitLineHandler(project, root, GitCommand.INIT); + for (GitLineHandlerListener listener : listeners) { + h.addLineListener(listener); } + h.setSilent(false); + return run(h); } /** diff --git a/plugins/git4idea/src/git4idea/history/browser/CherryPicker.java b/plugins/git4idea/src/git4idea/history/browser/CherryPicker.java index c2a2213a141d..23decfea9335 100644 --- a/plugins/git4idea/src/git4idea/history/browser/CherryPicker.java +++ b/plugins/git4idea/src/git4idea/history/browser/CherryPicker.java @@ -103,11 +103,11 @@ public class CherryPicker { public void run() { moveToCorrectLists(); } - }, InvokeAfterUpdateMode.SYNCHRONOUS_CANCELLABLE, "", new Consumer() { + }, InvokeAfterUpdateMode.SILENT, "", new Consumer() { public void consume(VcsDirtyScopeManager vcsDirtyScopeManager) { vcsDirtyScopeManager.filePathsDirty(getAllChangedFiles(), null); } - }, ModalityState.defaultModalityState()); + }, ModalityState.NON_MODAL); } private void showResults() { diff --git a/plugins/git4idea/src/git4idea/roots/GitIntegrationEnabler.java b/plugins/git4idea/src/git4idea/roots/GitIntegrationEnabler.java index ae1765dc4aaa..145ea231095a 100644 --- a/plugins/git4idea/src/git4idea/roots/GitIntegrationEnabler.java +++ b/plugins/git4idea/src/git4idea/roots/GitIntegrationEnabler.java @@ -20,13 +20,13 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.AbstractVcs; import com.intellij.openapi.vcs.ProjectLevelVcsManager; import com.intellij.openapi.vcs.VcsDirectoryMapping; -import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ui.UIUtil; import git4idea.Notificator; import git4idea.PlatformFacade; import git4idea.commands.Git; +import git4idea.commands.GitCommandResult; import org.jetbrains.annotations.NotNull; import java.util.*; @@ -77,15 +77,15 @@ public class GitIntegrationEnabler { } private boolean gitInitOrNotifyError(@NotNull Notificator notificator, @NotNull final VirtualFile projectDir) { - try { - myGit.init(myProject, projectDir); + GitCommandResult result = myGit.init(myProject, projectDir); + if (result.success()) { refreshGitDir(projectDir); notificator.notifySuccess("", "Created Git repository in \n" + projectDir.getPresentableUrl()); return true; } - catch (VcsException e) { - notificator.notifyError("Couldn't git init " + projectDir.getPresentableUrl(), e.getMessage()); - LOG.error(e); + else { + notificator.notifyError("Couldn't git init " + projectDir.getPresentableUrl(), result.getErrorOutputAsHtmlString()); + LOG.info(result.getErrorOutputAsHtmlString()); return false; } } diff --git a/plugins/git4idea/tests/git4idea/test/MockGit.groovy b/plugins/git4idea/tests/git4idea/test/MockGit.groovy index a90770008384..fb6497932119 100644 --- a/plugins/git4idea/tests/git4idea/test/MockGit.groovy +++ b/plugins/git4idea/tests/git4idea/test/MockGit.groovy @@ -22,6 +22,7 @@ import git4idea.commands.GitCommandResult import git4idea.commands.GitLineHandlerListener import git4idea.push.GitPushSpec import git4idea.repo.GitRepository +import org.jetbrains.annotations.NotNull /** * @@ -30,8 +31,9 @@ import git4idea.repo.GitRepository class MockGit implements Git { @Override - void init(Project project, VirtualFile root) { + GitCommandResult init(@NotNull Project project, @NotNull VirtualFile root, @NotNull GitLineHandlerListener... listeners) { new File(root.path, ".git").mkdir() + new GitCommandResult(true, 0, Collections.emptyList(), Collections.emptyList()) } @Override diff --git a/plugins/github/src/org/jetbrains/plugins/github/GithubShareAction.java b/plugins/github/src/org/jetbrains/plugins/github/GithubShareAction.java index 1903933b61e2..d306f0b6e297 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/GithubShareAction.java +++ b/plugins/github/src/org/jetbrains/plugins/github/GithubShareAction.java @@ -7,6 +7,7 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ex.ApplicationInfoEx; +import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; @@ -17,12 +18,10 @@ import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.Ref; import com.intellij.openapi.vcs.VcsException; -import com.intellij.openapi.vcs.VcsOutgoingChangesProvider; import com.intellij.openapi.vcs.changes.ChangeListManager; import com.intellij.openapi.vcs.changes.ChangeListManagerImpl; import com.intellij.openapi.vcs.changes.InvokeAfterUpdateMode; import com.intellij.openapi.vcs.changes.actions.RefreshAction; -import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.concurrency.Semaphore; @@ -32,11 +31,13 @@ import git4idea.GitUtil; import git4idea.GitVcs; import git4idea.actions.BasicAction; import git4idea.actions.GitInit; -import git4idea.push.GitPushUtils; import git4idea.commands.*; import git4idea.i18n.GitBundle; -import git4idea.util.GitUIUtil; +import git4idea.push.GitPushUtils; +import git4idea.repo.GitRepository; +import git4idea.repo.GitRepositoryManager; import git4idea.util.GitFileUtils; +import git4idea.util.GitUIUtil; import org.jetbrains.plugins.github.ui.GithubShareDialog; import java.io.IOException; @@ -129,12 +130,13 @@ public class GithubShareAction extends DumbAwareAction { Messages.showErrorDialog(e1.getMessage(), "Failed to create new GitHub repository"); return; } - bindToGithub(project, root, gitDetected, settings.getLogin(), name); - Notifications.Bus.notify(new Notification("github", "Success", "Successfully created project ''" + name + "'' on github", - NotificationType.INFORMATION)); + if (bindToGithub(project, root, gitDetected, settings.getLogin(), name)) { + Notifications.Bus.notify(new Notification("github", "Success", "Successfully created project ''" + name + "'' on github", + NotificationType.INFORMATION)); + } } - private void bindToGithub(final Project project, final VirtualFile root, final boolean gitDetected, final String login, final String name) { + private boolean bindToGithub(final Project project, final VirtualFile root, final boolean gitDetected, final String login, String name) { LOG.info("Binding local project with GitHub"); // creating empty git repo if git isnot initialized if (!gitDetected) { @@ -145,7 +147,7 @@ public class GithubShareAction extends DumbAwareAction { if (!h.errors().isEmpty()) { GitUIUtil.showOperationErrors(project, h.errors(), "git init"); LOG.info("Failed to create empty git repo: " + h.errors()); - return; + return false; } final ProgressManager manager = ProgressManager.getInstance(); manager.runProcessWithProgressSynchronously(new Runnable() { @@ -157,7 +159,9 @@ public class GithubShareAction extends DumbAwareAction { } // In this case we should create sample commit for binding project - performFirstCommitIfRequired(project, root); + if (!performFirstCommitIfRequired(project, root)) { + return false; + } //git remote add origin git@github.com:login/name.git LOG.info("Adding GitHub as a remote host"); @@ -169,13 +173,13 @@ public class GithubShareAction extends DumbAwareAction { addRemoteHandler.run(); if (addRemoteHandler.getExitCode() != 0) { Messages.showErrorDialog("Failed to add GitHub repository as remote", "Failed to add GitHub repository as remote"); - return; + return false; } } catch (VcsException e) { Messages.showErrorDialog(e.getMessage(), "Failed to add GitHub repository as remote"); LOG.info("Failed to add GitHub as remote: " + e.getMessage()); - return; + return false; } //git push origin master @@ -198,6 +202,7 @@ public class GithubShareAction extends DumbAwareAction { } // refresh vcs manually RefreshAction.doRefresh(project); + return true; } private boolean performFirstCommitIfRequired(final Project project, final VirtualFile root) { @@ -206,20 +211,21 @@ public class GithubShareAction extends DumbAwareAction { Messages.showErrorDialog(project, "Cannot find git initialized", "Failed to share"); return false; } - final VcsOutgoingChangesProvider provider = gitVcs.getOutgoingChangesProvider(); - if (provider == null) { - Messages.showErrorDialog(project, "Cannot find git initialized", "Failed to share"); + + GitRepositoryManager repositoryManager = ServiceManager.getService(project, GitRepositoryManager.class); + Git git = ServiceManager.getService(Git.class); + if (repositoryManager == null || git == null) { return false; } - try { - if (!provider.getOutgoingChanges(root, false).getSecond().isEmpty()){ - return true; - } - } - catch (VcsException e) { - Messages.showErrorDialog(project, e.getMessage(), "Failed to share"); + GitRepository repository = repositoryManager.getRepositoryForRoot(root); + if (repository == null) { + Messages.showErrorDialog(project, "Cannot find git repository for root " + root, "Failed to share"); return false; } + if (!repository.isFresh()) { + return true; + } + final Ref exceptionRef = new Ref(); // Creating or modifying readme file LOG.info("Touching file 'README' for initial commit"); diff --git a/plugins/groovy/resources/inspectionDescriptions/GroovyUnusedAssignment.html b/plugins/groovy/resources/inspectionDescriptions/GroovyUnusedAssignment.html index f10ac59f3c47..d093014e3eab 100644 --- a/plugins/groovy/resources/inspectionDescriptions/GroovyUnusedAssignment.html +++ b/plugins/groovy/resources/inspectionDescriptions/GroovyUnusedAssignment.html @@ -2,6 +2,6 @@ This inspection reports on unnecessary Groovy assignment statement -
Powered by InspectorGroovy +
diff --git a/plugins/groovy/resources/inspectionDescriptions/GroovyUnusedIncOrDec.html b/plugins/groovy/resources/inspectionDescriptions/GroovyUnusedIncOrDec.html new file mode 100644 index 000000000000..13ba5a2d65e5 --- /dev/null +++ b/plugins/groovy/resources/inspectionDescriptions/GroovyUnusedIncOrDec.html @@ -0,0 +1,6 @@ + + +This inspection reports on unnecessary Groovy incrementing and decrementing expressions +
+ + diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index f16a34ca5f0c..956a30ddf366 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -408,6 +408,9 @@ + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/actions/DumpGroovyControlFlowAction.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/actions/DumpGroovyControlFlowAction.java index 856b586a76c6..46f6694f41fd 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/actions/DumpGroovyControlFlowAction.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/actions/DumpGroovyControlFlowAction.java @@ -82,6 +82,7 @@ public class DumpGroovyControlFlowAction extends AnAction implements DumbAware { } private static void passInner(GrControlFlowOwner owner) { + System.out.println(owner.getText()); System.out.println(ControlFlowUtils.dumpControlFlow(owner.getControlFlow())); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java index 4858ab7eae7a..3a81c5db8a1d 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java @@ -290,7 +290,8 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator { for (PsiReference reference : ReferencesSearch.search(var, new LocalSearchScope(scope)).findAll()) { if (reference instanceof GrReferenceExpression && (PsiUtil.isLValue((GrReferenceExpression)reference) || - ((GrReferenceExpression)reference).getParent() instanceof GrPostfixExpression)) { + ((GrReferenceExpression)reference).getParent() instanceof GrUnaryExpression && + ((GrUnaryExpression)((GrReferenceExpression)reference).getParent()).isPostfix())) { if (hasAssignment) { return true; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyInspectionBundle.properties b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyInspectionBundle.properties index b650ca7468de..28f439a39a95 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyInspectionBundle.properties +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyInspectionBundle.properties @@ -35,6 +35,8 @@ groovy.dfa.issues=Data Flow Issues unused.assignment=Unused Assignment unused.assignment.tooltip=Assignment is not used +unused.inc.dec=Unused Incrementing or Decrementing + unassigned.access=Variable Not Assigned unassigned.access.short.name=VariableNotAssigned unassigned.access.tooltip=Variable ''{0}'' might not be assigned @@ -77,3 +79,7 @@ rtype.cannot.contain.ltype=''{1}'' cannot contain ''{0}'' new.instance.of.singleton=New instance of class annotated with @groovy.lang.Singleton replace.new.expression.with.0.instance=Replace with ''{0}.instance'' getter.0.clashes.with.getter.1={0} clashes with {1} +unused.0=Unused {0} +remove.0=Remove {0} +replace.postfix.0.with.prefix.0=Replace postfix {0} with prefix {0} +replace.0.with.1=Replace {0} with binary {1} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyLocalInspectionBase.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyLocalInspectionBase.java index d22a439132a4..c46ea77c0c87 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyLocalInspectionBase.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyLocalInspectionBase.java @@ -22,6 +22,7 @@ import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner; import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementVisitor; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrClassInitializer; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; @@ -42,7 +43,6 @@ public abstract class GroovyLocalInspectionBase extends GroovySuppressableInspec return new GroovyPsiElementVisitor(new GroovyElementVisitor() { public void visitClosure(GrClosableBlock closure) { check(closure, problemsHolder); - } public void visitMethod(GrMethod method) { @@ -55,6 +55,11 @@ public abstract class GroovyLocalInspectionBase extends GroovySuppressableInspec public void visitFile(GroovyFileBase file) { check(file, problemsHolder); } + + @Override + public void visitClassInitializer(GrClassInitializer initializer) { + check(initializer.getBlock(), problemsHolder); + } }); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/confusing/GrUnusedIncDecInspection.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/confusing/GrUnusedIncDecInspection.java new file mode 100644 index 000000000000..fadb994f0ec0 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/confusing/GrUnusedIncDecInspection.java @@ -0,0 +1,241 @@ +/* + * 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. + * 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 org.jetbrains.plugins.groovy.codeInspection.confusing; + +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.codeInspection.ProblemHighlightType; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.codeInspection.BaseInspection; +import org.jetbrains.plugins.groovy.codeInspection.BaseInspectionVisitor; +import org.jetbrains.plugins.groovy.codeInspection.GroovyInspectionBundle; +import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; +import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; +import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrUnaryExpression; +import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction; +import org.jetbrains.plugins.groovy.lang.psi.controlFlow.ReadWriteVariableInstruction; +import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; + +import java.util.Iterator; +import java.util.List; + +/** + * @author Max Medvedev + */ +public class GrUnusedIncDecInspection extends BaseInspection { + private static final Logger LOG = Logger.getInstance(GrUnusedIncDecInspection.class); + @Override + protected BaseInspectionVisitor buildVisitor() { + return new GrUnusedIncDecInspectionVisitor(); + } + + @Override + public boolean isEnabledByDefault() { + return true; + } + + @Nls + @NotNull + public String getGroupDisplayName() { + return GroovyInspectionBundle.message("groovy.dfa.issues"); + } + + @Nls + @NotNull + public String getDisplayName() { + return GroovyInspectionBundle.message("unused.inc.dec"); + } + + @NonNls + @NotNull + public String getShortName() { + return "GroovyUnusedIncOrDec"; + } + + private static class GrUnusedIncDecInspectionVisitor extends BaseInspectionVisitor { + @Override + public void visitUnaryExpression(GrUnaryExpression expression) { + super.visitUnaryExpression(expression); + + IElementType opType = expression.getOperationTokenType(); + if (opType != GroovyTokenTypes.mINC && opType != GroovyTokenTypes.mDEC) return; + + GrExpression operand = expression.getOperand(); + if (!(operand instanceof GrReferenceExpression)) return; + + PsiElement resolved = ((GrReferenceExpression)operand).resolve(); + if (!(resolved instanceof GrVariable) || resolved instanceof GrField) return; + + final GrControlFlowOwner owner = ControlFlowUtils.findControlFlowOwner(expression); + assert owner != null; + GrControlFlowOwner ownerOfDeclaration = ControlFlowUtils.findControlFlowOwner(resolved); + if (ownerOfDeclaration != owner) return; + + final Instruction cur = ControlFlowUtils.findInstruction(operand, owner.getControlFlow()); + + if (cur == null) { + LOG.error("no instruction found in flow." + "operand: " + operand.getText() + " cfo: " + owner.getText()); + } + + //get write access for inc or dec + Iterable successors = cur.allSuccessors(); + Iterator iterator = successors.iterator(); + LOG.assertTrue(iterator.hasNext()); + Instruction writeAccess = iterator.next(); + LOG.assertTrue(!iterator.hasNext()); + + List accesses = ControlFlowUtils.findAccess((GrVariable)resolved, true, false, writeAccess); + + boolean allAreWrite = true; + for (ReadWriteVariableInstruction access : accesses) { + if (!access.isWrite()) { + allAreWrite = false; + break; + } + } + + + if (allAreWrite) { + if (expression.isPostfix() && PsiUtil.isExpressionUsed(expression)) { + registerError(expression.getOperationToken(), + GroovyInspectionBundle.message("unused.0", expression.getOperationToken().getText()), + new LocalQuickFix[]{new ReplacePostfixIncWithPrefixFix(expression), new RemoveIncOrDecFix(expression)}, + ProblemHighlightType.LIKE_UNUSED_SYMBOL); + } + else if (!PsiUtil.isExpressionUsed(expression)) { + registerError(expression.getOperationToken(), + GroovyInspectionBundle.message("unused.0", expression.getOperationToken().getText()), LocalQuickFix.EMPTY_ARRAY, + ProblemHighlightType.LIKE_UNUSED_SYMBOL); + } + } + } + + private static class RemoveIncOrDecFix implements LocalQuickFix { + private final String myMessage; + + public RemoveIncOrDecFix(GrUnaryExpression expression) { + myMessage = GroovyInspectionBundle.message("remove.0", expression.getOperationToken().getText()); + } + + @NotNull + @Override + public String getName() { + return myMessage; + } + + @NotNull + @Override + public String getFamilyName() { + return myMessage; + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + GrUnaryExpression expr = findUnaryExpression(descriptor); + if (expr == null) return; + + expr.replaceWithExpression(expr.getOperand(), true); + } + } + + private static class ReplacePostfixIncWithPrefixFix implements LocalQuickFix { + private final String myMessage; + + public ReplacePostfixIncWithPrefixFix(GrUnaryExpression expression) { + myMessage = GroovyInspectionBundle.message("replace.postfix.0.with.prefix.0", expression.getOperationToken().getText()); + } + + @NotNull + @Override + public String getName() { + return myMessage; + } + + @NotNull + @Override + public String getFamilyName() { + return myMessage; + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + GrUnaryExpression expr = findUnaryExpression(descriptor); + if (expr == null) return; + + GrExpression prefix = GroovyPsiElementFactory.getInstance(project) + .createExpressionFromText(expr.getOperationToken().getText() + expr.getOperand().getText()); + + expr.replaceWithExpression(prefix, true); + } + } + + private static class ReplaceIncDecWithBinary implements LocalQuickFix { + private final String myMessage; + + public ReplaceIncDecWithBinary(GrUnaryExpression expression) { + String opToken = expression.getOperationToken().getText(); + myMessage = GroovyInspectionBundle.message("replace.0.with.1", opToken, opToken.substring(0, 1)); + } + + @NotNull + @Override + public String getName() { + return myMessage; + } + + @NotNull + @Override + public String getFamilyName() { + return myMessage; + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + GrUnaryExpression expr = findUnaryExpression(descriptor); + GrExpression newExpr = GroovyPsiElementFactory.getInstance(project) + .createExpressionFromText(expr.getOperand().getText() + expr.getOperationToken().getText().substring(0, 1) + "1"); + expr.replaceWithExpression(newExpr, true); + } + } + } + + @Nullable + private static GrUnaryExpression findUnaryExpression(ProblemDescriptor descriptor) { + GrUnaryExpression expr; + PsiElement element = descriptor.getPsiElement(); + if (element == null) return null; + PsiElement parent = element.getParent(); + IElementType opType = element.getNode().getElementType(); + if (opType != GroovyTokenTypes.mINC && opType != GroovyTokenTypes.mDEC) return null; + if (!(parent instanceof GrUnaryExpression)) return null; + expr = (GrUnaryExpression)parent; + return expr; + } +} + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/threading/GroovyWhileLoopSpinsOnFieldInspection.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/threading/GroovyWhileLoopSpinsOnFieldInspection.java index 01548294a59b..a7429db7e476 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/threading/GroovyWhileLoopSpinsOnFieldInspection.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/threading/GroovyWhileLoopSpinsOnFieldInspection.java @@ -30,7 +30,10 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrBlockStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrWhileStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrBinaryExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrUnaryExpression; import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; import javax.swing.*; @@ -94,9 +97,8 @@ public class GroovyWhileLoopSpinsOnFieldInspection extends BaseInspection { return true; } - if (condition instanceof GrPostfixExpression) { - final GrPostfixExpression postfixExpression = - (GrPostfixExpression) condition; + if (condition instanceof GrUnaryExpression && ((GrUnaryExpression)condition).isPostfix()) { + final GrUnaryExpression postfixExpression = (GrUnaryExpression) condition; final GrExpression operand = postfixExpression.getOperand(); return isSimpleFieldComparison(operand); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/unusedDef/UnusedDefInspection.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/unusedDef/UnusedDefInspection.java index fb8e2f0e6c12..da1bd028bac2 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/unusedDef/UnusedDefInspection.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/unusedDef/UnusedDefInspection.java @@ -24,7 +24,7 @@ import com.intellij.psi.PsiReference; import com.intellij.psi.impl.DebugUtil; import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.searches.ReferencesSearch; -import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.tree.IElementType; import com.intellij.util.Processor; import gnu.trove.TIntHashSet; import gnu.trove.TIntObjectHashMap; @@ -33,28 +33,25 @@ import gnu.trove.TObjectProcedure; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.codeInspection.GroovyInspectionBundle; import org.jetbrains.plugins.groovy.codeInspection.GroovyLocalInspectionBase; +import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; +import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner; -import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrClassInitializer; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrPostfixExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrUnaryExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction; import org.jetbrains.plugins.groovy.lang.psi.controlFlow.ReadWriteVariableInstruction; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.DFAEngine; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.ReachingDefinitionsDfaInstance; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.ReachingDefinitionsSemilattice; -import java.util.ArrayList; +import java.util.List; /** & @author ven @@ -86,7 +83,7 @@ public class UnusedDefInspection extends GroovyLocalInspectionBase { final ReachingDefinitionsDfaInstance dfaInstance = new ReachingDefinitionsDfaInstance(flow); final ReachingDefinitionsSemilattice lattice = new ReachingDefinitionsSemilattice(); final DFAEngine> engine = new DFAEngine>(flow, dfaInstance, lattice); - final ArrayList> dfaResult = engine.performDFA(); + final List> dfaResult = engine.performDFA(); final TIntHashSet unusedDefs = new TIntHashSet(); for (Instruction instruction : flow) { if (instruction instanceof ReadWriteVariableInstruction && ((ReadWriteVariableInstruction) instruction).isWrite()) { @@ -97,9 +94,9 @@ public class UnusedDefInspection extends GroovyLocalInspectionBase { for (int i = 0; i < dfaResult.size(); i++) { final Instruction instruction = flow[i]; if (instruction instanceof ReadWriteVariableInstruction) { - final ReadWriteVariableInstruction varInsn = (ReadWriteVariableInstruction) instruction; - if (!varInsn.isWrite()) { - final String varName = varInsn.getVariableName(); + final ReadWriteVariableInstruction varInst = (ReadWriteVariableInstruction) instruction; + if (!varInst.isWrite()) { + final String varName = varInst.getVariableName(); TIntObjectHashMap e = dfaResult.get(i); e.forEachValue(new TObjectProcedure() { public boolean execute(TIntHashSet reaching) { @@ -124,21 +121,8 @@ public class UnusedDefInspection extends GroovyLocalInspectionBase { final ReadWriteVariableInstruction instruction = (ReadWriteVariableInstruction)flow[num]; final PsiElement element = instruction.getElement(); if (element == null) return true; - PsiElement toHighlight = null; - if (isLocalAssignment(element) && isUsedInToplevelFlowOnly(element)) { - if (element instanceof GrReferenceExpression) { - PsiElement parent = element.getParent(); - if (parent instanceof GrAssignmentExpression) { - toHighlight = ((GrAssignmentExpression)parent).getLValue(); - } - if (parent instanceof GrPostfixExpression) { - toHighlight = parent; - } - } - else if (element instanceof GrVariable) { - toHighlight = ((GrVariable)element).getNameIdentifierGroovy(); - } - if (toHighlight == null) toHighlight = element; + if (isLocalAssignment(element) && isUsedInTopLevelFlowOnly(element) && !isIncOrDec(element)) { + PsiElement toHighlight = getHighlightElement(element); problemsHolder.registerProblem(toHighlight, GroovyInspectionBundle.message("unused.assignment.tooltip"), ProblemHighlightType.LIKE_UNUSED_SYMBOL); } @@ -147,25 +131,52 @@ public class UnusedDefInspection extends GroovyLocalInspectionBase { }); } - private static boolean isUsedInToplevelFlowOnly(PsiElement element) { + private static PsiElement getHighlightElement(PsiElement element) { + PsiElement toHighlight = null; + if (element instanceof GrReferenceExpression) { + PsiElement parent = element.getParent(); + if (parent instanceof GrAssignmentExpression) { + toHighlight = ((GrAssignmentExpression)parent).getLValue(); + } + if (parent instanceof GrUnaryExpression && ((GrUnaryExpression)parent).isPostfix()) { + toHighlight = parent; + } + } + else if (element instanceof GrVariable) { + toHighlight = ((GrVariable)element).getNameIdentifierGroovy(); + } + if (toHighlight == null) toHighlight = element; + return toHighlight; + } + + private static boolean isIncOrDec(PsiElement element) { + PsiElement parent = element.getParent(); + if (!(parent instanceof GrUnaryExpression)) return false; + + IElementType type = ((GrUnaryExpression)parent).getOperationTokenType(); + return type == GroovyTokenTypes.mINC || type == GroovyTokenTypes.mDEC; + } + + private static boolean isUsedInTopLevelFlowOnly(PsiElement element) { GrVariable var = null; if (element instanceof GrVariable) { - var = (GrVariable) element; - } else if (element instanceof GrReferenceExpression) { - final PsiElement resolved = ((GrReferenceExpression) element).resolve(); - if (resolved instanceof GrVariable) var = (GrVariable) resolved; + var = (GrVariable)element; + } + else if (element instanceof GrReferenceExpression) { + final PsiElement resolved = ((GrReferenceExpression)element).resolve(); + if (resolved instanceof GrVariable) var = (GrVariable)resolved; } if (var != null) { - final GroovyPsiElement scope = getScope(var); + final GroovyPsiElement scope = ControlFlowUtils.findControlFlowOwner(var); if (scope == null) { PsiFile file = var.getContainingFile(); - LOG.error(file == null ? "no file??? var of type"+ var.getClass().getCanonicalName() : DebugUtil.psiToString(file, true, false)); + LOG.error(file == null ? "no file??? var of type" + var.getClass().getCanonicalName() : DebugUtil.psiToString(file, true, false)); } return ReferencesSearch.search(var, new LocalSearchScope(scope)).forEach(new Processor() { public boolean process(PsiReference ref) { - return getScope(ref.getElement()) == scope; + return ControlFlowUtils.findControlFlowOwner(ref.getElement()) == scope; } }); } @@ -173,17 +184,14 @@ public class UnusedDefInspection extends GroovyLocalInspectionBase { return true; } - @Nullable - private static GroovyPsiElement getScope(PsiElement var) { - return PsiTreeUtil.getContextOfType(var, GrClosableBlock.class, GrMethod.class, GrClassInitializer.class, GroovyFileBase.class); - } private static boolean isLocalAssignment(PsiElement element) { if (element instanceof GrVariable) { - return isLocalVariable((GrVariable) element, false); - } else if (element instanceof GrReferenceExpression) { - final PsiElement resolved = ((GrReferenceExpression) element).resolve(); - return resolved instanceof GrVariable && isLocalVariable((GrVariable) resolved, true); + return isLocalVariable((GrVariable)element, false); + } + else if (element instanceof GrReferenceExpression) { + final PsiElement resolved = ((GrReferenceExpression)element).resolve(); + return resolved instanceof GrVariable && isLocalVariable((GrVariable)resolved, true); } return false; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/ControlFlowUtils.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/ControlFlowUtils.java index 36583cf8bba0..18fb9b913a0c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/ControlFlowUtils.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/ControlFlowUtils.java @@ -40,7 +40,6 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrReturnState import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrThrowStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrCaseSection; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrPostfixExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrUnaryExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; @@ -54,7 +53,6 @@ import org.jetbrains.plugins.groovy.lang.psi.dataFlow.DFAEngine; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.DfaInstance; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.Semilattice; import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; -import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil; import java.util.*; @@ -499,7 +497,6 @@ public class ControlFlowUtils { public static boolean isIncOrDecOperand(GrReferenceExpression referenceExpression) { final PsiElement parent = referenceExpression.getParent(); - if (parent instanceof GrPostfixExpression) return true; if (parent instanceof GrUnaryExpression) { final IElementType opType = ((GrUnaryExpression)parent).getOperationTokenType(); return opType == GroovyTokenTypes.mDEC || opType == GroovyTokenTypes.mINC; @@ -650,10 +647,10 @@ public class ControlFlowUtils { @Nullable public static GrControlFlowOwner findControlFlowOwner(PsiElement place) { if (place instanceof GrCodeBlock) { - place = place.getParent(); + place = place.getContext(); } while (true) { - place = place.getParent(); + place = place.getContext(); if (place == null) return null; if (place instanceof GrClosableBlock) return (GrClosableBlock)place; if (place instanceof GrMethod) return ((GrMethod)place).getBlock(); @@ -675,22 +672,28 @@ public class ControlFlowUtils { } public static List findAccess(GrVariable local, final PsiElement place, boolean ahead, boolean writeAccessOnly) { - LOG.assertTrue(GroovyRefactoringUtil.isLocalVariable(local), local.getClass()); + LOG.assertTrue(!(local instanceof GrField), local.getClass()); final GrControlFlowOwner owner = findControlFlowOwner(local); assert owner != null; final Instruction cur = findInstruction(place, owner.getControlFlow()); - if (cur == null) throw new IllegalArgumentException("place is not in the flow"); + if (cur == null) { + throw new IllegalArgumentException("place is not in the flow"); + } + return findAccess(local, ahead, writeAccessOnly, cur); + } + + public static List findAccess(GrVariable local, boolean ahead, boolean writeAccessOnly, Instruction cur) { String name = local.getName(); - + final ArrayList result = new ArrayList(); final HashSet visited = new HashSet(); - + visited.add(cur); - + Queue queue = new ArrayDeque(); for (Instruction i : ahead ? cur.allSuccessors() : cur.allPredecessors()) { @@ -698,11 +701,11 @@ public class ControlFlowUtils { queue.add(i); } } - + while (true) { Instruction instruction = queue.poll(); if (instruction == null) break; - + if (instruction instanceof ReadWriteVariableInstruction) { ReadWriteVariableInstruction rw = (ReadWriteVariableInstruction)instruction; if (name.equals(rw.getVariableName())) { @@ -710,13 +713,13 @@ public class ControlFlowUtils { result.add(rw); continue; } - + if (!writeAccessOnly) { result.add(rw); } } } - + for (Instruction i : ahead ? instruction.allSuccessors() : instruction.allPredecessors()) { if (visited.add(i)) { queue.add(i); @@ -726,9 +729,9 @@ public class ControlFlowUtils { return result; } - + @Nullable - private static Instruction findInstruction(final PsiElement place, Instruction[] controlFlow) { + public static Instruction findInstruction(final PsiElement place, Instruction[] controlFlow) { return ContainerUtil.find(controlFlow, new Condition() { @Override public boolean value(Instruction instruction) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/EquivalenceChecker.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/EquivalenceChecker.java index fdde5dd99efd..c67351f4dced 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/EquivalenceChecker.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/EquivalenceChecker.java @@ -445,8 +445,8 @@ public class EquivalenceChecker { return prefixExpressionsAreEquivalent((GrUnaryExpression) expToCompare1, (GrUnaryExpression) expToCompare2); case POSTFIX_EXPRESSION: - return postfixExpressionsAreEquivalent((GrPostfixExpression) expToCompare1, - (GrPostfixExpression) expToCompare2); + return postfixExpressionsAreEquivalent((GrUnaryExpression) expToCompare1, + (GrUnaryExpression) expToCompare2); case BINARY_EXPRESSION: return binaryExpressionsAreEquivalent((GrBinaryExpression) expToCompare1, (GrBinaryExpression) expToCompare2); @@ -674,8 +674,8 @@ public class EquivalenceChecker { return expressionsAreEquivalent(operand1, operand2); } - private static boolean postfixExpressionsAreEquivalent(@NotNull GrPostfixExpression postfixExp1, - @NotNull GrPostfixExpression postfixExp2) { + private static boolean postfixExpressionsAreEquivalent(@NotNull GrUnaryExpression postfixExp1, + @NotNull GrUnaryExpression postfixExp2) { final IElementType sign1 = postfixExp1.getOperationTokenType(); final IElementType sign2 = postfixExp2.getOperationTokenType(); if (!sign1.equals(sign2)) { @@ -690,7 +690,7 @@ public class EquivalenceChecker { @NotNull GrBinaryExpression binaryExp2) { final IElementType sign1 = binaryExp1.getOperationTokenType(); final IElementType sign2 = binaryExp2.getOperationTokenType(); - if (sign1 == null || sign2 == null || !sign1.equals(sign2)) { + if (!sign1.equals(sign2)) { return false; } final GrExpression lhs1 = binaryExp1.getLeftOperand(); @@ -834,11 +834,8 @@ public class EquivalenceChecker { if (exp instanceof GrMethodCall) { return CALL_EXPRESSION; } - if (exp instanceof GrPostfixExpression) { - return POSTFIX_EXPRESSION; - } if (exp instanceof GrUnaryExpression) { - return PREFIX_EXPRESSION; + return ((GrUnaryExpression)exp).isPostfix() ? POSTFIX_EXPRESSION : PREFIX_EXPRESSION; } if (exp instanceof GrAssignmentExpression) { return ASSIGNMENT_EXPRESSION; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/SideEffectChecker.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/SideEffectChecker.java index 13cc60aaf959..6f4946169677 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/SideEffectChecker.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/SideEffectChecker.java @@ -20,7 +20,10 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrNewExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrUnaryExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression; public class SideEffectChecker { @@ -71,19 +74,6 @@ public class SideEffectChecker { mayHaveSideEffects = true; } - public void visitPostfixExpression( - @NotNull GrPostfixExpression expression) { - if (mayHaveSideEffects) { - return; - } - super.visitPostfixExpression(expression); - final IElementType tokenType = expression.getOperationTokenType(); - if (tokenType.equals(GroovyTokenTypes.mINC) || - tokenType.equals(GroovyTokenTypes.mDEC)) { - mayHaveSideEffects = true; - } - } - public void visitUnaryExpression( @NotNull GrUnaryExpression expression) { if (mayHaveSideEffects) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/compiler/GroovyCompilerBase.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/compiler/GroovyCompilerBase.java index 9c0ee98046b6..d546b64d0ebc 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/compiler/GroovyCompilerBase.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/compiler/GroovyCompilerBase.java @@ -165,38 +165,40 @@ public abstract class GroovyCompilerBase implements TranslatingCompiler { parameters.setMainClass(GroovycRunner.class.getName()); - try { - final VirtualFile finalOutputDir = getMainOutput(compileContext, module, tests); - LOG.assertTrue(finalOutputDir != null, "No output directory for module " + module.getName() + (tests ? " tests" : " production")); - final Charset ideCharset = EncodingProjectManager.getInstance(myProject).getDefaultCharset(); - String encoding = ideCharset != null && !Comparing.equal(CharsetToolkit.getDefaultSystemCharset(), ideCharset) ? ideCharset.name() : null; - Set paths2Compile = ContainerUtil.map2Set(toCompile, new Function() { - @Override - public String fun(VirtualFile file) { - return file.getPath(); - } - }); - Map class2Src = new HashMap(); + final VirtualFile finalOutputDir = getMainOutput(compileContext, module, tests); + LOG.assertTrue(finalOutputDir != null, "No output directory for module " + module.getName() + (tests ? " tests" : " production")); + final Charset ideCharset = EncodingProjectManager.getInstance(myProject).getDefaultCharset(); + String encoding = ideCharset != null && !Comparing.equal(CharsetToolkit.getDefaultSystemCharset(), ideCharset) ? ideCharset.name() : null; + Set paths2Compile = ContainerUtil.map2Set(toCompile, new Function() { + @Override + public String fun(VirtualFile file) { + return file.getPath(); + } + }); + Map class2Src = new HashMap(); - for (VirtualFile file : enumerateGroovyFiles(module)) { - if (!paths2Compile.contains(file.getPath())) { - for (String name : TranslatingCompilerFilesMonitor.getInstance().getCompiledClassNames(file, myProject)) { - class2Src.put(name, file.getPath()); - } + for (VirtualFile file : enumerateGroovyFiles(module)) { + if (!paths2Compile.contains(file.getPath())) { + for (String name : TranslatingCompilerFilesMonitor.getInstance().getCompiledClassNames(file, myProject)) { + class2Src.put(name, file.getPath()); } } + } - File fileWithParameters = GroovycOSProcessHandler + final File fileWithParameters; + try { + fileWithParameters = GroovycOSProcessHandler .fillFileWithGroovycParameters(outputDir.getPath(), paths2Compile, FileUtil.toSystemDependentName(finalOutputDir.getPath()), class2Src, encoding, patchers); - - parameters.getProgramParametersList().add(forStubs ? "stubs" : "groovyc"); - parameters.getProgramParametersList().add(fileWithParameters.getPath()); } catch (IOException e) { - LOG.error(e); + LOG.info(e); + compileContext.addMessage(CompilerMessageCategory.ERROR, "Error creating a temp file to launch Groovy compiler: " + e.getMessage(), null, -1, -1); + return; } + parameters.getProgramParametersList().add(forStubs ? "stubs" : "groovyc"); + parameters.getProgramParametersList().add(fileWithParameters.getPath()); try { Process process = JdkUtil.setupJVMCommandLine(exePath, parameters, true).createProcess(); @@ -272,7 +274,8 @@ public abstract class GroovyCompilerBase implements TranslatingCompiler { sink.add(outputDir.getPath(), items, VfsUtil.toVirtualFileArray(toRecompile)); } catch (ExecutionException e) { - LOG.error(e); + LOG.info(e); + compileContext.addMessage(CompilerMessageCategory.ERROR, "Error running Groovy compiler: " + e.getMessage(), null, -1, -1); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/processors/GroovySpacingProcessor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/processors/GroovySpacingProcessor.java index 18c08d51d08a..970d869780a1 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/processors/GroovySpacingProcessor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/processors/GroovySpacingProcessor.java @@ -484,17 +484,8 @@ public class GroovySpacingProcessor extends GroovyElementVisitor { @Override public void visitUnaryExpression(GrUnaryExpression expression) { - //prefix only - LOG.assertTrue(!(expression instanceof GrPostfixExpression)); - - if (expression.getOperationToken() == myChild1) { - createSpaceInCode(mySettings.SPACE_AROUND_UNARY_OPERATOR); - } - } - - @Override - public void visitPostfixExpression(GrPostfixExpression expression) { - if (expression.getOperationToken() == myChild2) { + if (!expression.isPostfix() && expression.getOperationToken() == myChild1 || + expression.isPostfix() && expression.getOperationToken() == myChild2) { createSpaceInCode(mySettings.SPACE_AROUND_UNARY_OPERATOR); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/utils/ParenthesesUtils.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/utils/ParenthesesUtils.java index cf87e09df314..4c354de1c83a 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/utils/ParenthesesUtils.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/utils/ParenthesesUtils.java @@ -104,17 +104,14 @@ public class ParenthesesUtils { expression instanceof GrNewExpression) { return TYPE_CAST_PRECEDENCE; } - if (expression instanceof GrPostfixExpression) { - return POSTFIX_PRECEDENCE; - } if (expression instanceof GrUnaryExpression) { - return PREFIX_PRECEDENCE; + return ((GrUnaryExpression)expression).isPostfix()?POSTFIX_PRECEDENCE:PREFIX_PRECEDENCE; } if (expression instanceof GrBinaryExpression) { final GrBinaryExpression binaryExpression = (GrBinaryExpression) expression; final IElementType sign = binaryExpression.getOperationTokenType(); - if (sign != null) return precedenceForBinaryOperator(sign); + return precedenceForBinaryOperator(sign); } if (expression instanceof GrConditionalExpression) { return CONDITIONAL_PRECEDENCE; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/lexer/TokenSets.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/lexer/TokenSets.java index b04214d900aa..d09ec7ff336d 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/lexer/TokenSets.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/lexer/TokenSets.java @@ -201,14 +201,4 @@ public abstract class TokenSets { ); public static final TokenSet SHIFT_SIGNS = TokenSet.create(COMPOSITE_LSHIFT_SIGN, COMPOSITE_RSHIFT_SIGN, COMPOSITE_TRIPLE_SHIFT_SIGN); - - public static final TokenSet POSTFIXES = TokenSet.create(mINC,mDEC); - public static final TokenSet PREFIXES = TokenSet.create( - mPLUS, - mMINUS, - mINC, - mDEC, - mBNOT, - mLNOT - ); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyElementTypes.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyElementTypes.java index e1d73e4cc191..0ecc5f9e99f6 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyElementTypes.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyElementTypes.java @@ -222,7 +222,6 @@ public interface GroovyElementTypes extends GroovyTokenTypes, GroovyDocElementTy GroovyElementType CAST_EXPRESSION = new GroovyElementType("cast expression"); GroovyElementType SAFE_CAST_EXPRESSION = new GroovyElementType("safe cast expression"); GroovyElementType INSTANCEOF_EXPRESSION = new GroovyElementType("instanceof expression"); - GroovyElementType POSTFIX_EXPRESSION = new GroovyElementType("Postfix expression"); GroovyElementType PATH_PROPERTY_REFERENCE = new GroovyElementType("Property reference"); GroovyElementType PATH_METHOD_CALL = new GroovyElementType("Method call"); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyPsiCreator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyPsiCreator.java index 30c697c579a0..6f5d0be5057f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyPsiCreator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyPsiCreator.java @@ -28,7 +28,10 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyASTPsiElementImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.GrLabelImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.GrListOrMapImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.GrThrowsClauseImpl; -import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.annotation.*; +import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.annotation.GrAnnotationArgumentListImpl; +import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.annotation.GrAnnotationArrrayInitializerImpl; +import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.annotation.GrAnnotationImpl; +import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.annotation.GrAnnotationNameValuePairImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.modifiers.GrModifierListImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.*; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.arguments.GrArgumentLabelImpl; @@ -218,7 +221,6 @@ public class GroovyPsiCreator implements GroovyElementTypes { if (elem == POWER_EXPRESSION) return new GrPowerExpressionImpl(node); if (elem == POWER_EXPRESSION_SIMPLE) return new GrPowerExpressionImpl(node); if (elem == UNARY_EXPRESSION) return new GrUnaryExpressionImpl(node); - if (elem == POSTFIX_EXPRESSION) return new GrPostfixExprImpl(node); if (elem == CAST_EXPRESSION) return new GrTypeCastExpressionImpl(node); if (elem == SAFE_CAST_EXPRESSION) return new GrSafeCastExpressionImpl(node); if (elem == INSTANCEOF_EXPRESSION) return new GrInstanceofExpressionImpl(node); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/PostfixExpression.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/PostfixExpression.java index e8edbafc9d0b..5ab5c9efd0a9 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/PostfixExpression.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/PostfixExpression.java @@ -40,9 +40,9 @@ public class PostfixExpression implements GroovyElementTypes { } private static void subParse(PsiBuilder builder, PsiBuilder.Marker marker) { - if (ParserUtils.getToken(builder, TokenSets.POSTFIXES)) { + if (ParserUtils.getToken(builder, TokenSets.POSTFIX_UNARY_OP_SET)) { PsiBuilder.Marker newMarker = marker.precede(); - marker.done(POSTFIX_EXPRESSION); + marker.done(UNARY_EXPRESSION); subParse(builder, newMarker); } else { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/UnaryExpression.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/UnaryExpression.java index 64d4a3c3c515..d215334597f7 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/UnaryExpression.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/UnaryExpression.java @@ -30,7 +30,7 @@ public class UnaryExpression implements GroovyElementTypes { public static boolean parse(PsiBuilder builder, GroovyParser parser) { PsiBuilder.Marker marker = builder.mark(); - if (ParserUtils.getToken(builder, TokenSets.PREFIXES)) { + if (ParserUtils.getToken(builder, TokenSets.UNARY_OP_SET)) { ParserUtils.getToken(builder, mNLS); parse(builder, parser); marker.done(UNARY_EXPRESSION); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/GroovyElementVisitor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/GroovyElementVisitor.java index cdd57b7486ec..872546b7894a 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/GroovyElementVisitor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/GroovyElementVisitor.java @@ -168,10 +168,6 @@ public abstract class GroovyElementVisitor { visitExpression(expression); } - public void visitPostfixExpression(GrPostfixExpression expression) { - visitUnaryExpression(expression); - } - public void visitRegexExpression(GrRegex expression) { visitGStringExpression(expression); } @@ -443,4 +439,8 @@ public abstract class GroovyElementVisitor { public void visitTypeParameterList(GrTypeParameterList list) { visitElement(list); } + + public void visitClassInitializer(GrClassInitializer initializer) { + visitElement(initializer); + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrPostfixExpression.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrPostfixExpression.java deleted file mode 100644 index de2bb73e0d4d..000000000000 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrPostfixExpression.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2000-2009 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 org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions; - -/** - * @author ilyas - */ -public interface GrPostfixExpression extends GrUnaryExpression { -} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrUnaryExpression.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrUnaryExpression.java index ef4a8b6b5b85..03900c94f287 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrUnaryExpression.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrUnaryExpression.java @@ -35,4 +35,6 @@ public interface GrUnaryExpression extends GrExpression, PsiPolyVariantReference @NotNull @Override GroovyResolveResult[] multiResolve(final boolean incompleteCode); + + boolean isPostfix(); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/ReadWriteVariableInstruction.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/ReadWriteVariableInstruction.java index 247cdd929346..e68eddd8fa56 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/ReadWriteVariableInstruction.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/ReadWriteVariableInstruction.java @@ -23,13 +23,16 @@ import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.InstructionImpl; * @author ven */ public class ReadWriteVariableInstruction extends InstructionImpl { + public static final int WRITE = -1; + public static final int READ = 1; + private final boolean myIsWrite; private final String myName; - public ReadWriteVariableInstruction(@NotNull String varName, PsiElement element, int num, boolean isWrite) { + public ReadWriteVariableInstruction(@NotNull String varName, PsiElement element, int num, int accessType) { super(element, num); myName = varName; - myIsWrite = isWrite; + myIsWrite = accessType == WRITE; } @NotNull public String getVariableName() { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/impl/ControlFlowBuilder.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/impl/ControlFlowBuilder.java index 2e4f31e52201..1fea9d257838 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/impl/ControlFlowBuilder.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/impl/ControlFlowBuilder.java @@ -20,6 +20,7 @@ import com.intellij.openapi.util.Pair; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.containers.hash.HashSet; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; @@ -51,6 +52,9 @@ import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; import java.util.*; +import static org.jetbrains.plugins.groovy.lang.psi.controlFlow.ReadWriteVariableInstruction.READ; +import static org.jetbrains.plugins.groovy.lang.psi.controlFlow.ReadWriteVariableInstruction.WRITE; + /** * @author ven */ @@ -107,7 +111,7 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { final PsiElement lbrace = block.getLBrace(); if (lbrace != null && parent instanceof GrMethod) { for (GrParameter parameter : ((GrMethod)parent).getParameters()) { - addNode(new ReadWriteVariableInstruction(parameter.getName(), parameter, myInstructionNumber++, true)); + addNode(new ReadWriteVariableInstruction(parameter.getName(), parameter, myInstructionNumber++, WRITE)); } } super.visitOpenBlock(block); @@ -185,7 +189,7 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { private void buildFlowForClosure(final GrClosableBlock closure) { for (GrParameter parameter : closure.getAllParameters()) { - addNode(new ReadWriteVariableInstruction(parameter.getName(), parameter, myInstructionNumber++, true)); + addNode(new ReadWriteVariableInstruction(parameter.getName(), parameter, myInstructionNumber++, WRITE)); } final Set names = new LinkedHashSet(); @@ -207,7 +211,7 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { names.add("owner"); for (String name : names) { - addNode(new ReadWriteVariableInstruction(name, closure.getLBrace(), myInstructionNumber++, true)); + addNode(new ReadWriteVariableInstruction(name, closure.getLBrace(), myInstructionNumber++, WRITE)); } PsiElement child = closure.getFirstChild(); @@ -241,12 +245,26 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { //do not go inside closures except gstring injections if (closure.getParent() instanceof GrStringInjection) { super.visitClosure(closure); + return; } - else { - //create instruction for closure to use it in inline local refactoring - final InstructionImpl i = new InstructionImpl(closure, myInstructionNumber++); + + Set names = new HashSet(); + + ReadWriteVariableInstruction[] reads = ControlFlowBuilderUtil.getReadsWithoutPriorWrites(closure.getControlFlow()); + for (ReadWriteVariableInstruction read : reads) { + names.add(read.getVariableName()); + } + + for (String name : names) { + ReadWriteVariableInstruction i = new ReadWriteVariableInstruction(name, closure, myInstructionNumber++, READ); addNode(i); + checkPending(i); } + + + InstructionImpl i = new InstructionImpl(closure, myInstructionNumber++); + addNode(i); + checkPending(i); } public void visitBreakStatement(GrBreakStatement breakStatement) { @@ -364,7 +382,7 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { if (lValue instanceof GrReferenceExpression) { String referenceName = ((GrReferenceExpression)lValue).getReferenceName(); if (referenceName != null) { - ReadWriteVariableInstruction instruction = new ReadWriteVariableInstruction(referenceName, lValue, myInstructionNumber++, false); + ReadWriteVariableInstruction instruction = new ReadWriteVariableInstruction(referenceName, lValue, myInstructionNumber++, READ); addNode(instruction); checkPending(instruction); } @@ -413,17 +431,14 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { if (name == null) return; if (ControlFlowUtils.isIncOrDecOperand(refExpr) && !myAssertionsOnly) { - final ReadWriteVariableInstruction i = new ReadWriteVariableInstruction(name, refExpr, myInstructionNumber++, false); + final InstructionImpl i = new ReadWriteVariableInstruction(name, refExpr, myInstructionNumber++, READ); addNode(i); - addNode(new ReadWriteVariableInstruction(name, refExpr, myInstructionNumber++, true)); - if (refExpr.getParent() instanceof GrUnaryExpression) { - addNode(new ReadWriteVariableInstruction(name, refExpr, myInstructionNumber++, false)); - } + addNode(new ReadWriteVariableInstruction(name, refExpr, myInstructionNumber++, WRITE)); checkPending(i); } else { - final ReadWriteVariableInstruction i = - new ReadWriteVariableInstruction(name, refExpr, myInstructionNumber++, !myAssertionsOnly && PsiUtil.isLValue(refExpr)); + boolean isWrite = !myAssertionsOnly && PsiUtil.isLValue(refExpr); + final InstructionImpl i = new ReadWriteVariableInstruction(name, refExpr, myInstructionNumber++, isWrite ? WRITE : READ); addNode(i); checkPending(i); } @@ -563,10 +578,10 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { } GrVariable variable = clause.getDeclaredVariable(); if (variable != null) { - ReadWriteVariableInstruction writeInsn = - new ReadWriteVariableInstruction(variable.getName(), variable, myInstructionNumber++, true); - checkPending(writeInsn); - addNode(writeInsn); + ReadWriteVariableInstruction writeInst = + new ReadWriteVariableInstruction(variable.getName(), variable, myInstructionNumber++, WRITE); + checkPending(writeInst); + addNode(writeInst); } } @@ -755,6 +770,7 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { Set> pendingAfterTry = new LinkedHashSet>(myPending); + @SuppressWarnings("unchecked") List[] throwers = new List[catchClauses.length]; for (int i = 0; i < catchClauses.length; i++) { @@ -772,7 +788,7 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { final GrParameter parameter = catchClauses[i].getParameter(); if (parameter != null) { - addNode(new ReadWriteVariableInstruction(parameter.getName(), parameter, myInstructionNumber++, true)); + addNode(new ReadWriteVariableInstruction(parameter.getName(), parameter, myInstructionNumber++, WRITE)); } catchClauses[i].accept(this); catches[i] = myHead; @@ -867,19 +883,73 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { public void visitMethod(GrMethod method) { } - public void visitTypeDefinition(GrTypeDefinition typeDefinition) { - if (typeDefinition instanceof GrAnonymousClassDefinition) { - super.visitTypeDefinition(typeDefinition); + @Override + public void visitClassInitializer(GrClassInitializer initializer) { + } + + public void visitTypeDefinition(final GrTypeDefinition typeDefinition) { + if (!(typeDefinition instanceof GrAnonymousClassDefinition)) return; + + final Set vars = new HashSet(); + typeDefinition.acceptChildren(new GroovyRecursiveElementVisitor() { + private void collectVars(Instruction[] flow) { + ReadWriteVariableInstruction[] reads = ControlFlowBuilderUtil.getReadsWithoutPriorWrites(flow); + for (ReadWriteVariableInstruction instruction : reads) { + vars.add(instruction.getVariableName()); + } + } + + @Override + public void visitField(GrField field) { + GrExpression initializer = field.getInitializerGroovy(); + if (initializer != null) { + Instruction[] flow = new ControlFlowBuilder(field.getProject()).buildControlFlow(initializer); + collectVars(flow); + } + } + + @Override + public void visitMethod(GrMethod method) { + GrOpenBlock block = method.getBlock(); + if (block != null) { + collectVars(block.getControlFlow()); + } + } + + @Override + public void visitClassInitializer(GrClassInitializer initializer) { + GrOpenBlock block = initializer.getBlock(); + collectVars(block.getControlFlow()); + } + + @Override + public void visitTypeDefinition(GrTypeDefinition typeDefinition) { + typeDefinition.acceptChildren(this); + } + }); + + PsiField[] fields = typeDefinition.getAllFields(); + for (PsiField field : fields) { + vars.remove(field.getName()); } + + for (String var : vars) { + ReadWriteVariableInstruction i = new ReadWriteVariableInstruction(var, typeDefinition, myInstructionNumber++, READ); + addNode(i); + checkPending(i); + } + InstructionImpl i = new InstructionImpl(typeDefinition, myInstructionNumber++); + addNode(i); + checkPending(i); } public void visitVariable(GrVariable variable) { super.visitVariable(variable); if (variable.getInitializerGroovy() != null || variable.getParent() instanceof GrTupleDeclaration && ((GrTupleDeclaration)variable.getParent()).getInitializerGroovy() != null) { - ReadWriteVariableInstruction writeInsn = new ReadWriteVariableInstruction(variable.getName(), variable, myInstructionNumber++, true); - checkPending(writeInsn); - addNode(writeInsn); + ReadWriteVariableInstruction writeInst = new ReadWriteVariableInstruction(variable.getName(), variable, myInstructionNumber++, WRITE); + checkPending(writeInst); + addNode(writeInst); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/PsiImplUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/PsiImplUtil.java index f837f3687ec3..22d5d9ad691d 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/PsiImplUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/PsiImplUtil.java @@ -39,7 +39,6 @@ import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.formatter.GeeseUtil; -import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.lexer.TokenSets; import org.jetbrains.plugins.groovy.lang.psi.GrNamedElement; import org.jetbrains.plugins.groovy.lang.psi.GrQualifiedReference; @@ -317,15 +316,13 @@ public class PsiImplUtil { private static int getExprPriorityLevel(GrExpression expr) { int priority = 0; //if (expr instanceof GrNewExpression) priority = 1; - if (expr instanceof GrPostfixExpression) priority = 5; - else if (expr instanceof GrUnaryExpression || - expr instanceof GrTypeCastExpression) priority = 6; + if (expr instanceof GrUnaryExpression) priority = ((GrUnaryExpression)expr).isPostfix() ? 5 : 6; + else if (expr instanceof GrTypeCastExpression) priority = 6; else if (expr instanceof GrRangeExpressionImpl) priority = 11; else if (expr instanceof GrBinaryExpression) { final IElementType opToken = ((GrBinaryExpression)expr).getOperationTokenType(); - LOG.assertTrue(opToken != null, expr.getText()); if (opToken == mSTAR_STAR) priority = 7; else if (opToken == mSTAR || opToken == mDIV) priority = 8; @@ -507,7 +504,7 @@ public class PsiImplUtil { } else { if (oldQualifier == null) { if (refNameElement != null) { - node.addLeaf(GroovyTokenTypes.mDOT, ".", refNameElement.getNode()); + node.addLeaf(mDOT, ".", refNameElement.getNode()); ref.addBefore(newQualifier, refNameElement.getPrevSibling()); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrClassInitializerImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrClassInitializerImpl.java index ab2d9984d740..623daf3e780f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrClassInitializerImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrClassInitializerImpl.java @@ -25,6 +25,7 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; +import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrClassInitializer; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; @@ -42,6 +43,11 @@ public class GrClassInitializerImpl extends GroovyPsiElementImpl implements GrCl super(node); } + @Override + public void accept(GroovyElementVisitor visitor) { + visitor.visitClassInitializer(this); + } + public String toString() { return "Class initializer"; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/arithmetic/GrPostfixExprImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/arithmetic/GrPostfixExprImpl.java deleted file mode 100644 index f5f2092ad9be..000000000000 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/arithmetic/GrPostfixExprImpl.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2000-2009 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 org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.arithmetic; - -import com.intellij.lang.ASTNode; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrPostfixExpression; - -/** - * @author ilyas - */ -public class GrPostfixExprImpl extends GrUnaryExpressionImpl implements GrPostfixExpression { - - public GrPostfixExprImpl(@NotNull ASTNode node) { - super(node); - } - - public String toString() { - return "Postfix expression"; - } - - public void accept(GroovyElementVisitor visitor) { - visitor.visitPostfixExpression(this); - } -} \ No newline at end of file diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/arithmetic/GrUnaryExpressionImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/arithmetic/GrUnaryExpressionImpl.java index 2b95343cbaef..3ae033a3a647 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/arithmetic/GrUnaryExpressionImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/arithmetic/GrUnaryExpressionImpl.java @@ -125,6 +125,11 @@ public class GrUnaryExpressionImpl extends GrExpressionImpl implements GrUnaryEx return (GroovyResolveResult[])ResolveCache.getInstance(getProject()).resolveWithCaching(this, OUR_RESOLVER, false, incompleteCode); } + @Override + public boolean isPostfix() { + return getFirstChild() instanceof GrExpression; + } + @Override public PsiElement getElement() { return this; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java index 239574da80c1..ce38cea5b7cd 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java @@ -78,9 +78,9 @@ public class ResolveUtil { } /** - * + * * @param place - place to start tree walk up - * @param processor + * @param processor * @param processNonCodeMethods - this parameter tells us if we need non code members. But non code members are started to process only after we walk up any code block or script * @return */ @@ -95,10 +95,6 @@ public class ResolveUtil { while (run != null) { if (!run.processDeclarations(processor, ResolveState.initial(), lastParent, place)) return false; - if (run instanceof GrClosableBlock) { - PsiClass superClass = getLiteralSuperClass((GrClosableBlock)run); - if (superClass != null && !superClass.processDeclarations(processor, ResolveState.initial(), null, place)) return false; - } if (processNonCodeMethods) { if (!doProcessNonCodeMembers) { if (run instanceof GrCodeBlock) doProcessNonCodeMembers = true; @@ -115,6 +111,9 @@ public class ResolveUtil { } } else if (run instanceof GrClosableBlock) { + PsiClass superClass = getLiteralSuperClass((GrClosableBlock)run); + if (superClass != null && !superClass.processDeclarations(processor, ResolveState.initial(), null, place)) return false; + if (!GdkMethodUtil.categoryIteration((GrClosableBlock)run, processor)) return false; if (!GdkMethodUtil.withIteration((GrClosableBlock)run, processor, place)) return false; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToJava/ExpressionGenerator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToJava/ExpressionGenerator.java index b55c62da66d4..2a60c5494208 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToJava/ExpressionGenerator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToJava/ExpressionGenerator.java @@ -669,7 +669,7 @@ public class ExpressionGenerator extends Generator { @Override public void visitUnaryExpression(GrUnaryExpression expression) { - final boolean postfix = expression instanceof GrPostfixExpression; + final boolean postfix = expression.isPostfix(); final GroovyResolveResult resolveResult = PsiImplUtil.extractUniqueResult(expression.multiResolve(false)); final PsiElement resolved = resolveResult.getElement(); @@ -816,12 +816,12 @@ public class ExpressionGenerator extends Generator { private static void writeSimpleUnary(GrExpression operand, GrUnaryExpression unary, ExpressionGenerator generator) { String opTokenText = unary.getOperationToken().getText(); - boolean isPrefix = !(unary instanceof GrPostfixExpression); - if (isPrefix) { + boolean isPostfix = unary.isPostfix(); + if (!isPostfix) { generator.getBuilder().append(opTokenText); } operand.accept(generator); - if (!isPrefix) { + if (isPostfix) { generator.getBuilder().append(opTokenText); } } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy index d4f6fe10a871..e0ba3cd80dad 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy @@ -37,6 +37,7 @@ import org.jetbrains.plugins.groovy.codeInspection.assignment.GroovyAssignabilit import org.jetbrains.plugins.groovy.codeInspection.assignment.GroovyResultOfAssignmentUsedInspection import org.jetbrains.plugins.groovy.codeInspection.assignment.GroovyUncheckedAssignmentOfMemberOfRawTypeInspection import org.jetbrains.plugins.groovy.codeInspection.confusing.ClashingGettersInspection +import org.jetbrains.plugins.groovy.codeInspection.confusing.GrUnusedIncDecInspection import org.jetbrains.plugins.groovy.codeInspection.confusing.GroovyOctalIntegerInspection import org.jetbrains.plugins.groovy.codeInspection.confusing.GroovyResultOfIncrementOrDecrementUsedInspection import org.jetbrains.plugins.groovy.codeInspection.control.GroovyTrivialConditionalInspection @@ -188,10 +189,10 @@ public class GroovyHighlightingTest extends LightCodeInsightFixtureTestCase { public void testUnassigned3() throws Exception { doTest(new UnassignedVariableAccessInspection()); } public void testUnassignedTryFinally() throws Exception { doTest(new UnassignedVariableAccessInspection()); } - public void testUnusedVariable() throws Exception { doTest(new UnusedDefInspection()); } - public void testDefinitionUsedInClosure() throws Exception { doTest(new UnusedDefInspection()); } - public void testDefinitionUsedInClosure2() throws Exception { doTest(new UnusedDefInspection()); } - public void testDefinitionUsedInSwitchCase() throws Exception { doTest(new UnusedDefInspection()); } + public void testUnusedVariable() throws Exception { doTest(new UnusedDefInspection(), new GrUnusedIncDecInspection()); } + public void testDefinitionUsedInClosure() throws Exception { doTest(new UnusedDefInspection(), new GrUnusedIncDecInspection()); } + public void testDefinitionUsedInClosure2() throws Exception { doTest(new UnusedDefInspection(), new GrUnusedIncDecInspection()); } + public void testDefinitionUsedInSwitchCase() throws Exception { doTest(new UnusedDefInspection(), new GrUnusedIncDecInspection()); } public void testDuplicateInnerClass() throws Throwable{doTest();} public void testThisInStaticContext() throws Throwable {doTest();} @@ -248,8 +249,8 @@ class A { public void testByteArrayArgument() throws Exception {doTest(new GroovyAssignabilityCheckInspection());} public void testForLoopWithNestedEndlessLoop() throws Exception {doTest(new UnassignedVariableAccessInspection());} - public void testPrefixIncrementCfa() throws Exception {doTest(new UnusedDefInspection());} - public void testIfIncrementElseReturn() throws Exception {doTest(new UnusedDefInspection()); } + public void testPrefixIncrementCfa() throws Exception {doTest(new UnusedDefInspection(), new GrUnusedIncDecInspection());} + public void testIfIncrementElseReturn() throws Exception {doTest(new UnusedDefInspection(), new GrUnusedIncDecInspection()); } public void testArrayLikeAccess() throws Exception {doTest();} @@ -315,7 +316,7 @@ class A { public void testBuiltInTypeInstantiation() {doTest();} - public void testSwitchControlFlow() {doTest(new UnusedDefInspection(), new GroovyResultOfAssignmentUsedInspection());} + public void testSwitchControlFlow() {doTest(new UnusedDefInspection(), new GroovyResultOfAssignmentUsedInspection(), new GrUnusedIncDecInspection());} public void testRawTypeInAssignment() {doTest(new GroovyAssignabilityCheckInspection());} @@ -325,7 +326,7 @@ class A { IdeaTestUtil.assertTiming("", 10000, 1, new Runnable() { @Override public void run() { - doTest(new GroovyAssignabilityCheckInspection(), new UnusedDefInspection()); + doTest(new GroovyAssignabilityCheckInspection(), new UnusedDefInspection(), new GrUnusedIncDecInspection()); } }); } @@ -397,7 +398,7 @@ class A { doTest(new GroovyUnresolvedAccessInspection(), new GroovyUntypedAccessInspection()); } - public void testUsageInInjection() { doTest(new UnusedDefInspection()); } + public void testUsageInInjection() { doTest(new UnusedDefInspection(), new GrUnusedIncDecInspection()); } public void testDuplicatedNamedArgs() {doTest();} @@ -419,15 +420,19 @@ class A { } public void testUnusedDefsForArgs() { - doTest(new UnusedDefInspection()); + doTest(new UnusedDefInspection(), new GrUnusedIncDecInspection()); } public void testUsedDefBeforeTry1() { - doTest(new UnusedDefInspection()); + doTest(new UnusedDefInspection(), new GrUnusedIncDecInspection()); } public void testUsedDefBeforeTry2() { - doTest(new UnusedDefInspection()); + doTest(new UnusedDefInspection(), new GrUnusedIncDecInspection()); + } + + public void testUnusedInc() { + doTest(new UnusedDefInspection(), new GrUnusedIncDecInspection()) } public void testStringAssignableToChar() { diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/controlFlow/ControlFlowTest.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/controlFlow/ControlFlowTest.java index a6790d48e8d5..ff34da2ea32e 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/controlFlow/ControlFlowTest.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/controlFlow/ControlFlowTest.java @@ -55,6 +55,8 @@ public class ControlFlowTest extends LightCodeInsightFixtureTestCase { public void testIfInstanceofElse() throws Throwable { doTest(); } public void testReturnMapFromClosure() {doTest();} public void testSwitchInTryWithThrows() {doTest();} + public void testClosure() {doTest();} + public void testAnonymous() {doTest();} public void doTest() { final List input = TestUtils.readInput(getTestDataPath() + getTestName(true) + ".test"); diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/extract/method/ExtractMethodTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/extract/method/ExtractMethodTest.groovy index 04fee16b1f12..729e0d436827 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/extract/method/ExtractMethodTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/extract/method/ExtractMethodTest.groovy @@ -114,4 +114,7 @@ public class ExtractMethodTest extends LightGroovyTestCase { public void testParamChangedInsideExtractedMethod() {doTest();} public void testTerribleAppStatement() {doTest()} + + public void testArgsUsedOnlyInClosure() {doTest()} + public void testArgsUsedOnlyInAnonymousClass() {doTest()} } \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/controlFlow/anonymous.test b/plugins/groovy/testdata/groovy/controlFlow/anonymous.test new file mode 100644 index 000000000000..79b93ac0c3fd --- /dev/null +++ b/plugins/groovy/testdata/groovy/controlFlow/anonymous.test @@ -0,0 +1,17 @@ +int x = 0 +[1, 2, 3].each (new Closure(this, this) { + void call() { + x++ + } +}) + +print x +----- +0(1) element: null +1(2) WRITE x +2(3) READ x +3(4) element: Anonymous class +4(5) READ print +5(6) READ x +6(7) element: Call expression MAYBE_RETURN +7() element: null \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/controlFlow/closure.test b/plugins/groovy/testdata/groovy/controlFlow/closure.test new file mode 100644 index 000000000000..ed94fc177ee3 --- /dev/null +++ b/plugins/groovy/testdata/groovy/controlFlow/closure.test @@ -0,0 +1,15 @@ +int x = 0 +[1, 2, 3].each { + x++ +} + +print x +----- +0(1) element: null +1(2) WRITE x +2(3) READ x +3(4) element: Closable block +4(5) READ print +5(6) READ x +6(7) element: Call expression MAYBE_RETURN +7() element: null \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/controlFlow/for1.test b/plugins/groovy/testdata/groovy/controlFlow/for1.test index 0f0ebbd84c66..cefbd1ce77bf 100644 --- a/plugins/groovy/testdata/groovy/controlFlow/for1.test +++ b/plugins/groovy/testdata/groovy/controlFlow/for1.test @@ -3,8 +3,7 @@ for(int i = 0; i < 10; i++); 0(1) element: null 1(2) WRITE i 2(3) element: For statement -3(4,7) READ i +3(4,6) READ i 4(5) READ i -5(6) WRITE i -6(2) READ i -7() element: null \ No newline at end of file +5(2) WRITE i +6() element: null \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/controlFlow/while1.test b/plugins/groovy/testdata/groovy/controlFlow/while1.test index 3b7578eb1411..493df467f932 100644 --- a/plugins/groovy/testdata/groovy/controlFlow/while1.test +++ b/plugins/groovy/testdata/groovy/controlFlow/while1.test @@ -8,9 +8,8 @@ while (true) { 1(2) WRITE i 2(3) element: WHILE statement 3(4,5) element: IF statement -4(9) READ i +4(8) READ i 5(6) element: IF statement 6(7) READ i -7(8) WRITE i -8(2) READ i -9() element: null \ No newline at end of file +7(2) WRITE i +8() element: null \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/controlFlow/whileNonConstant.test b/plugins/groovy/testdata/groovy/controlFlow/whileNonConstant.test index a54fd3bee520..f1206ab03412 100644 --- a/plugins/groovy/testdata/groovy/controlFlow/whileNonConstant.test +++ b/plugins/groovy/testdata/groovy/controlFlow/whileNonConstant.test @@ -7,11 +7,10 @@ while (condition()) { 0(1) element: null 1(2) WRITE i 2(3) element: WHILE statement -3(4,10) READ condition +3(4,9) READ condition 4(5,6) element: IF statement -5(10) READ i +5(9) READ i 6(7) element: IF statement 7(8) READ i -8(9) WRITE i -9(2) READ i -10() element: null \ No newline at end of file +8(2) WRITE i +9() element: null \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/refactoring/extractMethod/argsUsedOnlyInAnonymousClass.test b/plugins/groovy/testdata/groovy/refactoring/extractMethod/argsUsedOnlyInAnonymousClass.test new file mode 100644 index 000000000000..0857daf5c4db --- /dev/null +++ b/plugins/groovy/testdata/groovy/refactoring/extractMethod/argsUsedOnlyInAnonymousClass.test @@ -0,0 +1,19 @@ +def foo(b, c) { + [].each (new Closure(this, this) { + void call() { + b.plus(c) + } + }) +} +----- +def foo(b, c) { + testMethod(b, c) +} + +private testMethod(b, c) { + [].each(new Closure(this, this) { + void call() { + b.plus(c) + } + }) +} \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/refactoring/extractMethod/argsUsedOnlyInClosure.test b/plugins/groovy/testdata/groovy/refactoring/extractMethod/argsUsedOnlyInClosure.test new file mode 100644 index 000000000000..7ff5598dbdff --- /dev/null +++ b/plugins/groovy/testdata/groovy/refactoring/extractMethod/argsUsedOnlyInClosure.test @@ -0,0 +1,15 @@ +def foo(b, c) { + [].each { + b.plus(c) + } +} +----- +def foo(b, c) { + testMethod(b, c) +} + +private testMethod(b, c) { + [].each { + b.plus(c) + } +} \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/refactoring/extractMethod/output1.test b/plugins/groovy/testdata/groovy/refactoring/extractMethod/output1.test index d77f380f9410..e1738a81d52c 100644 --- a/plugins/groovy/testdata/groovy/refactoring/extractMethod/output1.test +++ b/plugins/groovy/testdata/groovy/refactoring/extractMethod/output1.test @@ -12,7 +12,7 @@ class S { ----- class S { def r() { - Closure sin = testMethod() + Closure sin = testMethod() 10.times { @@ -21,7 +21,7 @@ class S { } - private Closure testMethod() { + private Closure testMethod() { Closure sin = {x -> Math.sin(x)} return sin } diff --git a/plugins/groovy/testdata/highlighting/PrefixIncrementCfa.groovy b/plugins/groovy/testdata/highlighting/PrefixIncrementCfa.groovy index 711bb7b8c062..e886f8664bdf 100644 --- a/plugins/groovy/testdata/highlighting/PrefixIncrementCfa.groovy +++ b/plugins/groovy/testdata/highlighting/PrefixIncrementCfa.groovy @@ -1,7 +1,7 @@ int idx idx = 2 idx = 3 -if (++idx == 8) { //Assignment is used here +if (++idx == 8) { idx = 33 } print idx \ No newline at end of file diff --git a/plugins/groovy/testdata/highlighting/UnusedInc.groovy b/plugins/groovy/testdata/highlighting/UnusedInc.groovy new file mode 100644 index 000000000000..3483ac2f7b68 --- /dev/null +++ b/plugins/groovy/testdata/highlighting/UnusedInc.groovy @@ -0,0 +1,7 @@ +def a = 4 +print (++a) +print (a++) + +def b = 3 +b++ +b = 3 \ No newline at end of file diff --git a/plugins/groovy/testdata/parsing/groovy/expressions/arithmetic/post1.test b/plugins/groovy/testdata/parsing/groovy/expressions/arithmetic/post1.test index 65d287aed2d6..e51d2abf5bda 100644 --- a/plugins/groovy/testdata/parsing/groovy/expressions/arithmetic/post1.test +++ b/plugins/groovy/testdata/parsing/groovy/expressions/arithmetic/post1.test @@ -3,8 +3,8 @@ Groovy script Unary expression PsiElement(!)('!') - Postfix expression - Postfix expression + Unary expression + Unary expression Literal PsiElement(Integer)('3') PsiElement(++)('++') diff --git a/plugins/groovy/testdata/parsing/groovy/statements/for/for10.test b/plugins/groovy/testdata/parsing/groovy/statements/for/for10.test index 24de169dd13b..bec24b69f0a0 100644 --- a/plugins/groovy/testdata/parsing/groovy/statements/for/for10.test +++ b/plugins/groovy/testdata/parsing/groovy/statements/for/for10.test @@ -31,7 +31,7 @@ Groovy script PsiWhiteSpace(' ') PsiElement(;)(';') PsiWhiteSpace(' ') - Postfix expression + Unary expression Reference expression PsiElement(identifier)('i') PsiElement(++)('++') diff --git a/plugins/groovy/testdata/parsing/groovy/statements/for/for6.test b/plugins/groovy/testdata/parsing/groovy/statements/for/for6.test index 8cea9c0925fb..42f99b3dda73 100644 --- a/plugins/groovy/testdata/parsing/groovy/statements/for/for6.test +++ b/plugins/groovy/testdata/parsing/groovy/statements/for/for6.test @@ -26,7 +26,7 @@ Groovy script PsiElement(Integer)('5') PsiElement(;)(';') PsiWhiteSpace(' ') - Postfix expression + Unary expression Reference expression PsiElement(identifier)('i') PsiElement(++)('++') diff --git a/plugins/groovy/testdata/parsing/groovy/statements/for/for9.test b/plugins/groovy/testdata/parsing/groovy/statements/for/for9.test index 24de169dd13b..bec24b69f0a0 100644 --- a/plugins/groovy/testdata/parsing/groovy/statements/for/for9.test +++ b/plugins/groovy/testdata/parsing/groovy/statements/for/for9.test @@ -31,7 +31,7 @@ Groovy script PsiWhiteSpace(' ') PsiElement(;)(';') PsiWhiteSpace(' ') - Postfix expression + Unary expression Reference expression PsiElement(identifier)('i') PsiElement(++)('++') diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPluginConfigurationDomExtender.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPluginConfigurationDomExtender.java index 44cb3dff60bd..eebd3737476a 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPluginConfigurationDomExtender.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPluginConfigurationDomExtender.java @@ -17,7 +17,9 @@ package org.jetbrains.idea.maven.dom; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiElement; import com.intellij.psi.codeStyle.NameUtil; +import com.intellij.psi.xml.XmlElement; import com.intellij.psi.xml.XmlTag; import com.intellij.util.xml.DomElement; import com.intellij.util.xml.GenericDomValue; @@ -34,13 +36,15 @@ import org.jetbrains.idea.maven.dom.converters.MavenPluginCustomParameterValueCo import org.jetbrains.idea.maven.dom.model.MavenDomConfiguration; import org.jetbrains.idea.maven.dom.model.MavenDomConfigurationParameter; import org.jetbrains.idea.maven.dom.model.MavenDomPluginExecution; -import org.jetbrains.idea.maven.dom.model.MavenDomPluginManagement; import org.jetbrains.idea.maven.dom.plugin.MavenDomMojo; import org.jetbrains.idea.maven.dom.plugin.MavenDomParameter; import org.jetbrains.idea.maven.dom.plugin.MavenDomPluginModel; import java.lang.annotation.Annotation; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; public class MavenPluginConfigurationDomExtender extends DomExtender { public static final Key PLUGIN_PARAMETER_KEY = Key.create("MavenPluginConfigurationDomExtender.PLUGIN_PARAMETER_KEY"); @@ -53,21 +57,26 @@ public class MavenPluginConfigurationDomExtender extends DomExtender collectParameters(MavenDomPluginModel pluginModel, MavenDomConfiguration config) { diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPluginDomUtil.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPluginDomUtil.java index 7cbcfa2e51de..63bcc9862a2b 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPluginDomUtil.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPluginDomUtil.java @@ -20,6 +20,7 @@ import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.xml.DomElement; +import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.maven.dom.model.MavenDomPlugin; import org.jetbrains.idea.maven.dom.plugin.MavenDomPluginModel; import org.jetbrains.idea.maven.project.MavenProjectsManager; @@ -28,6 +29,7 @@ import org.jetbrains.idea.maven.utils.MavenArtifactUtil; import java.io.File; public class MavenPluginDomUtil { + @Nullable public static MavenDomPluginModel getMavenPluginModel(DomElement element) { Project p = element.getXmlElement().getProject(); @@ -40,6 +42,7 @@ public class MavenPluginDomUtil { return MavenDomUtil.getMavenDomModel(p, pluginXmlFile, MavenDomPluginModel.class); } + @Nullable private static VirtualFile getPluginXmlFile(Project p, MavenDomPlugin pluginElement) { String groupId = pluginElement.getGroupId().getStringValue(); String artifactId = pluginElement.getArtifactId().getStringValue(); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenArtifactDownloader.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenArtifactDownloader.java index ced430176ea0..0aee05f9e8dc 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenArtifactDownloader.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenArtifactDownloader.java @@ -90,23 +90,8 @@ public class MavenArtifactDownloader { return download(artifacts, downloadedFiles); } finally { - scheduleFilesRefresh(downloadedFiles); - } - } - - private void scheduleFilesRefresh(final List downloadedFiles) { - Runnable refreshTask = new Runnable() { - public void run() { - LocalFileSystem.getInstance().refreshIoFiles(downloadedFiles); - } - }; - - if (ApplicationManager.getApplication().isUnitTestMode() - || ApplicationManager.getApplication().isDispatchThread()) { - refreshTask.run(); - } - else { - ApplicationManager.getApplication().invokeLater(refreshTask); + boolean isAsync = !ApplicationManager.getApplication().isUnitTestMode(); + LocalFileSystem.getInstance().refreshIoFiles(downloadedFiles, isAsync, false, null); } } diff --git a/plugins/properties/src/com/intellij/lang/properties/xml/XmlPropertiesFile.java b/plugins/properties/src/com/intellij/lang/properties/xml/XmlPropertiesFile.java index 48c21e52d951..ba9f2a9f7424 100644 --- a/plugins/properties/src/com/intellij/lang/properties/xml/XmlPropertiesFile.java +++ b/plugins/properties/src/com/intellij/lang/properties/xml/XmlPropertiesFile.java @@ -31,6 +31,7 @@ import java.util.*; */ public class XmlPropertiesFile implements PropertiesFile { + private static final Key> KEY = Key.create("xml properties file"); private final XmlFile myFile; private final List myProperties = new ArrayList(); private final MultiMap myPropertiesMap = new MultiMap(); @@ -42,7 +43,7 @@ public class XmlPropertiesFile implements PropertiesFile { public static PropertiesFile getPropertiesFile(final XmlFile file) { CachedValuesManager manager = CachedValuesManager.getManager(file.getProject()); - return manager.getCachedValue(file, Key.>create("xml properties file"), + return manager.getCachedValue(file, KEY, new CachedValueProvider() { @Override public Result compute() { @@ -104,13 +105,13 @@ public class XmlPropertiesFile implements PropertiesFile { @NotNull @Override public PsiElement addProperty(@NotNull IProperty property) throws IncorrectOperationException { - return null; //To change body of implemented methods use File | Settings | File Templates. + return null; } @NotNull @Override public PsiElement addPropertyAfter(@NotNull Property property, @Nullable Property anchor) throws IncorrectOperationException { - return null; //To change body of implemented methods use File | Settings | File Templates. + return null; } @Override diff --git a/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskManagerImpl.java b/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskManagerImpl.java index ab2bba3438e0..a3b5c5e6ab15 100644 --- a/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskManagerImpl.java +++ b/plugins/tasks/tasks-core/src/com/intellij/tasks/impl/TaskManagerImpl.java @@ -735,6 +735,7 @@ public class TaskManagerImpl extends TaskManager implements ProjectComponent, Pe ContainerUtil.addAll(issues, tasks); } catch (Exception e) { + LOG.warn("Cannot connect to " + repository, e); myBadRepositories.add(repository); if (forceRequest) { notifyAboutConnectionFailure(repository); diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/DomInvocationHandler.java b/xml/dom-impl/src/com/intellij/util/xml/impl/DomInvocationHandler.java index b7480910ed49..61b1bab28c3c 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/DomInvocationHandler.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/DomInvocationHandler.java @@ -80,7 +80,7 @@ public abstract class DomInvocationHandler" + parameter.getName() + "; method=" + method); + throw new AssertionError("No converter specified: String<->" + parameter.getName() + "; method=" + method + "; place=" + myChildDescription); } return converter; } diff --git a/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/RemoveAttributeIntentionFix.java b/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/RemoveAttributeIntentionFix.java index 8dfa7660095d..4814d65af145 100644 --- a/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/RemoveAttributeIntentionFix.java +++ b/xml/impl/src/com/intellij/codeInsight/daemon/impl/analysis/RemoveAttributeIntentionFix.java @@ -17,25 +17,24 @@ package com.intellij.codeInsight.daemon.impl.analysis; import com.intellij.codeInsight.CodeInsightUtilBase; import com.intellij.codeInsight.daemon.XmlErrorMessages; -import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.xml.XmlAttribute; -import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * @author Maxim.Mossienko */ -public class RemoveAttributeIntentionFix implements IntentionAction { +public class RemoveAttributeIntentionFix extends LocalQuickFixAndIntentionActionOnPsiElement { private final String myLocalName; - private final XmlAttribute myAttribute; public RemoveAttributeIntentionFix(final String localName, final @NotNull XmlAttribute attribute) { + super(attribute); myLocalName = localName; - myAttribute = attribute; } @NotNull @@ -48,20 +47,22 @@ public class RemoveAttributeIntentionFix implements IntentionAction { return XmlErrorMessages.message("remove.attribute.quickfix.family"); } - public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { - return myAttribute.isValid(); - } - - public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + @Override + public void invoke(@NotNull Project project, + @NotNull PsiFile file, + @Nullable("is null when called from inspection") Editor editor, + @NotNull PsiElement startElement, + @NotNull PsiElement endElement) { if (!CodeInsightUtilBase.prepareFileForWrite(file)) return; - PsiElement next = findNextAttribute(myAttribute); - myAttribute.delete(); + PsiElement next = findNextAttribute((XmlAttribute)startElement); + startElement.delete(); - if (next != null) { + if (next != null && editor != null) { editor.getCaretModel().moveToOffset(next.getTextRange().getStartOffset()); } } + @Nullable private static PsiElement findNextAttribute(final XmlAttribute attribute) { PsiElement nextSibling = attribute.getNextSibling(); while (nextSibling != null) { diff --git a/xml/impl/src/com/intellij/codeInsight/editorActions/XmlAutoPopupHandler.java b/xml/impl/src/com/intellij/codeInsight/editorActions/XmlAutoPopupHandler.java index 139ea62d72b4..97cbbcef64c8 100644 --- a/xml/impl/src/com/intellij/codeInsight/editorActions/XmlAutoPopupHandler.java +++ b/xml/impl/src/com/intellij/codeInsight/editorActions/XmlAutoPopupHandler.java @@ -117,7 +117,10 @@ public class XmlAutoPopupHandler extends TypedHandlerDelegate { Boolean result = isRelevantLanguage.get(); if (result == null) { Language language = element.getLanguage(); - if (element instanceof PsiWhiteSpace) language = element.getParent().getLanguage(); + PsiElement parent = element.getParent(); + if (element instanceof PsiWhiteSpace && parent != null) { + language = parent.getLanguage(); + } result = language instanceof XMLLanguage || isAntFile.booleanValue(); isRelevantLanguage.set(result); } diff --git a/xml/impl/src/com/intellij/xml/index/IndexedRelevantResource.java b/xml/impl/src/com/intellij/xml/index/IndexedRelevantResource.java index 17ba1da98290..075b32d8bdf2 100644 --- a/xml/impl/src/com/intellij/xml/index/IndexedRelevantResource.java +++ b/xml/impl/src/com/intellij/xml/index/IndexedRelevantResource.java @@ -21,7 +21,6 @@ import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.util.Function; import com.intellij.util.NullableFunction; import com.intellij.util.indexing.AdditionalIndexedRootsScope; import com.intellij.util.indexing.FileBasedIndex; @@ -38,22 +37,17 @@ import java.util.List; */ public class IndexedRelevantResource implements Comparable> { - public static final Function,VirtualFile> MAPPING = new Function, VirtualFile>() { - public VirtualFile fun(IndexedRelevantResource resource) { - return resource.getFile(); - } - }; - public static List> getResources(ID indexId, final K key, @Nullable final Module module, - @NotNull Project project) { + @NotNull Project project, + @Nullable final GlobalSearchScope additionalScope) { final ArrayList> resources = new ArrayList>(); final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex(); FileBasedIndex.getInstance().processValues(indexId, key, null, new FileBasedIndex.ValueProcessor() { public boolean process(VirtualFile file, V value) { - ResourceRelevance relevance = ResourceRelevance.getRelevance(file, module, fileIndex); + ResourceRelevance relevance = ResourceRelevance.getRelevance(file, module, fileIndex, additionalScope); if (relevance != ResourceRelevance.NONE) { resources.add(new IndexedRelevantResource(file, key, value, relevance)); } @@ -69,7 +63,8 @@ public class IndexedRelevantResource implements Compara ArrayList> all = new ArrayList>(); Collection allKeys = FileBasedIndex.getInstance().getAllKeys(indexId, module.getProject()); for (K key : allKeys) { - List> resources = getResources(indexId, key, module, module.getProject()); + List> resources = getResources(indexId, key, module, module.getProject(), + null); if (!resources.isEmpty()) { if (chooser == null) { all.add(resources.get(0)); diff --git a/xml/impl/src/com/intellij/xml/index/ResourceRelevance.java b/xml/impl/src/com/intellij/xml/index/ResourceRelevance.java index 411ebc65af19..834478dd78bd 100644 --- a/xml/impl/src/com/intellij/xml/index/ResourceRelevance.java +++ b/xml/impl/src/com/intellij/xml/index/ResourceRelevance.java @@ -34,14 +34,17 @@ public enum ResourceRelevance { SOURCE, MAPPED; - public static ResourceRelevance getRelevance(VirtualFile resource, @Nullable Module module, ProjectFileIndex fileIndex) { + public static ResourceRelevance getRelevance(VirtualFile resource, + @Nullable Module module, + ProjectFileIndex fileIndex, + @Nullable GlobalSearchScope additionalScope) { boolean inTest = fileIndex.isInTestSourceContent(resource); if (module != null) { GlobalSearchScope scope = module.getModuleRuntimeScope(inTest); Module resourceModule = fileIndex.getModuleForFile(resource); if (resourceModule != null && (resourceModule == module || scope.isSearchInModuleContent(resourceModule)) || - scope.contains(resource)) { + scope.contains(resource) || (additionalScope != null && additionalScope.contains(resource))) { return inTest || fileIndex.isInSource(resource) ? SOURCE : LIBRARY; } } diff --git a/xml/impl/src/com/intellij/xml/index/XmlNamespaceIndex.java b/xml/impl/src/com/intellij/xml/index/XmlNamespaceIndex.java index 071ea9df83b9..51f584c0ead8 100644 --- a/xml/impl/src/com/intellij/xml/index/XmlNamespaceIndex.java +++ b/xml/impl/src/com/intellij/xml/index/XmlNamespaceIndex.java @@ -45,7 +45,8 @@ public class XmlNamespaceIndex extends XmlIndex { } public static List> getResourcesByNamespace(String namespace, final Project project, Module module) { - List> resources = IndexedRelevantResource.getResources(NAME, namespace, module, project); + List> resources = IndexedRelevantResource.getResources(NAME, namespace, module, project, + null); Collections.sort(resources); return resources; } diff --git a/xml/impl/src/com/intellij/xml/util/ColorSampleLookupValue.java b/xml/impl/src/com/intellij/xml/util/ColorSampleLookupValue.java index ea5d0e6a52d8..0f564d2bf0ec 100644 --- a/xml/impl/src/com/intellij/xml/util/ColorSampleLookupValue.java +++ b/xml/impl/src/com/intellij/xml/util/ColorSampleLookupValue.java @@ -460,4 +460,9 @@ public class ColorSampleLookupValue implements LookupValueWithUIHint, DeferredUs } return null; } + + @Override + public String toString() { + return myName == null ? myValue : myValue + " " + myName; + } }