diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/FileProcessingCompilerAdapterTask.java b/java/compiler/impl/src/com/intellij/compiler/impl/FileProcessingCompilerAdapterTask.java index f3bb520aa61e..8ed8e88e30d0 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/FileProcessingCompilerAdapterTask.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/FileProcessingCompilerAdapterTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import com.intellij.openapi.compiler.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import com.intellij.openapi.vfs.LocalFileSystem; @@ -32,14 +33,14 @@ import java.util.ArrayList; import java.util.List; /** - * This is an adapter for running any FileProcessingCompiler as a compiler task - * + * This is an adapter for running any FileProcessingCompiler as a compiler task. * * @author Eugene Zhuravlev * Date: 9/5/12 */ -public class FileProcessingCompilerAdapterTask implements CompileTask{ +public class FileProcessingCompilerAdapterTask implements CompileTask { private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.FileProcessingCompilerAdapterTask"); + private final FileProcessingCompiler myCompiler; public FileProcessingCompilerAdapterTask(FileProcessingCompiler compiler) { @@ -56,15 +57,15 @@ public class FileProcessingCompilerAdapterTask implements CompileTask{ if (!CompilerWorkspaceConfiguration.getInstance(project).useOutOfProcessBuild()) { return true; } + try { final FileProcessingCompiler.ProcessingItem[] items = myCompiler.getProcessingItems(context); if (items.length == 0) { return true; } + final List toProcess = new ArrayList(); final Ref ex = new Ref(null); - - final FileProcessingCompilerStateCache cache = getCache(context); final boolean isMake = context.isMake(); DumbService.getInstance(project).runReadActionInSmartMode(new Runnable() { @@ -76,7 +77,7 @@ public class FileProcessingCompilerAdapterTask implements CompileTask{ if (isMake && cache.getTimestamp(url) == file.getTimeStamp()) { final ValidityState state = cache.getExtState(url); final ValidityState itemState = item.getValidityState(); - if (state != null ? state.equalsTo(itemState) : itemState == null) { + if (Comparing.equal(state, itemState)) { continue; } } @@ -89,16 +90,17 @@ public class FileProcessingCompilerAdapterTask implements CompileTask{ } }); - if (ex.get() != null) { - throw ex.get(); + IOException exception = ex.get(); + if (exception != null) { + throw exception; } if (toProcess.isEmpty()) { return true; } - final FileProcessingCompiler.ProcessingItem[] processed = myCompiler.process(context, toProcess.toArray(new FileProcessingCompiler.ProcessingItem[toProcess.size()])); - + final FileProcessingCompiler.ProcessingItem[] array = toProcess.toArray(new FileProcessingCompiler.ProcessingItem[toProcess.size()]); + final FileProcessingCompiler.ProcessingItem[] processed = myCompiler.process(context, array); if (processed.length == 0) { return true; } diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/SetValueAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/SetValueAction.java index 04f9dcb991bd..8adff3f3a6ff 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/SetValueAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/SetValueAction.java @@ -481,8 +481,10 @@ public class SetValueAction extends DebuggerAction { stateManager.addListener(new DebuggerContextListener() { public void changeEvent(DebuggerContextImpl newContext, int event) { - stateManager.removeListener(this); - editor.cancelEditing(); + if (event != DebuggerSession.EVENT_THREADS_REFRESH) { + stateManager.removeListener(this); + editor.cancelEditing(); + } } }); diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ViewTextAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/ViewTextAction.java index 780096c0ccda..9c4ec5d39bca 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/ViewTextAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/ViewTextAction.java @@ -102,6 +102,7 @@ public class ViewTextAction extends BaseValueAction { editor.setVerticalScrollbarVisible(true); editor.setEmbeddedIntoDialogWrapper(true); editor.getComponent().setPreferredSize(null); + editor.getSettings().setUseSoftWraps(true); return editor; } } diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java index d4da4d66486e..c54ab3b7c0e2 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java @@ -22,6 +22,7 @@ import com.intellij.debugger.engine.events.DebuggerCommandImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; import com.intellij.debugger.engine.requests.LocatableEventRequestor; import com.intellij.debugger.engine.requests.MethodReturnValueWatcher; +import com.intellij.debugger.impl.DebuggerSession; import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; import com.intellij.debugger.jdi.VirtualMachineProxyImpl; import com.intellij.debugger.requests.Requestor; @@ -39,7 +40,6 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.registry.Registry; import com.intellij.xdebugger.impl.XDebugSessionImpl; import com.sun.jdi.InternalException; import com.sun.jdi.ThreadReference; @@ -182,7 +182,7 @@ public class DebugProcessEvents extends DebugProcessImpl { getManagerThread().invokeAndWait(new DebuggerCommandImpl() { protected void action() throws Exception { - if (eventSet.suspendPolicy() == EventRequest.SUSPEND_ALL && !enableBreakpointsDuringEvaluation()) { + if (eventSet.suspendPolicy() == EventRequest.SUSPEND_ALL && !DebuggerSession.enableBreakpointsDuringEvaluation()) { // check if there is already one request with policy SUSPEND_ALL for (SuspendContextImpl context : getSuspendManager().getEventContexts()) { if (context.getSuspendPolicy() == EventRequest.SUSPEND_ALL) { @@ -420,7 +420,7 @@ public class DebugProcessEvents extends DebugProcessImpl { final SuspendManager suspendManager = getSuspendManager(); SuspendContextImpl evaluatingContext = SuspendManagerUtil.getEvaluatingContext(suspendManager, getSuspendContext().getThread()); - if (evaluatingContext != null && !enableBreakpointsDuringEvaluation()) { + if (evaluatingContext != null && !DebuggerSession.enableBreakpointsDuringEvaluation()) { // is inside evaluation, so ignore any breakpoints suspendManager.voteResume(suspendContext); return; @@ -476,10 +476,6 @@ public class DebugProcessEvents extends DebugProcessImpl { }); } - private static boolean enableBreakpointsDuringEvaluation() { - return Registry.is("debugger.enable.breakpoints.during.evaluation"); - } - private void processDefaultEvent(SuspendContextImpl suspendContext) { preprocessEvent(suspendContext, null); getSuspendManager().voteResume(suspendContext); diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerSession.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerSession.java index aa24a431dac8..19de95a585d3 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerSession.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerSession.java @@ -21,6 +21,7 @@ import com.intellij.debugger.engine.*; import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.EvaluationListener; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; +import com.intellij.debugger.engine.jdi.StackFrameProxy; import com.intellij.debugger.engine.requests.RequestManagerImpl; import com.intellij.debugger.jdi.StackFrameProxyImpl; import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; @@ -43,9 +44,11 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.registry.Registry; import com.intellij.psi.PsiCompiledElement; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; @@ -284,7 +287,12 @@ public class DebuggerSession implements AbstractDebuggerSession { public void resume() { final SuspendContextImpl suspendContext = getSuspendContext(); if(suspendContext != null) { - mySteppingThroughThreads.clear(); + if (suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_ALL) { + mySteppingThroughThreads.clear(); + } + else { + mySteppingThroughThreads.remove(suspendContext.getThread()); + } resetIgnoreStepFiltersFlag(); resumeAction(myDebugProcess.createResumeCommand(suspendContext), EVENT_RESUME); } @@ -408,6 +416,15 @@ public class DebuggerSession implements AbstractDebuggerSession { LOG.debug("paused"); } + if (!shouldSetAsActiveContext(suspendContext)) { + DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() { + public void run() { + getContextManager().fireStateChanged(getContextManager().getContext(), EVENT_THREADS_REFRESH); + } + }); + return; + } + ThreadReferenceProxyImpl currentThread = suspendContext.getThread(); final StackFrameContext positionContext; @@ -503,9 +520,10 @@ public class DebuggerSession implements AbstractDebuggerSession { else if (sourceMissing) { // adjust position to be position of the breakpoint in order to show the real originator of the event position = breakpointPosition; + final StackFrameProxy frameProxy = positionContext.getFrameProxy(); String className; try { - className = positionContext.getFrameProxy().location().declaringType().name(); + className = frameProxy != null? frameProxy.location().declaringType().name() : ""; } catch (EvaluateException e) { className = ""; @@ -527,6 +545,23 @@ public class DebuggerSession implements AbstractDebuggerSession { }); } + private boolean shouldSetAsActiveContext(final SuspendContextImpl suspendContext) { + final ThreadReferenceProxyImpl newThread = suspendContext.getThread(); + if (newThread == null || suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_ALL || isSteppingThrough(newThread)) { + return true; + } + final SuspendContextImpl currentSuspendContext = getContextManager().getContext().getSuspendContext(); + if (currentSuspendContext == null) { + return true; + } + if (enableBreakpointsDuringEvaluation()) { + final ThreadReferenceProxyImpl currentThread = currentSuspendContext.getThread(); + return currentThread == null || Comparing.equal(currentThread.getThreadReference(), newThread.getThreadReference()); + } + return false; + } + + public void resumed(final SuspendContextImpl suspendContext) { final SuspendContextImpl currentContext = getProcess().getSuspendManager().getPausedContext(); DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() { @@ -626,4 +661,9 @@ public class DebuggerSession implements AbstractDebuggerSession { }); } } + + public static boolean enableBreakpointsDuringEvaluation() { + return Registry.is("debugger.enable.breakpoints.during.evaluation"); + } + } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseLibrariesConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseLibrariesConfigurable.java index 321cd77fe74d..711b723141c9 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseLibrariesConfigurable.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/BaseLibrariesConfigurable.java @@ -46,6 +46,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; import java.util.*; public abstract class BaseLibrariesConfigurable extends BaseStructureConfigurable { @@ -257,14 +258,23 @@ public abstract class BaseLibrariesConfigurable extends BaseStructureConfigurabl } public void removeLibrary(@NotNull LibraryProjectStructureElement element) { - getModelProvider().getModifiableModel().removeLibrary(element.getLibrary()); - myContext.getDaemonAnalyzer().removeElement(element); - final MyNode node = findNodeByObject(myRoot, element.getLibrary()); - if (node != null) { - removePaths(TreeUtil.getPathFromRoot(node)); - } + removeLibraries(Collections.singletonList(element)); } + public void removeLibraries(@NotNull List libraries) { + List pathsToRemove = new ArrayList(); + for (LibraryProjectStructureElement element : libraries) { + getModelProvider().getModifiableModel().removeLibrary(element.getLibrary()); + MyNode node = findNodeByObject(myRoot, element.getLibrary()); + if (node != null) { + pathsToRemove.add(TreeUtil.getPathFromRoot(node)); + } + } + myContext.getDaemonAnalyzer().removeElements(libraries); + removePaths(pathsToRemove.toArray(new TreePath[pathsToRemove.size()])); + } + + @Override protected boolean removeLibrary(final Library library) { final LibraryTable table = library.getTable(); diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/LibraryProjectStructureElement.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/LibraryProjectStructureElement.java index 930cb514a80a..1fc3dbd66162 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/LibraryProjectStructureElement.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/LibraryProjectStructureElement.java @@ -41,6 +41,7 @@ import org.jetbrains.annotations.NotNull; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -148,7 +149,7 @@ public class LibraryProjectStructureElement extends ProjectStructureElement { @Override public ProjectStructureProblemDescription createUnusedElementWarning() { - final List fixes = Arrays.asList(new AddLibraryToDependenciesFix(), new RemoveLibraryFix()); + final List fixes = Arrays.asList(new AddLibraryToDependenciesFix(), new RemoveLibraryFix(), new RemoveAllUnusedLibrariesFix()); return new ProjectStructureProblemDescription("Library '" + StringUtil.escapeXml(myLibrary.getName()) + "'" + " is not used", null, createPlace(), ProjectStructureProblemType.unused("unused-library"), ProjectStructureProblemDescription.ProblemLevel.PROJECT, fixes, false); @@ -225,4 +226,24 @@ public class LibraryProjectStructureElement extends ProjectStructureElement { BaseLibrariesConfigurable.getInstance(myContext.getProject(), myLibrary.getTable().getTableLevel()).removeLibrary(LibraryProjectStructureElement.this); } } + + private class RemoveAllUnusedLibrariesFix extends ConfigurationErrorQuickFix { + private RemoveAllUnusedLibrariesFix() { + super("Remove All Unused Libraries"); + } + + @Override + public void performFix() { + BaseLibrariesConfigurable configurable = BaseLibrariesConfigurable.getInstance(myContext.getProject(), LibraryTablesRegistrar.PROJECT_LEVEL); + Library[] libraries = configurable.getModelProvider().getModifiableModel().getLibraries(); + List toRemove = new ArrayList(); + for (Library library : libraries) { + LibraryProjectStructureElement libraryElement = new LibraryProjectStructureElement(myContext, library); + if (myContext.getDaemonAnalyzer().getUsages(libraryElement).isEmpty()) { + toRemove.add(libraryElement); + } + } + configurable.removeLibraries(toRemove); + } + } } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureDaemonAnalyzer.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureDaemonAnalyzer.java index 91183b9b5655..97ed5b0da733 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureDaemonAnalyzer.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/projectRoot/daemon/ProjectStructureDaemonAnalyzer.java @@ -7,13 +7,13 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.MultiValuesMap; +import com.intellij.util.Alarm; import com.intellij.util.EventDispatcher; import com.intellij.util.ui.update.MergingUpdateQueue; import com.intellij.util.ui.update.Update; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.swing.*; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; @@ -29,6 +29,7 @@ public class ProjectStructureDaemonAnalyzer implements Disposable { private final Set myElementsToShowWarningIfUnused = new HashSet(); private final Map myWarningsAboutUnused = new HashMap(); private final MergingUpdateQueue myAnalyzerQueue; + private final MergingUpdateQueue myResultsUpdateQueue; private final EventDispatcher myDispatcher = EventDispatcher.create(ProjectStructureDaemonAnalyzerListener.class); private final AtomicBoolean myStopped = new AtomicBoolean(false); private final ProjectConfigurationProblems myProjectConfigurationProblems; @@ -36,7 +37,9 @@ public class ProjectStructureDaemonAnalyzer implements Disposable { public ProjectStructureDaemonAnalyzer(StructureConfigurableContext context) { Disposer.register(context, this); myProjectConfigurationProblems = new ProjectConfigurationProblems(this, context); - myAnalyzerQueue = new MergingUpdateQueue("Project Structure Daemon Analyzer", 300, false, null, this, null, false); + myAnalyzerQueue = new MergingUpdateQueue("Project Structure Daemon Analyzer", 300, false, null, this, null, Alarm.ThreadToUse.POOLED_THREAD); + myResultsUpdateQueue = new MergingUpdateQueue("Project Structure Analysis Results Updater", 300, false, MergingUpdateQueue.ANY_COMPONENT, + this, null, Alarm.ThreadToUse.SWING_THREAD); } private void doUpdate(final ProjectStructureElement element, final boolean check, final boolean collectUsages) { @@ -63,21 +66,7 @@ public class ProjectStructureDaemonAnalyzer implements Disposable { ProjectStructureValidator.check(element, problemsHolder); } }.execute(); - invokeLater(new Runnable() { - @Override - public void run() { - if (myStopped.get()) return; - - if (LOG.isDebugEnabled()) { - LOG.debug("updating problems for " + element); - } - final ProjectStructureProblemDescription warning = myWarningsAboutUnused.get(element); - if (warning != null) - problemsHolder.registerProblem(warning); - myProblemHolders.put(element, problemsHolder); - myDispatcher.getMulticaster().problemsChanged(element); - } - }); + myResultsUpdateQueue.queue(new ProblemsComputedUpdate(element, problemsHolder)); } private void doCollectUsages(final ProjectStructureElement element) { @@ -92,18 +81,9 @@ public class ProjectStructureDaemonAnalyzer implements Disposable { result.setResult(getUsagesInElement(element)); } }.execute().getResultObject(); - - invokeLater(new Runnable() { - @Override - public void run() { - if (myStopped.get() || usages == null) return; - - if (LOG.isDebugEnabled()) { - LOG.debug("updating usages for " + element); - } - updateUsages(element, usages); - } - }); + if (usages != null) { + myResultsUpdateQueue.queue(new UsagesCollectedUpdate(element, usages)); + } } private static List getUsagesInElement(final ProjectStructureElement element) { @@ -116,11 +96,7 @@ public class ProjectStructureDaemonAnalyzer implements Disposable { addUsage(usage); } myElementWithNotCalculatedUsages.remove(element); - reportUnusedElements(); - } - - private static void invokeLater(Runnable runnable) { - SwingUtilities.invokeLater(runnable); + myResultsUpdateQueue.queue(new ReportUnusedElementsUpdate()); } public void queueUpdate(@NotNull final ProjectStructureElement element) { @@ -141,21 +117,28 @@ public class ProjectStructureDaemonAnalyzer implements Disposable { } public void removeElement(ProjectStructureElement element) { - myElementWithNotCalculatedUsages.remove(element); - myElementsToShowWarningIfUnused.remove(element); - myWarningsAboutUnused.remove(element); - myProblemHolders.remove(element); - final Collection usages = mySourceElement2Usages.removeAll(element); - if (usages != null) { - for (ProjectStructureElementUsage usage : usages) { - myProblemHolders.remove(usage.getContainingElement()); - } - } - removeUsagesInElement(element); - myDispatcher.getMulticaster().problemsChanged(element); - reportUnusedElements(); + removeElements(Collections.singletonList(element)); } + public void removeElements(@NotNull List elements) { + myElementWithNotCalculatedUsages.removeAll(elements); + myElementsToShowWarningIfUnused.removeAll(elements); + for (ProjectStructureElement element : elements) { + myWarningsAboutUnused.remove(element); + myProblemHolders.remove(element); + final Collection usages = mySourceElement2Usages.removeAll(element); + if (usages != null) { + for (ProjectStructureElementUsage usage : usages) { + myProblemHolders.remove(usage.getContainingElement()); + } + } + removeUsagesInElement(element); + myDispatcher.getMulticaster().problemsChanged(element); + } + myResultsUpdateQueue.queue(new ReportUnusedElementsUpdate()); + } + + private void reportUnusedElements() { if (!myElementWithNotCalculatedUsages.isEmpty()) return; @@ -205,8 +188,10 @@ public class ProjectStructureDaemonAnalyzer implements Disposable { LOG.debug("analyzer stopped"); myStopped.set(true); myAnalyzerQueue.cancelAllUpdates(); + myResultsUpdateQueue.cancelAllUpdates(); clearCaches(); myAnalyzerQueue.deactivate(); + myResultsUpdateQueue.deactivate(); } public void clearCaches() { @@ -232,6 +217,7 @@ public class ProjectStructureDaemonAnalyzer implements Disposable { public void dispose() { myStopped.set(true); myAnalyzerQueue.cancelAllUpdates(); + myResultsUpdateQueue.cancelAllUpdates(); } @Nullable @@ -256,6 +242,7 @@ public class ProjectStructureDaemonAnalyzer implements Disposable { public void reset() { LOG.debug("analyzer started"); myAnalyzerQueue.activate(); + myResultsUpdateQueue.activate(); myAnalyzerQueue.queue(new Update("reset") { @Override public void run() { @@ -270,9 +257,7 @@ public class ProjectStructureDaemonAnalyzer implements Disposable { mySourceElement2Usages.clear(); myContainingElement2Usages.clear(); myElementWithNotCalculatedUsages.clear(); - if (myProjectConfigurationProblems != null) { - myProjectConfigurationProblems.clearProblems(); - } + myProjectConfigurationProblems.clearProblems(); } private class AnalyzeElementUpdate extends Update { @@ -312,4 +297,78 @@ public class ProjectStructureDaemonAnalyzer implements Disposable { } } } + + private class UsagesCollectedUpdate extends Update { + private final ProjectStructureElement myElement; + private final List myUsages; + private final Object[] myEqualityObjects; + + public UsagesCollectedUpdate(ProjectStructureElement element, List usages) { + super(element); + myElement = element; + myUsages = usages; + myEqualityObjects = new Object[]{element, "usages collected"}; + } + + @NotNull + @Override + public Object[] getEqualityObjects() { + return myEqualityObjects; + } + + @Override + public void run() { + if (myStopped.get()) return; + + if (LOG.isDebugEnabled()) { + LOG.debug("updating usages for " + myElement); + } + updateUsages(myElement, myUsages); + } + } + + private class ProblemsComputedUpdate extends Update { + private final ProjectStructureElement myElement; + private final ProjectStructureProblemsHolderImpl myProblemsHolder; + private final Object[] myEqualityObjects; + + public ProblemsComputedUpdate(ProjectStructureElement element, ProjectStructureProblemsHolderImpl problemsHolder) { + super(element); + myElement = element; + myProblemsHolder = problemsHolder; + myEqualityObjects = new Object[]{element, "problems computed"}; + } + + @NotNull + @Override + public Object[] getEqualityObjects() { + return myEqualityObjects; + } + + @Override + public void run() { + if (myStopped.get()) return; + + if (LOG.isDebugEnabled()) { + LOG.debug("updating problems for " + myElement); + } + final ProjectStructureProblemDescription warning = myWarningsAboutUnused.get(myElement); + if (warning != null) { + myProblemsHolder.registerProblem(warning); + } + myProblemHolders.put(myElement, myProblemsHolder); + myDispatcher.getMulticaster().problemsChanged(myElement); + } + } + + private class ReportUnusedElementsUpdate extends Update { + private ReportUnusedElementsUpdate() { + super("unused elements"); + } + + @Override + public void run() { + reportUnusedElements(); + } + } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/localCanBeFinal/LocalCanBeFinal.java b/java/java-analysis-impl/src/com/intellij/codeInspection/localCanBeFinal/LocalCanBeFinal.java index 028e9b7a7e9d..d4cff693643a 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/localCanBeFinal/LocalCanBeFinal.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/localCanBeFinal/LocalCanBeFinal.java @@ -18,6 +18,7 @@ package com.intellij.codeInspection.localCanBeFinal; import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInsight.daemon.GroupNames; import com.intellij.codeInspection.*; +import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.psi.*; @@ -44,6 +45,8 @@ public class LocalCanBeFinal extends BaseJavaBatchLocalInspectionTool { public boolean REPORT_VARIABLES = true; public boolean REPORT_PARAMETERS = true; + public boolean REPORT_CATCH_PARAMETERS = true; + public boolean REPORT_FOREACH_PARAMETERS = true; private final LocalQuickFix myQuickFix; @NonNls public static final String SHORT_NAME = "LocalCanBeFinal"; @@ -148,6 +151,7 @@ public class LocalCanBeFinal extends BaseJavaBatchLocalInspectionTool { @Override public void visitCatchSection(PsiCatchSection section) { super.visitCatchSection(section); + if (!REPORT_CATCH_PARAMETERS) return; final PsiParameter parameter = section.getParameter(); if (PsiTreeUtil.getParentOfType(parameter, PsiClass.class) != PsiTreeUtil.getParentOfType(body, PsiClass.class)) { return; @@ -164,6 +168,7 @@ public class LocalCanBeFinal extends BaseJavaBatchLocalInspectionTool { @Override public void visitForeachStatement(PsiForeachStatement statement) { super.visitForeachStatement(statement); + if (!REPORT_FOREACH_PARAMETERS) return; final PsiParameter param = statement.getIterationParameter(); if (PsiTreeUtil.getParentOfType(param, PsiClass.class) != PsiTreeUtil.getParentOfType(body, PsiClass.class)) { return; @@ -219,7 +224,7 @@ public class LocalCanBeFinal extends BaseJavaBatchLocalInspectionTool { } }); - if (body.getParent() instanceof PsiMethod && isReportParameters()) { + if (body.getParent() instanceof PsiMethod && REPORT_PARAMETERS) { final PsiMethod method = (PsiMethod)body.getParent(); if (!(method instanceof SyntheticElement)) { // e.g. JspHolderMethod Collections.addAll(result, method.getParameterList().getParameters()); @@ -231,6 +236,16 @@ public class LocalCanBeFinal extends BaseJavaBatchLocalInspectionTool { if (shouldBeIgnored(variable)) { iterator.remove(); } + final PsiElement parent = variable.getParent(); + if (!(parent instanceof PsiDeclarationStatement)) { + continue; + } + final PsiDeclarationStatement declarationStatement = (PsiDeclarationStatement)parent; + final PsiElement[] elements = declarationStatement.getDeclaredElements(); + final PsiElement grandParent = parent.getParent(); + if (elements.length > 1 && grandParent instanceof PsiForStatement) { + iterator.remove(); // do not report when more than 1 variable declared in for loop + } } for (PsiVariable writtenVariable : writtenVariables) { @@ -261,19 +276,21 @@ public class LocalCanBeFinal extends BaseJavaBatchLocalInspectionTool { private boolean shouldBeIgnored(PsiVariable psiVariable) { if (psiVariable.hasModifierProperty(PsiModifier.FINAL)) return true; - return isLocalVariable(psiVariable) ? !isReportVariables() : !isReportParameters(); - } - - private static boolean isLocalVariable(PsiVariable variable) { - if (variable instanceof PsiLocalVariable) { - return true; + if (psiVariable instanceof PsiLocalVariable) { + return !REPORT_VARIABLES; } - if (!(variable instanceof PsiParameter)) { - return false; + if (psiVariable instanceof PsiParameter) { + final PsiParameter parameter = (PsiParameter)psiVariable; + final PsiElement declarationScope = parameter.getDeclarationScope(); + if (declarationScope instanceof PsiCatchSection) { + return !REPORT_CATCH_PARAMETERS; + } + else if (declarationScope instanceof PsiForeachStatement) { + return !REPORT_FOREACH_PARAMETERS; + } + return !REPORT_PARAMETERS; } - final PsiParameter parameter = (PsiParameter)variable; - final PsiElement declarationScope = parameter.getDeclarationScope(); - return !(declarationScope instanceof PsiMethod) && !(declarationScope instanceof PsiLambdaExpression); + return true; } @Override @@ -326,55 +343,12 @@ public class LocalCanBeFinal extends BaseJavaBatchLocalInspectionTool { @Override public JComponent createOptionsPanel() { - return new OptionsPanel(); - } - - private boolean isReportVariables() { - return REPORT_VARIABLES; - } - - private boolean isReportParameters() { - return REPORT_PARAMETERS; - } - - private class OptionsPanel extends JPanel { - private final JCheckBox myReportVariablesCheckbox; - private final JCheckBox myReportParametersCheckbox; - - private OptionsPanel() { - super(new GridBagLayout()); - - GridBagConstraints gc = new GridBagConstraints(); - gc.weighty = 0; - gc.weightx = 1; - gc.fill = GridBagConstraints.HORIZONTAL; - gc.anchor = GridBagConstraints.NORTHWEST; - - - myReportVariablesCheckbox = new JCheckBox(InspectionsBundle.message("inspection.local.can.be.final.option")); - myReportVariablesCheckbox.setSelected(REPORT_VARIABLES); - myReportVariablesCheckbox.getModel().addChangeListener(new ChangeListener() { - @Override - public void stateChanged(ChangeEvent e) { - REPORT_VARIABLES = myReportVariablesCheckbox.isSelected(); - } - }); - gc.gridy = 0; - add(myReportVariablesCheckbox, gc); - - myReportParametersCheckbox = new JCheckBox(InspectionsBundle.message("inspection.local.can.be.final.option1")); - myReportParametersCheckbox.setSelected(REPORT_PARAMETERS); - myReportParametersCheckbox.getModel().addChangeListener(new ChangeListener() { - @Override - public void stateChanged(ChangeEvent e) { - REPORT_PARAMETERS = myReportParametersCheckbox.isSelected(); - } - }); - - gc.weighty = 1; - gc.gridy++; - add(myReportParametersCheckbox, gc); - } + final MultipleCheckboxOptionsPanel panel = new MultipleCheckboxOptionsPanel(this); + panel.addCheckbox(InspectionsBundle.message("inspection.local.can.be.final.option"), "REPORT_VARIABLES"); + panel.addCheckbox(InspectionsBundle.message("inspection.local.can.be.final.option1"), "REPORT_PARAMETERS"); + panel.addCheckbox(InspectionsBundle.message("inspection.local.can.be.final.option2"), "REPORT_CATCH_PARAMETERS"); + panel.addCheckbox(InspectionsBundle.message("inspection.local.can.be.final.option3"), "REPORT_FOREACH_PARAMETERS"); + return panel; } @Override diff --git a/java/java-impl/src/com/intellij/ide/util/gotoByName/DefaultSymbolNavigationContributor.java b/java/java-impl/src/com/intellij/ide/util/gotoByName/DefaultSymbolNavigationContributor.java index fa3cd31c2546..243ecb220419 100644 --- a/java/java-impl/src/com/intellij/ide/util/gotoByName/DefaultSymbolNavigationContributor.java +++ b/java/java-impl/src/com/intellij/ide/util/gotoByName/DefaultSymbolNavigationContributor.java @@ -19,6 +19,7 @@ import com.intellij.ide.util.DefaultPsiElementCellRenderer; import com.intellij.navigation.ChooseByNameContributorEx; import com.intellij.navigation.NavigationItem; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.impl.PsiSuperMethodImplUtil; @@ -30,13 +31,11 @@ import com.intellij.util.Processor; import com.intellij.util.containers.HashSet; import com.intellij.util.indexing.FindSymbolParameters; import com.intellij.util.indexing.IdFilter; +import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; +import java.util.*; public class DefaultSymbolNavigationContributor implements ChooseByNameContributorEx { private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.gotoByName.DefaultSymbolNavigationContributor"); @@ -111,27 +110,40 @@ public class DefaultSymbolNavigationContributor implements ChooseByNameContribut IdFilter filter = parameters.getIdFilter(); PsiShortNamesCache cache = PsiShortNamesCache.getInstance(scope.getProject()); //noinspection UnusedDeclaration - boolean dummy = cache.processFieldsWithName(name, new Processor() { + final Set collectedMethods = new THashSet(); + boolean success = cache.processFieldsWithName(name, new Processor() { @Override public boolean process(PsiField field) { if (isOpenable(field)) return processor.process(field); return true; } }, scope, filter) && - cache.processMethodsWithName(name, new Processor() { - @Override - public boolean process(PsiMethod method) { - if(!method.isConstructor() && isOpenable(method) && !hasSuperMethod(method)) return processor.process(method); - return true; - } - }, scope, filter) && cache.processClassesWithName(name, new Processor() { @Override public boolean process(PsiClass aClass) { if (isOpenable(aClass)) return processor.process(aClass); return true; } + }, scope, filter) && + cache.processMethodsWithName(name, new Processor() { + @Override + public boolean process(PsiMethod method) { + if(!method.isConstructor() && isOpenable(method)) { + collectedMethods.add(method); + } + return true; + } }, scope, filter); + if (success) { + // hashSuperMethod accesses index and can not be invoked without risk of the deadlock in processMethodsWithName + Iterator iterator = collectedMethods.iterator(); + while(iterator.hasNext()) { + PsiMethod method = iterator.next(); + if (!hasSuperMethod(method) && !processor.process(method)) return; + ProgressManager.checkCanceled(); + iterator.remove(); + } + } } private static class MyComparator implements Comparator{ diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java b/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java index d392d7a7a481..8855474a098d 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/AbstractJavaBlock.java @@ -1178,7 +1178,6 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo } private boolean shouldEnforceIndentToChildren(@NotNull ASTNode node) { - // Filter only anonymous class instances as method call arguments if (myNode.getElementType() != JavaElementType.EXPRESSION_LIST) { return false; } @@ -1186,13 +1185,10 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo if (parent == null || parent.getElementType() != JavaElementType.METHOD_CALL_EXPRESSION) { return false; } - if (!isAnonymousClass(node) || !JavaFormatterUtil.hasAnonymousClassesArguments((PsiExpressionList)myNode.getPsi(), 2)) { - return false; - } - - // Enforce indent only if anonymous class instance expression doesn't start new line and have anonymous class expression sibling. - ASTNode prev = node.getTreePrev(); - return prev != null && !(StringUtil.containsLineBreak(prev.getChars()) && prev.getElementType() != TokenType.WHITE_SPACE); + + PsiExpressionList methodParamsList = (PsiExpressionList)myNode.getPsi(); + return JavaFormatterUtil.hasMultilineArguments(methodParamsList) + && JavaFormatterUtil.isMultilineExceptArguments(methodParamsList); } private static boolean isAnonymousClass(@Nullable ASTNode node) { diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/JavaFormatterUtil.java b/java/java-impl/src/com/intellij/psi/formatter/java/JavaFormatterUtil.java index 2f6c7f9b6a54..3f63eda2fb11 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/JavaFormatterUtil.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/JavaFormatterUtil.java @@ -19,10 +19,10 @@ import com.intellij.lang.ASTNode; import com.intellij.psi.PsiExpression; import com.intellij.psi.PsiExpressionList; import com.intellij.psi.PsiPolyadicExpression; +import com.intellij.psi.PsiWhiteSpace; import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.HashSet; import java.util.Set; @@ -75,35 +75,36 @@ public class JavaFormatterUtil { return expression1.getOperationTokenType() == expression2.getOperationTokenType(); } - /** - * Allows to check if given expression list has given number of anonymous classes. - * - * @param count interested number of anonymous classes used at the given expression list - * @return true if given expression list contains given number of anonymous classes; - * false otherwise - */ - public static boolean hasAnonymousClassesArguments(@NotNull PsiExpressionList expressionList, int count) { - int found = 0; - for (PsiExpression expression : expressionList.getExpressions()) { - ASTNode node = expression.getNode(); - if (isAnonymousClass(node)) { - found++; - } - if (found >= count) { + + public static boolean hasMultilineArguments(@NotNull PsiExpressionList list) { + PsiExpression[] arguments = list.getExpressions(); + + for (PsiExpression argument: arguments) { + ASTNode node = argument.getNode(); + if (node.textContains('\n')) return true; - } } + return false; } - private static boolean isAnonymousClass(@Nullable final ASTNode node) { - if (node == null) { - return false; + public static boolean isMultilineExceptArguments(@NotNull PsiExpressionList list) { + PsiExpression[] arguments = list.getExpressions(); + + for (PsiExpression argument : arguments) { + ASTNode beforeArgument = argument.getNode().getTreePrev(); + if (isWhiteSpaceWithLineFeed(beforeArgument)) + return true; } - ASTNode nodeToCheck = node; - if (node.getElementType() == JavaElementType.NEW_EXPRESSION) { - nodeToCheck = node.getLastChildNode(); - } - return nodeToCheck != null && nodeToCheck.getElementType() == JavaElementType.ANONYMOUS_CLASS; + + PsiExpression lastArgument = arguments[arguments.length - 1]; + ASTNode afterLastArgument = lastArgument.getNode().getTreeNext(); + return isWhiteSpaceWithLineFeed(afterLastArgument); } + + private static boolean isWhiteSpaceWithLineFeed(@NotNull ASTNode node) { + return node instanceof PsiWhiteSpace + && node.textContains('\n'); + } + } diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java b/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java index 8f090b072d16..1dfe426c28ec 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java @@ -1135,7 +1135,7 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor { createParenthSpace(mySettings.CALL_PARAMETERS_LPAREN_ON_NEXT_LINE, mySettings.SPACE_WITHIN_EMPTY_METHOD_CALL_PARENTHESES); } else if (myRole2 == ChildRole.RPARENTH) { - if (JavaFormatterUtil.hasAnonymousClassesArguments(list, 2)) { + if (JavaFormatterUtil.hasMultilineArguments(list) && JavaFormatterUtil.isMultilineExceptArguments(list)) { myResult = Spacing.createSpacing(0, 0, 1, mySettings.KEEP_LINE_BREAKS, 0); } else { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiLiteralExpressionImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiLiteralExpressionImpl.java index d14c08ed1b6d..cf099fb33304 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiLiteralExpressionImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiLiteralExpressionImpl.java @@ -382,7 +382,10 @@ public class PsiLiteralExpressionImpl @Override @NotNull public PsiReference[] getReferences() { - if (textMatches(PsiKeyword.NULL)) return PsiReference.EMPTY_ARRAY; + IElementType type = getLiteralElementType(); + if (type != JavaTokenType.STRING_LITERAL && type != JavaTokenType.INTEGER_LITERAL) { + return PsiReference.EMPTY_ARRAY; // there are references in int literals in SQL API parameters + } return PsiReferenceService.getService().getContributedReferences(this); } diff --git a/java/java-tests/testData/inspection/localCanBeFinal/For/src/For.java b/java/java-tests/testData/inspection/localCanBeFinal/For/src/For.java index 529844bc5326..4a10521bf860 100644 --- a/java/java-tests/testData/inspection/localCanBeFinal/For/src/For.java +++ b/java/java-tests/testData/inspection/localCanBeFinal/For/src/For.java @@ -1,7 +1,8 @@ class For { void f(List list) { - for (Iterator it = list.iterator(); it.hasNext();) { // 'it' can be final but not reported + for (Iterator it = list.iterator(); it.hasNext();) { } for (int i = 0; i < 10; i++) {} + for (int i = 0, length = 10; i < length; i++) {} } } \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/ParametersAlignment_after.java b/java/java-tests/testData/psi/formatter/java/ParametersAlignment_after.java index 0e0dee8f9cf8..cf135bfa6837 100644 --- a/java/java-tests/testData/psi/formatter/java/ParametersAlignment_after.java +++ b/java/java-tests/testData/psi/formatter/java/ParametersAlignment_after.java @@ -7,6 +7,7 @@ class Foo { settings, indentOptions, child2.getTextRange(), - false); + false + ); } } \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/LocalCanBeFinalTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/LocalCanBeFinalTest.java index e29b65c31bcc..61895f6cb132 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/LocalCanBeFinalTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/LocalCanBeFinalTest.java @@ -127,12 +127,14 @@ public class LocalCanBeFinalTest extends InspectionTestCase { public void testForeachNotReported() throws Exception { myTool.REPORT_PARAMETERS = true; myTool.REPORT_VARIABLES = false; + myTool.REPORT_FOREACH_PARAMETERS = false; doTest(); } public void testNestedForeach() throws Exception { myTool.REPORT_PARAMETERS = false; myTool.REPORT_VARIABLES = true; + myTool.REPORT_FOREACH_PARAMETERS = true; doTest(); } diff --git a/java/java-tests/testSrc/com/intellij/openapi/roots/impl/DirectoryIndexTest.java b/java/java-tests/testSrc/com/intellij/openapi/roots/impl/DirectoryIndexTest.java index dbdbb5aff9b9..3641d9dc06f4 100644 --- a/java/java-tests/testSrc/com/intellij/openapi/roots/impl/DirectoryIndexTest.java +++ b/java/java-tests/testSrc/com/intellij/openapi/roots/impl/DirectoryIndexTest.java @@ -413,6 +413,9 @@ public class DirectoryIndexTest extends IdeaTestCase { checkInfoNull(module2TestOutput); assertFalse(myIndex.isProjectExcludeRoot(excluded)); + assertTrue(myIndex.isProjectExcludeRoot(projectOutput)); + assertTrue(myIndex.isProjectExcludeRoot(module2Output)); + assertFalse(myIndex.isProjectExcludeRoot(module2TestOutput)); excluded.delete(this); projectOutput.delete(this); @@ -426,14 +429,34 @@ public class DirectoryIndexTest extends IdeaTestCase { VirtualFile file = e.getFile(); checkInfoNull(file); created.add(file); + + String fileName = e.getFileName(); + if (fileName.equals("projectOutput")) { + //todo fix and uncomment +// assertTrue(myIndex.isProjectExcludeRoot(file)); + } + if (fileName.equals("module2Output")) { + //todo fix and uncomment +// assertTrue(myIndex.isProjectExcludeRoot(file)); + } + if (fileName.equals("module2TestOutput")) { + assertFalse(myIndex.isProjectExcludeRoot(file)); + } } }; VirtualFileManager.getInstance().addVirtualFileListener(l, getTestRootDisposable()); + excluded = myModule1Dir.createChildDirectory(this, excluded.getName()); assertFalse(myIndex.isProjectExcludeRoot(excluded)); + projectOutput = myModule1Dir.createChildDirectory(this, projectOutput.getName()); + assertTrue(myIndex.isProjectExcludeRoot(projectOutput)); + module2Output = myModule1Dir.createChildDirectory(this, module2Output.getName()); + assertTrue(myIndex.isProjectExcludeRoot(module2Output)); + module2TestOutput = myModule2Dir.createChildDirectory(this, module2TestOutput.getName()); + assertFalse(myIndex.isProjectExcludeRoot(module2TestOutput)); checkInfoNull(excluded); checkInfoNull(projectOutput); @@ -443,6 +466,9 @@ public class DirectoryIndexTest extends IdeaTestCase { assertEquals(created.toString(), 4, created.size()); assertFalse(myIndex.isProjectExcludeRoot(excluded)); + assertTrue(myIndex.isProjectExcludeRoot(projectOutput)); + assertTrue(myIndex.isProjectExcludeRoot(module2Output)); + assertFalse(myIndex.isProjectExcludeRoot(module2TestOutput)); } public void testExcludesShouldBeRecognizedRightOnRefresh() throws Exception { @@ -539,6 +565,28 @@ public class DirectoryIndexTest extends IdeaTestCase { assertTrue(myIndex.isProjectExcludeRoot(myOutputDir)); assertTrue(myIndex.isProjectExcludeRoot(myModule1OutputDir)); assertTrue(fileIndex.isIgnored(myModule1OutputDir)); + + PsiTestUtil.setCompilerOutputPath(myModule, moduleOutputUrl, true); + PsiTestUtil.setCompilerOutputPath(myModule2, moduleOutputUrl, false); + PsiTestUtil.setCompilerOutputPath(myModule2, moduleOutputUrl, true); + PsiTestUtil.setCompilerOutputPath(myModule3, moduleOutputUrl, false); + PsiTestUtil.setCompilerOutputPath(myModule3, moduleOutputUrl, true); + + // now no module inherits project output dir, but it still should be project-excluded + assertTrue(myIndex.isProjectExcludeRoot(myOutputDir)); + + // project output inside module content shouldn't be projectExcludeRoot + VirtualFile projectOutputUnderContent = myModule1Dir.createChildDirectory(this, "projectOutputUnderContent"); + getCompilerProjectExtension().setCompilerOutputUrl(projectOutputUnderContent.getUrl()); + fireRootsChanged(); + + assertFalse(myIndex.isProjectExcludeRoot(myOutputDir)); + //todo uncomment assertFalse(myIndex.isProjectExcludeRoot(projectOutputUnderContent)); + + projectOutputUnderContent.delete(this); + projectOutputUnderContent = myModule1Dir.createChildDirectory(this, "projectOutputUnderContent"); + assertFalse(myIndex.isProjectExcludeRoot(myOutputDir)); + //todo uncomment assertFalse(myIndex.isProjectExcludeRoot(projectOutputUnderContent)); } private void checkInfo(VirtualFile dir, diff --git a/java/java-tests/testSrc/com/intellij/psi/ClsDuplicatesTest.java b/java/java-tests/testSrc/com/intellij/psi/ClsDuplicatesTest.java index 2e0ee1608276..47189fb7d493 100644 --- a/java/java-tests/testSrc/com/intellij/psi/ClsDuplicatesTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/ClsDuplicatesTest.java @@ -39,7 +39,7 @@ public class ClsDuplicatesTest extends PsiTestCase { JavaRecursiveElementVisitor visitor = new JavaRecursiveElementVisitor() { @Override public void visitPackage(PsiPackage aPackage) { - System.out.println(aPackage.getQualifiedName()); +// System.out.println(aPackage.getQualifiedName()); visit(aPackage); for (PsiPackage subPackage : aPackage.getSubPackages(scope)) { @@ -65,7 +65,7 @@ public class ClsDuplicatesTest extends PsiTestCase { } super.visitClass(aClass); PsiElement parent = aClass.getParent(); - if (!(parent instanceof PsiClass)){ + if (parent instanceof PsiFile){ uniques.clear(); } } diff --git a/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerFieldReferenceTest.groovy b/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerFieldReferenceTest.groovy index a2a996a36048..2ec1fb2a439a 100644 --- a/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerFieldReferenceTest.groovy +++ b/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerFieldReferenceTest.groovy @@ -84,8 +84,8 @@ public class CodeFormatTest { public static String TEST = "OK!"; public static String BOOK = "ATLAS"; private static String PREFIX = "prefix."; - private static String PRIVATE_NAME = PREFIX + "private name"; public static String NAME = PREFIX + "name"; + private static String PRIVATE_NAME = PREFIX + "private name"; } ''', rules: defaultFieldsArrangement diff --git a/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerGrouperTest.groovy b/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerGrouperTest.groovy index e730a41d59ef..e094e866573e 100644 --- a/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerGrouperTest.groovy +++ b/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerGrouperTest.groovy @@ -126,7 +126,8 @@ class Sub extends Base { void test2() {} }''') } - void "test overriden and utility methods"() { + + void "do not test overriden and utility methods"() { doTest( initial: '''\ class Base { @@ -188,4 +189,51 @@ class Test { }''' ) } + + + void "test keep dependent methods together multiple times produce same result"() { + def groups = [group(DEPENDENT_METHODS, BREADTH_FIRST)] + def before = "public class SuperClass {\n" + + "\n" + + " public void doSmth1() {\n" + + " }\n" + + "\n" + + " public void doSmth2() {\n" + + " }\n" + + "\n" + + " public void doSmth3() {\n" + + " }\n" + + "\n" + + " public void doSmth4() {\n" + + " }\n" + + "\n" + + " public void doSmth() {\n" + + " this.doSmth1();\n" + + " this.doSmth2();\n" + + " this.doSmth3();\n" + + " this.doSmth4();\n" + + " }\n" + + "}" + def after = "public class SuperClass {\n" + + "\n" + + " public void doSmth() {\n" + + " this.doSmth1();\n" + + " this.doSmth2();\n" + + " this.doSmth3();\n" + + " this.doSmth4();\n" + + " }\n" + + " public void doSmth1() {\n" + + " }\n" + + " public void doSmth2() {\n" + + " }\n" + + " public void doSmth3() {\n" + + " }\n" + + " public void doSmth4() {\n" + + " }\n" + + "}" + doTest(initial: before, expected: after, groups: groups) + doTest(initial: after, expected: after, groups: groups) + } + + } diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterIndentationTest.java b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterIndentationTest.java index b34720759c8a..ec41ee6842ff 100644 --- a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterIndentationTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterIndentationTest.java @@ -340,10 +340,11 @@ public class JavaFormatterIndentationTest extends AbstractJavaFormatterTest { "});", "foo(1,\n" + " 2, new Runnable() {\n" + - " @Override\n" + - " public void run() {\n" + - " }\n" + - "});" + " @Override\n" + + " public void run() {\n" + + " }\n" + + " }\n" + + ");" ); doMethodTest( @@ -446,16 +447,95 @@ public class JavaFormatterIndentationTest extends AbstractJavaFormatterTest { " }\n" + " });", "foo(new Runnable() {\n" + - " public void run() {\n" + - " }\n" + - " }, new Runnable() {\n" + - " public void run() {\n" + - " }\n" + + " public void run() {\n" + " }\n" + - ");" + "}, new Runnable() {\n" + + " public void run() {\n" + + " }\n" + + "});" ); } - + + public void testAlignMultipleAnonymousClasses_PassedAsMethodParameters() throws Exception { + String text = "test(new Runnable() {\n" + + " @Override\n" + + " public void run() {\n" + + " System.out.println(\"AAA!\");\n" + + " }\n" + + "}, new Runnable() {\n" + + " @Override\n" + + " public void run() {\n" + + " System.out.println(\"BBB!\");\n" + + " }\n" + + "});\n"; + doMethodTest(text, text); + } + + public void testAlignmentAdditionalParamsWithMultipleAnonymousClasses_PassedAsMethodParameters() throws Exception { + String text = "foo(1221, new Runnable() {\n" + + " @Override\n" + + " public void run() {\n" + + " System.out.println(\"A\");\n" + + " }\n" + + "}, new Runnable() {\n" + + " @Override\n" + + " public void run() {\n" + + " System.out.println(\"BB\");\n" + + " }\n" + + "});"; + doMethodTest(text, text); + } + + public void testAlignmentMultipleParamsWithAnonymousClass_PassedAsMethodParams() throws Exception { + getSettings().ALIGN_MULTILINE_PARAMETERS_IN_CALLS = true; + String text = "test(1000,\n" + + " new Runnable() {\n" + + " @Override\n" + + " public void run() {\n" + + " System.out.println(\"BBB\");\n" + + " }\n" + + " }\n" + + ");"; + doMethodTest(text, text); + } + + public void testAlignmentMultipleAnonymousClassesOnNewLines() throws Exception { + getSettings().ALIGN_MULTILINE_PARAMETERS_IN_CALLS = true; + String text = "test(1000,\n" + + " new Runnable() {\n" + + " @Override\n" + + " public void run() {\n" + + " System.out.println(\"BBB\");\n" + + " }\n" + + " },\n" + + " new Runnable() {\n" + + " @Override\n" + + " public void run() {\n" + + " System.out.println(\"BBB\");\n" + + " }\n" + + " }\n" + + ");"; + doMethodTest(text, text); + } + + public void testEnforceChildrenIndent_OfAnonymousClasses_IfAnyOfParamsIsLocatedOnNewLine() throws Exception { + getSettings().ALIGN_MULTILINE_PARAMETERS_IN_CALLS = true; + String text = "test(\"Suuuuuuuuuuuuuuuuuper loooooooooooong string\",\n" + + " \"Next loooooooooooooooooooooong striiiiiiiiiiing\", new Runnable() {\n" + + " @Override\n" + + " public void run() {\n" + + "\n" + + " }\n" + + " }, new Runnable() {\n" + + " @Override\n" + + " public void run() {\n" + + "\n" + + " }\n" + + " }\n" + + ");\n"; + doMethodTest(text, text); + } + public void testPackagePrivateAnnotation() { // Inspired by IDEA-67294 @@ -480,13 +560,12 @@ public class JavaFormatterIndentationTest extends AbstractJavaFormatterTest { " }\n" + " }, )", "test(new Runnable() {\n" + - " public void run() {\n" + - " }\n" + - " }, new Runnable() {\n" + - " public void run() {\n" + - " }\n" + - " },\n" + - ")" + " public void run() {\n" + + " }\n" + + "}, new Runnable() {\n" + + " public void run() {\n" + + " }\n" + + "}, )" ); } diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterWrapTest.java b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterWrapTest.java index c867bf141e68..c5853deddbec 100644 --- a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterWrapTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterWrapTest.java @@ -325,7 +325,8 @@ public class JavaFormatterWrapTest extends AbstractJavaFormatterTest { "test(1,\n" + " 2,\n" + " Test.\n" + - " loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooongMethod());\n" + + " loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooongMethod()\n" + + ");\n" + "int i = 1;\n" + "int j = 2;"; doMethodTest(text, text); diff --git a/platform/core-api/src/com/intellij/openapi/application/BaseActionRunnable.java b/platform/core-api/src/com/intellij/openapi/application/BaseActionRunnable.java index 212ac028a756..6bd8f05eb0c7 100644 --- a/platform/core-api/src/com/intellij/openapi/application/BaseActionRunnable.java +++ b/platform/core-api/src/com/intellij/openapi/application/BaseActionRunnable.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ public abstract class BaseActionRunnable { return mySilentExecution; } - protected abstract void run(Result result) throws Throwable; + protected abstract void run(@NotNull Result result) throws Throwable; @NotNull public abstract RunResult execute(); diff --git a/platform/core-api/src/com/intellij/openapi/application/RunResult.java b/platform/core-api/src/com/intellij/openapi/application/RunResult.java index 8ff9327e9cff..85c84480a6cf 100644 --- a/platform/core-api/src/com/intellij/openapi/application/RunResult.java +++ b/platform/core-api/src/com/intellij/openapi/application/RunResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package com.intellij.openapi.application; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProcessCanceledException; +import org.jetbrains.annotations.NotNull; public class RunResult extends Result { @@ -28,7 +29,7 @@ public class RunResult extends Result { protected RunResult() { } - public RunResult(BaseActionRunnable action) { + public RunResult(@NotNull BaseActionRunnable action) { myActionRunnable = action; } diff --git a/platform/core-impl/src/com/intellij/openapi/editor/impl/CharArray.java b/platform/core-impl/src/com/intellij/openapi/editor/impl/CharArray.java index be82de1e06bd..62dc9d961071 100644 --- a/platform/core-impl/src/com/intellij/openapi/editor/impl/CharArray.java +++ b/platform/core-impl/src/com/intellij/openapi/editor/impl/CharArray.java @@ -19,7 +19,6 @@ import com.intellij.diagnostic.Dumpable; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.ArrayUtil; import com.intellij.util.LocalTimeCounter; import com.intellij.util.text.CharArrayCharSequence; import com.intellij.util.text.CharArrayUtil; @@ -468,7 +467,7 @@ abstract class CharArray implements CharSequenceBackedByArray, Dumpable { try { flushDeferredChanged(); if (myOriginalSequence != null && myArray == null) { - myArray = array = ArrayUtil.realloc(CharArrayUtil.fromSequence(myOriginalSequence), myOriginalSequence.length()); + myArray = array = CharArrayUtil.fromSequence(myOriginalSequence); myStringRef = null; } } diff --git a/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitProcessor.java b/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitProcessor.java index dd3deece51a6..4eeb7527b908 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitProcessor.java +++ b/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitProcessor.java @@ -18,7 +18,6 @@ package com.intellij.psi.impl; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; @@ -26,7 +25,6 @@ import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.UnfairTextRange; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.PomManager; import com.intellij.pom.PomModel; import com.intellij.pom.event.PomModelEvent; @@ -41,15 +39,12 @@ import com.intellij.psi.impl.source.PsiFileImpl; import com.intellij.psi.impl.source.text.DiffLog; import com.intellij.psi.impl.source.tree.FileElement; import com.intellij.psi.text.BlockSupport; -import com.intellij.util.FileContentUtilCore; import com.intellij.util.IncorrectOperationException; import com.intellij.util.Processor; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Arrays; - public abstract class DocumentCommitProcessor { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.DocumentCommitThread"); @@ -108,35 +103,26 @@ public abstract class DocumentCommitProcessor { @NotNull final PsiFile file, final boolean synchronously) { Document document = task.document; + if (PsiDocumentManager.getInstance(task.project).isCommitted(document)) return null; final TextBlock textBlock = TextBlock.get(file); - if (textBlock.isEmpty()) return null; final long startDocModificationTimeStamp = document.getModificationStamp(); final FileElement myTreeElementBeingReparsedSoItWontBeCollected = ((PsiFileImpl)file).calcTreeElement(); - if (textBlock.isEmpty()) return null; // if tree was just loaded above textBlock will be cleared by contentsLoaded final CharSequence chars = document.getCharsSequence(); final Boolean data = document.getUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY); if (data != null) { document.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, null); file.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, data); } - final String oldPsiText = ApplicationManager.getApplication().isInternal() && ApplicationManager.getApplication().isUnitTestMode() - ? myTreeElementBeingReparsedSoItWontBeCollected.getText() - : null; - final TextRange changedPsiRange = - getChangedPsiRange(file, textBlock.getStartOffset(), textBlock.getPsiEndOffset(), document.getTextLength()); - if (!assertBeforeCommit(document, file, textBlock, chars, oldPsiText, myTreeElementBeingReparsedSoItWontBeCollected)) { - return new Processor() { - @Override - public boolean process(Document document) { - VirtualFile vFile = FileDocumentManager.getInstance().getFile(document); - log("Recovering from assertBeforeCommit", task, synchronously, vFile, vFile != null && vFile.isValid()); - if (vFile != null && vFile.isValid()) { - FileContentUtilCore.reparseFiles(Arrays.asList(vFile)); - } - return true; - } - }; + final String oldPsiText = myTreeElementBeingReparsedSoItWontBeCollected.getText(); + int commonPrefixLength = StringUtil.commonPrefixLength(oldPsiText, chars); + final TextRange changedPsiRange; + if (commonPrefixLength == chars.length()) { + changedPsiRange = getChangedPsiRange(file, 0, 0, chars.length()); + } else { + int commonSuffixLength = StringUtil.commonSuffixLength(oldPsiText, chars); + changedPsiRange = getChangedPsiRange(file, commonPrefixLength, oldPsiText.length() - commonSuffixLength, chars.length()); } + BlockSupport blockSupport = BlockSupport.getInstance(file.getProject()); final DiffLog diffLog = blockSupport.reparseRange(file, changedPsiRange, chars, task.indicator); @@ -206,45 +192,6 @@ public abstract class DocumentCommitProcessor { } } - private static boolean assertBeforeCommit(@NotNull Document document, - @NotNull PsiFile file, - @NotNull TextBlock textBlock, - @NotNull CharSequence chars, - String oldPsiText, - @NotNull FileElement myTreeElementBeingReparsedSoItWontBeCollected) { - int startOffset = textBlock.getStartOffset(); - int psiEndOffset = textBlock.getPsiEndOffset(); - if (oldPsiText != null) { - @NonNls String msg = "PSI/document inconsistency before reparse: file=" + file + " of class " + file.getClass(); - if (startOffset >= oldPsiText.length()) { - msg += "\nstartOffset=" + oldPsiText + " while text length is " + oldPsiText.length() + "; "; - startOffset = oldPsiText.length(); - } - - String psiPrefix = oldPsiText.substring(0, startOffset); - String docPrefix = chars.subSequence(0, startOffset).toString(); - String psiSuffix = psiEndOffset > oldPsiText.length() ? "" : oldPsiText.substring(psiEndOffset); - String docSuffix = chars.subSequence(textBlock.getTextEndOffset(), chars.length()).toString(); - if (!psiPrefix.equals(docPrefix) || !psiSuffix.equals(docSuffix)) { - if (!psiPrefix.equals(docPrefix)) { - msg = msg + "\n\npsiPrefix=" + psiPrefix + "\n\ndocPrefix=" + docPrefix; - } - if (!psiSuffix.equals(docSuffix)) { - msg = msg + "\n\npsiSuffix=" + psiSuffix + "\n\ndocSuffix=" + docSuffix; - } - LOG.error(msg); - return false; - } - } - else if (document.getTextLength() - textBlock.getTextEndOffset() != - myTreeElementBeingReparsedSoItWontBeCollected.getTextLength() - psiEndOffset) { - LOG.error("PSI/document inconsistency before reparse: file=" + file); - return false; - } - - return true; - } - private void assertAfterCommit(Document document, final PsiFile file, String oldPsiText, diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java b/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java index 3c45de18afca..7c046953b4a7 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java @@ -617,7 +617,7 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF @Override @NotNull public char[] textToCharArray() { - return CharArrayUtil.fromSequenceStrict(getViewProvider().getContents()); + return CharArrayUtil.fromSequence(getViewProvider().getContents()); } @NotNull @@ -1000,11 +1000,13 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF IElementType contentElementType = getContentElementType(); if (!(contentElementType instanceof IStubFileElementType)) { VirtualFile vFile = getVirtualFile(); - throw new AssertionError("ContentElementType: " + contentElementType + "; file: " + this + - "\n\t" + "Boolean.TRUE.equals(getUserData(BUILDING_STUB)) = " + Boolean.TRUE.equals(getUserData(BUILDING_STUB)) + - "\n\t" + "getTreeElement() = " + getTreeElement() + - "\n\t" + "vFile instanceof VirtualFileWithId = " + (vFile instanceof VirtualFileWithId) + - "\n\t" + "StubUpdatingIndex.canHaveStub(vFile) = " + StubTreeLoader.getInstance().canHaveStub(vFile)); + String message = "ContentElementType: " + contentElementType + "; file: " + this + + "\n\t" + "Boolean.TRUE.equals(getUserData(BUILDING_STUB)) = " + Boolean.TRUE.equals(getUserData(BUILDING_STUB)) + + "\n\t" + "getTreeElement() = " + getTreeElement() + + "\n\t" + "vFile instanceof VirtualFileWithId = " + (vFile instanceof VirtualFileWithId) + + "\n\t" + "StubUpdatingIndex.canHaveStub(vFile) = " + StubTreeLoader.getInstance().canHaveStub(vFile); + rebuildStub(); + throw new AssertionError(message); } StubElement currentStubTree = ((IStubFileElementType)contentElementType).getBuilder().buildStubTree(this); diff --git a/platform/dvcs/src/com/intellij/dvcs/DvcsUtil.java b/platform/dvcs/src/com/intellij/dvcs/DvcsUtil.java index 27a353c88ce5..9c8f1734e259 100644 --- a/platform/dvcs/src/com/intellij/dvcs/DvcsUtil.java +++ b/platform/dvcs/src/com/intellij/dvcs/DvcsUtil.java @@ -16,19 +16,31 @@ package com.intellij.dvcs; import com.intellij.dvcs.repo.Repository; +import com.intellij.dvcs.repo.RepositoryManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.AbstractVcs; +import com.intellij.openapi.vcs.ProjectLevelVcsManager; +import com.intellij.openapi.vcs.VcsKey; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.StatusBar; import com.intellij.openapi.wm.StatusBarWidget; import com.intellij.openapi.wm.WindowManager; import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.vcs.log.VcsLog; +import com.intellij.vcs.log.VcsLogProvider; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.File; +import java.util.Arrays; import java.util.Collection; +import java.util.List; /** * @author Kirill Likhodedov @@ -97,4 +109,33 @@ public class DvcsUtil { } return false; } + + /** + * Report a warning that the given root has no associated Repositories. + */ + public static void noVcsRepositoryForRoot(@NotNull Logger log, + @NotNull VirtualFile root, + @NotNull Project project, + @NotNull RepositoryManager repositoryManager, + @Nullable AbstractVcs vcs) { + if (vcs == null) { + return; + } + ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(project); + List roots = Arrays.asList(vcsManager.getRootsUnderVcs(vcs)); + log.warn(String.format("Repository not found for root: %s. All roots: %s, all repositories: %s", root, roots, + repositoryManager.getRepositories())); + } + + /** + * Checks if there are hg roots in the VCS log. + */ + public static boolean logHasRootForVcs(@NotNull VcsLog log, @Nullable final VcsKey vcsKey) { + return ContainerUtil.find(log.getLogProviders(), new Condition() { + @Override + public boolean value(VcsLogProvider logProvider) { + return logProvider.getSupportedVcs().equals(vcsKey); + } + }) != null; + } } diff --git a/platform/icons/src/debugger/threadStates/daemon_sign@2x.png b/platform/icons/src/debugger/threadStates/daemon_sign@2x.png new file mode 100644 index 000000000000..f3f8198ff92a Binary files /dev/null and b/platform/icons/src/debugger/threadStates/daemon_sign@2x.png differ diff --git a/platform/icons/src/debugger/threadStates/daemon_sign@2x_dark.png b/platform/icons/src/debugger/threadStates/daemon_sign@2x_dark.png new file mode 100644 index 000000000000..fbd336db1d1b Binary files /dev/null and b/platform/icons/src/debugger/threadStates/daemon_sign@2x_dark.png differ diff --git a/platform/icons/src/debugger/threadStates/daemon_sign_dark.png b/platform/icons/src/debugger/threadStates/daemon_sign_dark.png new file mode 100644 index 000000000000..93d0ef4c232c Binary files /dev/null and b/platform/icons/src/debugger/threadStates/daemon_sign_dark.png differ diff --git a/platform/icons/src/debugger/threadStates/edtBusy@2x.png b/platform/icons/src/debugger/threadStates/edtBusy@2x.png new file mode 100644 index 000000000000..435807e004ca Binary files /dev/null and b/platform/icons/src/debugger/threadStates/edtBusy@2x.png differ diff --git a/platform/icons/src/debugger/threadStates/exception@2x.png b/platform/icons/src/debugger/threadStates/exception@2x.png new file mode 100644 index 000000000000..00d7dcc291c7 Binary files /dev/null and b/platform/icons/src/debugger/threadStates/exception@2x.png differ diff --git a/platform/icons/src/debugger/threadStates/exception@2x_dark.png b/platform/icons/src/debugger/threadStates/exception@2x_dark.png new file mode 100644 index 000000000000..47929c30e800 Binary files /dev/null and b/platform/icons/src/debugger/threadStates/exception@2x_dark.png differ diff --git a/platform/icons/src/debugger/threadStates/exception_dark.png b/platform/icons/src/debugger/threadStates/exception_dark.png new file mode 100644 index 000000000000..f5fcdcb2e40e Binary files /dev/null and b/platform/icons/src/debugger/threadStates/exception_dark.png differ diff --git a/platform/icons/src/debugger/threadStates/idle.png b/platform/icons/src/debugger/threadStates/idle.png index 1f02c73f1c79..b6703024d018 100644 Binary files a/platform/icons/src/debugger/threadStates/idle.png and b/platform/icons/src/debugger/threadStates/idle.png differ diff --git a/platform/icons/src/debugger/threadStates/idle@2x.png b/platform/icons/src/debugger/threadStates/idle@2x.png new file mode 100644 index 000000000000..cd791532b3df Binary files /dev/null and b/platform/icons/src/debugger/threadStates/idle@2x.png differ diff --git a/platform/icons/src/debugger/threadStates/io@2x.png b/platform/icons/src/debugger/threadStates/io@2x.png new file mode 100644 index 000000000000..8b239848e74f Binary files /dev/null and b/platform/icons/src/debugger/threadStates/io@2x.png differ diff --git a/platform/icons/src/debugger/threadStates/locked@2x.png b/platform/icons/src/debugger/threadStates/locked@2x.png new file mode 100644 index 000000000000..0371a1e18c4d Binary files /dev/null and b/platform/icons/src/debugger/threadStates/locked@2x.png differ diff --git a/platform/icons/src/debugger/threadStates/locked@2x_dark.png b/platform/icons/src/debugger/threadStates/locked@2x_dark.png new file mode 100644 index 000000000000..e1ba1ec934d9 Binary files /dev/null and b/platform/icons/src/debugger/threadStates/locked@2x_dark.png differ diff --git a/platform/icons/src/debugger/threadStates/locked_dark.png b/platform/icons/src/debugger/threadStates/locked_dark.png new file mode 100644 index 000000000000..47f25d7b66a7 Binary files /dev/null and b/platform/icons/src/debugger/threadStates/locked_dark.png differ diff --git a/platform/icons/src/debugger/threadStates/paused@2x.png b/platform/icons/src/debugger/threadStates/paused@2x.png new file mode 100644 index 000000000000..b9d693ac0eda Binary files /dev/null and b/platform/icons/src/debugger/threadStates/paused@2x.png differ diff --git a/platform/icons/src/debugger/threadStates/running@2x.png b/platform/icons/src/debugger/threadStates/running@2x.png new file mode 100644 index 000000000000..60b6ee375cb8 Binary files /dev/null and b/platform/icons/src/debugger/threadStates/running@2x.png differ diff --git a/platform/icons/src/debugger/threadStates/socket@2x.png b/platform/icons/src/debugger/threadStates/socket@2x.png new file mode 100644 index 000000000000..22d8394724bf Binary files /dev/null and b/platform/icons/src/debugger/threadStates/socket@2x.png differ diff --git a/platform/icons/src/debugger/threadStates/socket@2x_dark.png b/platform/icons/src/debugger/threadStates/socket@2x_dark.png new file mode 100644 index 000000000000..9e2d4e9a2a78 Binary files /dev/null and b/platform/icons/src/debugger/threadStates/socket@2x_dark.png differ diff --git a/platform/icons/src/debugger/threadStates/socket_dark.png b/platform/icons/src/debugger/threadStates/socket_dark.png new file mode 100644 index 000000000000..7a8094aceead Binary files /dev/null and b/platform/icons/src/debugger/threadStates/socket_dark.png differ diff --git a/platform/icons/src/debugger/threadStates/threaddump@2x.png b/platform/icons/src/debugger/threadStates/threaddump@2x.png new file mode 100644 index 000000000000..56abf46294bd Binary files /dev/null and b/platform/icons/src/debugger/threadStates/threaddump@2x.png differ diff --git a/platform/icons/src/debugger/threadStates/threaddump@2x_dark.png b/platform/icons/src/debugger/threadStates/threaddump@2x_dark.png new file mode 100644 index 000000000000..4e7a083c01a9 Binary files /dev/null and b/platform/icons/src/debugger/threadStates/threaddump@2x_dark.png differ diff --git a/platform/icons/src/debugger/threadStates/threaddump_dark.png b/platform/icons/src/debugger/threadStates/threaddump_dark.png new file mode 100644 index 000000000000..a655ebd2f940 Binary files /dev/null and b/platform/icons/src/debugger/threadStates/threaddump_dark.png differ diff --git a/platform/icons/src/general/autohideOff.png b/platform/icons/src/general/autohideOff.png index 3f4f45d262c6..7ec50e54c70f 100644 Binary files a/platform/icons/src/general/autohideOff.png and b/platform/icons/src/general/autohideOff.png differ diff --git a/platform/icons/src/general/autohideOff@2x.png b/platform/icons/src/general/autohideOff@2x.png new file mode 100644 index 000000000000..4d4718721a95 Binary files /dev/null and b/platform/icons/src/general/autohideOff@2x.png differ diff --git a/platform/icons/src/general/autohideOff@2x_dark.png b/platform/icons/src/general/autohideOff@2x_dark.png new file mode 100644 index 000000000000..42cbd504a2e8 Binary files /dev/null and b/platform/icons/src/general/autohideOff@2x_dark.png differ diff --git a/platform/icons/src/general/autohideOffInactive.png b/platform/icons/src/general/autohideOffInactive.png index c8657a08694e..f60866f8cc49 100644 Binary files a/platform/icons/src/general/autohideOffInactive.png and b/platform/icons/src/general/autohideOffInactive.png differ diff --git a/platform/icons/src/general/autohideOffInactive@2x.png b/platform/icons/src/general/autohideOffInactive@2x.png new file mode 100644 index 000000000000..0bd9dc4f0edd Binary files /dev/null and b/platform/icons/src/general/autohideOffInactive@2x.png differ diff --git a/platform/icons/src/general/autohideOffInactive@2x_dark.png b/platform/icons/src/general/autohideOffInactive@2x_dark.png new file mode 100644 index 000000000000..ca7f23e98360 Binary files /dev/null and b/platform/icons/src/general/autohideOffInactive@2x_dark.png differ diff --git a/platform/icons/src/general/autohideOffInactive_dark.png b/platform/icons/src/general/autohideOffInactive_dark.png new file mode 100644 index 000000000000..c8a9b2f43eca Binary files /dev/null and b/platform/icons/src/general/autohideOffInactive_dark.png differ diff --git a/platform/icons/src/general/autohideOff_dark.png b/platform/icons/src/general/autohideOff_dark.png new file mode 100644 index 000000000000..aa66d91c60a2 Binary files /dev/null and b/platform/icons/src/general/autohideOff_dark.png differ diff --git a/platform/icons/src/general/balloonClose.png b/platform/icons/src/general/balloonClose.png index 9c4275ccaff3..aa4edd0ae465 100644 Binary files a/platform/icons/src/general/balloonClose.png and b/platform/icons/src/general/balloonClose.png differ diff --git a/platform/icons/src/general/balloonClose@2x.png b/platform/icons/src/general/balloonClose@2x.png new file mode 100644 index 000000000000..339aa107e392 Binary files /dev/null and b/platform/icons/src/general/balloonClose@2x.png differ diff --git a/platform/icons/src/general/error@2x.png b/platform/icons/src/general/error@2x.png new file mode 100644 index 000000000000..462854d89480 Binary files /dev/null and b/platform/icons/src/general/error@2x.png differ diff --git a/platform/icons/src/general/floating.png b/platform/icons/src/general/floating.png index 9d9581050c62..5ee3f199997e 100644 Binary files a/platform/icons/src/general/floating.png and b/platform/icons/src/general/floating.png differ diff --git a/platform/icons/src/general/floating@2x.png b/platform/icons/src/general/floating@2x.png new file mode 100644 index 000000000000..e7b51cbc1a74 Binary files /dev/null and b/platform/icons/src/general/floating@2x.png differ diff --git a/platform/icons/src/general/floating@2x_dark.png b/platform/icons/src/general/floating@2x_dark.png new file mode 100644 index 000000000000..64874e19a2ab Binary files /dev/null and b/platform/icons/src/general/floating@2x_dark.png differ diff --git a/platform/icons/src/general/floating_dark.png b/platform/icons/src/general/floating_dark.png new file mode 100644 index 000000000000..78450e5e7593 Binary files /dev/null and b/platform/icons/src/general/floating_dark.png differ diff --git a/platform/icons/src/general/help_small.png b/platform/icons/src/general/help_small.png index 6c04835138fa..5f418a165e80 100644 Binary files a/platform/icons/src/general/help_small.png and b/platform/icons/src/general/help_small.png differ diff --git a/platform/icons/src/general/help_small@2x.png b/platform/icons/src/general/help_small@2x.png new file mode 100644 index 000000000000..a879dc2c3786 Binary files /dev/null and b/platform/icons/src/general/help_small@2x.png differ diff --git a/platform/icons/src/general/ijLogo@2x.png b/platform/icons/src/general/ijLogo@2x.png new file mode 100644 index 000000000000..45aed3567f81 Binary files /dev/null and b/platform/icons/src/general/ijLogo@2x.png differ diff --git a/platform/icons/src/general/information.png b/platform/icons/src/general/information.png index af48de960864..4d3783491c28 100644 Binary files a/platform/icons/src/general/information.png and b/platform/icons/src/general/information.png differ diff --git a/platform/icons/src/general/information@2x.png b/platform/icons/src/general/information@2x.png new file mode 100644 index 000000000000..7f757fa39402 Binary files /dev/null and b/platform/icons/src/general/information@2x.png differ diff --git a/platform/icons/src/general/inspectionInProgress.png b/platform/icons/src/general/inspectionInProgress.png index a3dec2b95f40..34d4875e14d4 100644 Binary files a/platform/icons/src/general/inspectionInProgress.png and b/platform/icons/src/general/inspectionInProgress.png differ diff --git a/platform/icons/src/general/inspectionInProgress@2x.png b/platform/icons/src/general/inspectionInProgress@2x.png new file mode 100644 index 000000000000..97ae7295e75d Binary files /dev/null and b/platform/icons/src/general/inspectionInProgress@2x.png differ diff --git a/platform/icons/src/general/mouse.png b/platform/icons/src/general/mouse.png index 4a131be9256f..4a07a796641f 100644 Binary files a/platform/icons/src/general/mouse.png and b/platform/icons/src/general/mouse.png differ diff --git a/platform/icons/src/general/mouse@2x.png b/platform/icons/src/general/mouse@2x.png new file mode 100644 index 000000000000..4abe2c8aa154 Binary files /dev/null and b/platform/icons/src/general/mouse@2x.png differ diff --git a/platform/icons/src/general/mouse@2x_dark.png b/platform/icons/src/general/mouse@2x_dark.png new file mode 100644 index 000000000000..a753e349e0ab Binary files /dev/null and b/platform/icons/src/general/mouse@2x_dark.png differ diff --git a/platform/icons/src/general/mouse_dark.png b/platform/icons/src/general/mouse_dark.png new file mode 100644 index 000000000000..ecbdc5bf7fbf Binary files /dev/null and b/platform/icons/src/general/mouse_dark.png differ diff --git a/platform/icons/src/general/tbHidden.png b/platform/icons/src/general/tbHidden.png index cc68f6b2e6f5..7f7d52ffca53 100644 Binary files a/platform/icons/src/general/tbHidden.png and b/platform/icons/src/general/tbHidden.png differ diff --git a/platform/icons/src/general/tbHidden@2x.png b/platform/icons/src/general/tbHidden@2x.png new file mode 100644 index 000000000000..25d4326480ea Binary files /dev/null and b/platform/icons/src/general/tbHidden@2x.png differ diff --git a/platform/icons/src/general/tbHidden@2x_dark.png b/platform/icons/src/general/tbHidden@2x_dark.png new file mode 100644 index 000000000000..e6358ef43cb9 Binary files /dev/null and b/platform/icons/src/general/tbHidden@2x_dark.png differ diff --git a/platform/icons/src/general/tbHidden_dark.png b/platform/icons/src/general/tbHidden_dark.png index 08af008394ce..9b3af4078149 100644 Binary files a/platform/icons/src/general/tbHidden_dark.png and b/platform/icons/src/general/tbHidden_dark.png differ diff --git a/platform/icons/src/general/tbShown.png b/platform/icons/src/general/tbShown.png index b9689ea10b13..367d9bcfe9a5 100644 Binary files a/platform/icons/src/general/tbShown.png and b/platform/icons/src/general/tbShown.png differ diff --git a/platform/icons/src/general/tbShown@2x.png b/platform/icons/src/general/tbShown@2x.png new file mode 100644 index 000000000000..758f1289bb91 Binary files /dev/null and b/platform/icons/src/general/tbShown@2x.png differ diff --git a/platform/icons/src/general/tbShown@2x_dark.png b/platform/icons/src/general/tbShown@2x_dark.png new file mode 100644 index 000000000000..87c38cc7c8c1 Binary files /dev/null and b/platform/icons/src/general/tbShown@2x_dark.png differ diff --git a/platform/icons/src/general/tbShown_dark.png b/platform/icons/src/general/tbShown_dark.png index 50fa42124f7f..c7dd40cb4d1c 100644 Binary files a/platform/icons/src/general/tbShown_dark.png and b/platform/icons/src/general/tbShown_dark.png differ diff --git a/platform/icons/src/toolwindows/toolWindowPalette@2x.png b/platform/icons/src/toolwindows/toolWindowPalette@2x.png new file mode 100644 index 000000000000..bc14c3e7d6c5 Binary files /dev/null and b/platform/icons/src/toolwindows/toolWindowPalette@2x.png differ diff --git a/platform/icons/src/toolwindows/toolWindowProject@2x.png b/platform/icons/src/toolwindows/toolWindowProject@2x.png new file mode 100644 index 000000000000..658b9dcf7b80 Binary files /dev/null and b/platform/icons/src/toolwindows/toolWindowProject@2x.png differ diff --git a/platform/icons/src/toolwindows/toolWindowRun@2x.png b/platform/icons/src/toolwindows/toolWindowRun@2x.png new file mode 100644 index 000000000000..7b7f4c576546 Binary files /dev/null and b/platform/icons/src/toolwindows/toolWindowRun@2x.png differ diff --git a/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java b/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java index bc0fb118071a..f0393574996a 100644 --- a/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java +++ b/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java @@ -22,13 +22,18 @@ import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.application.Result; import com.intellij.openapi.application.ex.ApplicationEx; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.fileEditor.impl.LoadTextUtil; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressIndicatorProvider; import com.intellij.openapi.progress.util.TooManyUsagesStatus; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.FileIndexFacade; -import com.intellij.openapi.util.*; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; @@ -40,6 +45,7 @@ import com.intellij.psi.search.*; import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.CommonProcessors; import com.intellij.util.Processor; +import com.intellij.util.SmartList; import com.intellij.util.codeInsight.CommentUtilCore; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; @@ -344,13 +350,14 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { final AtomicBoolean canceled = new AtomicBoolean(false); AsyncFutureResult asyncFutureResult = AsyncFutureFactory.getInstance().createAsyncFutureResult(); - final List failedFiles = new ArrayList(); + final List failedFiles = Collections.synchronizedList(new SmartList()); try { boolean completed = JobLauncher.getInstance().invokeConcurrentlyUnderProgress(files, progress, false, false, new Processor() { @Override public boolean process(final VirtualFile vfile) { try { + TooManyUsagesStatus.getFrom(progress).pauseProcessingIfTooManyUsages(); processVirtualFile(vfile, progress, localProcessor, canceled, counter, totalSize); } catch (CannotRunReadActionException action) { @@ -361,6 +368,7 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { }); if (!failedFiles.isEmpty()) { for (final VirtualFile vfile : failedFiles) { + TooManyUsagesStatus.getFrom(progress).pauseProcessingIfTooManyUsages(); // we failed to run read action in job launcher thread // run read action in our thread instead ApplicationManager.getApplication().runReadAction(new Runnable() { @@ -381,10 +389,12 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { return asyncFutureResult; } - private void processVirtualFile(final VirtualFile vfile, + private void processVirtualFile(@NotNull final VirtualFile vfile, final ProgressIndicator progress, - final Processor localProcessor, - final AtomicBoolean canceled, AtomicInteger counter, int totalSize) { + @NotNull final Processor localProcessor, + @NotNull final AtomicBoolean canceled, + @NotNull AtomicInteger counter, + int totalSize) { final PsiFile file = tryRead(new Computable() { @Override public PsiFile compute() { @@ -392,19 +402,16 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { } }); if (file != null && !(file instanceof PsiBinaryFile)) { - file.getViewProvider().getContents(); // load contents outside read action - if (myManager.getProject().isDisposed()) throw new ProcessCanceledException(); - final List psiRoots = tryRead(new Computable>() { - @Override - public List compute() { - return file.getViewProvider().getAllFiles(); - } - }); - final Set processed = new THashSet(psiRoots.size() * 2, (float)0.5); - TooManyUsagesStatus.getFrom(progress).pauseProcessingIfTooManyUsages(); + // load contents outside read action + if (FileDocumentManager.getInstance().getCachedDocument(vfile) == null) { + LoadTextUtil.loadText(vfile); // cache bytes in vfs + } tryRead(new Computable() { @Override public Void compute() { + if (myManager.getProject().isDisposed()) throw new ProcessCanceledException(); + List psiRoots = file.getViewProvider().getAllFiles(); + Set processed = new THashSet(psiRoots.size() * 2, (float)0.5); for (final PsiFile psiRoot : psiRoots) { checkCanceled(progress); assert psiRoot != null : "One of the roots of file " + file + " is null. All roots: " + psiRoots + diff --git a/platform/lang-api/src/com/intellij/psi/LanguageInjector.java b/platform/lang-api/src/com/intellij/psi/LanguageInjector.java index d45e2b570791..b92f092f00c8 100644 --- a/platform/lang-api/src/com/intellij/psi/LanguageInjector.java +++ b/platform/lang-api/src/com/intellij/psi/LanguageInjector.java @@ -23,6 +23,7 @@ import org.jetbrains.annotations.NotNull; * Describes logic for injecting language inside hosting PSI element. * E.g. "inject XPath language into all XML attributes named 'select' that sit inside XML tag prefixed with 'xsl:'". * @see com.intellij.psi.PsiLanguageInjectionHost + * @see com.intellij.lang.injection.MultiHostInjector */ public interface LanguageInjector { ExtensionPointName EXTENSION_POINT_NAME = ExtensionPointName.create("com.intellij.languageInjector"); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hint/ShowParameterInfoContext.java b/platform/lang-impl/src/com/intellij/codeInsight/hint/ShowParameterInfoContext.java index 5f61a846d14e..14e7487cc216 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hint/ShowParameterInfoContext.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/hint/ShowParameterInfoContext.java @@ -110,11 +110,13 @@ public class ShowParameterInfoContext implements CreateParameterInfoContext { showMethodInfo(getProject(), getEditor(), element, getHighlightedElement(), itemsToShow, offset, handler); } - private static void showParameterHint(final PsiElement element, final Editor editor, final Object[] descriptors, - final Project project, final ShowParameterInfoHandler.BestLocationPointProvider provider, + private static void showParameterHint(final PsiElement element, + final Editor editor, + final Object[] descriptors, + final Project project, @Nullable PsiElement highlighted, - final int elementStart, final ParameterInfoHandler handler - ) { + final int elementStart, + final ParameterInfoHandler handler) { if (ParameterInfoController.isAlreadyShown(editor, elementStart)) return; if (editor.isDisposed() || !editor.getComponent().isVisible()) return; @@ -129,6 +131,7 @@ public class ShowParameterInfoContext implements CreateParameterInfoContext { final LightweightHint hint = new LightweightHint(component); hint.setSelectingHint(true); final HintManagerImpl hintManager = HintManagerImpl.getInstanceImpl(); + final ShowParameterInfoHandler.BestLocationPointProvider provider = new MyBestLocationPointProvider(editor); final Pair pos = provider.getBestPointPosition(hint, element, elementStart, true, HintManager.UNDER); ApplicationManager.getApplication().invokeLater(new Runnable() { @@ -143,6 +146,8 @@ public class ShowParameterInfoContext implements CreateParameterInfoContext { hintHint.setExplicitClose(true); Editor editorToShow = editor instanceof EditorWindow ? ((EditorWindow)editor).getDelegate() : editor; + // is case of injection we need to calculate position for EditorWindow + // also we need to show the hint in the main editor because of intention bulb hintManager.showEditorHint(hint, editorToShow, pos.getFirst(), HintManager.HIDE_BY_ESCAPE | HintManager.UPDATE_BY_SCROLLING, 0, false, hintHint); new ParameterInfoController(project, editor, elementStart, hint, handler, provider); } @@ -156,8 +161,7 @@ public class ShowParameterInfoContext implements CreateParameterInfoContext { int offset, ParameterInfoHandler handler ) { - showParameterHint(list, editor, candidates, project, new MyBestLocationPointProvider(editor), - candidates.length > 1 ? highlighted: null,offset, handler); + showParameterHint(list, editor, candidates, project, candidates.length > 1 ? highlighted : null, offset, handler); } /** diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/TemplateBuilderImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/template/TemplateBuilderImpl.java index 29125be518ed..26d46527065b 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/TemplateBuilderImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/TemplateBuilderImpl.java @@ -115,7 +115,8 @@ public class TemplateBuilderImpl implements TemplateBuilder { } public void replaceElement(PsiElement element, TextRange textRange, String primaryVariableName, String otherVariableName, boolean alwaysStopAt) { - final RangeMarker key = myDocument.createRangeMarker(textRange.shiftRight(element.getTextRange().getStartOffset())); + final TextRange elementTextRange = InjectedLanguageManager.getInstance(element.getProject()).injectedToHost(element, element.getTextRange()); + final RangeMarker key = myDocument.createRangeMarker(textRange.shiftRight(elementTextRange.getStartOffset())); myAlwaysStopAtMap.put(key, alwaysStopAt ? Boolean.TRUE : Boolean.FALSE); myVariableNamesMap.put(key, primaryVariableName); myVariableExpressions.put(key, otherVariableName); diff --git a/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java b/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java index da4024ed1147..eda7b1946ebc 100644 --- a/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java +++ b/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java @@ -357,7 +357,7 @@ public class EditorSearchComponent extends EditorHeaderComponent implements Data } private void setupHistoryToSearchField(SearchTextField field, String[] strings) { - field.setHistorySize(strings.length); + field.setHistorySize(20); field.setHistory(ContainerUtil.reverse(Arrays.asList(strings))); } diff --git a/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesManager.java b/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesManager.java index 20ed2d15be6f..7047f9268dab 100644 --- a/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesManager.java +++ b/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -87,7 +87,7 @@ public class FindUsagesManager implements JDOMExternalizable { @NonNls private static final String VALUE_START_USAGE_AGAIN = "START_AGAIN"; private final Project myProject; private final com.intellij.usages.UsageViewManager myAnotherManager; - private boolean myToOpenInNewTab = false; + private boolean myToOpenInNewTab = true; public static class SearchData { public SmartPsiElementPointer[] myElements = null; diff --git a/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java b/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java index 2c3d3b956b3c..81b51bfb03ba 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/SearchEverywhereAction.java @@ -80,6 +80,7 @@ import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBList; import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.components.OnOffButton; +import com.intellij.ui.components.panels.NonOpaquePanel; import com.intellij.ui.popup.AbstractPopup; import com.intellij.ui.popup.PopupPositionManager; import com.intellij.util.*; @@ -136,6 +137,7 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA private Alarm myAlarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, ApplicationManager.getApplication()); private Alarm myUpdateAlarm = new Alarm(ApplicationManager.getApplication()); private JBList myList = new JBList(myListModel); + private JCheckBox myNonProjectCheckBox = new JCheckBox(); private AnActionEvent myActionEvent; private Component myContextComponent; private CalcThread myCalcThread; @@ -248,8 +250,8 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA @Override protected void paintComponent(Graphics g) { if (myBalloon != null && !myBalloon.isDisposed() && myActionEvent != null && myActionEvent.getInputEvent() instanceof MouseEvent) { - ((Graphics2D)g).setPaint(new GradientPaint(0,0, new JBColor(new Color(101, 136, 242), new Color(48, 87, 186)), 0, getHeight(), - new JBColor(new Color(44, 96, 238), new Color(48, 87, 186)))); + ((Graphics2D)g).setPaint(new GradientPaint(0,0, new JBColor(new Color(147, 162, 174), new Color(64, 80, 94)), 0, getHeight(), + new JBColor(new Color(116, 128, 143), new Color(53, 65, 87)))); g.fillRect(0,0,getWidth(), getHeight()); } else { super.paintComponent(g); @@ -311,6 +313,30 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA } } }); + + myNonProjectCheckBox.setOpaque(false); + myNonProjectCheckBox.setAlignmentX(1.0f); + myNonProjectCheckBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (showAll.get() != myNonProjectCheckBox.isSelected()) { + showAll.set(!showAll.get()); + final JTextField editor = UIUtil.findComponentOfType(myBalloon.getContent(), JTextField.class); + if (editor != null) { + final String pattern = editor.getText(); + myAlarm.cancelAllRequests(); + myAlarm.addRequest(new Runnable() { + @Override + public void run() { + if (editor.hasFocus()) { + rebuildList(pattern); + } + } + }, 30); + } + } + } + }); //noinspection SSBasedInspection SwingUtilities.invokeLater(new Runnable() { public void run() { @@ -321,13 +347,8 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA } private void initTooltip(JLabel label) { - final Shortcut[] shortcuts = KeymapManager.getInstance().getActiveKeymap().getShortcuts(IdeActions.ACTION_SEARCH_EVERYWHERE); final String shortcutText; - if (shortcuts.length == 0) { - shortcutText = "Double " + (SystemInfo.isMac ? MacKeymapUtil.SHIFT : "Shift"); - } else { - shortcutText = KeymapUtil.getShortcutsText(shortcuts); - } + shortcutText = getShortcut(); label.setToolTipText("Search Everywhere
Press " + shortcutText @@ -335,6 +356,17 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA } + private static String getShortcut() { + String shortcutText; + final Shortcut[] shortcuts = KeymapManager.getInstance().getActiveKeymap().getShortcuts(IdeActions.ACTION_SEARCH_EVERYWHERE); + if (shortcuts.length == 0) { + shortcutText = "Double " + (SystemInfo.isMac ? MacKeymapUtil.SHIFT : "Shift"); + } else { + shortcutText = KeymapUtil.getShortcutsText(shortcuts); + } + return shortcutText; + } + private void initSearchField(final MySearchTextField search) { final JTextField editor = search.getTextEditor(); // onFocusLost(); @@ -355,8 +387,14 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA } }); editor.addFocusListener(new FocusAdapter() { + boolean skip = false; + @Override public void focusGained(FocusEvent e) { + if (skip) { + skip = false; + return; + } search.setText(""); search.getTextEditor().setForeground(UIUtil.getLabelForeground()); myTitleIndexes = new TitleIndexes(); @@ -384,6 +422,11 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA && ((myList == e.getOppositeComponent()) || ((AbstractPopup)myPopup).getPopupWindow() == e.getOppositeComponent())) { return; } + if (myNonProjectCheckBox == e.getOppositeComponent()) { + skip = true; + editor.requestFocus(); + return; + } onFocusLost(); } }); @@ -535,7 +578,8 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA public void actionPerformed(AnActionEvent e, MouseEvent me) { if (myBalloon != null && myBalloon.isVisible()) { showAll.set(!showAll.get()); - myPopupField.getTextEditor().setBackground(showAll.get() ? new JBColor(new Color(0xffffe4), new Color(0x494539)) : UIUtil.getTextFieldBackground()); + myNonProjectCheckBox.setSelected(showAll.get()); +// myPopupField.getTextEditor().setBackground(showAll.get() ? new JBColor(new Color(0xffffe4), new Color(0x494539)) : UIUtil.getTextFieldBackground()); rebuildList(myPopupField.getText()); return; } @@ -561,20 +605,24 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA final JPanel panel = new JPanel(new BorderLayout()) { @Override protected void paintComponent(Graphics g) { - ((Graphics2D)g).setPaint(new GradientPaint(0,0, new JBColor(new Color(211, 232, 253), new Color(64, 80, 94)), 0, getHeight(), - new JBColor(new Color(200, 215, 239), new Color(53, 65, 87)))); + ((Graphics2D)g).setPaint(new GradientPaint(0,0, new JBColor(new Color(147, 162, 174), new Color(64, 80, 94)), 0, getHeight(), + new JBColor(new Color(116, 128, 143), new Color(53, 65, 87)))); g.fillRect(0, 0, getWidth(), getHeight()); } }; - final JLabel title = new JLabel(" Search Everywhere:"); + final JLabel title = new JLabel(" Search Everywhere: "); + final JPanel topPanel = new NonOpaquePanel(new BorderLayout()); title.setForeground(new JBColor(Gray._50, Gray._180)); if (SystemInfo.isMac) { title.setFont(title.getFont().deriveFont(Font.BOLD, title.getFont().getSize() - 1f)); } else { title.setFont(title.getFont().deriveFont(Font.BOLD)); } - panel.add(title, BorderLayout.WEST); + topPanel.add(title, BorderLayout.WEST); + myNonProjectCheckBox.setText("Include non-project items (" + getShortcut() + ")"); + topPanel.add(myNonProjectCheckBox, BorderLayout.EAST); panel.add(myPopupField, BorderLayout.CENTER); + panel.add(topPanel, BorderLayout.NORTH); panel.setBorder(IdeBorderFactory.createEmptyBorder(3, 5, 4, 5)); final ComponentPopupBuilder builder = JBPopupFactory.getInstance().createComponentPopupBuilder(panel, myPopupField.getTextEditor()); myBalloon = builder @@ -595,7 +643,7 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA showPoint = new RelativePoint(button, new Point(button.getWidth() - panel.getPreferredSize().width, button.getHeight())); } else { if (parent != null) { - int height = UISettings.getInstance().SHOW_MAIN_TOOLBAR ? 95 : 75; + int height = UISettings.getInstance().SHOW_MAIN_TOOLBAR ? 135 : 115; if (parent instanceof IdeFrameImpl && ((IdeFrameImpl)parent).isInFullScreen()) { height -= 20; } @@ -1165,6 +1213,11 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA } private void buildClasses(String pattern, boolean includeLibraries) { + if (pattern.indexOf('.') != -1) { + //todo[kb] it's not a mistake. If we search for "*.png" or "index.xml" in SearchEverywhere + //todo[kb] we don't want to see Java classes started with Png or Xml. This approach should be reworked someday. + return; + } boolean includeLibs = includeLibraries || showAll.get(); int clsCounter = 0; final int maxCount = includeLibraries ? 5 : MAX_CLASSES; @@ -1435,6 +1488,7 @@ public class SearchEverywhereAction extends AnAction implements CustomComponentA myCalcThread = null; myPopup = null; showAll.set(false); + myNonProjectCheckBox.setSelected(false); ActionToolbarImpl.updateAllToolbarsImmediately(); if (myActionEvent != null && myActionEvent.getInputEvent() instanceof MouseEvent) { final Component component = myActionEvent.getInputEvent().getComponent(); diff --git a/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java b/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java index 9a32f8b89f0d..8b7da068f968 100644 --- a/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java +++ b/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java @@ -36,6 +36,7 @@ import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.editor.highlighter.EditorHighlighter; import com.intellij.openapi.editor.highlighter.LightHighlighterClient; import com.intellij.openapi.editor.impl.EditorImpl; +import com.intellij.openapi.editor.impl.SoftWrapModelImpl; import com.intellij.openapi.editor.impl.TextDrawingCallback; import com.intellij.openapi.editor.impl.softwrap.SoftWrapAppliancePlaces; import com.intellij.openapi.editor.markup.MarkupModel; @@ -73,6 +74,7 @@ public class EditorWindow extends UserDataHolderBase implements EditorEx { private boolean myDisposed; private final MarkupModelWindow myMarkupModelDelegate; private final FoldingModelWindow myFoldingModelWindow; + private final SoftWrapModelImpl mySoftWrapModel; public static Editor create(@NotNull final DocumentWindowImpl documentRange, @NotNull final EditorImpl editor, @NotNull final PsiFile injectedFile) { assert documentRange.isValid(); @@ -106,6 +108,8 @@ public class EditorWindow extends UserDataHolderBase implements EditorEx { mySelectionModelDelegate = new SelectionModelWindow(myDelegate, myDocumentWindow,this); myMarkupModelDelegate = new MarkupModelWindow((MarkupModelEx)myDelegate.getMarkupModel(), myDocumentWindow); myFoldingModelWindow = new FoldingModelWindow(delegate.getFoldingModel(), documentWindow, this); + mySoftWrapModel = new SoftWrapModelImpl(this); + Disposer.register(myDocumentWindow, mySoftWrapModel); } public static void disposeInvalidEditors() { @@ -167,12 +171,8 @@ public class EditorWindow extends UserDataHolderBase implements EditorEx { else { int offset = lineStartOffset + column; int hostOffset = getDocument().injectedToHost(offset); - int hostLineNumber = getDocument().getDelegate().getLineNumber(hostOffset); - int hostLineStart = getDocument().getDelegate().getLineStartOffset(hostLineNumber); - - return new LogicalPosition(hostLineNumber, hostOffset - hostLineStart); + return myDelegate.offsetToLogicalPosition(hostOffset); } - } private void dispose() { @@ -266,7 +266,7 @@ public class EditorWindow extends UserDataHolderBase implements EditorEx { @Override @NotNull public SoftWrapModelEx getSoftWrapModel() { - return myDelegate.getSoftWrapModel(); + return mySoftWrapModel; } @Override diff --git a/platform/lang-impl/src/com/intellij/lang/parser/GeneratedParserUtilBase.java b/platform/lang-impl/src/com/intellij/lang/parser/GeneratedParserUtilBase.java index 382e6eed58d7..d18bf4c5dff7 100644 --- a/platform/lang-impl/src/com/intellij/lang/parser/GeneratedParserUtilBase.java +++ b/platform/lang-impl/src/com/intellij/lang/parser/GeneratedParserUtilBase.java @@ -35,11 +35,18 @@ import java.util.LinkedList; /** * @author gregsh */ -@SuppressWarnings("StringEquality") public class GeneratedParserUtilBase { private static final Logger LOG = Logger.getInstance("org.intellij.grammar.parser.GeneratedParserUtilBase"); + private static final int MAX_RECURSION_LEVEL = 1000; + private static final int MAX_VARIANTS_SIZE = 10000; + private static final int MAX_VARIANTS_TO_DISPLAY = 50; + + private static final int INITIAL_VARIANTS_SIZE = 1000; + private static final int VARIANTS_POOL_SIZE = 10000; + private static final int FRAMES_POOL_SIZE = 500; + public static final IElementType DUMMY_BLOCK = new DummyBlockElementType(); public interface Parser { @@ -66,16 +73,29 @@ public class GeneratedParserUtilBase { return builder_.eof(); } + public static int current_position_(PsiBuilder builder_) { + return builder_.rawTokenIndex(); + } + public static boolean recursion_guard_(PsiBuilder builder_, int level_, String funcName_) { - if (level_ > 1000) { - builder_.error("Maximum recursion level (" + 1000 + ") reached in " + funcName_); + if (level_ > MAX_RECURSION_LEVEL) { + builder_.error("Maximum recursion level (" + MAX_RECURSION_LEVEL + ") reached in '" + funcName_ + "'"); return false; } return true; } + @Deprecated public static void empty_element_parsed_guard_(PsiBuilder builder_, int offset_, String funcName_) { - builder_.error("Empty element parsed in " + funcName_ +" at offset " + offset_); + builder_.error("Empty element parsed in '" + funcName_ + "' at offset " + offset_); + } + + public static boolean empty_element_parsed_guard_(PsiBuilder builder_, String funcName_, int prev_position_) { + if (prev_position_ == current_position_(builder_)) { + builder_.error("Empty element parsed in '" + funcName_ + "' at offset " + builder_.getCurrentOffset()); + return false; + } + return true; } public static boolean invalid_left_marker_guard_(PsiBuilder builder_, PsiBuilder.Marker marker_, String funcName_) { @@ -153,6 +173,33 @@ public class GeneratedParserUtilBase { return builder_.getTokenType() == token; } + public static boolean nextTokenIsFast(PsiBuilder builder_, IElementType... tokens) { + IElementType tokenType = builder_.getTokenType(); + for (IElementType token : tokens) { + if (token == tokenType) return true; + } + return false; + } + + public static boolean nextTokenIs(PsiBuilder builder_, String frameName, IElementType... tokens) { + ErrorState state = ErrorState.get(builder_); + if (state.completionState != null) return true; + boolean track = !state.suppressErrors && state.predicateCount < 2 && state.predicateSign; + if (!track) return nextTokenIsFast(builder_, tokens); + boolean useFrameName = StringUtil.isNotEmpty(frameName); + IElementType tokenType = builder_.getTokenType(); + if (tokenType == null) return false; + boolean result = false; + for (IElementType token : tokens) { + if (!useFrameName) addVariant(builder_, state, token); + result |= tokenType == token; + } + if (useFrameName) { + addVariantInner(state, builder_.getCurrentOffset(), frameName); + } + return result; + } + public static boolean nextTokenIs(PsiBuilder builder_, IElementType token) { return nextTokenIsInner(builder_, token, false); } @@ -167,6 +214,7 @@ public class GeneratedParserUtilBase { return token == tokenType; } + @Deprecated public static boolean replaceVariants(PsiBuilder builder_, int variantCount, String frameName) { ErrorState state = ErrorState.get(builder_); if (!state.suppressErrors && state.predicateCount < 2 && state.predicateSign) { @@ -282,6 +330,8 @@ public class GeneratedParserUtilBase { public static final String _SECTION_RECOVER_ = "_SECTION_RECOVER_"; public static final String _SECTION_GENERAL_ = "_SECTION_GENERAL_"; + @SuppressWarnings("StringEquality") + @Deprecated public static void enterErrorRecordingSection(PsiBuilder builder_, int level, @NotNull String sectionType, @Nullable String frameName) { int modifiers = sectionType == _SECTION_GENERAL_ ? _NONE_ : sectionType == _SECTION_NOT_ ? _NOT_ : @@ -289,6 +339,7 @@ public class GeneratedParserUtilBase { enter_section_impl_(builder_, level, modifiers, frameName); } + @Deprecated public static boolean exitErrorRecordingSection(PsiBuilder builder_, int level, boolean result, @@ -356,7 +407,7 @@ public class GeneratedParserUtilBase { public static void exit_section_(PsiBuilder builder_, int level, PsiBuilder.Marker marker, - IElementType elementType, + @Nullable IElementType elementType, boolean result, boolean pinned, @Nullable Parser eatMore) { @@ -464,7 +515,9 @@ public class GeneratedParserUtilBase { if ((frame.modifiers & _COLLAPSE_) != 0) { LighterASTNode last = result || pinned? builder_.getLatestDoneMarker() : null; if (last != null && last.getStartOffset() == frame.offset && state.typeExtends(last.getTokenType(), elementType)) { - marker.drop(); + IElementType resultType = last.getTokenType(); + ((PsiBuilder.Marker)last).drop(); + marker.done(resultType); return; } } @@ -527,7 +580,7 @@ public class GeneratedParserUtilBase { public static void report_error_(PsiBuilder builder_, ErrorState state, boolean advance) { Frame frame = state.frameStack.isEmpty()? null : state.frameStack.getLast(); if (frame == null) { - LOG.error("Unbalanced error section: got null , expected " + frame); + LOG.error("unbalanced enter/exit section call: got null"); return; } int offset = builder_.getCurrentOffset(); @@ -628,10 +681,10 @@ public class GeneratedParserUtilBase { public boolean altMode; private int lastExpectedVariantOffset = -1; - public MyList variants = new MyList(500); - public MyList unexpected = new MyList(10); + public MyList variants = new MyList(INITIAL_VARIANTS_SIZE); + public MyList unexpected = new MyList(INITIAL_VARIANTS_SIZE / 10); - final LimitedPool VARIANTS = new LimitedPool(1000, new LimitedPool.ObjectFactory() { + final LimitedPool VARIANTS = new LimitedPool(VARIANTS_POOL_SIZE, new LimitedPool.ObjectFactory() { @Override public Variant create() { return new Variant(); @@ -641,7 +694,7 @@ public class GeneratedParserUtilBase { public void cleanup(final Variant o) { } }); - final LimitedPool FRAMES = new LimitedPool(100, new LimitedPool.ObjectFactory() { + final LimitedPool FRAMES = new LimitedPool(FRAMES_POOL_SIZE, new LimitedPool.ObjectFactory() { @Override public Frame create() { return new Frame(); @@ -677,7 +730,6 @@ public class GeneratedParserUtilBase { return sb.toString(); } - private static final int MAX_VARIANTS_TO_DISPLAY = Integer.MAX_VALUE; private boolean addExpected(StringBuilder sb, int offset, boolean expected) { MyList list = expected ? variants : unexpected; String[] strings = new String[list.size()]; @@ -729,14 +781,13 @@ public class GeneratedParserUtilBase { } boolean typeExtends(IElementType child_, IElementType parent_) { - if (extendsSets == null) { - return child_ == parent_ || - altExtendsChecker != null && altExtendsChecker.process(child_, parent_); + if (child_ == parent_) return true; + if (extendsSets != null) { + for (TokenSet set : extendsSets) { + if (set.contains(child_) && set.contains(parent_)) return true; + } } - for (TokenSet set : extendsSets) { - if (set.contains(child_) && set.contains(parent_)) return true; - } - return false; + return altExtendsChecker != null && altExtendsChecker.process(child_, parent_); } } @@ -816,15 +867,6 @@ public class GeneratedParserUtilBase { } } - @Nullable - private static IElementType getClosingBracket(ErrorState state, IElementType type) { - if (state.braces == null) return null; - for (BracePair pair : state.braces) { - if (type == pair.getLeftBraceType()) return pair.getRightBraceType(); - } - return null; - } - private static final int MAX_CHILDREN_IN_TREE = 10; public static boolean parseAsTree(ErrorState state, final PsiBuilder builder_, int level, final IElementType chunkType, @@ -961,7 +1003,16 @@ public class GeneratedParserUtilBase { } protected void setSize(int fromIndex) { - super.removeRange(fromIndex, size()); + removeRange(fromIndex, size()); + } + + @Override + public void ensureCapacity(int minCapacity) { + int size = size(); + if (size >= MAX_VARIANTS_SIZE) { + removeRange(MAX_VARIANTS_SIZE / 4, size - MAX_VARIANTS_SIZE / 4); + } + super.ensureCapacity(minCapacity); } } } diff --git a/platform/lang-impl/src/com/intellij/openapi/module/WebModuleBuilder.java b/platform/lang-impl/src/com/intellij/openapi/module/WebModuleBuilder.java index cee30d38edf2..991ae5f84cf8 100644 --- a/platform/lang-impl/src/com/intellij/openapi/module/WebModuleBuilder.java +++ b/platform/lang-impl/src/com/intellij/openapi/module/WebModuleBuilder.java @@ -24,14 +24,12 @@ import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.ModuleRootManager; -import com.intellij.openapi.roots.ui.configuration.ModulesProvider; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.platform.WebProjectGenerator; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.util.List; /** * @author Dmitry Avdeev @@ -72,13 +70,14 @@ public class WebModuleBuilder extends ModuleBuilder { return myTemplate != null ? myTemplate.getIcon() : ICON; } + @Nullable @Override - public List commit(Project project, ModifiableModuleModel model, ModulesProvider modulesProvider) { - List modules = super.commit(project, model, modulesProvider); - if (modules != null && !modules.isEmpty() && myTemplate != null) { - doGenerate(myTemplate, modules.get(0)); + public Module commitModule(@NotNull Project project, @Nullable ModifiableModuleModel model) { + Module module = super.commitModule(project, model); + if (module != null && myTemplate != null) { + doGenerate(myTemplate, module); } - return modules; + return module; } private static void doGenerate(@NotNull WebProjectTemplate template, @NotNull Module module) { diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/DirectoryIndexImpl.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/DirectoryIndexImpl.java index 8c2948ffce22..ab711d4c4852 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/DirectoryIndexImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/DirectoryIndexImpl.java @@ -657,15 +657,10 @@ public class DirectoryIndexImpl extends DirectoryIndex { return getRootIndex().isProjectExcludeRoot(dir); } - - //noinspection UnnecessaryLocalVariable boolean standardResult = myState.myProjectExcludeRoots.contains(((NewVirtualFile)dir).getId()); -/* todo RootIndex rootIndex = getRootIndex(); Boolean riResult = rootIndex != null ? rootIndex.isProjectExcludeRoot(dir) : null; return assertConsistentResult(dir, riResult, standardResult); -*/ - return standardResult; } private VirtualFile findFileById(int dir) { diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/RootIndex.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/RootIndex.java index 7bc634152925..cc01c67973d4 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/RootIndex.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/RootIndex.java @@ -25,6 +25,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.impl.libraries.LibraryEx; import com.intellij.openapi.roots.libraries.Library; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; @@ -32,6 +33,7 @@ import com.intellij.openapi.vfs.newvfs.events.VFileEvent; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import gnu.trove.TObjectIntHashMap; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.model.java.JavaSourceRootType; @@ -44,6 +46,7 @@ class RootIndex { private static final DirectoryInfo NULL_INFO = DirectoryInfo.createNew(); private final Set myProjectExcludedRoots = ContainerUtil.newHashSet(); + private final Set myModuleExcludedRoots = ContainerUtil.newHashSet(); private final Set myLibraryExcludedRoots = ContainerUtil.newHashSet(); private final Map myRoots = ContainerUtil.newTroveMap(); private final Map> myPackagePrefixRoots = ContainerUtil.newHashMap(); @@ -71,7 +74,13 @@ class RootIndex { for (ContentEntry contentEntry : contentEntries) { // Init excluded roots - Collections.addAll(myProjectExcludedRoots, contentEntry.getExcludeFolderFiles()); + VirtualFile[] excluded = contentEntry.getExcludeFolderFiles(); + for (VirtualFile excludeRoot : excluded) { + if (!FileUtil.startsWith(excludeRoot.getUrl(), contentEntry.getUrl())) { + myProjectExcludedRoots.add(excludeRoot); + } + myModuleExcludedRoots.add(excludeRoot); + } // Init module sources SourceFolder[] sourceFolders = contentEntry.getSourceFolders(); @@ -157,7 +166,9 @@ class RootIndex { } for (DirectoryIndexExcludePolicy policy : Extensions.getExtensions(DirectoryIndexExcludePolicy.EP_NAME, project)) { - Collections.addAll(myProjectExcludedRoots, policy.getExcludeRootsForProject()); + VirtualFile[] excludeRoots = policy.getExcludeRootsForProject(); + Collections.addAll(myModuleExcludedRoots, excludeRoots); + Collections.addAll(myProjectExcludedRoots, excludeRoots); } // fill ordered entries @@ -224,7 +235,10 @@ class RootIndex { } public void checkConsistency() { - for (VirtualFile file : myProjectExcludedRoots) { + for (VirtualFile file : myLibraryExcludedRoots) { + assert file.exists() : file.getPath() + " does not exist"; + } + for (VirtualFile file : myModuleExcludedRoots) { assert file.exists() : file.getPath() + " does not exist"; } for (VirtualFile file : myLibraryExcludedRoots) { @@ -346,7 +360,7 @@ class RootIndex { } private boolean isAnyExcludeRoot(VirtualFile root) { - return myProjectExcludedRoots.contains(root) || myLibraryExcludedRoots.contains(root); + return myModuleExcludedRoots.contains(root) || myLibraryExcludedRoots.contains(root); } public boolean isProjectExcludeRoot(@NotNull final VirtualFile dir) { @@ -390,6 +404,7 @@ class RootIndex { return result; } + @Contract("_,null->false") private boolean isValidPackageDirectory(boolean includeLibrarySources, @Nullable VirtualFile file) { if (file != null) { DirectoryInfo info = getInfoForDirectory(file); diff --git a/platform/lang-impl/src/com/intellij/psi/codeStyle/arrangement/engine/ArrangementEngine.java b/platform/lang-impl/src/com/intellij/psi/codeStyle/arrangement/engine/ArrangementEngine.java index d792e8db13b2..9090bc371b1f 100644 --- a/platform/lang-impl/src/com/intellij/psi/codeStyle/arrangement/engine/ArrangementEngine.java +++ b/platform/lang-impl/src/com/intellij/psi/codeStyle/arrangement/engine/ArrangementEngine.java @@ -273,14 +273,18 @@ public class ArrangementEngine { for (int i = 0; i < arranged.size() && !dependent.isEmpty(); i++) { E e = arranged.get(i); + List shouldBeAddedAfterCurrentElement = ContainerUtil.newArrayList(); + for (Iterator, E>> iterator = dependent.iterator(); iterator.hasNext(); ) { Pair, E> pair = iterator.next(); pair.first.remove(e); if (pair.first.isEmpty()) { iterator.remove(); - arranged.add(i + 1, pair.second); + shouldBeAddedAfterCurrentElement.add(pair.second); } } + + arranged.addAll(i + 1, shouldBeAddedAfterCurrentElement); } return arranged; diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/LightPsiFileImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/source/LightPsiFileImpl.java index de7c965e89cf..7d25327c607e 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/LightPsiFileImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/LightPsiFileImpl.java @@ -258,7 +258,7 @@ public abstract class LightPsiFileImpl extends PsiElementBase implements PsiFile @Override @NotNull public char[] textToCharArray() { - return CharArrayUtil.fromSequenceStrict(getViewProvider().getContents()); + return CharArrayUtil.fromSequence(getViewProvider().getContents()); } @Override diff --git a/platform/lang-impl/src/com/intellij/refactoring/introduce/inplace/AbstractInplaceIntroducer.java b/platform/lang-impl/src/com/intellij/refactoring/introduce/inplace/AbstractInplaceIntroducer.java index cf4eaf6fd04e..e761e9a3f2f3 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/introduce/inplace/AbstractInplaceIntroducer.java +++ b/platform/lang-impl/src/com/intellij/refactoring/introduce/inplace/AbstractInplaceIntroducer.java @@ -81,7 +81,7 @@ public abstract class AbstractInplaceIntroducer extends In myExprMarker = exprMarker; } + @Nullable public E getExpr() { return myExpr != null && myExpr.isValid() && myExpr.isPhysical() ? myExpr : null; } diff --git a/platform/platform-api/src/com/intellij/execution/process/CapturingProcessHandler.java b/platform/platform-api/src/com/intellij/execution/process/CapturingProcessHandler.java index 0cea7ab1525e..d76817b98f97 100644 --- a/platform/platform-api/src/com/intellij/execution/process/CapturingProcessHandler.java +++ b/platform/platform-api/src/com/intellij/execution/process/CapturingProcessHandler.java @@ -66,6 +66,16 @@ public class CapturingProcessHandler extends OSProcessHandler { * @param timeoutInMilliseconds non-positive means infinity */ public ProcessOutput runProcess(int timeoutInMilliseconds) { + return runProcess(timeoutInMilliseconds, true); + } + + /** + * Starts process with specified timeout + * + * @param timeoutInMilliseconds non-positive means infinity + * @param destroyOnTimeout whether to kill the process after timeout passes + */ + public ProcessOutput runProcess(int timeoutInMilliseconds, boolean destroyOnTimeout) { if (timeoutInMilliseconds <= 0) { return runProcess(); } @@ -75,7 +85,9 @@ public class CapturingProcessHandler extends OSProcessHandler { myOutput.setExitCode(getProcess().exitValue()); } else { - destroyProcess(); + if (destroyOnTimeout) { + destroyProcess(); + } myOutput.setTimeout(); } return myOutput; diff --git a/platform/platform-api/src/com/intellij/ui/ScreenUtil.java b/platform/platform-api/src/com/intellij/ui/ScreenUtil.java index 00300b390604..76bb6f25e357 100644 --- a/platform/platform-api/src/com/intellij/ui/ScreenUtil.java +++ b/platform/platform-api/src/com/intellij/ui/ScreenUtil.java @@ -165,7 +165,7 @@ public class ScreenUtil { } private static Insets calcInsets(GraphicsConfiguration gc) { - if (Patches.SUN_BUG_ID_9000030 && GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices().length > 1) { + if (Patches.SUN_BUG_ID_7172665 && GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices().length > 1) { return new Insets(0, 0, 0, 0); } diff --git a/platform/platform-api/src/com/intellij/ui/SearchTextField.java b/platform/platform-api/src/com/intellij/ui/SearchTextField.java index ac1b73b05ec9..c6a76b830b65 100644 --- a/platform/platform-api/src/com/intellij/ui/SearchTextField.java +++ b/platform/platform-api/src/com/intellij/ui/SearchTextField.java @@ -233,8 +233,9 @@ public class SearchTextField extends JPanel { } } - public void setHistorySize(int aHistorySize) { - myHistorySize = aHistorySize; + public void setHistorySize(int historySize) { + if (historySize <= 0) throw new IllegalArgumentException("history size must be a positive number"); + myHistorySize = historySize; } public void setHistory(List aHistory) { diff --git a/platform/platform-api/src/com/intellij/ui/SimpleColoredComponent.java b/platform/platform-api/src/com/intellij/ui/SimpleColoredComponent.java index 7e0d6cd78586..25bd8ba46cdc 100644 --- a/platform/platform-api/src/com/intellij/ui/SimpleColoredComponent.java +++ b/platform/platform-api/src/com/intellij/ui/SimpleColoredComponent.java @@ -632,7 +632,7 @@ public class SimpleColoredComponent extends JComponent implements Accessible, Co final String fragment = myFragments.get(i); final int fragmentWidth = isOracleRetina ? GraphicsUtil.stringWidth(fragment, font) : metrics.stringWidth(fragment); - final Color bgColor = attributes.getBgColor(); + final Color bgColor = attributes.isSearchMatch() ? null : attributes.getBgColor(); if ((attributes.isOpaque() || isOpaque()) && bgColor != null) { g.setColor(bgColor); g.fillRect(offset, 0, fragmentWidth, getHeight()); diff --git a/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java b/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java index 8696faa2b7a0..45f0da6dd5ab 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java @@ -1027,6 +1027,10 @@ public class Switcher extends AnAction implements DumbAware { public void propertyChange(PropertyChangeEvent evt) { final MyList list = getSelectedList(); final Object value = list.getSelectedValue(); + if (project.isDisposed()) { + myPopup.cancel(); + return; + } ((NameFilteringListModel)files.getModel()).refilter(); ((NameFilteringListModel)toolWindows.getModel()).refilter(); if (files.getModel().getSize() + toolWindows.getModel().getSize() == 0) { diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaTextFieldUI.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaTextFieldUI.java index 7fd004f45f6d..a6686fe1b9fb 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaTextFieldUI.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaTextFieldUI.java @@ -115,7 +115,7 @@ public class DarculaTextFieldUI extends BasicTextFieldUI { } protected Rectangle getDrawingRect() { - final JTextComponent c = getComponent(); + final JTextComponent c = myTextField; final JBInsets i = JBInsets.create(c.getInsets()); final int x = i.right - 4 - 16; final int y = i.top - 3; diff --git a/platform/platform-impl/src/com/intellij/ide/util/TipUIUtil.java b/platform/platform-impl/src/com/intellij/ide/util/TipUIUtil.java index 62b5a2389cbe..0e531bfc1fc9 100644 --- a/platform/platform-impl/src/com/intellij/ide/util/TipUIUtil.java +++ b/platform/platform-impl/src/com/intellij/ide/util/TipUIUtil.java @@ -91,9 +91,9 @@ public class TipUIUtil { private static void updateImages(StringBuffer text, Class providerClass) { final boolean dark = UIUtil.isUnderDarcula(); final boolean retina = UIUtil.isRetina(); - if (!dark && !retina) { - return; - } +// if (!dark && !retina) { +// return; +// } String suffix = ""; if (retina) suffix += "@2x"; diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/ex/temp/TempFileSystem.java b/platform/platform-impl/src/com/intellij/openapi/vfs/ex/temp/TempFileSystem.java index 92e7ed73e486..8744cd293dee 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/ex/temp/TempFileSystem.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/ex/temp/TempFileSystem.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.intellij.openapi.vfs.ex.temp; import com.intellij.openapi.application.ApplicationManager; @@ -46,6 +45,7 @@ import java.util.Set; public class TempFileSystem extends LocalFileSystemBase { private final FSItem myRoot = new FSDir(null, "/"); + @SuppressWarnings("MethodOverridesStaticMethodOfSuperclass") public static TempFileSystem getInstance() { return ApplicationManager.getApplication().getComponent(TempFileSystem.class); } @@ -53,7 +53,6 @@ public class TempFileSystem extends LocalFileSystemBase { @NotNull @Override protected String extractRootPath(@NotNull final String path) { - //return path.startsWith("/") ? "/" : ""; return "/"; } @@ -66,6 +65,7 @@ public class TempFileSystem extends LocalFileSystemBase { private FSItem convert(VirtualFile file) { final VirtualFile parentFile = file.getParent(); if (parentFile == null) return myRoot; + FSItem parentItem = convert(parentFile); if (parentItem == null || !parentItem.isDirectory()) { return null; @@ -74,15 +74,9 @@ public class TempFileSystem extends LocalFileSystemBase { return parentItem.findChild(file.getName()); } - @Override - public VirtualFile copyFile(final Object requestor, @NotNull final VirtualFile file, @NotNull final VirtualFile newParent, @NotNull final String copyName) - throws IOException { - return VfsUtilCore.copyFile(requestor, file, newParent, copyName); - } - @Override @NotNull - public VirtualFile createChildDirectory(final Object requestor, @NotNull final VirtualFile parent, @NotNull final String dir) throws IOException { + public VirtualFile createChildDirectory(Object requestor, @NotNull VirtualFile parent, @NotNull String dir) throws IOException { final FSItem fsItem = convert(parent); assert fsItem != null && fsItem.isDirectory(); @@ -95,27 +89,34 @@ public class TempFileSystem extends LocalFileSystemBase { throw new IOException("Directory already contains a file named " + dir); } - return new FakeVirtualFile(parent, dir); } @Override - public VirtualFile createChildFile(final Object requestor, @NotNull final VirtualFile parent, @NotNull final String file) throws IOException { + public VirtualFile createChildFile(Object requestor, @NotNull VirtualFile parent, @NotNull String file) throws IOException { final FSItem fsItem = convert(parent); if (fsItem == null) { FSRecords.invalidateCaches(); throw new IllegalStateException("cannot find parent directory: " + parent.getPath()); } - assert fsItem.isDirectory(): "parent is not a directory: " + parent.getPath(); + assert fsItem.isDirectory() : "parent is not a directory: " + parent.getPath(); final FSDir fsDir = (FSDir)fsItem; - assert fsDir.findChild(file) == null: "File " + file + " already exists in " + parent.getPath(); + assert fsDir.findChild(file) == null : "File " + file + " already exists in " + parent.getPath(); fsDir.addChild(new FSFile(fsDir, file)); return new FakeVirtualFile(parent, file); } + @Override + public VirtualFile copyFile(Object requestor, + @NotNull VirtualFile file, + @NotNull VirtualFile newParent, + @NotNull String copyName) throws IOException { + return VfsUtilCore.copyFile(requestor, file, newParent, copyName); + } + @Override public void deleteFile(final Object requestor, @NotNull final VirtualFile file) throws IOException { final FSItem fsItem = convert(file); @@ -129,23 +130,22 @@ public class TempFileSystem extends LocalFileSystemBase { @Override public void moveFile(final Object requestor, @NotNull final VirtualFile file, @NotNull final VirtualFile newParent) throws IOException { final FSItem fsItem = convert(file); - assert fsItem != null: "failed to move file " + file.getPath(); + assert fsItem != null : "failed to move file " + file.getPath(); final FSItem newParentItem = convert(newParent); - assert newParentItem != null && newParentItem.isDirectory(): "failed to find move target " + file.getPath(); - FSDir newDir = (FSDir) newParentItem; + assert newParentItem != null && newParentItem.isDirectory() : "failed to find move target " + file.getPath(); + FSDir newDir = (FSDir)newParentItem; if (newDir.findChild(file.getName()) != null) { throw new IOException("Directory already contains a file named " + file.getName()); } fsItem.getParent().removeChild(fsItem); - ((FSDir) newParentItem).addChild(fsItem); + ((FSDir)newParentItem).addChild(fsItem); } @Override public void renameFile(final Object requestor, @NotNull final VirtualFile file, @NotNull final String newName) throws IOException { final FSItem fsItem = convert(file); assert fsItem != null; - fsItem.setName(newName); } @@ -165,7 +165,6 @@ public class TempFileSystem extends LocalFileSystemBase { public String[] list(@NotNull final VirtualFile file) { final FSItem fsItem = convert(file); assert fsItem != null; - return fsItem.list(); } @@ -177,8 +176,7 @@ public class TempFileSystem extends LocalFileSystemBase { @Override public long getTimeStamp(@NotNull final VirtualFile file) { final FSItem fsItem = convert(file); - assert fsItem != null: "cannot find item for path " + file.getPath(); - + assert fsItem != null : "cannot find item for path " + file.getPath(); return fsItem.myTimestamp; } @@ -186,7 +184,6 @@ public class TempFileSystem extends LocalFileSystemBase { public void setTimeStamp(@NotNull final VirtualFile file, final long timeStamp) { final FSItem fsItem = convert(file); assert fsItem != null; - fsItem.myTimestamp = timeStamp > 0 ? timeStamp : LocalTimeCounter.currentTime(); } @@ -194,7 +191,6 @@ public class TempFileSystem extends LocalFileSystemBase { public boolean isWritable(@NotNull final VirtualFile file) { final FSItem fsItem = convert(file); assert fsItem != null; - return fsItem.myWritable; } @@ -202,7 +198,6 @@ public class TempFileSystem extends LocalFileSystemBase { public void setWritable(@NotNull final VirtualFile file, final boolean writableFlag) throws IOException { final FSItem fsItem = convert(file); assert fsItem != null; - fsItem.myWritable = writableFlag; } @@ -211,9 +206,7 @@ public class TempFileSystem extends LocalFileSystemBase { public byte[] contentsToByteArray(@NotNull final VirtualFile file) throws IOException { final FSItem fsItem = convert(file); if (fsItem == null) throw new FileNotFoundException("Cannot find temp for " + file.getPath()); - assert fsItem instanceof FSFile; - return ((FSFile)fsItem).myContent; } @@ -225,8 +218,10 @@ public class TempFileSystem extends LocalFileSystemBase { @Override @NotNull - public OutputStream getOutputStream(@NotNull final VirtualFile file, final Object requestor, final long modStamp, final long timeStamp) - throws IOException { + public OutputStream getOutputStream(@NotNull final VirtualFile file, + final Object requestor, + final long modStamp, + final long timeStamp) throws IOException { return new ByteArrayOutputStream() { @Override public void close() throws IOException { diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/FileNameCache.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/FileNameCache.java index 283ec24d6884..ab9655a75c03 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/FileNameCache.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/FileNameCache.java @@ -79,7 +79,7 @@ public class FileNameCache { if (length == 0) return ""; if (!IOUtil.isAscii(name)) { - return name; + return new String(name); // So we don't hold whole char[] buffer of a lengthy path on JDK 6 } byte[] bytes = new byte[length]; diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualDirectoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualDirectoryImpl.java index 8a0406d4f4e0..a2e94a323d5d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualDirectoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualDirectoryImpl.java @@ -61,7 +61,7 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vfs.newvfs.impl.VirtualDirectoryImpl"); public static boolean CHECK = ApplicationManager.getApplication().isUnitTestMode(); - static final VirtualDirectoryImpl NULL_VIRTUAL_FILE = new VirtualDirectoryImpl("*?;%NULL", null, LocalFileSystem.getInstance(), -42, 0) { + static final VirtualDirectoryImpl NULL_VIRTUAL_FILE = new VirtualDirectoryImpl(FileNameCache.storeName("*?;%NULL"), null, LocalFileSystem.getInstance(), -42, 0) { public String toString() { return "NULL"; } @@ -82,12 +82,12 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { */ private VirtualFileSystemEntry[] myChildren = EMPTY_ARRAY; - public VirtualDirectoryImpl(@NonNls @NotNull final String name, + public VirtualDirectoryImpl(@NonNls final int nameId, @Nullable final VirtualDirectoryImpl parent, @NotNull final NewVirtualFileSystem fs, final int id, @PersistentFS.Attributes final int attributes) { - super(name, parent, id, attributes); + super(nameId, parent, id, attributes); myFS = fs; } @@ -103,7 +103,7 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { boolean ensureCanonicalName, @NotNull NewVirtualFileSystem delegate) { boolean ignoreCase = !delegate.isCaseSensitive(); - Comparator comparator = getComparator(name, ignoreCase); + Comparator comparator = getComparator(ignoreCase); VirtualFileSystemEntry result = doFindChild(name, ensureCanonicalName, delegate, comparator); if (result == NULL_VIRTUAL_FILE) { @@ -115,16 +115,16 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { } if (result == null) { - addToAdoptedChildren(name, !delegate.isCaseSensitive(), comparator); + addToAdoptedChildren(!delegate.isCaseSensitive(), name, comparator); } return result; } - private synchronized void addToAdoptedChildren(@NotNull final String name, - final boolean ignoreCase, + private synchronized void addToAdoptedChildren(final boolean ignoreCase, + @NotNull final String name, @NotNull Comparator comparator) { - long r = findIndexInBoth(myChildren, comparator); + long r = findIndexInBoth(myChildren, name, comparator); int indexInReal = (int)(r >> 32); int indexInAdopted = (int)r; if (indexInAdopted >= 0) return; //already added @@ -141,15 +141,45 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { } private static class AdoptedChild extends VirtualFileImpl { + private final String myName; + private AdoptedChild(String name) { - super(name, NULL_VIRTUAL_FILE, -42, -1); + super(-1, NULL_VIRTUAL_FILE, -42, -1); + myName = name; + } + + @NotNull + @Override + public String getName() { + return myName; + } + + @Override + public void setNewName(@NotNull String newName) { + throw new IncorrectOperationException(); + } + + @Override + public int compareNameTo(@NotNull String name, boolean ignoreCase) { + return compareNames(myName, name, ignoreCase); + } + + @Override + protected char[] appendPathOnFileSystem(int accumulatedPathLength, int[] positionRef) { + char[] chars = getParent().appendPathOnFileSystem(accumulatedPathLength + 1 + myName.length(), positionRef); + if (positionRef[0] > 0 && chars[positionRef[0] - 1] != '/') { + chars[positionRef[0]++] = '/'; + } + positionRef[0] = VirtualFileSystemEntry.copyString(chars, positionRef[0], myName); + return chars; + } } @Nullable // null if there can't be a child with this name, NULL_VIRTUAL_FILE - private synchronized VirtualFileSystemEntry doFindChildInArray(@NotNull Comparator comparator) { + private synchronized VirtualFileSystemEntry doFindChildInArray(@NotNull String name, @NotNull Comparator comparator) { VirtualFileSystemEntry[] array = myChildren; - long r = findIndexInBoth(array, comparator); + long r = findIndexInBoth(array, name, comparator); int indexInReal = (int)(r >> 32); int indexInAdopted = (int)r; if (indexInAdopted >= 0) return NULL_VIRTUAL_FILE; @@ -169,7 +199,7 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { return null; } - VirtualFileSystemEntry found = doFindChildInArray(comparator); + VirtualFileSystemEntry found = doFindChildInArray(name, comparator); if (found != null) return found; if (allChildrenLoaded()) { @@ -185,7 +215,7 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { synchronized (this) { // maybe another doFindChild() sneaked in the middle VirtualFileSystemEntry[] array = myChildren; - long r = findIndexInBoth(array, comparator); + long r = findIndexInBoth(array, name, comparator); int indexInReal = (int)(r >> 32); int indexInAdopted = (int)r; if (indexInAdopted >= 0) return NULL_VIRTUAL_FILE; @@ -199,8 +229,7 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { if (id <= 0) { return null; } - String shorty = new String(name); - VirtualFileSystemEntry child = createChild(shorty, id, delegate); // So we don't hold whole char[] buffer of a lengthy path + VirtualFileSystemEntry child = createChild(FileNameCache.storeName(name), id, delegate); VirtualFileSystemEntry[] after = myChildren; if (after != array) { @@ -216,14 +245,21 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { } } + private static final Comparator CASE_SENSITIVE = new Comparator() { + @Override + public int compareFileNameTo(@NotNull String myName, @NotNull VirtualFileSystemEntry file) { + return -file.compareNameTo(myName, false); + } + }; + private static final Comparator CASE_INSENSITIVE = new Comparator() { + @Override + public int compareFileNameTo(@NotNull String myName, @NotNull VirtualFileSystemEntry file) { + return -file.compareNameTo(myName, true); + } + }; @NotNull - private static Comparator getComparator(@NotNull final String name, final boolean ignoreCase) { - return new Comparator() { - @Override - public int compareMyKeyTo(@NotNull VirtualFileSystemEntry file) { - return -file.compareNameTo(name, ignoreCase); - } - }; + private static Comparator getComparator(final boolean ignoreCase) { + return ignoreCase ? CASE_INSENSITIVE : CASE_SENSITIVE; } private synchronized VirtualFileSystemEntry[] getArraySafely() { @@ -231,15 +267,20 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { } @NotNull - public VirtualFileSystemEntry createChild(@NotNull String name, int id, @NotNull NewVirtualFileSystem delegate) { + public VirtualFileSystemEntry createChild(String name, int id, @NotNull NewVirtualFileSystem delegate) { + return createChild(FileNameCache.storeName(name), id, delegate); + } + + @NotNull + private VirtualFileSystemEntry createChild(int nameId, int id, @NotNull NewVirtualFileSystem delegate) { VirtualFileSystemEntry child; final int attributes = ourPersistence.getFileAttributes(id); if (PersistentFS.isDirectory(attributes)) { - child = new VirtualDirectoryImpl(name, this, delegate, id, attributes); + child = new VirtualDirectoryImpl(nameId, this, delegate, id, attributes); } else { - child = new VirtualFileImpl(name, this, id, attributes); + child = new VirtualFileImpl(nameId, this, id, attributes); //noinspection TestOnlyProblems assertAccessInTests(child, delegate); } @@ -391,19 +432,21 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { int start, int end, final boolean isAdopted, - @NotNull final Comparator comparator) { - return binSearch(array, start, end, new Comparator() { + @NotNull String name, @NotNull final Comparator comparator) { + return binSearch(array, start, end, name, new Comparator() { @Override - public int compareMyKeyTo(@NotNull VirtualFileSystemEntry file) { + public int compareFileNameTo(@NotNull String myName, @NotNull VirtualFileSystemEntry file) { if (isAdopted && !isAdoptedChild(file)) return 1; if (!isAdopted && isAdoptedChild(file)) return -1; - return comparator.compareMyKeyTo(file); + return comparator.compareFileNameTo(myName, file); } }); } // returns two int indices packed into one long. left index is for the real file array half, right is for the adopted children name array - private static long findIndexInBoth(@NotNull VirtualFileSystemEntry[] array, @NotNull Comparator comparator) { + private static long findIndexInBoth(@NotNull VirtualFileSystemEntry[] array, + @NotNull String name, + @NotNull Comparator comparator) { int high = array.length - 1; if (high == -1) { return pack(-1, -1); @@ -412,7 +455,7 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { boolean startInAdopted = isAdoptedChild(array[low]); boolean endInAdopted = isAdoptedChild(array[high]); if (startInAdopted == endInAdopted) { - int index = findIndexInOneHalf(array, low, high + 1, startInAdopted, comparator); + int index = findIndexInOneHalf(array, low, high + 1, startInAdopted, name, comparator); int otherIndex = startInAdopted ? -1 : -array.length - 1; return startInAdopted ? pack(otherIndex, index) : pack(index, otherIndex); } @@ -423,15 +466,15 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { while (low <= high) { mid = low + high >>> 1; VirtualFileSystemEntry file = array[mid]; - cmp = comparator.compareMyKeyTo(file); + cmp = comparator.compareFileNameTo(name, file); adopted = isAdoptedChild(file); if (cmp == 0) { foundIndex = mid; break; } if ((adopted || cmp <= 0) && (!adopted || cmp >= 0)) { - int indexInAdopted = findIndexInOneHalf(array, mid + 1, high + 1, true, comparator); - int indexInReal = findIndexInOneHalf(array, low, mid, false, comparator); + int indexInAdopted = findIndexInOneHalf(array, mid + 1, high + 1, true, name, comparator); + int indexInReal = findIndexInOneHalf(array, low, mid, false, name, comparator); return pack(indexInReal, indexInAdopted); } @@ -447,7 +490,7 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { if (cmp != 0) foundIndex = -low-1; int newStart = adopted ? low : mid + 1; int newEnd = adopted ? mid + 1 : high + 1; - int theOtherHalfIndex = newStart < newEnd ? findIndexInOneHalf(array, newStart, newEnd, !adopted, comparator) : -newStart-1; + int theOtherHalfIndex = newStart < newEnd ? findIndexInOneHalf(array, newStart, newEnd, !adopted, name, comparator) : -newStart-1; return adopted ? pack(theOtherHalfIndex, foundIndex) : pack(foundIndex, theOtherHalfIndex); } @@ -459,8 +502,8 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { @Nullable public synchronized NewVirtualFile findChildIfCached(@NotNull String name) { final boolean ignoreCase = !getFileSystem().isCaseSensitive(); - Comparator comparator = getComparator(name, ignoreCase); - VirtualFileSystemEntry found = doFindChildInArray(comparator); + Comparator comparator = getComparator(ignoreCase); + VirtualFileSystemEntry found = doFindChildInArray(name, comparator); return found == NULL_VIRTUAL_FILE ? null : found; } @@ -534,7 +577,7 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { resultFile = children[i++]; } else { - resultFile = createChild(nameId.name, nameId.id, delegate); + resultFile = createChild(nameId.nameId, nameId.id, delegate); } result[delegateI++] = resultFile; } @@ -637,7 +680,7 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { VirtualFileSystemEntry[] array = myChildren; final String childName = child.getName(); final boolean ignoreCase = !getFileSystem().isCaseSensitive(); - long r = findIndexInBoth(array, getComparator(childName, ignoreCase)); + long r = findIndexInBoth(array, childName, getComparator(ignoreCase)); int indexInReal = (int)(r >> 32); int indexInAdopted = (int)r; @@ -666,7 +709,7 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { boolean ignoreCase = !getFileSystem().isCaseSensitive(); String name = file.getName(); - addToAdoptedChildren(name, ignoreCase, getComparator(name, ignoreCase)); + addToAdoptedChildren(ignoreCase, name, getComparator(ignoreCase)); assertConsistency(myChildren, ignoreCase, file); } @@ -699,9 +742,9 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { } private int getAdoptedChildrenStart() { - int index = binSearch(myChildren, 0, myChildren.length, new Comparator() { + int index = binSearch(myChildren, 0, myChildren.length, "", new Comparator() { @Override - public int compareMyKeyTo(@NotNull VirtualFileSystemEntry v) { + public int compareFileNameTo(@NotNull String myName, @NotNull VirtualFileSystemEntry v) { return isAdoptedChild(v) ? -1 : 1; } }); @@ -713,12 +756,13 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { } private interface Comparator { - int compareMyKeyTo(@NotNull VirtualFileSystemEntry file); + int compareFileNameTo(@NotNull String myName, @NotNull VirtualFileSystemEntry file); } private static int binSearch(@NotNull VirtualFileSystemEntry[] array, int start, int end, + @NotNull String name, @NotNull Comparator comparator) { int low = start; int high = end - 1; @@ -726,7 +770,7 @@ public class VirtualDirectoryImpl extends VirtualFileSystemEntry { while (low <= high) { int mid = low + high >>> 1; - int cmp = comparator.compareMyKeyTo(array[mid]); + int cmp = comparator.compareFileNameTo(name, array[mid]); if (cmp > 0) { low = mid + 1; } diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualFileImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualFileImpl.java index 995670f2ae96..33c024fc5cfe 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualFileImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualFileImpl.java @@ -39,8 +39,8 @@ import java.util.Collections; public class VirtualFileImpl extends VirtualFileSystemEntry { private Charset myCharset; - VirtualFileImpl(String name, VirtualDirectoryImpl parent, int id, @PersistentFS.Attributes final int attributes) { - super(name, parent, id, attributes); + VirtualFileImpl(int nameId, VirtualDirectoryImpl parent, int id, @PersistentFS.Attributes final int attributes) { + super(nameId, parent, id, attributes); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualFileSystemEntry.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualFileSystemEntry.java index 7da918a6a75f..2d5b5195dad6 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualFileSystemEntry.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VirtualFileSystemEntry.java @@ -48,7 +48,6 @@ import java.nio.charset.Charset; public abstract class VirtualFileSystemEntry extends NewVirtualFile { public static final VirtualFileSystemEntry[] EMPTY_ARRAY = new VirtualFileSystemEntry[0]; - @NonNls protected static final String FS_ROOT_FAKE_NAME = ""; protected static final PersistentFS ourPersistence = PersistentFS.getInstance(); private static final Key SYMLINK_TARGET = Key.create("local.vfs.symlink.target"); @@ -70,13 +69,10 @@ public abstract class VirtualFileSystemEntry extends NewVirtualFile { private volatile int myFlags; private volatile int myId; - public VirtualFileSystemEntry(@NotNull String name, VirtualDirectoryImpl parent, int id, @PersistentFS.Attributes int attributes) { + public VirtualFileSystemEntry(int nameId, VirtualDirectoryImpl parent, int id, @PersistentFS.Attributes int attributes) { myParent = parent; myId = id; - - if (name != FS_ROOT_FAKE_NAME) { - storeName(name); - } + myNameId = nameId; if (parent != null && parent != VirtualDirectoryImpl.NULL_VIRTUAL_FILE) { setFlagInt(IS_SYMLINK_FLAG, PersistentFS.isSymLink(attributes)); @@ -90,10 +86,6 @@ public abstract class VirtualFileSystemEntry extends NewVirtualFile { setModificationStamp(LocalTimeCounter.currentTime()); } - private void storeName(@NotNull String name) { - myNameId = FileNameCache.storeName(name.replace('\\', '/')); // note: on Unix-style FS names may contain backslashes - } - private void updateLinkStatus() { boolean isSymLink = is(VFileProperty.SYMLINK); if (isSymLink) { @@ -113,7 +105,7 @@ public abstract class VirtualFileSystemEntry extends NewVirtualFile { return FileNameCache.compareNameTo(myNameId, name, ignoreCase); } - static int compareNames(@NotNull String name1, @NotNull String name2, boolean ignoreCase) { + protected static int compareNames(@NotNull String name1, @NotNull String name2, boolean ignoreCase) { return compareNames(name1, name2, ignoreCase, 0); } @@ -350,7 +342,7 @@ public abstract class VirtualFileSystemEntry extends NewVirtualFile { } myParent.removeChild(this); - storeName(newName); + myNameId = FileNameCache.storeName(newName); myParent.addChild(this); } diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/FSRecords.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/FSRecords.java index bd6691fb1601..c1c7cd6371fb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/FSRecords.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/FSRecords.java @@ -29,6 +29,7 @@ import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; import com.intellij.openapi.util.io.ByteSequence; import com.intellij.openapi.util.io.FileAttributes; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.newvfs.impl.FileNameCache; import com.intellij.util.ArrayUtil; import com.intellij.util.SystemProperties; import com.intellij.util.containers.ConcurrentHashMap; @@ -879,9 +880,11 @@ public class FSRecords implements Forceable { public static final NameId[] EMPTY_ARRAY = new NameId[0]; public final int id; public final String name; + public final int nameId; - public NameId(int id, @NotNull String name) { + public NameId(int id, int nameId, @NotNull String name) { this.id = id; + this.nameId = nameId; this.name = name; } @@ -904,7 +907,8 @@ public class FSRecords implements Forceable { for (int i = 0; i < count; i++) { int id = DataInputOutputUtil.readINT(input); id = id >= 0 ? id + parentId : -id; - result[i] = new NameId(id, getName(id)); + int nameId = getNameId(id); + result[i] = new NameId(id, nameId, FileNameCache.getVFileName(nameId)); } input.close(); return result; diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java index ec39e360282b..00087b175916 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java @@ -27,6 +27,7 @@ import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.newvfs.*; import com.intellij.openapi.vfs.newvfs.events.*; import com.intellij.openapi.vfs.newvfs.impl.FakeVirtualFile; +import com.intellij.openapi.vfs.newvfs.impl.FileNameCache; import com.intellij.openapi.vfs.newvfs.impl.VirtualDirectoryImpl; import com.intellij.openapi.vfs.newvfs.impl.VirtualFileSystemEntry; import com.intellij.util.*; @@ -173,43 +174,26 @@ public class PersistentFSImpl extends PersistentFS implements ApplicationCompone return current; } - THashMap result = new THashMap(); - if (current.length == 0) { - for (String name : delegateNames) { - result.put(name, new FSRecords.NameId(-1, name)); - } - } - else { - for (FSRecords.NameId nameId : current) { - result.put(nameId.name, nameId); - } - for (String name : delegateNames) { - if (!result.containsKey(name)) { - result.put(name, new FSRecords.NameId(-1, name)); - } - } + Set toAdd = ContainerUtil.newHashSet(delegateNames); + for (FSRecords.NameId nameId : current) { + toAdd.remove(nameId.name); } - final TIntArrayList childrenIds = new TIntArrayList(result.size()); - final List nameIds = ContainerUtil.newArrayListWithExpectedSize(result.size()); - result.forEachValue(new TObjectProcedure() { - @Override - public boolean execute(FSRecords.NameId nameId) { - if (nameId.id < 0) { - FakeVirtualFile child = new FakeVirtualFile(file, nameId.name); - FileAttributes attributes = fs.getAttributes(child); - if (attributes != null) { - int childId = createAndFillRecord(fs, child, id, attributes); - nameId = new FSRecords.NameId(childId, nameId.name); - } - } - if (nameId.id > 0) { - childrenIds.add(nameId.id); - nameIds.add(nameId); - } - return true; + final TIntArrayList childrenIds = new TIntArrayList(current.length + toAdd.size()); + final List nameIds = ContainerUtil.newArrayListWithExpectedSize(current.length + toAdd.size()); + for (FSRecords.NameId nameId : current) { + childrenIds.add(nameId.id); + nameIds.add(nameId); + } + for (String newName : toAdd) { + FakeVirtualFile child = new FakeVirtualFile(file, newName); + FileAttributes attributes = fs.getAttributes(child); + if (attributes != null) { + int childId = createAndFillRecord(fs, child, id, attributes); + childrenIds.add(childId); + nameIds.add(new FSRecords.NameId(childId, FileNameCache.storeName(newName), newName)); } - }); + } FSRecords.updateList(id, childrenIds.toNativeArray()); setChildrenCached(id); @@ -1291,16 +1275,26 @@ public class PersistentFSImpl extends PersistentFS implements ApplicationCompone private abstract static class AbstractRoot extends VirtualDirectoryImpl { protected AbstractRoot(@NotNull NewVirtualFileSystem fs, int id) { - super(FS_ROOT_FAKE_NAME, null, fs, id, 0); + super(-1, null, fs, id, 0); } @NotNull @Override public abstract String getName(); + @Override + public int compareNameTo(@NotNull String name, boolean ignoreCase) { + return VirtualFileSystemEntry.compareNames(getName(), name, ignoreCase); + } + @Override protected abstract char[] appendPathOnFileSystem(int accumulatedPathLength, int[] positionRef); + @Override + public void setNewName(@NotNull String newName) { + throw new IncorrectOperationException(); + } + @Override public final void setParent(@NotNull VirtualFile newParent) { throw new IncorrectOperationException(); @@ -1315,7 +1309,7 @@ public class PersistentFSImpl extends PersistentFS implements ApplicationCompone @NotNull @Override public String getName() { - return FS_ROOT_FAKE_NAME; + return ""; } @SuppressWarnings("NonSynchronizedMethodOverridesSynchronizedMethod") diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/RefreshWorker.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/RefreshWorker.java index 6dfe3082fd57..ad9a978134af 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/RefreshWorker.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/RefreshWorker.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.vfs.newvfs.persistent; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Pair; @@ -29,6 +30,7 @@ import com.intellij.openapi.vfs.newvfs.NewVirtualFileSystem; import com.intellij.openapi.vfs.newvfs.events.*; import com.intellij.openapi.vfs.newvfs.impl.FakeVirtualFile; import com.intellij.openapi.vfs.newvfs.impl.VirtualDirectoryImpl; +import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.OpenTHashSet; import com.intellij.util.containers.Queue; @@ -36,8 +38,12 @@ import com.intellij.util.text.FilePathHashingStrategy; import gnu.trove.TObjectHashingStrategy; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; import static com.intellij.openapi.diagnostic.LogUtil.debug; import static com.intellij.util.containers.ContainerUtil.newTroveSet; @@ -97,14 +103,14 @@ public class RefreshWorker { TObjectHashingStrategy strategy = FilePathHashingStrategy.create(fs.isCaseSensitive()); while (!myRefreshQueue.isEmpty()) { - checkCancelled(); - Pair pair = myRefreshQueue.pullFirst(); NewVirtualFile file = pair.first; boolean fileDirty = file.isDirty(); debug(LOG, "file=%s dirty=%b", file, fileDirty); if (!fileDirty) continue; + checkCancelled(file); + FileAttributes attributes = pair.second != null ? pair.second : fs.getAttributes(file); if (attributes == null) { scheduleDeletion(file); @@ -139,7 +145,7 @@ public class RefreshWorker { } for (String name : newNames) { - checkCancelled(); + checkCancelled(file); FileAttributes childAttributes = fs.getAttributes(new FakeVirtualFile(file, name)); if (childAttributes != null) { scheduleCreation(file, name, childAttributes.isDirectory(), false); @@ -150,7 +156,7 @@ public class RefreshWorker { } for (VirtualFile child : file.getChildren()) { - checkCancelled(); + checkCancelled(file); if (!deletedNames.contains(child.getName())) { FileAttributes childAttributes = fs.getAttributes(child); if (childAttributes != null) { @@ -173,7 +179,7 @@ public class RefreshWorker { debug(LOG, "cached=%s actual=%s", cachedChildren, actualNames); for (VirtualFile child : cachedChildren) { - checkCancelled(); + checkCancelled(file); FileAttributes childAttributes = fs.getAttributes(child); if (childAttributes != null) { checkAndScheduleChildRefresh(file, child, childAttributes); @@ -187,7 +193,7 @@ public class RefreshWorker { List names = dir.getSuspiciousNames(); debug(LOG, "suspicious=%s", names); for (String name : names) { - checkCancelled(); + checkCancelled(file); if (name.isEmpty()) continue; VirtualFile fake = new FakeVirtualFile(file, name); @@ -250,12 +256,22 @@ public class RefreshWorker { private static class RefreshCancelledException extends RuntimeException { } - private void checkCancelled() { - if (myCancelled) { + private void checkCancelled(@NotNull NewVirtualFile stopAt) { + if (myCancelled || ourCancellingCondition != null && ourCancellingCondition.fun(stopAt)) { + forceMarkDirty(stopAt); + while (!myRefreshQueue.isEmpty()) { + NewVirtualFile next = myRefreshQueue.pullFirst().first; + forceMarkDirty(next); + } throw new RefreshCancelledException(); } } + private static void forceMarkDirty(NewVirtualFile file) { + file.markClean(); // otherwise consequent markDirty() won't have any effect + file.markDirty(); + } + private void checkAndScheduleChildRefresh(@NotNull VirtualFile parent, @NotNull VirtualFile child, @NotNull FileAttributes childAttributes) { @@ -307,4 +323,12 @@ public class RefreshWorker { myEvents.add(new VFileDeleteEvent(null, file, true)); } } + + private static Function ourCancellingCondition = null; + + @TestOnly + public static void setCancellingCondition(@Nullable Function condition) { + assert ApplicationManager.getApplication().isUnitTestMode(); + ourCancellingCondition = condition; + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/SystemDock.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/SystemDock.java index d04caaa7cdbc..b57e538a4387 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/SystemDock.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/SystemDock.java @@ -26,20 +26,28 @@ import com.intellij.ui.win.WinDockDelegate; * @author Denis Fokin */ public class SystemDock { - - private static Delegate delegate; + private static final Delegate ourDelegate; static { - if (SystemInfo.isMac) { - delegate = MacDockDelegate.getInstance(); - } else if (SystemInfo.isWin7OrNewer && Registry.is("windows.jumplist") && !ApplicationManager.getApplication().isUnitTestMode()) { - delegate = WinDockDelegate.getInstance(); + Delegate delegate = null; + + Application app = ApplicationManager.getApplication(); + if (app != null && !app.isUnitTestMode()) { + if (SystemInfo.isMac) { + delegate = MacDockDelegate.getInstance(); + } + else if (SystemInfo.isWin7OrNewer && Registry.is("windows.jumplist")) { + delegate = WinDockDelegate.getInstance(); + } } + + ourDelegate = delegate; } - public static void updateMenu () { - if (delegate == null) return; - delegate.updateRecentProjectsMenu(); + public static void updateMenu() { + if (ourDelegate != null) { + ourDelegate.updateRecentProjectsMenu(); + } } public interface Delegate { diff --git a/platform/platform-impl/src/com/intellij/ui/mac/MacMessagesImpl.java b/platform/platform-impl/src/com/intellij/ui/mac/MacMessagesImpl.java index cafcffd6a0ff..db65c46fb15d 100644 --- a/platform/platform-impl/src/com/intellij/ui/mac/MacMessagesImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/mac/MacMessagesImpl.java @@ -24,10 +24,12 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.impl.ModalityHelper; +import com.intellij.ui.mac.foundation.Foundation; import com.intellij.ui.mac.foundation.ID; import com.intellij.ui.mac.foundation.MacUtil; import com.intellij.util.ui.UIUtil; import com.sun.jna.Callback; +import com.sun.jna.Pointer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -200,6 +202,19 @@ public class MacMessagesImpl extends MacMessages { private MacMessagesImpl() {} + private static final Callback windowDidBecomeMainCallback = new Callback() { + public void callback(ID self, + ID nsNotification) + { + synchronized (lock) { + if (!windowFromId.keySet().contains(self.longValue())) { + return; + } + } + invoke(self, "oldWindowDidBecomeMain:", nsNotification); + } + }; + static { if (SystemInfo.isMac) { final ID delegateClass = allocateObjcClassPair(getObjcClass("NSObject"), "NSAlertDelegate_"); @@ -213,6 +228,19 @@ public class MacMessagesImpl extends MacMessages { throw new RuntimeException("Unable to add method to objective-c delegate class!"); } registerObjcClassPair(delegateClass); + + if (SystemInfo.isJavaVersionAtLeast("1.7")) { + + ID awtWindow = Foundation.getObjcClass("AWTWindow"); + + Pointer windowWillEnterFullScreenMethod = Foundation.createSelector("windowDidBecomeMain:"); + ID originalWindowWillEnterFullScreen = Foundation.class_replaceMethod(awtWindow, windowWillEnterFullScreenMethod, + windowDidBecomeMainCallback, "v@::@"); + + Foundation.addMethodByID(awtWindow, Foundation.createSelector("oldWindowDidBecomeMain:"), + originalWindowWillEnterFullScreen, "v@::@"); + + } } } @@ -327,8 +355,9 @@ public class MacMessagesImpl extends MacMessages { } private static void startModal(final Window w, ID windowId) { + long windowPtr = windowId.longValue(); synchronized (lock) { - windowFromId.put(windowId.longValue(), w); + windowFromId.put(windowPtr, w); if (blockedDocumentRoots.keySet().contains(w)) { blockedDocumentRoots.put(w, blockedDocumentRoots.get(w) + 1); } else { @@ -337,6 +366,9 @@ public class MacMessagesImpl extends MacMessages { } pumpEventsDocumentExclusively(w); + synchronized (lock) { + windowFromId.remove(windowPtr); + } } private enum COMMON_DIALOG_PARAM_TYPE { @@ -717,9 +749,9 @@ public class MacMessagesImpl extends MacMessages { _window = _window.getOwner(); //At least our frame should have a title } - + while (Registry.is("skip.untitled.windows.for.mac.messages") && _window != null && _window instanceof JDialog && !((JDialog)_window).isModal()) { - _window = _window.getOwner(); + _window = _window.getOwner(); } return _window; diff --git a/platform/platform-resources-en/src/messages/InspectionsBundle.properties b/platform/platform-resources-en/src/messages/InspectionsBundle.properties index 1387caba4afb..0665e5c4ed89 100644 --- a/platform/platform-resources-en/src/messages/InspectionsBundle.properties +++ b/platform/platform-resources-en/src/messages/InspectionsBundle.properties @@ -123,6 +123,8 @@ inspection.1.7.problem.descriptor=Usage of generified after 1.6 API which would inspection.local.can.be.final.display.name=Local variable or parameter can be final inspection.local.can.be.final.option=Report local variables inspection.local.can.be.final.option1=Report method parameters +inspection.local.can.be.final.option2=Report catch parameters +inspection.local.can.be.final.option3=Report foreach parameters inspection.can.be.local.parameter.problem.descriptor=Parameter #ref can have final modifier inspection.can.be.local.variable.problem.descriptor=Variable #ref can have final modifier diff --git a/platform/platform-resources-en/src/misc/registry.properties b/platform/platform-resources-en/src/misc/registry.properties index ce1cc8bf1d74..26d1936f2401 100644 --- a/platform/platform-resources-en/src/misc/registry.properties +++ b/platform/platform-resources-en/src/misc/registry.properties @@ -189,13 +189,12 @@ compiler.automake.trigger.delay.description=Delay in milliseconds before trigger compiler.document.save.trigger.delay=1500 # suppress inspection "UnusedProperty" -compiler.document.save.trigger.delay.description=Delay in milliseconds before triggering save of unchenged documents in response to document changed events +compiler.document.save.trigger.delay.description=Delay in milliseconds before triggering save in response to document changes vcs.show.colored.annotations=true vcs.showConsole=true psi.incremental.reparse.depth.limit=1000 -psi.viewer.selection.color=0,153,153 psi.deferIconLoading=true find.search.in.project.files=false diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilTest.java index c27a18de57ba..5dc480257786 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilTest.java @@ -372,17 +372,17 @@ public class VfsUtilTest extends PlatformLangTestCase { assertNotNull(vDir); assertTrue(vDir.isDirectory()); - for (int i=0; i<10000; i++) { - final String name = i + ".txt"; - new WriteCommandAction.Simple(getProject()) { - @Override - protected void run() throws Throwable { + new WriteCommandAction.Simple(getProject()) { + @Override + protected void run() throws Throwable { + for (int i=0; i<10000; i++) { + final String name = i + ".txt"; vDir.createChildData(vDir, name); } - }.execute().throwException(); - } + } + }.execute(); final VirtualFile theChild = vDir.findChild("5111.txt"); - + System.out.println("Start searching..."); PlatformTestUtil.startPerformanceTest("find child is slow", 450, new ThrowableRunnable() { @Override public void run() throws Throwable { @@ -456,4 +456,26 @@ public class VfsUtilTest extends PlatformLangTestCase { } }).assertTiming(); } + + public void testNotCanonicallyNamedChild() { + File tempDir = new WriteAction() { + @Override + protected void run(Result result) throws Throwable { + File res = createTempDirectory(); + new File(res, "libFiles").createNewFile(); + new File(res, "CssInvalidElement").createNewFile(); + new File(res, "extFiles").createNewFile(); + result.setResult(res); + } + }.execute().getResultObject(); + final VirtualFile vDir = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(tempDir); + assertNotNull(vDir); + assertTrue(vDir.isDirectory()); + + assertNotNull(vDir.findChild("libFiles")); + assertNotNull(vDir.findChild("CssInvalidElement")); + + VirtualFile ext = vDir.findChild("/extFiles/"); + assertNotNull(ext); + } } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java index 6947c92c1e07..f15f19d26ac8 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/FileWatcherTest.java @@ -685,6 +685,12 @@ public class FileWatcherTest extends PlatformLangTestCase { LocalFileSystemTest.doTestPartialRefresh(top); } + public void testInterruptedRefresh() throws Exception { + // tests the same scenario with an active file watcher (prevents explicit marking of refreshed paths) + File top = createTestDir("top"); + LocalFileSystemTest.doTestInterruptedRefresh(top); + } + public void testUnicodePaths() throws Exception { if (!SystemInfo.isUnix || SystemInfo.isMac) { System.err.println("Ignored: well-defined FS required"); diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/LocalFileSystemTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/LocalFileSystemTest.java index e53f4007022e..37122a77b349 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/LocalFileSystemTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/LocalFileSystemTest.java @@ -32,8 +32,10 @@ import com.intellij.openapi.vfs.newvfs.impl.VirtualDirectoryImpl; import com.intellij.openapi.vfs.newvfs.impl.VirtualFileSystemEntry; import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS; import com.intellij.openapi.vfs.newvfs.persistent.PersistentFSImpl; +import com.intellij.openapi.vfs.newvfs.persistent.RefreshWorker; import com.intellij.testFramework.PlatformLangTestCase; import com.intellij.testFramework.PlatformTestUtil; +import com.intellij.util.Function; import com.intellij.util.messages.MessageBusConnection; import org.jetbrains.annotations.NotNull; @@ -471,7 +473,7 @@ public class LocalFileSystemTest extends PlatformLangTestCase { doTestPartialRefresh(top); } - public static void doTestPartialRefresh(File top) throws IOException { + public static void doTestPartialRefresh(@NotNull File top) throws IOException { File sub = IoTestUtil.createTestDir(top, "sub"); File file = IoTestUtil.createTestFile(top, "sub.txt"); LocalFileSystem lfs = LocalFileSystem.getInstance(); @@ -541,4 +543,51 @@ public class LocalFileSystemTest extends PlatformLangTestCase { assertTrue(vLink.isValid()); assertTrue(vLink.isDirectory()); } + + public void testInterruptedRefresh() throws Exception { + File top = createTempDirectory(false); + doTestInterruptedRefresh(top); + } + + public static void doTestInterruptedRefresh(@NotNull File top) throws Exception { + File sub = IoTestUtil.createTestDir(top, "sub"); + File subSub = IoTestUtil.createTestDir(sub, "sub_sub"); + File file1 = IoTestUtil.createTestFile(sub, "sub_file_to_stop_at"); + File file2 = IoTestUtil.createTestFile(subSub, "sub_sub_file"); + LocalFileSystem lfs = LocalFileSystem.getInstance(); + NewVirtualFile topDir = (NewVirtualFile)lfs.refreshAndFindFileByIoFile(top); + assertNotNull(topDir); + NewVirtualFile subFile1 = (NewVirtualFile)lfs.refreshAndFindFileByIoFile(file1); + assertNotNull(subFile1); + NewVirtualFile subFile2 = (NewVirtualFile)lfs.refreshAndFindFileByIoFile(file2); + assertNotNull(subFile2); + topDir.refresh(false, true); + assertFalse(topDir.isDirty()); + assertFalse(subFile1.isDirty()); + assertFalse(subFile2.isDirty()); + + try { + subFile1.markDirty(); + subFile2.markDirty(); + RefreshWorker.setCancellingCondition(new Function() { + @Override + public Boolean fun(VirtualFile file) { + return "sub_file_to_stop_at".equals(file.getName()); + } + }); + topDir.refresh(false, true); + // should remain dirty after aborted refresh + assertTrue(subFile1.isDirty()); + assertTrue(subFile2.isDirty()); + + RefreshWorker.setCancellingCondition(null); + topDir.refresh(false, true); + assertFalse(topDir.isDirty()); + assertFalse(subFile1.isDirty()); + assertFalse(subFile2.isDirty()); + } + finally { + RefreshWorker.setCancellingCondition(null); + } + } } diff --git a/platform/platform-tests/testSrc/com/intellij/ui/FinderRecursivePanelSelectionUpdateTest.java b/platform/platform-tests/testSrc/com/intellij/ui/FinderRecursivePanelSelectionUpdateTest.java index 07371bd9db40..a637db61165f 100644 --- a/platform/platform-tests/testSrc/com/intellij/ui/FinderRecursivePanelSelectionUpdateTest.java +++ b/platform/platform-tests/testSrc/com/intellij/ui/FinderRecursivePanelSelectionUpdateTest.java @@ -15,6 +15,7 @@ */ package com.intellij.ui; +import com.intellij.idea.Bombed; import com.intellij.openapi.project.Project; import com.intellij.testFramework.PlatformTestCase; import com.intellij.testFramework.SkipInHeadlessEnvironment; @@ -24,11 +25,13 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.Arrays; +import java.util.Calendar; import java.util.List; @SkipInHeadlessEnvironment public class FinderRecursivePanelSelectionUpdateTest extends PlatformTestCase { + @Bombed(year = 2014, month = Calendar.JANUARY, day = 31, user = "Yann Cebron") public void testUpdate() throws InterruptedException { StringFinderRecursivePanel panel_0 = new StringFinderRecursivePanel(getProject()) { @NotNull diff --git a/platform/testFramework/testSrc/com/intellij/testFramework/vcs/TestClientRunner.java b/platform/testFramework/testSrc/com/intellij/testFramework/vcs/TestClientRunner.java index 88985a81797b..65851dcc1f5a 100644 --- a/platform/testFramework/testSrc/com/intellij/testFramework/vcs/TestClientRunner.java +++ b/platform/testFramework/testSrc/com/intellij/testFramework/vcs/TestClientRunner.java @@ -93,7 +93,7 @@ public class TestClientRunner { } final CapturingProcessHandler handler = new CapturingProcessHandler(clientProcess, CharsetToolkit.getDefaultSystemCharset()); - final ProcessOutput result = handler.runProcess(100*1000); + final ProcessOutput result = handler.runProcess(100*1000, false); if (myTraceClient || result.isTimeout()) { LOG.debug("*** result: " + result.getExitCode()); final String out = result.getStdout().trim(); @@ -107,7 +107,9 @@ public class TestClientRunner { } if (result.isTimeout()) { - throw new RuntimeException("Timeout waiting for VCS client to finish execution:\n" + EnvironmentUtil.getProcessList()); + String processList = EnvironmentUtil.getProcessList(); + handler.destroyProcess(); + throw new RuntimeException("Timeout waiting for VCS client to finish execution:\n" + processList); } return result; } diff --git a/platform/util/src/com/intellij/Patches.java b/platform/util/src/com/intellij/Patches.java index 055a673ce675..40c517641ba4 100644 --- a/platform/util/src/com/intellij/Patches.java +++ b/platform/util/src/com/intellij/Patches.java @@ -113,9 +113,9 @@ public class Patches { /** * Java 7 incorrectly calculates screen insets on multi-monitor X Window configurations. - * See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=9000030. + * See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7172665. */ - public static final boolean SUN_BUG_ID_9000030 = SystemInfo.isXWindow && SystemInfo.isJavaVersionAtLeast("1.7"); + public static final boolean SUN_BUG_ID_7172665 = SystemInfo.isXWindow && SystemInfo.isJavaVersionAtLeast("1.7"); /** * On some WMs modal dialogs may show behind full screen window. diff --git a/platform/util/src/com/intellij/openapi/util/io/FileUtil.java b/platform/util/src/com/intellij/openapi/util/io/FileUtil.java index 9895f2cb1d29..da515f2d982f 100644 --- a/platform/util/src/com/intellij/openapi/util/io/FileUtil.java +++ b/platform/util/src/com/intellij/openapi/util/io/FileUtil.java @@ -1419,4 +1419,21 @@ public class FileUtil extends FileUtilRt { final String name = file.getName(); return StringUtil.endsWithIgnoreCase(name, ".jar") || StringUtil.endsWithIgnoreCase(name, ".zip"); } + + public static boolean visitFiles(@NotNull File root, @NotNull Processor processor) { + if (!processor.process(root)) { + return false; + } + + File[] children = root.listFiles(); + if (children != null) { + for (File child : children) { + if (!visitFiles(child, processor)) { + return false; + } + } + } + + return true; + } } diff --git a/platform/util/src/com/intellij/util/containers/hash/LinkedHashMap.java b/platform/util/src/com/intellij/util/containers/hash/LinkedHashMap.java index d50bc4e63e7f..7843297816de 100644 --- a/platform/util/src/com/intellij/util/containers/hash/LinkedHashMap.java +++ b/platform/util/src/com/intellij/util/containers/hash/LinkedHashMap.java @@ -310,7 +310,7 @@ public class LinkedHashMap extends AbstractMap implements Map private abstract class LinkedHashIterator implements Iterator { - private LinkedHashMap.Entry e = top; + private LinkedHashMap.Entry e = back; private LinkedHashMap.Entry last; @Override @@ -329,7 +329,7 @@ public class LinkedHashMap extends AbstractMap implements Map protected LinkedHashMap.Entry nextEntry() { final LinkedHashMap.Entry result = last = e; - e = result.next; + e = result.previous; return result; } } diff --git a/platform/util/src/com/intellij/util/lang/UrlClassLoader.java b/platform/util/src/com/intellij/util/lang/UrlClassLoader.java index b6f981638294..33733e5d4c08 100644 --- a/platform/util/src/com/intellij/util/lang/UrlClassLoader.java +++ b/platform/util/src/com/intellij/util/lang/UrlClassLoader.java @@ -235,7 +235,8 @@ public class UrlClassLoader extends ClassLoader { if (!new File(libPath = PathManager.getHomePath() + "/community/bin/" + platform + libFileName).exists()) { if (!new File(libPath = PathManager.getHomePath() + "/bin/" + platform + libFileName).exists()) { if (!new File(libPath = PathManager.getHomePathFor(IdeaWin32.class) + "/bin/" + libFileName).exists()) { - throw new UnsatisfiedLinkError("'" + libFileName + "' not found among " + Arrays.toString(new File(PathManager.getBinPath()).listFiles())); + File libDir = new File(PathManager.getBinPath()); + throw new UnsatisfiedLinkError("'" + libFileName + "' not found in '" + libDir + "' among " + Arrays.toString(libDir.list())); } } } diff --git a/platform/util/src/com/intellij/util/text/CharArrayUtil.java b/platform/util/src/com/intellij/util/text/CharArrayUtil.java index 3889596df481..24025774263b 100644 --- a/platform/util/src/com/intellij/util/text/CharArrayUtil.java +++ b/platform/util/src/com/intellij/util/text/CharArrayUtil.java @@ -99,13 +99,12 @@ public class CharArrayUtil { } } + /** + * @deprecated use {@link #fromSequence(CharSequence)} + */ @NotNull public static char[] fromSequenceStrict(@NotNull CharSequence seq) { - char[] chars = fromSequence(seq); - if (seq.length() == chars.length) return chars; - char[] strictChars = new char[seq.length()]; - System.arraycopy(chars, 0, strictChars, 0, seq.length()); - return strictChars; + return fromSequence(seq); } @Nullable @@ -126,7 +125,6 @@ public class CharArrayUtil { /** * @return the underlying char[] array if any, or the new chara array if not - * NOTE RETURNED ARRAY LENGTH MAY HAVE BE DIFFERENT FROM THE seq.length() */ @NotNull public static char[] fromSequence(@NotNull CharSequence seq) { @@ -137,11 +135,10 @@ public class CharArrayUtil { if (seq instanceof CharBuffer) { final CharBuffer buffer = (CharBuffer)seq; if (buffer.hasArray() && !buffer.isReadOnly() && buffer.arrayOffset() == 0) { - return buffer.array(); - // final char[] bufArray = buffer.array(); - // return larger array. Clients may use seq.length() to calculate correct processing range. - // if (bufArray.length == seq.length()) - // return bufArray; + char[] array = buffer.array(); + if (buffer.length() == array.length) { + return array; + } } char[] chars = new char[seq.length()]; @@ -156,6 +153,11 @@ public class CharArrayUtil { ((StringBuffer)seq).getChars(0, seq.length(), chars, 0); return chars; } + if (seq instanceof StringBuilder) { + char[] chars = new char[seq.length()]; + ((StringBuilder)seq).getChars(0, seq.length(), chars, 0); + return chars; + } if (seq instanceof String) { char[] chars = new char[seq.length()]; diff --git a/platform/util/testSrc/com/intellij/util/containers/hash/LinkedHashMapTest.java b/platform/util/testSrc/com/intellij/util/containers/hash/LinkedHashMapTest.java index a1791cdb71f2..8f3ee0331ebb 100644 --- a/platform/util/testSrc/com/intellij/util/containers/hash/LinkedHashMapTest.java +++ b/platform/util/testSrc/com/intellij/util/containers/hash/LinkedHashMapTest.java @@ -87,9 +87,9 @@ public class LinkedHashMapTest { for (int i = 0; i < 10000; ++i) { tested.put(i, Integer.toString(i)); } - int i = 10000; + int i = 0; for (Integer key : tested.keySet()) { - assertEquals(--i, key.intValue()); + assertEquals(i++, key.intValue()); } } @@ -109,7 +109,7 @@ public class LinkedHashMapTest { assertEquals(5000, tested.size()); it = tested.keySet().iterator(); - for (int i = 9999; i > 0; i -= 2) { + for (int i = 1; i <= 9999; i += 2) { Assert.assertTrue(it.hasNext()); assertEquals(i, it.next().intValue()); } @@ -180,16 +180,28 @@ public class LinkedHashMapTest { } @Test - public void iteration() { - ///todo[nik] it iterates in reversed order, may be it is a bug? + public void valuesIteration() { Map map = new LinkedHashMap(); map.put(1, "a"); map.put(2, "b"); map.put(3, "c"); Iterator iterator = map.values().iterator(); - assertEquals("c", iterator.next()); - assertEquals("b", iterator.next()); assertEquals("a", iterator.next()); + assertEquals("b", iterator.next()); + assertEquals("c", iterator.next()); + assertFalse(iterator.hasNext()); + } + + @Test + public void accessOrderValuesIteration() { + Map map = new LinkedHashMap(0, true); + map.put(1, "a"); + map.put(2, "b"); + map.put(3, "c"); + Iterator iterator = map.values().iterator(); + assertEquals("a", iterator.next()); + assertEquals("b", iterator.next()); + assertEquals("c", iterator.next()); assertFalse(iterator.hasNext()); } diff --git a/platform/vcs-log/impl/src/META-INF/vcs-log.xml b/platform/vcs-log/impl/src/META-INF/vcs-log.xml index 3c0749b0b2c6..5e014d560553 100644 --- a/platform/vcs-log/impl/src/META-INF/vcs-log.xml +++ b/platform/vcs-log/impl/src/META-INF/vcs-log.xml @@ -19,12 +19,11 @@ icon="AllIcons.Actions.Copy" text="Copy Hash" description="Copy hash value of the selected commit to clipboard" /> + text="Go To Hash/Branch/Tag" description="Specify hash or name of a branch or a tag to navigate to the commit it points"/> - diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/ContainingBranchesGetter.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/ContainingBranchesGetter.java new file mode 100644 index 000000000000..5db2212553f7 --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/ContainingBranchesGetter.java @@ -0,0 +1,178 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.vcs.log.data; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Condition; +import com.intellij.util.ThrowableConsumer; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.SLRUMap; +import com.intellij.vcs.log.VcsRef; +import com.intellij.vcs.log.graph.elements.Edge; +import com.intellij.vcs.log.graph.elements.Node; +import com.intellij.vcs.log.util.SequentialLimitedLifoExecutor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.*; +import java.util.*; +import java.util.List; + +/** + * Provides capabilities to asynchronously calculate "contained in branches" information. + */ +public class ContainingBranchesGetter { + + private static final Logger LOG = Logger.getInstance(ContainingBranchesGetter.class); + + @NotNull private final SequentialLimitedLifoExecutor myTaskExecutor; + @NotNull private volatile SLRUMap> myCache = createCache(); + @Nullable private Runnable myLoadingFinishedListener; // access only from EDT + + ContainingBranchesGetter(@NotNull Disposable parentDisposable) { + myTaskExecutor = new SequentialLimitedLifoExecutor(parentDisposable, 10, new ThrowableConsumer() { + @Override + public void consume(final Task task) throws Throwable { + final List branches = getContainingBranches(task.pack, task.node); + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + // if cache is cleared (because of log refresh) during this task execution, + // this will put obsolete value into the old instance we don't care anymore + task.cache.put(task.node, branches); + notifyListener(); + } + }); + } + }); + } + + void clearCache() { + myCache = createCache(); + myTaskExecutor.clear(); + // re-request containing branches information for the commit user (possibly) currently stays on + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + notifyListener(); + } + }); + } + + /** + * This task will be executed each time the calculating process completes. + */ + public void setTaskCompletedListener(@NotNull Runnable runnable) { + LOG.assertTrue(EventQueue.isDispatchThread()); + myLoadingFinishedListener = runnable; + } + + private void notifyListener() { + LOG.assertTrue(EventQueue.isDispatchThread()); + if (myLoadingFinishedListener != null) { + myLoadingFinishedListener.run(); + } + } + + /** + * Returns the alphabetically sorted list of branches containing the specified node, if this information is ready; + * if it is not available, starts calculating in the background and returns null. + */ + @Nullable + public List requestContainingBranches(@NotNull DataPack dataPack, @NotNull Node node) { + List refs = myCache.get(node); + if (refs == null) { + myTaskExecutor.queue(new Task(dataPack, node, myCache)); + } + return refs; + } + + @NotNull + private static SLRUMap> createCache() { + return new SLRUMap>(1000, 1000); + } + + @NotNull + private List getContainingBranches(@NotNull DataPack dataPack, @NotNull Node node) { + RefsModel refsModel = dataPack.getRefsModel(); + Set containingBranches = ContainerUtil.newHashSet(); + + Set visitedNodes = ContainerUtil.newHashSet(); + Set nodesToCheck = ContainerUtil.newHashSet(); + nodesToCheck.add(node); + while (!nodesToCheck.isEmpty()) { + Iterator nodeIterator = nodesToCheck.iterator(); + Node nextNode = nodeIterator.next(); + nodeIterator.remove(); + + if (!visitedNodes.add(nextNode)) { + continue; + } + + for (Edge edge : nextNode.getUpEdges()) { + Node upNode = edge.getUpNode(); + // optimization: the node is contained in all branches which contain its child => no need to walk over this graph branch + List upRefs = myCache.get(upNode); + if (upRefs != null) { + containingBranches.addAll(upRefs); + } + else { + nodesToCheck.add(upNode); + } + } + + containingBranches.addAll(getBranchesPointingToThisNode(refsModel, nextNode)); + } + + return sortByName(containingBranches); + } + + @NotNull + private static Collection getBranchesPointingToThisNode(@NotNull RefsModel refsModel, @NotNull Node node) { + return ContainerUtil.filter(refsModel.refsToCommit(node.getCommitIndex()), new Condition() { + @Override + public boolean value(VcsRef ref) { + return ref.getType().isBranch(); + } + }); + } + + private static List sortByName(Collection branches) { + List branchesList = new ArrayList(branches); + ContainerUtil.sort(branchesList, new Comparator() { + @Override + public int compare(VcsRef o1, VcsRef o2) { + return o1.getName().compareTo(o2.getName()); + } + }); + return branchesList; + } + + private static class Task { + private final DataPack pack; + private final Node node; + private final SLRUMap> cache; + + public Task(DataPack pack, Node node, SLRUMap> cache) { + this.pack = pack; + this.node = node; + this.cache = cache; + } + } + +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataGetter.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataGetter.java index dabd64cdd4aa..ba0af62ec1b7 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataGetter.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataGetter.java @@ -4,8 +4,7 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.Consumer; -import com.intellij.util.concurrency.QueueProcessor; +import com.intellij.util.ThrowableConsumer; import com.intellij.util.containers.MultiMap; import com.intellij.util.ui.UIUtil; import com.intellij.vcs.log.Hash; @@ -14,6 +13,7 @@ import com.intellij.vcs.log.VcsShortCommitDetails; import com.intellij.vcs.log.graph.Graph; import com.intellij.vcs.log.graph.elements.Node; import com.intellij.vcs.log.graph.elements.NodeRow; +import com.intellij.vcs.log.util.SequentialLimitedLifoExecutor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -43,13 +43,13 @@ public abstract class DataGetter implements Dis @NotNull protected final VcsLogDataHolder myDataHolder; @NotNull private final Map myLogProviders; @NotNull private final VcsCommitCache myCache; + @NotNull private final SequentialLimitedLifoExecutor myLoader; /** * The sequence number of the current "loading" task. */ private long myCurrentTaskIndex = 0; - @NotNull private final QueueProcessor myLoader = new QueueProcessor(new DetailsLoadingTask()); @NotNull private final Collection myLoadingFinishedListeners = new ArrayList(); DataGetter(@NotNull VcsLogDataHolder dataHolder, @NotNull Map logProviders, @@ -58,12 +58,26 @@ public abstract class DataGetter implements Dis myLogProviders = logProviders; myCache = cache; Disposer.register(dataHolder, this); + myLoader = new SequentialLimitedLifoExecutor(this, MAX_LOADING_TASKS, + new ThrowableConsumer() { + @Override + public void consume(TaskDescriptor task) throws VcsException { + preLoadCommitData(task.nodes); + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + for (Runnable loadingFinishedListener : myLoadingFinishedListeners) { + loadingFinishedListener.run(); + } + } + }); + } + }); } @Override public void dispose() { myLoadingFinishedListeners.clear(); - myLoader.clear(); } @NotNull @@ -150,7 +164,7 @@ public abstract class DataGetter implements Dis } } TaskDescriptor task = new TaskDescriptor(nodes, taskNumber); - myLoader.addFirst(task); + myLoader.queue(task); return task; } @@ -204,25 +218,4 @@ public abstract class DataGetter implements Dis } } - private class DetailsLoadingTask implements Consumer { - - @Override - public void consume(final TaskDescriptor task) { - try { - myLoader.dismissLastTasks(MAX_LOADING_TASKS); - preLoadCommitData(task.nodes); - UIUtil.invokeAndWaitIfNeeded(new Runnable() { - @Override - public void run() { - for (Runnable loadingFinishedListener : myLoadingFinishedListeners) { - loadingFinishedListener.run(); - } - } - }); - } - catch (VcsException e) { - throw new RuntimeException(e); // todo - } - } - } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataPack.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataPack.java index baef6132d5d5..b0b402ca71da 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataPack.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataPack.java @@ -52,7 +52,7 @@ public class DataPack { } }); - final RefsModel refsModel = new RefsModel(allRefs, hashGetter); + final RefsModel refsModel = new RefsModel(allRefs, indexGetter); graphModel.getFragmentManager().setUnconcealedNodeFunction(new Function() { @NotNull @Override diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/RefsModel.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/RefsModel.java index 4078bbfe7965..8f79a75076a4 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/RefsModel.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/RefsModel.java @@ -1,7 +1,7 @@ package com.intellij.vcs.log.data; import com.intellij.openapi.util.Condition; -import com.intellij.util.Function; +import com.intellij.util.NotNullFunction; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import com.intellij.vcs.log.Hash; @@ -19,10 +19,11 @@ public class RefsModel { @NotNull private final Collection myBranches; @NotNull private final MultiMap myRefsToHashes; - private final Function myHashGetter; + @NotNull private final MultiMap myRefsToIndices; + @NotNull private final NotNullFunction myIndexGetter; - public RefsModel(@NotNull Collection allRefs, Function hashGetter) { - myHashGetter = hashGetter; + public RefsModel(@NotNull Collection allRefs, @NotNull NotNullFunction indexGetter) { + myIndexGetter = indexGetter; myBranches = ContainerUtil.filter(allRefs, new Condition() { @Override public boolean value(VcsRef ref) { @@ -31,6 +32,16 @@ public class RefsModel { }); myRefsToHashes = prepareRefsMap(allRefs); + myRefsToIndices = prepareRefsToIndicesMap(allRefs); + } + + @NotNull + private MultiMap prepareRefsToIndicesMap(@NotNull Collection refs) { + MultiMap map = MultiMap.create(); + for (VcsRef ref : refs) { + map.putValue(myIndexGetter.fun(ref.getCommitHash()), ref); + } + return map; } @NotNull @@ -42,8 +53,8 @@ public class RefsModel { return map; } - public boolean isBranchRef(@NotNull Hash commitHash) { - for (VcsRef ref : refsToCommit(commitHash)) { + public boolean isBranchRef(int hash) { + for (VcsRef ref : refsToCommit(hash)) { if (ref.getType().isBranch()) { return true; } @@ -59,6 +70,11 @@ public class RefsModel { return Collections.emptyList(); } + @NotNull + public Collection refsToCommit(int index) { + return myRefsToIndices.containsKey(index) ? myRefsToIndices.get(index) : Collections.emptyList(); + } + @NotNull public Collection getBranches() { return myBranches; @@ -69,8 +85,4 @@ public class RefsModel { return new ArrayList(myRefsToHashes.values()); } - public boolean isBranchRef(int hash) { - return isBranchRef(myHashGetter.fun(hash)); - } - } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDataHolder.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDataHolder.java index 4af25dff7093..de7b48f18ef1 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDataHolder.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogDataHolder.java @@ -23,6 +23,7 @@ import com.intellij.openapi.progress.BackgroundTaskQueue; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vcs.VcsException; @@ -145,6 +146,7 @@ public class VcsLogDataHolder implements Disposable { private final VcsLogHashMap myHashMap; private final NotNullFunction myHashGetter; private final NotNullFunction myIndexGetter; + private final ContainingBranchesGetter myContainingBranchesGetter; public VcsLogDataHolder(@NotNull Project project, @NotNull Map logProviders, @NotNull VcsLogSettings settings) { @@ -179,6 +181,7 @@ public class VcsLogDataHolder implements Disposable { return putHash(hash); } }; + myContainingBranchesGetter = new ContainingBranchesGetter(this); } @NotNull @@ -373,8 +376,12 @@ public class VcsLogDataHolder implements Disposable { VirtualFile root = entry.getKey(); RecentCommitsInfo info = entry.getValue(); - Pair, Integer> joinResult = myLogJoiner.addCommits(myLogData.getLog(root), myLogData.getRefs(root), + Collection oldRefs = myLogData.getRefs(root); + Pair, Integer> joinResult = myLogJoiner.addCommits(myLogData.getLog(root), oldRefs, info.firstBlockCommits, info.newRefs); + if (!Comparing.haveEqualElements(oldRefs, info.newRefs)) { + myContainingBranchesGetter.clearCache(); + } List refreshedLog = joinResult.getFirst(); int newCommitsCount = joinResult.getSecond(); // the value can significantly increase if user keeps IDEA open for a long time, and frequently receives many new commits, @@ -437,6 +444,7 @@ public class VcsLogDataHolder implements Disposable { myLogData = new LogData(logsToBuild, refsByRoot, compoundLog, dataPack, false); } + myContainingBranchesGetter.clearCache(); handleOnSuccessInEdt(onSuccess, dataPack); } @@ -550,6 +558,10 @@ public class VcsLogDataHolder implements Disposable { return mySettings; } + public ContainingBranchesGetter getContainingBranchesGetter() { + return myContainingBranchesGetter; + } + private static class RecentCommitsInfo { List firstBlockCommits; Collection newRefs; diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiProperties.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiProperties.java index 1d7f197e703f..be746e6b30e8 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiProperties.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogUiProperties.java @@ -41,7 +41,7 @@ public class VcsLogUiProperties implements PersistentStateComponent RECENTLY_FILTERED_USERS = new ArrayDeque(); } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/GoToRefAction.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/GoToRefAction.java index 4514217a25d9..12e67eb7ebf9 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/GoToRefAction.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/GoToRefAction.java @@ -61,7 +61,7 @@ public class GoToRefAction extends DumbAwareAction { @Override public void update(AnActionEvent e) { VcsLog log = e.getData(VcsLogDataKeys.VSC_LOG); - getTemplatePresentation().setEnabledAndVisible(e.getProject() != null && log != null); + e.getPresentation().setEnabledAndVisible(e.getProject() != null && log != null); } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/DetailsPanel.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/DetailsPanel.java index f93b0295fdba..b37092e0e95f 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/DetailsPanel.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/DetailsPanel.java @@ -1,17 +1,21 @@ package com.intellij.vcs.log.ui.frame; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.util.ProgressWindow; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.changes.issueLinks.IssueLinkHtmlRenderer; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.BrowserHyperlinkListener; import com.intellij.ui.ScrollPaneFactory; import com.intellij.ui.components.JBLoadingPanel; +import com.intellij.util.Function; import com.intellij.util.text.DateFormatUtil; import com.intellij.util.ui.UIUtil; import com.intellij.vcs.log.Hash; import com.intellij.vcs.log.VcsFullCommitDetails; import com.intellij.vcs.log.VcsRef; +import com.intellij.vcs.log.data.DataPack; import com.intellij.vcs.log.data.LoadingDetails; import com.intellij.vcs.log.data.VcsLogDataHolder; import com.intellij.vcs.log.graph.render.PrintParameters; @@ -61,7 +65,7 @@ class DetailsPanel extends JPanel implements ListSelectionListener { content.add(myDataPanel); content.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5)); - myLoadingPanel = new JBLoadingPanel(new BorderLayout(), logDataHolder); + myLoadingPanel = new JBLoadingPanel(new BorderLayout(), logDataHolder, ProgressWindow.DEFAULT_PROGRESS_DIALOG_POSTPONE_TIME_MILLIS); myLoadingPanel.add(ScrollPaneFactory.createScrollPane(content)); add(myLoadingPanel, STANDARD_LAYER); @@ -82,21 +86,24 @@ class DetailsPanel extends JPanel implements ListSelectionListener { } else { ((CardLayout)getLayout()).show(this, STANDARD_LAYER); - Hash hash = ((AbstractVcsLogTableModel)myGraphTable.getModel()).getHashAtRow(rows[0]); + int row = rows[0]; + Hash hash = ((AbstractVcsLogTableModel)myGraphTable.getModel()).getHashAtRow(row); if (hash == null) { showMessage("No commits selected"); return; } VcsFullCommitDetails commitData = myLogDataHolder.getCommitDetailsGetter().getCommitData(hash); - if (commitData instanceof LoadingDetails) { + DataPack dataPack = myLogDataHolder.getDataPack(); + List branches = myLogDataHolder.getContainingBranchesGetter().requestContainingBranches(dataPack, dataPack.getNode(row)); + if (commitData instanceof LoadingDetails || branches == null) { myLoadingPanel.startLoading(); - myDataPanel.setData(null); + myDataPanel.setData(null, null); myRefsPanel.setRefs(Collections.emptyList()); } else { myLoadingPanel.stopLoading(); - myDataPanel.setData(commitData); + myDataPanel.setData(commitData, branches); myRefsPanel.setRefs(sortRefs(hash, commitData.getRoot())); } } @@ -123,19 +130,30 @@ class DetailsPanel extends JPanel implements ListSelectionListener { setEditable(false); myProject = project; addHyperlinkListener(new BrowserHyperlinkListener()); + setPreferredSize(new Dimension(150, 100)); } - void setData(@Nullable VcsFullCommitDetails commit) { - if (commit == null) { + void setData(@Nullable VcsFullCommitDetails commit, @Nullable List branches) { + if (commit == null || branches == null) { setText(""); } else { - String body = getHashText(commit) + "
" + getAuthorText(commit) + "

" + getMessageText(commit) + "

"; + String body = getHashText(commit) + "
" + getAuthorText(commit) + "

" + getMessageText(commit) + "

" + + "

" + getContainedBranchesText(branches) + "

"; setText("" + UIUtil.getCssFontDeclaration(UIUtil.getLabelFont()) + "" + body + ""); setCaretPosition(0); } } + private static String getContainedBranchesText(List branches) { + return "Contained in branches: " + StringUtil.join(branches, new Function() { + @Override + public String fun(VcsRef ref) { + return ref.getName(); + } + }, ", "); + } + private String getMessageText(VcsFullCommitDetails commit) { String subject = commit.getSubject(); String description = subject.length() < commit.getFullMessage().length() ? commit.getFullMessage().substring(subject.length()) : ""; diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/MainFrame.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/MainFrame.java index 008cde930488..c58abbb17ff4 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/MainFrame.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/frame/MainFrame.java @@ -4,6 +4,7 @@ import com.intellij.icons.AllIcons; import com.intellij.ide.actions.RefreshAction; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Splitter; @@ -123,6 +124,12 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider { myDetailsPanel.valueChanged(null); } }); + myLogDataHolder.getContainingBranchesGetter().setTaskCompletedListener(new Runnable() { + @Override + public void run() { + myDetailsPanel.valueChanged(null); + } + }); } public void setupDetailsSplitter(boolean state) { @@ -170,34 +177,8 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider { } }; - AnAction showFullPatchAction = new ToggleAction("Show long edges", - "Show long branch edges even if commits are invisible in the current view.", - VcsLogIcons.ShowHideLongEdges) { - @Override - public boolean isSelected(AnActionEvent e) { - return !myUI.areLongEdgesHidden(); - } - - @Override - public void setSelected(AnActionEvent e, boolean state) { - myUI.setLongEdgeVisibility(state); - } - }; - - ToggleAction showDetailsAction = new ToggleAction("Show Details", "Display details panel", AllIcons.Actions.Preview) { - @Override - public boolean isSelected(AnActionEvent e) { - return !myProject.isDisposed() && myUiProperties.isShowDetails(); - } - - @Override - public void setSelected(AnActionEvent e, boolean state) { - setupDetailsSplitter(state); - if (!myProject.isDisposed()) { - myUiProperties.setShowDetails(state); - } - } - }; + AnAction showFullPatchAction = new ShowLongEdgesAction(); + AnAction showDetailsAction = new ShowDetailsAction(); refreshAction.registerShortcutOn(this); @@ -209,7 +190,9 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider { mainGroup.add(myFilterUi.getFilterActionComponents()); mainGroup.addSeparator(); mainGroup.add(toolbarGroup); - return ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, mainGroup, true).getComponent(); + ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, mainGroup, true); + toolbar.setTargetComponent(this); + return toolbar.getComponent(); } public JComponent getMainComponent() { @@ -276,4 +259,40 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider { } } } + + private class ShowDetailsAction extends ToggleAction implements DumbAware { + + public ShowDetailsAction() { + super("Show Details", "Display details panel", AllIcons.Actions.Preview); + } + + @Override + public boolean isSelected(AnActionEvent e) { + return !myProject.isDisposed() && myUiProperties.isShowDetails(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + setupDetailsSplitter(state); + if (!myProject.isDisposed()) { + myUiProperties.setShowDetails(state); + } + } + } + + private class ShowLongEdgesAction extends ToggleAction implements DumbAware { + public ShowLongEdgesAction() { + super("Show long edges", "Show long branch edges even if commits are invisible in the current view.", VcsLogIcons.ShowHideLongEdges); + } + + @Override + public boolean isSelected(AnActionEvent e) { + return !myUI.areLongEdgesHidden(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + myUI.setLongEdgeVisibility(state); + } + } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/AbstractPaddingCellRender.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/AbstractPaddingCellRender.java index 9f1cf5a85f17..d75ca1d6624f 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/AbstractPaddingCellRender.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/AbstractPaddingCellRender.java @@ -1,24 +1,33 @@ package com.intellij.vcs.log.ui.render; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.vcs.changes.issueLinks.IssueLinkRenderer; import com.intellij.ui.ColoredTableCellRenderer; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.vcs.log.VcsRef; import com.intellij.vcs.log.graph.render.GraphCommitCell; import com.intellij.vcs.log.printmodel.SpecialPrintElement; +import com.intellij.vcs.log.ui.VcsLogColorManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; +import java.util.Collection; +import java.util.List; +import java.util.Map; public abstract class AbstractPaddingCellRender extends ColoredTableCellRenderer { public static final Color MARKED_BACKGROUND = new Color(200, 255, 250); + @NotNull protected final RefPainter myRefPainter; @NotNull private final IssueLinkRenderer myIssueLinkRenderer; @Nullable private Object myValue; - protected AbstractPaddingCellRender(@NotNull Project project) { + protected AbstractPaddingCellRender(@NotNull Project project, VcsLogColorManager colorManager) { + myRefPainter = new RefPainter(colorManager, false); myIssueLinkRenderer = new IssueLinkRenderer(project, this); } @@ -58,4 +67,47 @@ public abstract class AbstractPaddingCellRender extends ColoredTableCellRenderer super.paint(g); additionPaint(g, myValue); } + + protected void drawRefs(@NotNull Graphics2D g2, @NotNull Collection refs, int padding) { + myRefPainter.drawLabels(g2, collectLabelsForRefs(refs), padding); + } + + @NotNull + private static Map collectLabelsForRefs(@NotNull Collection refs) { + List branches = getBranches(refs); + Collection tags = ContainerUtil.subtract(refs, branches); + return getLabelsForRefs(branches, tags); + } + + protected int calcRefsPadding(@NotNull Collection refs, @NotNull Graphics2D g2) { + return myRefPainter.padding(collectLabelsForRefs(refs).keySet(), g2); + } + + @NotNull + private static Map getLabelsForRefs(@NotNull List branches, @NotNull Collection tags) { + Map labels = ContainerUtil.newLinkedHashMap(); + for (VcsRef branch : branches) { + labels.put(branch.getName(), branch.getType().getBackgroundColor()); + } + if (!tags.isEmpty()) { + VcsRef firstTag = tags.iterator().next(); + Color color = firstTag.getType().getBackgroundColor(); + if (tags.size() > 1) { + labels.put(firstTag.getName() + " +", color); + } + else { + labels.put(firstTag.getName(), color); + } + } + return labels; + } + + private static List getBranches(Collection refs) { + return ContainerUtil.filter(refs, new Condition() { + @Override + public boolean value(VcsRef ref) { + return ref.getType().isBranch(); + } + }); + } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/CommitCellRender.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/CommitCellRender.java index 7547655c8759..702af3f1a667 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/CommitCellRender.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/CommitCellRender.java @@ -7,26 +7,20 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; -import java.awt.font.FontRenderContext; /** * @author erokhins */ public class CommitCellRender extends AbstractPaddingCellRender { - @NotNull private final RefPainter refPainter; - public CommitCellRender(@NotNull VcsLogColorManager colorManager, Project project) { - super(project); - refPainter = new RefPainter(colorManager, false); + super(project, colorManager); } @Override protected int getLeftPadding(JTable table, Object value) { CommitCell cell = getAssertCommitCell(value); - - FontRenderContext fontContext = ((Graphics2D)table.getGraphics()).getFontRenderContext(); - return refPainter.padding(cell.getRefsToThisCommit(), fontContext); + return calcRefsPadding(cell.getRefsToThisCommit(), (Graphics2D)table.getGraphics()); } private static CommitCell getAssertCommitCell(Object value) { @@ -48,6 +42,7 @@ public class CommitCellRender extends AbstractPaddingCellRender { protected void additionPaint(Graphics g, Object value) { CommitCell cell = getAssertCommitCell(value); Graphics2D g2 = (Graphics2D)g; - refPainter.draw(g2, cell.getRefsToThisCommit(), 0, -1); + drawRefs(g2, cell.getRefsToThisCommit(), 0); } + } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/GraphCommitCellRender.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/GraphCommitCellRender.java index 130c3c49af7e..0752ce3ebdbc 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/GraphCommitCellRender.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/GraphCommitCellRender.java @@ -27,7 +27,6 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; -import java.awt.font.FontRenderContext; import java.awt.image.BufferedImage; import java.util.Collection; @@ -41,14 +40,12 @@ public class GraphCommitCellRender extends AbstractPaddingCellRender { @NotNull private final GraphCellPainter graphPainter; @NotNull private final VcsLogDataHolder myDataHolder; - @NotNull private final RefPainter refPainter; public GraphCommitCellRender(@NotNull GraphCellPainter graphPainter, @NotNull VcsLogDataHolder logDataHolder, @NotNull VcsLogColorManager colorManager) { - super(logDataHolder.getProject()); + super(logDataHolder.getProject(), colorManager); this.graphPainter = graphPainter; myDataHolder = logDataHolder; - refPainter = new RefPainter(colorManager, false); } @Override @@ -59,8 +56,7 @@ public class GraphCommitCellRender extends AbstractPaddingCellRender { return 0; } - FontRenderContext fontContext = ((Graphics2D)table.getGraphics()).getFontRenderContext(); - int refPadding = refPainter.padding(cell.getRefsToThisCommit(), fontContext); + int refPadding = calcRefsPadding(cell.getRefsToThisCommit(), (Graphics2D)table.getGraphics()); int countCells = cell.getPrintCell().countCell(); int graphPadding = countCells * WIDTH_NODE; @@ -99,7 +95,7 @@ public class GraphCommitCellRender extends AbstractPaddingCellRender { VirtualFile root = refs.iterator().next().getRoot(); // all refs are from the same commit => they have the same root refs = myDataHolder.getLogProvider(root).getReferenceManager().sort(refs); } - refPainter.draw(g2, refs, padding, -1); // TODO think how to behave if there are too many refs here (even if tags are collapsed) + drawRefs(g2, refs, padding); UIUtil.drawImage(g, image, 0, 0, null); } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/RefPainter.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/RefPainter.java index ae52d3f84230..004a9eda8c17 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/RefPainter.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/render/RefPainter.java @@ -4,6 +4,7 @@ import com.intellij.ui.JBColor; import com.intellij.vcs.log.VcsRef; import com.intellij.vcs.log.ui.VcsLogColorManager; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.awt.*; import java.awt.font.FontRenderContext; @@ -21,6 +22,7 @@ public class RefPainter { private static final int RECTANGLE_X_PADDING = 4; private static final int RECTANGLE_Y_PADDING = 3; private static final int REF_PADDING = 13; + private static final int LABEL_PADDING = 3; private static final int FLAG_WIDTH = 8; private static final int FLAG_PADDING = 8; @@ -40,7 +42,11 @@ public class RefPainter { } private double paddingStr(@NotNull String str, @NotNull FontRenderContext renderContext) { - return DEFAULT_FONT.getStringBounds(str, renderContext).getWidth() + REF_PADDING + flagWidth(); + return getStringWidth(str, renderContext) + REF_PADDING + flagWidth(); + } + + private static double getStringWidth(@NotNull String str, @NotNull FontRenderContext renderContext) { + return DEFAULT_FONT.getStringBounds(str, renderContext).getWidth(); } private int flagWidth() { @@ -79,12 +85,16 @@ public class RefPainter { g2.drawPolygon(polygon); } - public int padding(@NotNull Collection refs, @NotNull FontRenderContext renderContext) { - float p = 0; - for (VcsRef ref : refs) { - p += paddingStr(ref.getName(), renderContext); + public int padding(@NotNull Collection refs, @NotNull Graphics2D g2) { + int p = 0; + for (String ref : refs) { + XAndWidth xAndWidth = getXAndWidth(ref, 0, g2.getFontMetrics(DEFAULT_FONT)); + p += xAndWidth.x + xAndWidth.width + LABEL_PADDING; } - return Math.round(p); + if (p > 0) { // additional padding after all references looks better + p += LABEL_PADDING; + } + return p; } public Map draw(@NotNull Graphics2D g2, @NotNull Collection refs, int startPadding, int maxWidth) { @@ -109,12 +119,28 @@ public class RefPainter { g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); } - public Rectangle drawLabel(@NotNull Graphics2D g2, @NotNull String label, int paddingX, @NotNull Color bgColor, Color rootIndicatorColor) { + public void drawLabels(@NotNull Graphics2D g2, @NotNull Map labels, int startPadding) { + int padding = startPadding; + for (Map.Entry entry : labels.entrySet()) { + Rectangle rectangle = drawLabel(g2, entry.getKey(), padding, entry.getValue(), null); + padding += rectangle.width + LABEL_PADDING; + } + } + + private XAndWidth getXAndWidth(String label, int paddingX, FontMetrics metrics) { + int x = paddingX + REF_PADDING / 2 - RECTANGLE_X_PADDING; + int width = metrics.stringWidth(label) + 2 * RECTANGLE_X_PADDING + flagWidth(); + return new XAndWidth(x, width); + } + + public Rectangle drawLabel(@NotNull Graphics2D g2, @NotNull String label, int paddingX, @NotNull Color bgColor, + @Nullable Color rootIndicatorColor) { setupGraphics(g2); FontMetrics metrics = g2.getFontMetrics(); - int x = paddingX + REF_PADDING / 2 - RECTANGLE_X_PADDING; + XAndWidth xAndWidth = getXAndWidth(label, paddingX, metrics); + int x = xAndWidth.x; + int width = xAndWidth.width; int y = RECTANGLE_Y_PADDING; - int width = metrics.stringWidth(label) + 2 * RECTANGLE_X_PADDING + flagWidth(); int height = HEIGHT_CELL - 2 * RECTANGLE_Y_PADDING; RoundRectangle2D rectangle2D = new RoundRectangle2D.Double(x, y, width, height, ROUND_RADIUS, ROUND_RADIUS); @@ -127,11 +153,21 @@ public class RefPainter { g2.setColor(JBColor.BLACK); drawText(g2, label, paddingX + flagWidth()); - if (myColorManager.isMultipleRoots() && myDrawMultiRepoIndicator) { + if (rootIndicatorColor != null && myColorManager.isMultipleRoots() && myDrawMultiRepoIndicator) { drawRootIndicator(g2, paddingX, y, height, rootIndicatorColor); } return new Rectangle(x, y, width, height); } + private static class XAndWidth { + private final int x; + private final int width; + + private XAndWidth(int x, int width) { + this.x = x; + this.width = width; + } + } + } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/util/SequentialLimitedLifoExecutor.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/util/SequentialLimitedLifoExecutor.java new file mode 100644 index 000000000000..d0eeea575980 --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/util/SequentialLimitedLifoExecutor.java @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.vcs.log.util; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.util.Disposer; +import com.intellij.util.Consumer; +import com.intellij.util.ThrowableConsumer; +import com.intellij.util.concurrency.QueueProcessor; +import org.jetbrains.annotations.NotNull; + +/** + * Queue with a limited number of tasks, and with higher priority for new tasks, than for older ones. + */ +public class SequentialLimitedLifoExecutor implements Disposable { + + private final int myMaxTasks; + @NotNull private final ThrowableConsumer myLoadProcess; + @NotNull private final QueueProcessor myLoader; + + public SequentialLimitedLifoExecutor(Disposable parentDisposable, int maxTasks, + @NotNull ThrowableConsumer loadProcess) { + myMaxTasks = maxTasks; + myLoadProcess = loadProcess; + myLoader = new QueueProcessor(new DetailsLoadingTask()); + Disposer.register(parentDisposable, this); + } + + public void queue(Task task) { + myLoader.addFirst(task); + } + + public void clear() { + myLoader.clear(); + } + + @Override + public void dispose() { + clear(); + } + + private class DetailsLoadingTask implements Consumer { + @Override + public void consume(final Task task) { + try { + myLoader.dismissLastTasks(myMaxTasks); + myLoadProcess.consume(task); + } + catch (Throwable e) { + throw new RuntimeException(e); // todo + } + } + } + +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/CastThatLosesPrecisionInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/CastThatLosesPrecisionInspection.java index d45abb156159..01829b05d6cb 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/CastThatLosesPrecisionInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/CastThatLosesPrecisionInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2012 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2013 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,29 +24,13 @@ import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.psiutils.ClassUtils; import com.siyeh.ig.psiutils.ExpressionUtils; import com.siyeh.ig.psiutils.MethodUtils; +import com.siyeh.ig.psiutils.TypeUtils; import org.jetbrains.annotations.NotNull; import javax.swing.*; -import java.util.HashMap; -import java.util.Map; public class CastThatLosesPrecisionInspection extends BaseInspection { - /** - * @noinspection StaticCollection - */ - private static final Map typePrecisions = new HashMap(7); - - static { - typePrecisions.put(PsiType.BYTE, 1); - typePrecisions.put(PsiType.CHAR, 2); - typePrecisions.put(PsiType.SHORT, 2); - typePrecisions.put(PsiType.INT, 3); - typePrecisions.put(PsiType.LONG, 4); - typePrecisions.put(PsiType.FLOAT, 5); - typePrecisions.put(PsiType.DOUBLE, 6); - } - @SuppressWarnings({"PublicField"}) public boolean ignoreIntegerCharCasts = false; @@ -81,8 +65,7 @@ public class CastThatLosesPrecisionInspection extends BaseInspection { return new CastThatLosesPrecisionVisitor(); } - private class CastThatLosesPrecisionVisitor - extends BaseInspectionVisitor { + private class CastThatLosesPrecisionVisitor extends BaseInspectionVisitor { @Override public void visitTypeCastExpression(@NotNull PsiTypeCastExpression expression) { @@ -95,10 +78,7 @@ public class CastThatLosesPrecisionInspection extends BaseInspection { return; } final PsiType operandType = operand.getType(); - if (!ClassUtils.isPrimitiveNumericType(operandType)) { - return; - } - if (hasLowerPrecision(operandType, castType)) { + if (!ClassUtils.isPrimitiveNumericType(operandType) || !TypeUtils.isNarrowingConversion(operandType, castType)) { return; } if (ignoreIntegerCharCasts) { @@ -129,12 +109,6 @@ public class CastThatLosesPrecisionInspection extends BaseInspection { registerError(castTypeElement, operandType); } - private boolean hasLowerPrecision(PsiType operandType, PsiType castType) { - final Integer operandPrecision = typePrecisions.get(operandType); - final Integer castPrecision = typePrecisions.get(castType); - return operandPrecision.intValue() <= castPrecision.intValue(); - } - private boolean valueIsContainableInType(Number value, PsiType type) { final long longValue = value.longValue(); final double doubleValue = value.doubleValue(); diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/UnnecessaryExplicitNumericCastInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/UnnecessaryExplicitNumericCastInspection.java index f7eeccffb09d..a850823e8624 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/UnnecessaryExplicitNumericCastInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/UnnecessaryExplicitNumericCastInspection.java @@ -28,6 +28,7 @@ import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.psiutils.ClassUtils; import com.siyeh.ig.psiutils.ExpectedTypeUtils; import com.siyeh.ig.psiutils.ExpressionUtils; +import com.siyeh.ig.psiutils.TypeUtils; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; @@ -158,17 +159,21 @@ public class UnnecessaryExplicitNumericCastInspection extends BaseInspection { final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)parent; final IElementType tokenType = polyadicExpression.getOperationTokenType(); if (binaryPromotionOperators.contains(tokenType)) { + if (TypeUtils.isNarrowingConversion(operandType, castType)) { + return true; + } if (PsiType.INT.equals(castType)) { return PsiType.LONG.equals(operandType) || PsiType.FLOAT.equals(operandType) || PsiType.DOUBLE.equals(operandType); } if (PsiType.LONG.equals(castType) || PsiType.FLOAT.equals(castType) || PsiType.DOUBLE.equals(castType)) { final PsiExpression[] operands = polyadicExpression.getOperands(); for (PsiExpression operand1 : operands) { - if (!PsiTreeUtil.isAncestor(operand1, expression, false)) { - final PsiType type = operand1.getType(); - if (castType.equals(type)) { - return false; - } + if (PsiTreeUtil.isAncestor(operand1, expression, false)) { + continue; + } + final PsiType type = operand1.getType(); + if (castType.equals(type)) { + return false; } } } @@ -283,49 +288,31 @@ public class UnnecessaryExplicitNumericCastInspection extends BaseInspection { static boolean isLegalAssignmentConversion(PsiExpression expression, PsiType assignmentType) { // JLS 5.2 Assignment Conversion - final PsiType operandType = expression.getType(); if (isLegalWideningConversion(expression, assignmentType)) { return true; } - else if (PsiType.SHORT.equals(assignmentType)) { - if (PsiType.INT.equals(operandType)) { - final Object constant = ExpressionUtils.computeConstantExpression(expression); - if (!(constant instanceof Integer)) { - return false; - } - final int i = ((Integer)constant).intValue(); - if (i >= Short.MIN_VALUE && i <= Short.MAX_VALUE) { - // narrowing - return true; - } - } + if (PsiType.SHORT.equals(assignmentType)) { + return canValueBeContained(expression, Short.MIN_VALUE, Short.MAX_VALUE); } else if (PsiType.CHAR.equals(assignmentType)) { - if (PsiType.INT.equals(operandType)) { - final Object constant = ExpressionUtils.computeConstantExpression(expression); - if (!(constant instanceof Integer)) { - return false; - } - final int i = ((Integer)constant).intValue(); - if (i >= Character.MIN_VALUE && i <= Character.MAX_VALUE) { - // narrowing - return true; - } - } + return canValueBeContained(expression, Character.MIN_VALUE, Character.MAX_VALUE); } else if (PsiType.BYTE.equals(assignmentType)) { - if (PsiType.INT.equals(operandType)) { - final Object constant = ExpressionUtils.computeConstantExpression(expression); - if (!(constant instanceof Integer)) { - return false; - } - final int i = ((Integer)constant).intValue(); - if (i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE) { - // narrowing - return true; - } - } + return canValueBeContained(expression, Byte.MIN_VALUE, Byte.MAX_VALUE); } return false; } + + private static boolean canValueBeContained(PsiExpression expression, int lowerBound, int upperBound) { + final PsiType expressionType = expression.getType(); + if (!PsiType.INT.equals(expressionType)) { + return false; + } + final Object constant = ExpressionUtils.computeConstantExpression(expression); + if (!(constant instanceof Integer)) { + return false; + } + final int i = ((Integer)constant).intValue(); + return i >= lowerBound && i <= upperBound; + } } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/TypeUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/TypeUtils.java index 34110a4cc702..51a650461481 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/TypeUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/TypeUtils.java @@ -23,8 +23,23 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.HashMap; +import java.util.Map; + public class TypeUtils { + private static final Map typePrecisions = new HashMap(7); + + static { + typePrecisions.put(PsiType.BYTE, 1); + typePrecisions.put(PsiType.CHAR, 2); + typePrecisions.put(PsiType.SHORT, 2); + typePrecisions.put(PsiType.INT, 3); + typePrecisions.put(PsiType.LONG, 4); + typePrecisions.put(PsiType.FLOAT, 5); + typePrecisions.put(PsiType.DOUBLE, 6); + } + private TypeUtils() {} public static boolean typeEquals(@NonNls @NotNull String typeName, @Nullable PsiType targetType) { @@ -50,6 +65,12 @@ public class TypeUtils { return getType(CommonClassNames.JAVA_LANG_STRING, context); } + public static boolean isNarrowingConversion(PsiType operandType, PsiType castType) { + final Integer operandPrecision = typePrecisions.get(operandType); + final Integer castPrecision = typePrecisions.get(castType); + return operandPrecision.intValue() > castPrecision.intValue(); + } + public static boolean isJavaLangObject(@Nullable PsiType targetType) { return typeEquals(CommonClassNames.JAVA_LANG_OBJECT, targetType); } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/visibility/TypeParameterHidesVisibleTypeInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/visibility/TypeParameterHidesVisibleTypeInspectionBase.java index 61a865296c10..8f7e6f7e3059 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/visibility/TypeParameterHidesVisibleTypeInspectionBase.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/visibility/TypeParameterHidesVisibleTypeInspectionBase.java @@ -35,6 +35,11 @@ public class TypeParameterHidesVisibleTypeInspectionBase extends BaseInspection return true; } + @Override + public boolean isEnabledByDefault() { + return true; + } + @Override @NotNull public String buildErrorString(Object... infos) { diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/unnecessary_explicit_numeric_cast/UnnecessaryExplicitNumericCast.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/unnecessary_explicit_numeric_cast/UnnecessaryExplicitNumericCast.java index 9468de1baf32..26cc95afef11 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/unnecessary_explicit_numeric_cast/UnnecessaryExplicitNumericCast.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/unnecessary_explicit_numeric_cast/UnnecessaryExplicitNumericCast.java @@ -53,4 +53,14 @@ public class UnnecessaryExplicitNumericCast { float x = 2; target((int) x); // this line complains: 'x' unnecessarily cast to 'int' } + + void a(float angleFromTo) { + float f = (float) Math.cos(0.5) * 1.0f; // necessary + final long l = (long) i() * 9L; + float angle2 = angleFromTo + (float) (Math.PI / 2); + } + + int i() { + return 10; + } } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/unnecessary_explicit_numeric_cast/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/unnecessary_explicit_numeric_cast/expected.xml index 19f2cadeff52..1e24f7ab3d58 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/unnecessary_explicit_numeric_cast/expected.xml +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/numeric/unnecessary_explicit_numeric_cast/expected.xml @@ -100,4 +100,11 @@ Unnecessary explicit numeric cast ''b'' unnecessarily cast to <code>int</code> #loc + + + UnnecessaryExplicitNumericCast.java + 59 + Unnecessary explicit numeric cast + 'i()' unnecessarily cast to <code>long</code> #loc + \ No newline at end of file diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/unicode/ReplaceOctalEscapeWithUnicodeEscapeIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/unicode/ReplaceOctalEscapeWithUnicodeEscapeIntention.java index 564db76755e5..1d479be7129c 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/unicode/ReplaceOctalEscapeWithUnicodeEscapeIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/unicode/ReplaceOctalEscapeWithUnicodeEscapeIntention.java @@ -150,8 +150,14 @@ public class ReplaceOctalEscapeWithUnicodeEscapeIntention extends Intention { } final SelectionModel selectionModel = editor.getSelectionModel(); if (selectionModel.hasSelection()) { - final String selectedText = selectionModel.getSelectedText(); - return selectedText != null && indexOfOctalEscape(selectedText, 1) >= 0; + final int start = selectionModel.getSelectionStart(); + final int end = selectionModel.getSelectionEnd(); + if (start < 0 || end < 0 || start > end) { + // shouldn't happen but http://ea.jetbrains.com/browser/ea_problems/51155 + return false; + } + final String text = editor.getDocument().getCharsSequence().subSequence(start, end).toString(); + return indexOfOctalEscape(text, 1) >= 0; } else if (element instanceof PsiLiteralExpression) { final PsiLiteralExpression literalExpression = (PsiLiteralExpression)element; diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/unicode/UnicodeUnescapeIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/unicode/UnicodeUnescapeIntention.java index e885764e6ecd..0137f987a32e 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/unicode/UnicodeUnescapeIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/unicode/UnicodeUnescapeIntention.java @@ -140,14 +140,19 @@ public class UnicodeUnescapeIntention extends Intention { return false; } final SelectionModel selectionModel = editor.getSelectionModel(); + final Document document = editor.getDocument(); if (selectionModel.hasSelection()) { - final String text = selectionModel.getSelectedText(); - // an editor can have a selection, but still null for selected text (because of threading?). - return text != null && indexOfUnicodeEscape(text, 1) >= 0; + final int start = selectionModel.getSelectionStart(); + final int end = selectionModel.getSelectionEnd(); + if (start < 0 || end < 0 || start > end) { + // shouldn't happen but http://ea.jetbrains.com/browser/ea_problems/50192 + return false; + } + final String text = document.getCharsSequence().subSequence(start, end).toString(); + return indexOfUnicodeEscape(text, 1) >= 0; } else { final CaretModel caretModel = editor.getCaretModel(); - final Document document = editor.getDocument(); final int lineNumber = document.getLineNumber(caretModel.getOffset()); final String line = document.getText(new TextRange(document.getLineStartOffset(lineNumber), document.getLineEndOffset(lineNumber))); final int column = caretModel.getLogicalPosition().column; diff --git a/plugins/ant/src/com/intellij/lang/ant/dom/AntResolveInspection.java b/plugins/ant/src/com/intellij/lang/ant/dom/AntResolveInspection.java index 93869991d4a1..b17f29ae11ac 100644 --- a/plugins/ant/src/com/intellij/lang/ant/dom/AntResolveInspection.java +++ b/plugins/ant/src/com/intellij/lang/ant/dom/AntResolveInspection.java @@ -21,6 +21,7 @@ import com.intellij.lang.ant.AntBundle; import com.intellij.lang.ant.AntSupport; import com.intellij.lang.ant.quickfix.AntChangeContextLocalFix; import com.intellij.lang.ant.quickfix.AntCreatePropertyFix; +import com.intellij.lang.ant.quickfix.AntCreateTargetFix; import com.intellij.lang.ant.validation.AntInspection; import com.intellij.lang.properties.psi.PropertiesFile; import com.intellij.psi.PsiFile; @@ -126,6 +127,9 @@ public class AntResolveInspection extends AntInspection { } } } + else if (ref instanceof AntDomTargetReference) { + quickFixList.add(new AntCreateTargetFix(ref.getCanonicalText())); + } holder.createProblem( domElement, diff --git a/plugins/ant/src/com/intellij/lang/ant/quickfix/AntCreateTargetFix.java b/plugins/ant/src/com/intellij/lang/ant/quickfix/AntCreateTargetFix.java new file mode 100644 index 000000000000..908769385ac1 --- /dev/null +++ b/plugins/ant/src/com/intellij/lang/ant/quickfix/AntCreateTargetFix.java @@ -0,0 +1,93 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.lang.ant.quickfix; + +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.lang.ant.AntBundle; +import com.intellij.lang.ant.dom.AntDomTarget; +import com.intellij.openapi.fileEditor.OpenFileDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.pom.Navigatable; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.xml.DomElement; +import com.intellij.util.xml.DomUtil; +import org.jetbrains.annotations.NotNull; + +public class AntCreateTargetFix implements LocalQuickFix { + private static final String TAG_NAME = "target"; + private static final String NAME_ATTR = "name"; + private final String myCanonicalText; + + public AntCreateTargetFix(String canonicalText) { + myCanonicalText = canonicalText; + } + + @NotNull + public String getName() { + return AntBundle.message("ant.create.target.intention.description", myCanonicalText); + } + + @NotNull + public String getFamilyName() { + return AntBundle.message("ant.intention.create.target.family.name"); + } + + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + final PsiElement psiElement = descriptor.getPsiElement(); + final PsiFile containingFile = psiElement.getContainingFile(); + + + Navigatable result = null; + if (containingFile instanceof XmlFile) { + final XmlFile xmlFile = (XmlFile)containingFile; + final XmlTag rootTag = xmlFile.getRootTag(); + if (rootTag != null) { + final XmlTag propTag = rootTag.createChildTag(TAG_NAME, rootTag.getNamespace(), "", false); + propTag.setAttribute(NAME_ATTR, myCanonicalText); + final DomElement contextElement = DomUtil.getDomElement(descriptor.getPsiElement()); + PsiElement generated; + if (contextElement == null) { + generated = rootTag.addSubTag(propTag, true); + } + else { + final AntDomTarget containingTarget = contextElement.getParentOfType(AntDomTarget.class, false); + final DomElement anchor = containingTarget != null ? containingTarget : contextElement; + final XmlTag tag = anchor.getXmlTag(); + if (!rootTag.equals(tag)) { + generated = tag.getParent().addBefore(propTag, tag); + } + else { + generated = rootTag.addSubTag(propTag, true); + } + } + if (generated instanceof XmlTag) { + result = new OpenFileDescriptor(project, containingFile.getVirtualFile(), ((XmlTag)generated).getValue().getTextRange().getEndOffset()); + } + if (result == null && generated instanceof Navigatable) { + result = (Navigatable)generated; + } + } + } + + if (result != null) { + result.navigate(true); + } + } +} diff --git a/plugins/git4idea/src/git4idea/GitUtil.java b/plugins/git4idea/src/git4idea/GitUtil.java index b507e4a6a161..6e49379f91d0 100644 --- a/plugins/git4idea/src/git4idea/GitUtil.java +++ b/plugins/git4idea/src/git4idea/GitUtil.java @@ -23,7 +23,6 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; @@ -38,10 +37,7 @@ import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Consumer; import com.intellij.util.Function; -import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.UIUtil; -import com.intellij.vcs.log.VcsLog; -import com.intellij.vcs.log.VcsLogProvider; import com.intellij.vcsUtil.VcsFileUtil; import com.intellij.vcsUtil.VcsUtil; import git4idea.branch.GitBranchUtil; @@ -944,27 +940,4 @@ public class GitUtil { return !output.trim().isEmpty(); } - /** - * Report a warning that the given root has no associated GitRepositories. - */ - public static void noRepositoryForRoot(@NotNull Logger log, @NotNull VirtualFile root, @NotNull Project project) { - GitRepositoryManager repositoryManager = getRepositoryManager(project); - ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(project); - List roots = Arrays.asList(vcsManager.getRootsUnderVcs(GitVcs.getInstance(project))); - log.warn(String.format("Repository not found for root: %s. All roots: %s, all repositories: %s", root, roots, - repositoryManager.getRepositories())); - } - - /** - * Checks if there are Git roots in the VCS log. - */ - public static boolean logHasGitRoot(@NotNull VcsLog log) { - return ContainerUtil.find(log.getLogProviders(), new Condition() { - @Override - public boolean value(VcsLogProvider logProvider) { - return logProvider.getSupportedVcs().equals(GitVcs.getKey()); - } - }) != null; - } - } diff --git a/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java b/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java index 0ec2ab63e877..39cfb2a625ad 100644 --- a/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java +++ b/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java @@ -15,6 +15,7 @@ */ package git4idea.actions; +import com.intellij.dvcs.DvcsUtil; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.components.ServiceManager; @@ -24,7 +25,7 @@ import com.intellij.openapi.project.Project; import com.intellij.vcs.log.VcsFullCommitDetails; import com.intellij.vcs.log.VcsLog; import com.intellij.vcs.log.VcsLogDataKeys; -import git4idea.GitUtil; +import git4idea.GitVcs; import git4idea.repo.GitRepository; import git4idea.repo.GitRepositoryManager; import org.jetbrains.annotations.NotNull; @@ -56,10 +57,10 @@ public abstract class GitLogSingleCommitAction extends DumbAwareAction { GitRepositoryManager repositoryManager = ServiceManager.getService(data.project, GitRepositoryManager.class); final GitRepository repository = repositoryManager.getRepositoryForRoot(commit.getRoot()); if (repository == null) { - GitUtil.noRepositoryForRoot(LOG, commit.getRoot(), data.project); + DvcsUtil.noVcsRepositoryForRoot(LOG, commit.getRoot(), data.project, repositoryManager, GitVcs.getInstance(data.project)); return; } - + actionPerformed(repository, commit); } @@ -67,8 +68,8 @@ public abstract class GitLogSingleCommitAction extends DumbAwareAction { public void update(AnActionEvent e) { Data data = Data.collect(e); boolean enabled = data.isValid() && data.log.getSelectedCommits().size() == 1; - getTemplatePresentation().setVisible(data.isValid()); - getTemplatePresentation().setEnabled(enabled); + e.getPresentation().setVisible(data.isValid()); + e.getPresentation().setEnabled(enabled); } private static class Data { @@ -83,7 +84,7 @@ public abstract class GitLogSingleCommitAction extends DumbAwareAction { } boolean isValid() { - return project != null && log != null && GitUtil.logHasGitRoot(log); + return project != null && log != null && DvcsUtil.logHasRootForVcs(log, GitVcs.getKey()); } } diff --git a/plugins/git4idea/src/git4idea/cherrypick/GitCherryPickAction.java b/plugins/git4idea/src/git4idea/cherrypick/GitCherryPickAction.java index 7d24caf30152..8547848a0ae3 100644 --- a/plugins/git4idea/src/git4idea/cherrypick/GitCherryPickAction.java +++ b/plugins/git4idea/src/git4idea/cherrypick/GitCherryPickAction.java @@ -15,6 +15,7 @@ */ package git4idea.cherrypick; +import com.intellij.dvcs.DvcsUtil; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ServiceManager; @@ -29,7 +30,6 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.*; import git4idea.GitLocalBranch; import git4idea.GitPlatformFacade; -import git4idea.GitUtil; import git4idea.GitVcs; import git4idea.commands.Git; import git4idea.config.GitVcsSettings; @@ -133,7 +133,7 @@ public class GitCherryPickAction extends DumbAwareAction { public void update(AnActionEvent e) { super.update(e); final VcsLog log = getVcsLog(e); - if (log != null && !GitUtil.logHasGitRoot(log)) { + if (log != null && !DvcsUtil.logHasRootForVcs(log, GitVcs.getKey())) { e.getPresentation().setEnabledAndVisible(false); } else { diff --git a/plugins/git4idea/src/git4idea/roots/GitIntegrationEnabler.java b/plugins/git4idea/src/git4idea/roots/GitIntegrationEnabler.java index a9e0c6ee0ad0..15c98d9c8e92 100644 --- a/plugins/git4idea/src/git4idea/roots/GitIntegrationEnabler.java +++ b/plugins/git4idea/src/git4idea/roots/GitIntegrationEnabler.java @@ -61,8 +61,8 @@ public class GitIntegrationEnabler { Collection gitRoots = ContainerUtil.filter(detectInfo.getRoots(), new Condition() { @Override public boolean value(VcsRoot root) { - AbstractVcs gitVcs = root.getVcs(); - return gitVcs != null && gitVcs.getName().equals(GitVcs.NAME); + AbstractVcs vcs = root.getVcs(); + return vcs != null && vcs.getName().equals(GitVcs.NAME); } }); Collection roots = VcsRootErrorsFinder.vcsRootsToVirtualFiles(gitRoots); diff --git a/plugins/gradle/resources/inspectionDescriptions/MultipleRepositoryUrls.html b/plugins/gradle/resources/inspectionDescriptions/MultipleRepositoryUrls.html new file mode 100644 index 000000000000..f8c19bf9b4a4 --- /dev/null +++ b/plugins/gradle/resources/inspectionDescriptions/MultipleRepositoryUrls.html @@ -0,0 +1,6 @@ + + +This inspection reports usage of multiple urls per repository(maven or ivy) block. +The problem is that no more than one url will be picked up for the repo and others will be ignored. + + \ No newline at end of file diff --git a/plugins/gradle/src/META-INF/plugin.xml b/plugins/gradle/src/META-INF/plugin.xml index 653c626c467e..93e9448c7745 100644 --- a/plugins/gradle/src/META-INF/plugin.xml +++ b/plugins/gradle/src/META-INF/plugin.xml @@ -80,6 +80,11 @@ + + diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/codeInspection/GradleBaseInspection.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/codeInspection/GradleBaseInspection.java new file mode 100644 index 000000000000..91e6756b760a --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/codeInspection/GradleBaseInspection.java @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.plugins.gradle.codeInspection; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.codeInspection.BaseInspection; + +/** + * @author Vladislav.Soroka + * @since 11/21/13 + */ +public abstract class GradleBaseInspection extends BaseInspection { + @NotNull + @Override + public String[] getGroupPath() { + return new String[]{"Gradle", getGroupDisplayName()}; + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/codeInspection/GradleInspectionBundle.properties b/plugins/gradle/src/org/jetbrains/plugins/gradle/codeInspection/GradleInspectionBundle.properties index e2ef587835e1..0f2bed413857 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/codeInspection/GradleInspectionBundle.properties +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/codeInspection/GradleInspectionBundle.properties @@ -17,4 +17,5 @@ # ######################################################################################################################## group.other=Other - +multiple.repository.urls=Multiple repository urls +multiple.repository.urls.fix.name=Replace multiple repository urls with separate repository blocks diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/codeInspection/MultipleRepositoryUrlsFix.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/codeInspection/MultipleRepositoryUrlsFix.java new file mode 100644 index 000000000000..99ec0b4cdd84 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/codeInspection/MultipleRepositoryUrlsFix.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.plugins.gradle.codeInspection; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.codeInspection.GroovyFix; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrCallExpression; +import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Vladislav.Soroka + * @since 11/21/13 + */ +public class MultipleRepositoryUrlsFix extends GroovyFix { + private final GrClosableBlock myClosure; + private final String myRepoType; + + public MultipleRepositoryUrlsFix(@NotNull GrClosableBlock closure, @NotNull String repoType) { + myClosure = closure; + myRepoType = repoType; + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { + List statements = MultipleRepositoryUrlsInspection.findUrlCallExpressions(myClosure); + if (statements.size() <= 1) return; + statements.remove(0); + + List elements = new ArrayList(statements); + for (GrCallExpression statement : statements) { + PsiElement newLineCandidate = statement.getNextSibling(); + if (PsiUtil.isNewLine(newLineCandidate)) { + elements.add(newLineCandidate); + } + } + + myClosure.removeElements(elements.toArray(new PsiElement[elements.size()])); + GrClosableBlock closableBlock = PsiTreeUtil.getParentOfType(myClosure, GrClosableBlock.class); + if (closableBlock == null) return; + + GroovyPsiElementFactory elementFactory = GroovyPsiElementFactory.getInstance(project); + for (GrCallExpression statement : statements) { + closableBlock.addStatementBefore(elementFactory.createStatementFromText(myRepoType + '{' + statement.getText() + '}'), null); + } + } + + @NotNull + @Override + public String getName() { + return GradleInspectionBundle.message("multiple.repository.urls.fix.name"); + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/codeInspection/MultipleRepositoryUrlsInspection.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/codeInspection/MultipleRepositoryUrlsInspection.java new file mode 100644 index 000000000000..e29d9170155c --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/codeInspection/MultipleRepositoryUrlsInspection.java @@ -0,0 +1,113 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.plugins.gradle.codeInspection; + +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemHighlightType; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.ArrayUtil; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.codeInspection.BaseInspectionVisitor; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrCallExpression; + +import java.util.Collections; +import java.util.List; + +/** + * @author Vladislav.Soroka + * @since 11/21/13 + */ +public class MultipleRepositoryUrlsInspection extends GradleBaseInspection { + + @NotNull + @Override + protected BaseInspectionVisitor buildVisitor() { + return new MyVisitor(); + } + + @Nls + @NotNull + @Override + public String getGroupDisplayName() { + return PROBABLE_BUGS; + } + + + @Override + protected String buildErrorString(Object... args) { + return GradleInspectionBundle.message("multiple.repository.urls", args); + } + + @Nls + @NotNull + @Override + public String getDisplayName() { + return GradleInspectionBundle.message("multiple.repository.urls"); + } + + private static class MyVisitor extends BaseInspectionVisitor { + @Override + public void visitClosure(GrClosableBlock closure) { + super.visitClosure(closure); + GrMethodCall mavenMethodCall = PsiTreeUtil.getParentOfType(closure, GrMethodCall.class); + if (mavenMethodCall == null) return; + GrExpression mavenMethodExpression = mavenMethodCall.getInvokedExpression(); + if (mavenMethodExpression == null || + !ArrayUtil.contains(mavenMethodExpression.getText(), "maven", "ivy")) { + return; + } + + GrMethodCall repositoryMethodCall = PsiTreeUtil.getParentOfType(mavenMethodCall, GrMethodCall.class); + if (repositoryMethodCall == null) return; + GrExpression repositoryMethodExpression = repositoryMethodCall.getInvokedExpression(); + if (repositoryMethodExpression == null || !repositoryMethodExpression.getText().equals("repositories")) return; + + List statements = findUrlCallExpressions(closure); + if (statements.size() > 1) { + registerError(closure); + + registerError(closure, GradleInspectionBundle.message("multiple.repository.urls"), + new LocalQuickFix[]{new MultipleRepositoryUrlsFix(closure, mavenMethodExpression.getText())}, + ProblemHighlightType.GENERIC_ERROR); + } + } + } + + @NotNull + static List findUrlCallExpressions(@NotNull GrClosableBlock closure) { + GrCallExpression[] applicationStatements = PsiTreeUtil.getChildrenOfType(closure, GrCallExpression.class); + if (applicationStatements == null) return Collections.emptyList(); + + List statements = ContainerUtil.newArrayList(); + for (GrCallExpression statement : applicationStatements) { + GrReferenceExpression[] referenceExpressions = PsiTreeUtil.getChildrenOfType(statement, GrReferenceExpression.class); + if (referenceExpressions == null) continue; + for (GrReferenceExpression expression : referenceExpressions) { + String expressionText = expression.getText(); + if ("url".equals(expressionText) || "setUrl".equals(expressionText)) { + statements.add(statement); + } + } + } + return statements; + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/noReturnMethod/MissingReturnInspection.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/noReturnMethod/MissingReturnInspection.java index fc23c014df46..31a97407fd13 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/noReturnMethod/MissingReturnInspection.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/noReturnMethod/MissingReturnInspection.java @@ -16,6 +16,7 @@ package org.jetbrains.plugins.groovy.codeInspection.noReturnMethod; import com.intellij.codeInspection.ProblemsHolder; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; @@ -29,16 +30,26 @@ import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner; import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementVisitor; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrCodeBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrReturnStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall; 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.impl.MaybeReturnInstruction; import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.ThrowingInstruction; import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.GroovyExpectedTypesProvider; +import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil; +import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * @author ven @@ -67,7 +78,7 @@ public class MissingReturnInspection extends GroovySuppressableInspectionTool { public static ReturnStatus getReturnStatus(PsiElement subject) { if (subject instanceof GrClosableBlock) { - final PsiType inferredReturnType = GroovyExpectedTypesProvider.getExpectedClosureReturnType((GrClosableBlock)subject); + final PsiType inferredReturnType = getExpectedClosureReturnType((GrClosableBlock)subject); if (inferredReturnType instanceof PsiClassType) { PsiClass resolved = ((PsiClassType)inferredReturnType).resolve(); if (resolved != null && !(resolved instanceof PsiTypeParameter)) return mustReturnValue; @@ -83,6 +94,55 @@ public class MissingReturnInspection extends GroovySuppressableInspectionTool { } } + @Nullable + public static PsiType getExpectedClosureReturnType(GrClosableBlock closure) { + List expectedReturnTypes = new ArrayList(); + + PsiElement parent = closure.getParent(); + if (parent instanceof GrArgumentList && parent.getParent() instanceof GrMethodCall || parent instanceof GrMethodCall) { + GrMethodCall call = (GrMethodCall)(parent instanceof GrArgumentList ? parent.getParent() : parent); + + GroovyResolveResult[] variants = call.getCallVariants(null); + + for (GroovyResolveResult variant : variants) { + Map> map = + GrClosureSignatureUtil.mapArgumentsToParameters(variant, closure, true, true, call.getNamedArguments(), call.getExpressionArguments(), call.getClosureArguments()); + + if (map != null) { + Pair pair = map.get(closure); + if (pair == null) continue; + + PsiParameter parameter = pair.getFirst(); + + PsiType type = parameter.getType(); + if (TypesUtil.isPsiClassTypeToClosure(type)) { + PsiType[] parameters = ((PsiClassType)type).getParameters(); + if (parameters.length == 1) { + expectedReturnTypes.add(parameters[0]); + } + } + } + } + } + else { + final Set expectedTypes = GroovyExpectedTypesProvider.getDefaultExpectedTypes(closure); + + for (PsiType expectedType : expectedTypes) { + if (TypesUtil.isPsiClassTypeToClosure(expectedType)) { + PsiType[] parameters = ((PsiClassType)expectedType).getParameters(); + if (parameters.length == 1) { + expectedReturnTypes.add(parameters[0]); + } + } + } + } + + for (PsiType type : expectedReturnTypes) { + if (PsiType.VOID.equals(type)) return PsiType.VOID; + } + return TypesUtil.getLeastUpperBoundNullable(expectedReturnTypes, closure.getManager()); + } + @NotNull public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder problemsHolder, boolean onTheFly) { return new GroovyPsiElementVisitor(new GroovyElementVisitor() { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppTypeConverter.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppTypeConverter.java index 7c8714897b5b..0b4e639a0035 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppTypeConverter.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppTypeConverter.java @@ -73,10 +73,13 @@ public class GppTypeConverter extends GrTypeConverter { final PsiType expectedComponent = PsiUtil.extractIterableTypeParameter(lType, false); if (expectedComponent != null && isMethodCallConversion(context)) { - PsiType tupleComponent = tupleType.getParameters()[0]; - if (tupleComponent != null && - TypesUtil.isAssignable(expectedComponent, tupleComponent, context) && hasDefaultConstructor(lType)) { - return true; + PsiType[] parameters = tupleType.getParameters(); + if (parameters.length == 1) { + PsiType tupleComponent = parameters[0]; + if (tupleComponent != null && + TypesUtil.isAssignable(expectedComponent, tupleComponent, context) && hasDefaultConstructor(lType)) { + return true; + } } } @@ -88,10 +91,10 @@ public class GppTypeConverter extends GrTypeConverter { final PsiType lKeyType = PsiUtil.substituteTypeParameter(lType, CommonClassNames.JAVA_UTIL_MAP, 0, false); final PsiType lValueType = PsiUtil.substituteTypeParameter(lType, CommonClassNames.JAVA_UTIL_MAP, 1, false); final PsiType[] parameters = ((GrMapType)rType).getParameters(); - if (lKeyType != null && lValueType != null && + if (parameters.length == 2 && lKeyType != null && lValueType != null && parameters[0] != null && parameters[1] != null && - (!TypesUtil.isAssignable(lKeyType, parameters[0], context) || !TypesUtil - .isAssignable(lValueType, parameters[1], context))) { + (!TypesUtil.isAssignable(lKeyType, parameters[0], context) || + !TypesUtil.isAssignable(lValueType, parameters[1], context))) { return null; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesProvider.java index d03a7101861e..f3779a15910a 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/expectedTypes/GroovyExpectedTypesProvider.java @@ -105,32 +105,6 @@ public class GroovyExpectedTypesProvider { return result; } - @Nullable - public static PsiType getExpectedClosureReturnType(GrClosableBlock closure) { - final Set expectedTypes = getDefaultExpectedTypes(closure); - - List expectedReturnTypes = new ArrayList(); - for (PsiType expectedType : expectedTypes) { - if (!(expectedType instanceof PsiClassType)) return null; - - final PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)expectedType).resolveGenerics(); - final PsiClass resolved = resolveResult.getElement(); - if (resolved == null || !(GroovyCommonClassNames.GROOVY_LANG_CLOSURE.equals(resolved.getQualifiedName()))) return null; - - final PsiTypeParameter[] typeParameters = resolved.getTypeParameters(); - if (typeParameters.length != 1) return null; - - final PsiTypeParameter expected = typeParameters[0]; - final PsiType expectedReturnType = resolveResult.getSubstitutor().substitute(expected); - if (expectedReturnType == PsiType.VOID || expectedReturnType == null) return null; - - expectedReturnTypes.add(expectedReturnType); - } - - return TypesUtil.getLeastUpperBoundNullable(expectedReturnTypes, closure.getManager()); -} - - private static class MyCalculator extends GroovyElementVisitor { private TypeConstraint[] myResult; private final GrExpression myExpression; @@ -214,7 +188,7 @@ public class GroovyExpectedTypesProvider { final GrExpression[] expressionArgs = argumentList == null ? GrExpression.EMPTY_ARRAY : argumentList.getExpressionArguments(); try { final Map> map = - GrClosureSignatureUtil.mapArgumentsToParameters(variant, methodCall, true, true, namedArgs, expressionArgs, closureArgs); + GrClosureSignatureUtil.mapArgumentsToParameters(variant, methodCall, true, false, namedArgs, expressionArgs, closureArgs); addConstraintsFromMap(constraints, map); } catch (RuntimeException e) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrClosureType.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrClosureType.java index 04db42793f7f..6d59dd8adc28 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrClosureType.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrClosureType.java @@ -39,7 +39,7 @@ import java.util.List; * @author ven */ public class GrClosureType extends GrLiteralClassType { - private final @NotNull GrSignature mySignature; + private final GrSignature mySignature; private PsiType[] myTypeArgs = null; private GrClosureType(@NotNull LanguageLevel languageLevel, @@ -52,21 +52,22 @@ public class GrClosureType extends GrLiteralClassType { if (!shouldInferTypeParameters) myTypeArgs = PsiType.EMPTY_ARRAY; } + private GrClosureType(@NotNull LanguageLevel level, + @NotNull GlobalSearchScope scope, + @NotNull JavaPsiFacade facade, + @NotNull GrSignature signature, + @NotNull PsiType[] typeArgs) { + super(level, scope, facade); + + mySignature = signature; + myTypeArgs = typeArgs; + } + @NotNull public String getClassName() { return "Closure"; } - @Override - public int getParameterCount() { - if (myTypeArgs != null) { - return myTypeArgs.length; - } - - final PsiClass psiClass = resolve(); - return psiClass != null && psiClass.getTypeParameters().length == 1 ? 1 : 0; - } - @NotNull public PsiType[] getParameters() { if (myTypeArgs == null) { @@ -79,7 +80,7 @@ public class GrClosureType extends GrLiteralClassType { final PsiClass psiClass = resolve(); if (psiClass != null && psiClass.getTypeParameters().length == 1) { final PsiType type = GrClosureSignatureUtil.getReturnType(mySignature); - if (type == PsiType.NULL) { + if (type == PsiType.NULL || type == null) { return new PsiType[]{null}; } else { @@ -129,9 +130,7 @@ public class GrClosureType extends GrLiteralClassType { @NotNull public PsiClassType setLanguageLevel(@NotNull final LanguageLevel languageLevel) { - final GrClosureType result = create(mySignature, myScope, myFacade, languageLevel, true); - result.myTypeArgs = this.myTypeArgs; - return result; + return new GrClosureType(languageLevel, myScope, myFacade, mySignature, myTypeArgs); } public static GrClosureType create(GroovyResolveResult[] results, GroovyPsiElement context) { @@ -187,9 +186,7 @@ public class GrClosureType extends GrLiteralClassType { public PsiType curry(@NotNull PsiType[] args, int position, @NotNull GroovyPsiElement context) { final GrSignature newSignature = mySignature.curry(args, position, context); if (newSignature == null) return null; - final GrClosureType result = create(newSignature, myScope, myFacade, myLanguageLevel, true); - result.myTypeArgs = this.myTypeArgs; - return result; + return new GrClosureType(myLanguageLevel, myScope, myFacade, newSignature, myTypeArgs); } @NotNull diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrLiteralClassType.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrLiteralClassType.java index 55c8d34e9594..3595f8d70dc5 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrLiteralClassType.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrLiteralClassType.java @@ -46,20 +46,7 @@ public abstract class GrLiteralClassType extends PsiClassType { @NotNull public ClassResolveResult resolveGenerics() { final PsiClass myBaseClass = resolve(); - final PsiSubstitutor substitutor; - if (myBaseClass != null) { - final PsiType[] typeArgs = getParameters(); - final PsiTypeParameter[] typeParams = myBaseClass.getTypeParameters(); - if (typeParams.length == typeArgs.length) { - substitutor = PsiSubstitutor.EMPTY.putAll(myBaseClass, typeArgs); - } - else { - substitutor = PsiSubstitutor.EMPTY; - } - } - else { - substitutor = PsiSubstitutor.EMPTY; - } + final PsiSubstitutor substitutor = inferSubstitutor(myBaseClass); return new ClassResolveResult() { @@ -94,6 +81,23 @@ public abstract class GrLiteralClassType extends PsiClassType { }; } + @NotNull + private PsiSubstitutor inferSubstitutor(@Nullable PsiClass myBaseClass) { + if (myBaseClass != null) { + final PsiType[] typeArgs = getParameters(); + final PsiTypeParameter[] typeParams = myBaseClass.getTypeParameters(); + if (typeParams.length == typeArgs.length) { + return PsiSubstitutor.EMPTY.putAll(myBaseClass, typeArgs); + } + else { + return PsiSubstitutor.EMPTY.putAll(myBaseClass, new PsiType[typeParams.length]); + } + } + else { + return PsiSubstitutor.EMPTY; + } + } + @Override @NotNull public abstract String getClassName() ; @@ -161,7 +165,7 @@ public abstract class GrLiteralClassType extends PsiClassType { } @NotNull - protected PsiType getLeastUpperBound(PsiType[] psiTypes) { + protected PsiType getLeastUpperBound(PsiType... psiTypes) { PsiType result = null; final PsiManager manager = getPsiManager(); for (final PsiType other : psiTypes) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrMapType.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrMapType.java index 69f11760cb8d..3387f988057e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrMapType.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrMapType.java @@ -37,8 +37,6 @@ import static org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames. * @author peter */ public class GrMapType extends GrLiteralClassType { - private static final PsiType[] RAW_PARAMETERS = new PsiType[]{null, null}; - private final Map myStringEntries; private final List> myOtherEntries; private final String myJavaClassName; @@ -83,12 +81,6 @@ public class GrMapType extends GrLiteralClassType { } } - @NotNull - @Override - public PsiClassType rawType() { - return new GrMapType(myFacade, getResolveScope(), Collections.emptyMap(), Collections.>emptyList(), getLanguageLevel()); - } - @NotNull @Override protected String getJavaClassName() { @@ -136,7 +128,7 @@ public class GrMapType extends GrLiteralClassType { final PsiType[] keyTypes = getAllKeyTypes(); final PsiType[] valueTypes = getAllValueTypes(); if (keyTypes.length == 0 && valueTypes.length == 0) { - return RAW_PARAMETERS; + return EMPTY_ARRAY; } return new PsiType[]{getLeastUpperBound(keyTypes), getLeastUpperBound(valueTypes)}; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrRangeType.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrRangeType.java index bdd93682e691..ed887fff4c8c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrRangeType.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrRangeType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package org.jetbrains.plugins.groovy.lang.psi.impl; import com.intellij.openapi.util.text.StringUtil; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiClass; import com.intellij.psi.PsiClassType; import com.intellij.psi.PsiType; import com.intellij.psi.search.GlobalSearchScope; @@ -38,6 +39,8 @@ public class GrRangeType extends GrLiteralClassType { private final PsiType myIterationType; private final String myQualifiedName; + private final PsiType[] myParameters; + public GrRangeType(LanguageLevel languageLevel, GlobalSearchScope scope, JavaPsiFacade facade, @@ -53,6 +56,8 @@ public class GrRangeType extends GrLiteralClassType { else { myQualifiedName = GroovyCommonClassNames.GROOVY_LANG_OBJECT_RANGE; } + + myParameters = inferParameters(); } public GrRangeType(GlobalSearchScope scope, JavaPsiFacade facade, @Nullable PsiType left, @Nullable PsiType right) { @@ -74,7 +79,16 @@ public class GrRangeType extends GrLiteralClassType { @NotNull @Override public PsiType[] getParameters() { - return PsiType.EMPTY_ARRAY; + return myParameters; + } + + private PsiType[] inferParameters() { + if (myIterationType == null) return EMPTY_ARRAY; + + PsiClass resolved = resolve(); + if (resolved == null || resolved.getTypeParameters().length == 0) return EMPTY_ARRAY; + + return new PsiType[]{myIterationType}; } @NotNull diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrSpreadType.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrSpreadType.java deleted file mode 100644 index 99b1556901de..000000000000 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrSpreadType.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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.lang.psi.impl; - -import com.intellij.openapi.project.Project; -import com.intellij.pom.java.LanguageLevel; -import com.intellij.psi.JavaPsiFacade; -import com.intellij.psi.PsiClassType; -import com.intellij.psi.PsiType; -import com.intellij.psi.search.GlobalSearchScope; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; - -/** - * @author Max Medvedev - */ -public class GrSpreadType extends GrLiteralClassType { - - private PsiType myType; - - public GrSpreadType(PsiType original, PsiType containerType, GlobalSearchScope scope) { - this(original, containerType, LanguageLevel.JDK_1_5, scope, JavaPsiFacade.getInstance(scope.getProject())); - } - - public GrSpreadType(PsiType original, PsiType containerType, LanguageLevel languageLevel, GlobalSearchScope scope, JavaPsiFacade facade) { - super(languageLevel, scope, facade); - - final Project project = facade.getProject(); - myType = TypesUtil.createSimilarCollection(containerType, project, original); - } - - @NotNull - @Override - protected String getJavaClassName() { - return null; //To change body of implemented methods use File | Settings | File Templates. - } - - @NotNull - @Override - public String getClassName() { - return null; //To change body of implemented methods use File | Settings | File Templates. - } - - @NotNull - @Override - public PsiType[] getParameters() { - return new PsiType[0]; //To change body of implemented methods use File | Settings | File Templates. - } - - @NotNull - @Override - public PsiClassType setLanguageLevel(@NotNull LanguageLevel languageLevel) { - return null; //To change body of implemented methods use File | Settings | File Templates. - } - - @Override - public String getInternalCanonicalText() { - return null; //To change body of implemented methods use File | Settings | File Templates. - } - - @Override - public boolean isValid() { - return false; //To change body of implemented methods use File | Settings | File Templates. - } -} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrTupleType.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrTupleType.java index 0be4a6698d82..9d1b305d0a8b 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrTupleType.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrTupleType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,8 +31,8 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUt * @author ven */ public class GrTupleType extends GrLiteralClassType { - private static final PsiType[] RAW_PARAMETERS = new PsiType[]{null}; private final PsiType[] myComponentTypes; + private final PsiType[] myParameters; public GrTupleType(PsiType[] componentTypes, JavaPsiFacade facade, GlobalSearchScope scope) { this(componentTypes, facade, scope,LanguageLevel.JDK_1_5); @@ -40,6 +40,8 @@ public class GrTupleType extends GrLiteralClassType { public GrTupleType(PsiType[] componentTypes, JavaPsiFacade facade, GlobalSearchScope scope,LanguageLevel languageLevel) { super(languageLevel, scope, facade); myComponentTypes = componentTypes; + + myParameters = inferParameters(); } @NotNull @@ -48,12 +50,6 @@ public class GrTupleType extends GrLiteralClassType { return CommonClassNames.JAVA_UTIL_ARRAY_LIST; } - @NotNull - @Override - public PsiClassType rawType() { - return new GrTupleType(RAW_PARAMETERS, myFacade, getResolveScope(), getLanguageLevel()); - } - @NotNull public String getClassName() { return StringUtil.getShortName(getJavaClassName()); @@ -61,9 +57,13 @@ public class GrTupleType extends GrLiteralClassType { @NotNull public PsiType[] getParameters() { - if (myComponentTypes.length == 0) return RAW_PARAMETERS; + return myParameters; + } + + private PsiType[] inferParameters() { + if (myComponentTypes.length == 0) return PsiType.EMPTY_ARRAY; final PsiType leastUpperBound = getLeastUpperBound(myComponentTypes); - if (leastUpperBound == PsiType.NULL) return RAW_PARAMETERS; + if (leastUpperBound == PsiType.NULL) return EMPTY_ARRAY; return new PsiType[]{leastUpperBound}; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypesUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypesUtil.java index b3955eece999..ad3219cf8810 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypesUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypesUtil.java @@ -913,4 +913,13 @@ public class TypesUtil { if (!changed) return null; return JavaPsiFacade.getElementFactory(project).createType(element, parameters); } + + public static boolean isPsiClassTypeToClosure(PsiType type) { + if (!(type instanceof PsiClassType)) return false; + + final PsiClass psiClass = ((PsiClassType)type).resolve(); + if (psiClass == null) return false; + + return GROOVY_LANG_CLOSURE.equals(psiClass.getQualifiedName()); + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/DefaultCallExpressionTypeCalculator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/DefaultCallExpressionTypeCalculator.java index 7c6c7b97b250..b8d1dedd7c79 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/DefaultCallExpressionTypeCalculator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/DefaultCallExpressionTypeCalculator.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -109,7 +109,7 @@ public class DefaultCallExpressionTypeCalculator extends GrCallExpressionTypeCal if (type instanceof GrClosureType) { returnType = GrClosureSignatureUtil.getReturnType(((GrClosureType)type).getSignature(), callExpression); } - else if (isPsiClassTypeToClosure(type)) { + else if (TypesUtil.isPsiClassTypeToClosure(type)) { assert type instanceof PsiClassType; final PsiType[] parameters = ((PsiClassType)type).getParameters(); if (parameters.length == 1) { @@ -131,15 +131,6 @@ public class DefaultCallExpressionTypeCalculator extends GrCallExpressionTypeCal } - private static boolean isPsiClassTypeToClosure(PsiType type) { - if (!(type instanceof PsiClassType)) return false; - - final PsiClass psiClass = ((PsiClassType)type).resolve(); - if (psiClass == null) return false; - - return GroovyCommonClassNames.GROOVY_LANG_CLOSURE.equals(psiClass.getQualifiedName()); - } - private static final Set CLOSURE_METHODS = new HashSet(); static { CLOSURE_METHODS.add("call"); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GrStringUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GrStringUtil.java index 11348e734474..4cc4db1233da 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GrStringUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/GrStringUtil.java @@ -690,6 +690,10 @@ public class GrStringUtil { break; default: outChars.append('\\').append(c); + if (sourceOffsets != null) { + sourceOffsets[outChars.length() - outOffset] = index; + } + } } return true; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/constant/GrInplaceConstantIntroducer.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/constant/GrInplaceConstantIntroducer.java index ec1671495604..a23723a42ebe 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/constant/GrInplaceConstantIntroducer.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/constant/GrInplaceConstantIntroducer.java @@ -15,7 +15,6 @@ */ package org.jetbrains.plugins.groovy.refactoring.introduce.constant; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.psi.*; import com.intellij.refactoring.JavaRefactoringSettings; import com.intellij.refactoring.introduce.inplace.OccurrencesChooser; @@ -171,29 +170,24 @@ public class GrInplaceConstantIntroducer extends GrAbstractInplaceIntroducer[super: 2]" - assertEquals point.constructors[1], resolveReference() - - configureGppScript "Point p = [2]" - assertEquals point.constructors[1], resolveReference() - - configureGppScript "Point p = []" - assertEquals point.constructors[0], resolveReference() - - configureGppScript "Point p = [:]" - assertEquals point.constructors[0], resolveReference() - - configureGppScript "Point p = [239, 42]" - assertEquals 2, multiResolveReference().size() - - configureGppScript """ -def foo(Point p) {} -foo([2, 3]) -""" - assertEquals 2, multiResolveReference().size() - - configureGppScript """ -def foo(Point... p) {} -foo(['super':[2, 3]]) -""" - assertEquals 2, multiResolveReference().size() - } - public void testGotoClassFromLiteralOnsetsWhenNoConstructorsPresent() throws Exception { PsiClass point = myFixture.addClass(""" class Point { }""") configureGppScript "Point p = [super: 2]" diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyRegexFindExpressionTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyRegexFindExpressionTest.groovy index 617dd41d704b..73ea9170b78d 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyRegexFindExpressionTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyRegexFindExpressionTest.groovy @@ -53,4 +53,10 @@ class A { myFixture.checkHighlighting(true, false, true) } + + public void testRegex() { + myFixture.configureByText('a.groovy', '\'foo\' =~ /\\s/') + myFixture.checkHighlighting(true, false, true) + } + } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/LiteralConstructorUsagesTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/LiteralConstructorUsagesTest.groovy index d24fa7de36b5..f56c2cb87bd4 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/LiteralConstructorUsagesTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/LiteralConstructorUsagesTest.groovy @@ -42,7 +42,7 @@ class LiteralConstructorUsagesTest extends LightGroovyTestCase { myFixture.addFileToProject "a.gpp", "Foo x = []" assertOneElement(ReferencesSearch.search(foo.constructors[0]).findAll()) } - + public void testList_ReturnValue() throws Exception { def foo = myFixture.addClass("""class Foo { Foo() {} @@ -97,98 +97,6 @@ Foo untyped() { [] } assertEquals(2, ReferencesSearch.search(foo.constructors[0]).findAll().size()) } - public void testList_GppMethodCall() throws Exception { - //------------------------declarations - def foo = myFixture.addClass(""" - package z; - class Foo { - public Foo() {} - } - """) - - myFixture.addClass(""" - package z; - public class Bar { - public static void giveMeFoo(int a, Foo f) {} - } -""") - myFixture.addFileToProject("Decl.groovy", "static def giveMeFooAsWell(z.Foo f) {}") - - //----------------------usages - myFixture.addFileToProject "a.gpp", "z.Bar.giveMeFoo(2, []) //usage" - myFixture.addFileToProject "b.groovy", """ - @Typed package aa; - z.Bar.giveMeFoo(3, []) //usage - """ - myFixture.addFileToProject "c.groovy", """ - @Typed def someMethod() { - z.Bar.giveMeFoo 4, [] //usage - Decl.giveMeFooAsWell([]) //usage - } - z.Bar.giveMeFoo 5, [] //non-typed context - Decl.giveMeFooAsWell([]) - """ - myFixture.addFileToProject "invalid.gpp", "z.Bar.giveMeFoo 42, 239, []" - myFixture.addFileToProject "nonGpp.groovy", "z.Bar.giveMeFoo(6, [])" - assertEquals(4, ReferencesSearch.search(foo.constructors[0]).findAll().size()) - } - - public void testList_GppConstructorCallWithSeveralParameters() throws Exception { - def foo = myFixture.addClass(""" - class Foo { - Foo() {} - } - """) - - myFixture.addClass(""" - class Bar { - Bar(Foo f1, Foo f2, Foo f3) {} - } - """) - myFixture.addFileToProject "a.gpp", "new Bar([],[],[])" - assertEquals(3, ReferencesSearch.search(foo.constructors[0]).findAll().size()) - } - - public void testMap_GppOverloads() throws Exception { - def foo = myFixture.addClass(""" - class Foo { - Foo() {} - Foo(int a) {} - } - """) - - myFixture.addClass(""" - class Bar { - static void foo(Foo f1, Foo f2) {} - } - """) - myFixture.addFileToProject "a.gpp", "Bar.foo([:], [super:2])" - assertEquals(1, ReferencesSearch.search(foo.constructors[0]).findAll().size()) - assertEquals(1, ReferencesSearch.search(foo.constructors[1]).findAll().size()) - } - - public void testGppCallVarargs() throws Exception { - def foo = myFixture.addClass(""" - class Foo { - Foo() {} - Foo(int a) {} - } - """) - - myFixture.addClass(""" - class Bar { - static void foo(Foo f1, Foo f2) {} - static void doo(int a, Foo f1, Foo f2) {} - } - """) - myFixture.addFileToProject "a.gpp", """ - Bar.foo([:], [super:2]) - Bar.doo 3, [:], [super:2] - """ - assertEquals(2, ReferencesSearch.search(foo.constructors[0]).findAll().size()) - assertEquals(2, ReferencesSearch.search(foo.constructors[1]).findAll().size()) - } - public void testOverloadedConstructorUsages() throws Exception { def foo = myFixture.addClass(""" class Foo { diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/formatter/FormatterTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/formatter/FormatterTest.groovy index 544bbd46ed66..f31fa1461810 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/formatter/FormatterTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/formatter/FormatterTest.groovy @@ -763,7 +763,7 @@ def foo() { } void testGDocAfterImports() { doTest() } - void testGDocAfterImports2() { doTest() } + void testGroovyDocAfterImports2() { doTest() } void testRegexExpressions() { doTest() } diff --git a/plugins/groovy/testdata/groovy/formatter/GDocAfterImports2.test b/plugins/groovy/testdata/groovy/formatter/groovyDocAfterImports2.test similarity index 100% rename from plugins/groovy/testdata/groovy/formatter/GDocAfterImports2.test rename to plugins/groovy/testdata/groovy/formatter/groovyDocAfterImports2.test diff --git a/plugins/groovy/testdata/refactoring/convertGroovyToJava/codeBlock/emptyList.java b/plugins/groovy/testdata/refactoring/convertGroovyToJava/codeBlock/emptyList.java index f1283b852258..8f64cd13364f 100644 --- a/plugins/groovy/testdata/refactoring/convertGroovyToJava/codeBlock/emptyList.java +++ b/plugins/groovy/testdata/refactoring/convertGroovyToJava/codeBlock/emptyList.java @@ -1,3 +1,3 @@ -java.util.ArrayList list = new java.util.ArrayList(); +java.util.ArrayList list = new java.util.ArrayList(); java.lang.Integer[] arr = new java.lang.Integer[0]; java.util.List strings = new java.util.ArrayList(); diff --git a/plugins/groovy/testdata/refactoring/convertGroovyToJava/codeBlock/emptyMap.java b/plugins/groovy/testdata/refactoring/convertGroovyToJava/codeBlock/emptyMap.java index ea2a48f2aa2e..75eabcd8e91c 100644 --- a/plugins/groovy/testdata/refactoring/convertGroovyToJava/codeBlock/emptyMap.java +++ b/plugins/groovy/testdata/refactoring/convertGroovyToJava/codeBlock/emptyMap.java @@ -1,2 +1,2 @@ -java.util.Map map = new java.util.Map(); +java.util.Map map = new java.util.Map(); print(map.get(1)); diff --git a/plugins/groovy/testdata/refactoring/convertGroovyToJava/file/dynamicMethodsAccess.java b/plugins/groovy/testdata/refactoring/convertGroovyToJava/file/dynamicMethodsAccess.java index 61cfcc94699b..6a5310f384ae 100644 --- a/plugins/groovy/testdata/refactoring/convertGroovyToJava/file/dynamicMethodsAccess.java +++ b/plugins/groovy/testdata/refactoring/convertGroovyToJava/file/dynamicMethodsAccess.java @@ -8,7 +8,7 @@ java.lang.String s = "a"; org.codehaus.groovy.runtime.DefaultGroovyMethods.invokeMethod(s, "bar", new java.lang.Object[]{4}); org.codehaus.groovy.runtime.DefaultGroovyMethods.print(this, org.codehaus.groovy.runtime.DefaultGroovyMethods.invokeMethod(s, "bar", new java.lang.Object[]{5})); -return org.codehaus.groovy.runtime.DefaultGroovyMethods.invokeMethod(s, "anme", new java.util.ArrayList()); +return org.codehaus.groovy.runtime.DefaultGroovyMethods.invokeMethod(s, "anme", new java.util.ArrayList()); } } diff --git a/plugins/groovy/testdata/refactoring/convertGroovyToJava/file/reflectedMethodWithEllipsis.java b/plugins/groovy/testdata/refactoring/convertGroovyToJava/file/reflectedMethodWithEllipsis.java index 3abffed415d8..6ae1849b2f89 100644 --- a/plugins/groovy/testdata/refactoring/convertGroovyToJava/file/reflectedMethodWithEllipsis.java +++ b/plugins/groovy/testdata/refactoring/convertGroovyToJava/file/reflectedMethodWithEllipsis.java @@ -2,7 +2,7 @@ public class MyClass { public void foo(java.util.Map args, java.lang.String a, java.lang.String... b) {} public void foo(java.lang.String a, java.lang.String... b) { -foo(new java.util.LinkedHashMap(), a, b); +foo(new java.util.LinkedHashMap(), a, b); } } diff --git a/plugins/hg4idea/src/META-INF/plugin.xml b/plugins/hg4idea/src/META-INF/plugin.xml index 8eb193bd5fd6..049afad76350 100644 --- a/plugins/hg4idea/src/META-INF/plugin.xml +++ b/plugins/hg4idea/src/META-INF/plugin.xml @@ -75,6 +75,20 @@ + + + + + + + + + + + + + org.zmlx.hg4idea.HgVcsMessages diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgCreateTagAction.java b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgCreateTagAction.java index f5635cd3f3fe..1aec1a0e56e4 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgCreateTagAction.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgCreateTagAction.java @@ -27,13 +27,16 @@ import java.util.Collection; public class HgCreateTagAction extends HgAbstractGlobalAction { - protected void execute(@NotNull final Project project, @NotNull Collection repos, @Nullable VirtualFile selectedRepo) { + public void execute(@NotNull final Project project, + @NotNull Collection repos, + @Nullable VirtualFile selectedRepo, + @Nullable final String reference) { final HgTagDialog dialog = new HgTagDialog(project); dialog.setRoots(repos, selectedRepo); dialog.show(); if (dialog.isOK()) { try { - new HgTagCreateCommand(project, dialog.getRepository(), dialog.getTagName()).execute(new HgCommandResultHandler() { + new HgTagCreateCommand(project, dialog.getRepository(), dialog.getTagName(), reference).execute(new HgCommandResultHandler() { @Override public void process(@Nullable HgCommandResult result) { if (HgErrorUtil.hasErrorsInCommandExecution(result)) { @@ -48,4 +51,8 @@ public class HgCreateTagAction extends HgAbstractGlobalAction { } } } + + protected void execute(@NotNull final Project project, @NotNull Collection repos, @Nullable VirtualFile selectedRepo) { + execute(project, repos, selectedRepo, null); + } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgCreateTagFromLogAction.java b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgCreateTagFromLogAction.java new file mode 100644 index 000000000000..18a42d735046 --- /dev/null +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgCreateTagFromLogAction.java @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.zmlx.hg4idea.action; + +import com.intellij.vcs.log.VcsFullCommitDetails; +import org.jetbrains.annotations.NotNull; +import org.zmlx.hg4idea.repo.HgRepository; + +import java.util.Arrays; + +/** + * @author Nadya Zabrodina + */ +public class HgCreateTagFromLogAction extends HgLogSingleCommitAction { + @Override + protected void actionPerformed(@NotNull HgRepository repository, @NotNull VcsFullCommitDetails commit) { + String revisionHash = commit.getHash().asString(); + new HgCreateTagAction().execute(repository.getProject(), Arrays.asList(repository.getRoot()), repository.getRoot(), revisionHash); + } +} diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgLogSingleCommitAction.java b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgLogSingleCommitAction.java new file mode 100644 index 000000000000..572c32a0cd04 --- /dev/null +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgLogSingleCommitAction.java @@ -0,0 +1,91 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.zmlx.hg4idea.action; + +import com.intellij.dvcs.DvcsUtil; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.DumbAwareAction; +import com.intellij.openapi.project.Project; +import com.intellij.vcs.log.VcsFullCommitDetails; +import com.intellij.vcs.log.VcsLog; +import com.intellij.vcs.log.VcsLogDataKeys; +import org.jetbrains.annotations.NotNull; +import org.zmlx.hg4idea.HgVcs; +import org.zmlx.hg4idea.repo.HgRepository; +import org.zmlx.hg4idea.repo.HgRepositoryManager; + +import java.util.List; + +/** + * @author Nadya Zabrodina + */ +public abstract class HgLogSingleCommitAction extends DumbAwareAction { + + private static final Logger LOG = Logger.getInstance(HgLogSingleCommitAction.class); + + protected abstract void actionPerformed(@NotNull HgRepository repository, @NotNull VcsFullCommitDetails commit); + + @Override + public void actionPerformed(AnActionEvent e) { + Data data = Data.collect(e); + if (!data.isValid()) { + return; + } + + List details = data.log.getSelectedDetails(); + if (details.size() != 1) { + return; + } + VcsFullCommitDetails commit = details.get(0); + + HgRepositoryManager repositoryManager = ServiceManager.getService(data.project, HgRepositoryManager.class); + final HgRepository repository = repositoryManager.getRepositoryForRoot(commit.getRoot()); + if (repository == null) { + DvcsUtil.noVcsRepositoryForRoot(LOG, commit.getRoot(), data.project, repositoryManager, HgVcs.getInstance(data.project)); + return; + } + + actionPerformed(repository, commit); + } + + @Override + public void update(AnActionEvent e) { + Data data = Data.collect(e); + boolean enabled = data.isValid() && data.log.getSelectedCommits().size() == 1; + getTemplatePresentation().setVisible(data.isValid()); + getTemplatePresentation().setEnabled(enabled); + } + + private static class Data { + Project project; + VcsLog log; + + static Data collect(AnActionEvent e) { + Data data = new Data(); + data.project = e.getData(CommonDataKeys.PROJECT); + data.log = e.getData(VcsLogDataKeys.VSC_LOG); + return data; + } + + boolean isValid() { + return project != null && log != null && DvcsUtil.logHasRootForVcs(log, HgVcs.getKey()); + } + } +} diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgUpdateToFromLogAction.java b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgUpdateToFromLogAction.java new file mode 100644 index 000000000000..214eb4f5170d --- /dev/null +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgUpdateToFromLogAction.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.zmlx.hg4idea.action; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.vcs.log.VcsFullCommitDetails; +import org.jetbrains.annotations.NotNull; +import org.zmlx.hg4idea.HgVcs; +import org.zmlx.hg4idea.command.HgUpdateCommand; +import org.zmlx.hg4idea.execution.HgCommandResult; +import org.zmlx.hg4idea.provider.update.HgConflictResolver; +import org.zmlx.hg4idea.repo.HgRepository; +import org.zmlx.hg4idea.util.HgErrorUtil; + +/** + * @author Nadya Zabrodina + */ +public class HgUpdateToFromLogAction extends HgLogSingleCommitAction { + @Override + protected void actionPerformed(@NotNull HgRepository repository, @NotNull VcsFullCommitDetails commit) { + String revisionHash = commit.getHash().asString(); + Project project = repository.getProject(); + VirtualFile rootFile = repository.getRoot(); + HgUpdateCommand updateCommand = new HgUpdateCommand(project, rootFile); + updateCommand.setRevision(revisionHash); + HgCommandResult result = updateCommand.execute(); + new HgConflictResolver(project).resolve(rootFile); + if (HgErrorUtil.hasErrorsInCommandExecution(result)) { + new HgCommandResultNotifier(project).notifyError(result, "", "Update failed"); + } + project.getMessageBus().syncPublisher(HgVcs.BRANCH_TOPIC).update(project, null); + } +} diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgTagCreateCommand.java b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgTagCreateCommand.java index 5c36d7dd6915..5a52f34f4b22 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgTagCreateCommand.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgTagCreateCommand.java @@ -20,25 +20,33 @@ import org.zmlx.hg4idea.execution.HgCommandException; import org.zmlx.hg4idea.execution.HgCommandExecutor; import org.zmlx.hg4idea.execution.HgCommandResultHandler; -import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; public class HgTagCreateCommand { private final Project project; private final VirtualFile repo; private final String tagName; + private final String revisionNumberOrHash; - public HgTagCreateCommand(Project project, @NotNull VirtualFile repo, String tagName) { + public HgTagCreateCommand(Project project, @NotNull VirtualFile repo, String tagName, String revisionNumberOrHash) { this.project = project; this.repo = repo; this.tagName = tagName; + this.revisionNumberOrHash = revisionNumberOrHash; } public void execute(HgCommandResultHandler resultHandler) throws HgCommandException { if (StringUtil.isEmptyOrSpaces(tagName)) { throw new HgCommandException("tag name is empty"); } - new HgCommandExecutor(project).execute(repo, "tag", Arrays.asList(tagName), resultHandler); + List arguments = new ArrayList(); + arguments.add(tagName); + if (!StringUtil.isEmptyOrSpaces(revisionNumberOrHash)) { + arguments.add("--rev"); + arguments.add(revisionNumberOrHash); + } + new HgCommandExecutor(project).execute(repo, "tag", arguments, resultHandler); } - } diff --git a/plugins/hg4idea/testSrc/hg4idea/test/history/HgBrowseChangesTest.java b/plugins/hg4idea/testSrc/hg4idea/test/history/HgBrowseChangesTest.java index 3f87aff5da78..edfd46d63806 100644 --- a/plugins/hg4idea/testSrc/hg4idea/test/history/HgBrowseChangesTest.java +++ b/plugins/hg4idea/testSrc/hg4idea/test/history/HgBrowseChangesTest.java @@ -53,8 +53,7 @@ public class HgBrowseChangesTest extends HgPlatformTest { hg("commit -m add"); java.util.Calendar now = java.util.Calendar.getInstance(); int year = java.util.Calendar.YEAR; - now.set(year, - +1); + now.set(year, now.get(year) + 1); dateBefore = ChangeBrowserSettings.DATE_FORMAT.format(now.getTime()); now.set(year, 1970); dateAfter = ChangeBrowserSettings.DATE_FORMAT.format(now.getTime()); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/intentions/AddMavenDependencyQuickFix.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/intentions/AddMavenDependencyQuickFix.java index a7e2e5acd4c2..773829819cf7 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/intentions/AddMavenDependencyQuickFix.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/intentions/AddMavenDependencyQuickFix.java @@ -90,7 +90,7 @@ public class AddMavenDependencyQuickFix implements IntentionAction, LowPriorityA VirtualFile virtualFile = file.getOriginalFile().getVirtualFile(); if (virtualFile != null) { - isTestSource = ProjectRootManager.getInstance(project).getFileIndex().isInSourceContent(virtualFile); + isTestSource = ProjectRootManager.getInstance(project).getFileIndex().isInTestSourceContent(virtualFile); } for (MavenId each : ids) { diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/indices/MavenArtifactSearchDialog.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/indices/MavenArtifactSearchDialog.java index d4926d66a405..c028a25becf4 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/indices/MavenArtifactSearchDialog.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/indices/MavenArtifactSearchDialog.java @@ -15,6 +15,7 @@ */ package org.jetbrains.idea.maven.indices; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Pair; @@ -35,6 +36,8 @@ import java.util.List; public class MavenArtifactSearchDialog extends DialogWrapper { private List myResult = Collections.emptyList(); + public static List ourResultForTest; + private TabbedPaneWrapper myTabbedPane; private MavenArtifactSearchPanel myArtifactsPanel; private MavenArtifactSearchPanel myClassesPanel; @@ -45,6 +48,14 @@ public class MavenArtifactSearchDialog extends DialogWrapper { @NotNull public static List searchForClass(Project project, String className) { + if (ApplicationManager.getApplication().isUnitTestMode()) { + assert ourResultForTest != null; + + List res = ourResultForTest; + ourResultForTest = null; + return res; + } + MavenArtifactSearchDialog d = new MavenArtifactSearchDialog(project, className, true); d.show(); if (!d.isOK()) return Collections.emptyList(); @@ -54,6 +65,14 @@ public class MavenArtifactSearchDialog extends DialogWrapper { @NotNull public static List searchForArtifact(Project project, Collection managedDependencies) { + if (ApplicationManager.getApplication().isUnitTestMode()) { + assert ourResultForTest != null; + + List res = ourResultForTest; + ourResultForTest = null; + return res; + } + MavenArtifactSearchDialog d = new MavenArtifactSearchDialog(project, "", false); d.setManagedDependencies(managedDependencies); diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/actions/AddMavenDependencyQuickFixTest.java b/plugins/maven/src/test/java/org/jetbrains/idea/maven/actions/AddMavenDependencyQuickFixTest.java new file mode 100644 index 000000000000..c934e6c5aa8f --- /dev/null +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/actions/AddMavenDependencyQuickFixTest.java @@ -0,0 +1,119 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.maven.actions; + +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiManager; +import org.jetbrains.idea.maven.dom.MavenDomWithIndicesTestCase; +import org.jetbrains.idea.maven.indices.MavenArtifactSearchDialog; +import org.jetbrains.idea.maven.model.MavenId; + +import java.io.IOException; +import java.util.Collections; + +/** + * @author Sergey Evdokimov + */ +public class AddMavenDependencyQuickFixTest extends MavenDomWithIndicesTestCase { + + private IntentionAction findAddMavenIntention() { + for (IntentionAction intention : myFixture.getAvailableIntentions()) { + if (intention.getText().contains("Add Maven")) { + return intention; + } + } + + return null; + } + + public void testAddDependency() throws IOException { + VirtualFile f = createProjectSubFile("src/main/java/A.java", "import org.apache.commons.io.IOUtils;\n" + + "\n" + + "public class Aaa {\n" + + "\n" + + " public void xxx() {\n" + + " IOUtils u;\n" + + " }\n" + + "\n" + + "}"); + + importProject("test" + + "project" + + "1"); + + myFixture.configureFromExistingVirtualFile(f); + + IntentionAction intentionAction = findAddMavenIntention(); + + MavenArtifactSearchDialog.ourResultForTest = Collections.singletonList(new MavenId("commons-io", "commons-io", "2.4")); + + intentionAction.invoke(myProject, myFixture.getEditor(), myFixture.getFile()); + + String pomText = PsiManager.getInstance(myProject).findFile(myProjectPom).getText(); + assert pomText.matches( + "(?s).*\\s*commons-io\\s*commons-io\\s*2.4\\s*.*"); + } + + public void testAddDependencyInTest() throws IOException { + VirtualFile f = createProjectSubFile("src/test/java/A.java", "import org.apache.commons.io.IOUtils;\n" + + "\n" + + "public class Aaa {\n" + + "\n" + + " public void xxx() {\n" + + " IOUtils u;\n" + + " }\n" + + "\n" + + "}"); + + importProject("test" + + "project" + + "1"); + + myFixture.configureFromExistingVirtualFile(f); + + IntentionAction intentionAction = findAddMavenIntention(); + + MavenArtifactSearchDialog.ourResultForTest = Collections.singletonList(new MavenId("commons-io", "commons-io", "2.4")); + + intentionAction.invoke(myProject, myFixture.getEditor(), myFixture.getFile()); + + String pomText = PsiManager.getInstance(myProject).findFile(myProjectPom).getText(); + assert pomText.matches( + "(?s).*\\s*commons-io\\s*commons-io\\s*2.4\\s*test\\s*.*"); + } + +// public void testAddDependencyStatic() throws IOException { +// VirtualFile f = createProjectSubFile("src/main/java/A.java", "import org.apache.commons.io.IOUtils;\n" + +// "\n" + +// "public class Aaa {\n" + +// "\n" + +// " public void xxx() {\n" + +// " IOUtils.closeQuietly(null);\n" + +// " }\n" + +// "\n" + +// "}"); +// +// importProject("test" + +// "project" + +// "1"); +// +// myFixture.configureFromExistingVirtualFile(f); +// +// getIntentionAtCaret("Add Maven"); +// } + +} diff --git a/plugins/ui-designer-core/src/com/intellij/designer/designSurface/OperationContext.java b/plugins/ui-designer-core/src/com/intellij/designer/designSurface/OperationContext.java index b3a423d1e4ab..f2e7d9aa3ed4 100644 --- a/plugins/ui-designer-core/src/com/intellij/designer/designSurface/OperationContext.java +++ b/plugins/ui-designer-core/src/com/intellij/designer/designSurface/OperationContext.java @@ -34,6 +34,7 @@ public final class OperationContext { private Object myType; private EditableArea myArea; + private RadComponent myContainer; private List myComponents; private InputEvent myInputEvent; private int myModifiers; @@ -102,6 +103,14 @@ public final class OperationContext { myArea = area; } + public RadComponent getContainer() { + return myContainer; + } + + public void setContainer(RadComponent container) { + myContainer = container; + } + public List getComponents() { return myComponents; } diff --git a/plugins/ui-designer-core/src/com/intellij/designer/designSurface/tools/TargetingTool.java b/plugins/ui-designer-core/src/com/intellij/designer/designSurface/tools/TargetingTool.java index f66b5b07f1b1..900fe2703ecb 100644 --- a/plugins/ui-designer-core/src/com/intellij/designer/designSurface/tools/TargetingTool.java +++ b/plugins/ui-designer-core/src/com/intellij/designer/designSurface/tools/TargetingTool.java @@ -155,7 +155,9 @@ public abstract class TargetingTool extends InputTool { RadLayout layout = target.getLayout(); if (layout != null) { + myContext.setContainer(target); myOperation = layout.processChildOperation(myContext); + myContext.setContainer(null); } return myOperation != null; diff --git a/python/IntelliLang-python/src/META-INF/intellilang-python-support.xml b/python/IntelliLang-python/src/META-INF/intellilang-python-support.xml index 2ffa9f7c2325..624d146a0378 100644 --- a/python/IntelliLang-python/src/META-INF/intellilang-python-support.xml +++ b/python/IntelliLang-python/src/META-INF/intellilang-python-support.xml @@ -7,5 +7,7 @@ + + \ No newline at end of file diff --git a/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyConfigurationInjector.java b/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyConfigurationInjector.java new file mode 100644 index 000000000000..f672f5d17ee6 --- /dev/null +++ b/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyConfigurationInjector.java @@ -0,0 +1,33 @@ +package com.jetbrains.python.intelliLang; + +import com.intellij.lang.Language; +import com.intellij.psi.PsiElement; +import com.jetbrains.python.codeInsight.PyInjectorBase; +import org.intellij.plugins.intelliLang.Configuration; +import org.intellij.plugins.intelliLang.inject.InjectedLanguage; +import org.intellij.plugins.intelliLang.inject.InjectorUtils; +import org.intellij.plugins.intelliLang.inject.LanguageInjectionSupport; +import org.intellij.plugins.intelliLang.inject.config.BaseInjection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author vlan + */ +public class PyConfigurationInjector extends PyInjectorBase { + @Nullable + @Override + public Language getInjectedLanguage(@NotNull PsiElement context) { + for (LanguageInjectionSupport support : InjectorUtils.getActiveInjectionSupports()) { + if (support instanceof PyLanguageInjectionSupport) { + final Configuration configuration = Configuration.getInstance(); + for (BaseInjection injection : configuration.getInjections(support.getId())) { + if (injection.acceptsPsiElement(context)) { + return InjectedLanguage.findLanguageById(injection.getInjectedLanguageId()); + } + } + } + } + return null; + } +} diff --git a/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyLanguageInjectionSupport.java b/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyLanguageInjectionSupport.java index 5a91db5ce994..43db1f20d814 100644 --- a/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyLanguageInjectionSupport.java +++ b/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyLanguageInjectionSupport.java @@ -15,22 +15,14 @@ */ package com.jetbrains.python.intelliLang; -import com.intellij.lang.Language; -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiElement; import com.intellij.psi.PsiLanguageInjectionHost; import com.jetbrains.python.patterns.PythonPatterns; import com.jetbrains.python.psi.PyElement; -import com.jetbrains.python.psi.PyStringLiteralExpression; import org.intellij.plugins.intelliLang.inject.AbstractLanguageInjectionSupport; -import org.intellij.plugins.intelliLang.inject.config.BaseInjection; -import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.List; - /** * @author yole */ @@ -54,33 +46,6 @@ public class PyLanguageInjectionSupport extends AbstractLanguageInjectionSupport return host instanceof PyElement; } - @Override - public boolean useDefaultInjector(PsiLanguageInjectionHost host) { - return true; - } - - @Override - public BaseInjection createInjection(Element element) { - // This is how DefaultLanguageInjector gets its injection ranges - return new BaseInjection(getId()) { - @NotNull - @Override - public List getInjectedArea(PsiElement element) { - if (element instanceof PyStringLiteralExpression) { - return ((PyStringLiteralExpression)element).getStringValueTextRanges(); - } - return super.getInjectedArea(element); - } - }; - } - - @Override - public boolean addInjectionInPlace(Language language, PsiLanguageInjectionHost psiElement) { - // XXX: Disable temporary injections via intention actions for Python elements, since TemporaryPlacesInjector cannot handle elements - // with multiple injection text ranges (PY-10691) - return true; - } - @Nullable @Override public String getHelpId() { diff --git a/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyTemporaryInjector.java b/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyTemporaryInjector.java new file mode 100644 index 000000000000..d88905d623cf --- /dev/null +++ b/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyTemporaryInjector.java @@ -0,0 +1,30 @@ +package com.jetbrains.python.intelliLang; + +import com.intellij.lang.Language; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiLanguageInjectionHost; +import com.jetbrains.python.codeInsight.PyInjectorBase; +import org.intellij.plugins.intelliLang.inject.InjectedLanguage; +import org.intellij.plugins.intelliLang.inject.TemporaryPlacesRegistry; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author vlan + */ +public class PyTemporaryInjector extends PyInjectorBase { + @Nullable + @Override + public Language getInjectedLanguage(@NotNull PsiElement context) { + final TemporaryPlacesRegistry registry = TemporaryPlacesRegistry.getInstance(context.getProject()); + if (context instanceof PsiLanguageInjectionHost) { + final PsiFile file = context.getContainingFile(); + final InjectedLanguage injectedLanguage = registry.getLanguageFor((PsiLanguageInjectionHost)context, file); + if (injectedLanguage != null) { + return injectedLanguage.getLanguage(); + } + } + return null; + } +} diff --git a/python/IntelliLang-python/src/pyInjections.xml b/python/IntelliLang-python/src/pyInjections.xml index e9ae34e1a348..a75015e02441 100644 --- a/python/IntelliLang-python/src/pyInjections.xml +++ b/python/IntelliLang-python/src/pyInjections.xml @@ -9,6 +9,6 @@ "SQL select/delete/insert/update/create" - + diff --git a/python/src/com/jetbrains/python/codeInsight/PyInjectionUtil.java b/python/src/com/jetbrains/python/codeInsight/PyInjectionUtil.java index d68e6c61c47f..7ce44b5531c9 100644 --- a/python/src/com/jetbrains/python/codeInsight/PyInjectionUtil.java +++ b/python/src/com/jetbrains/python/codeInsight/PyInjectionUtil.java @@ -23,6 +23,7 @@ import com.jetbrains.python.psi.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Arrays; import java.util.List; import static com.jetbrains.python.inspections.PyStringFormatParser.*; @@ -31,15 +32,22 @@ import static com.jetbrains.python.inspections.PyStringFormatParser.*; * @author vlan */ public class PyInjectionUtil { + public static final List> ELEMENTS_TO_INJECT_IN = + Arrays.asList(PyStringLiteralExpression.class, PyParenthesizedExpression.class, PyBinaryExpression.class, PyCallExpression.class); + private PyInjectionUtil() {} /** - * Returns true if the element is the largest expression that represents a string literal, possibly with concatenation, parentheses, - * or formatting. + * Returns the largest expression in the specified context that represents a string literal suitable for language injection, possibly + * with concatenation, parentheses, or formatting. */ - public static boolean isLargestStringLiteral(@NotNull PsiElement element) { - final PsiElement parent = element.getParent(); - return isStringLiteralPart(element) && (parent == null || !isStringLiteralPart(parent)); + @Nullable + public static PsiElement getLargestStringLiteral(@NotNull PsiElement context) { + PsiElement element = null; + for (PsiElement current = context; current != null && isStringLiteralPart(current); current = current.getParent()) { + element = current; + } + return element; } /** @@ -47,7 +55,7 @@ public class PyInjectionUtil { * string concatenations or formatting. */ public static void registerStringLiteralInjection(@NotNull PsiElement element, @NotNull MultiHostRegistrar registrar) { - processStringLiteral(element, registrar, "", "", Formatting.NONE); + processStringLiteral(element, registrar, "", "", Formatting.PERCENT); } private static boolean isStringLiteralPart(@NotNull PsiElement element) { @@ -100,9 +108,25 @@ public class PyInjectionUtil { final FormatStringChunk chunk = chunks.get(i); if (chunk instanceof ConstantChunk) { final int nextIndex = i + 1; - final String chunkPrefix = i == 1 && chunks.get(0) instanceof SubstitutionChunk ? missingValue : ""; - final String chunkSuffix = nextIndex < chunks.size() && - chunks.get(nextIndex) instanceof SubstitutionChunk ? missingValue : ""; + final String chunkPrefix; + if (i == 1 && chunks.get(0) instanceof SubstitutionChunk) { + chunkPrefix = missingValue; + } + else if (i == 0) { + chunkPrefix = prefix; + } else { + chunkPrefix = ""; + } + final String chunkSuffix; + if (nextIndex < chunks.size() && chunks.get(nextIndex) instanceof SubstitutionChunk) { + chunkSuffix = missingValue; + } + else if (nextIndex == chunks.size()) { + chunkSuffix = suffix; + } + else { + chunkSuffix = ""; + } final TextRange chunkRange = chunk.getTextRange().shiftRight(range.getStartOffset()); registrar.addPlace(chunkPrefix, chunkSuffix, expr, chunkRange); } diff --git a/python/src/com/jetbrains/python/codeInsight/PyInjectorBase.java b/python/src/com/jetbrains/python/codeInsight/PyInjectorBase.java new file mode 100644 index 000000000000..48b62368af3d --- /dev/null +++ b/python/src/com/jetbrains/python/codeInsight/PyInjectorBase.java @@ -0,0 +1,37 @@ +package com.jetbrains.python.codeInsight; + +import com.intellij.lang.Language; +import com.intellij.lang.injection.MultiHostInjector; +import com.intellij.lang.injection.MultiHostRegistrar; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * @author vlan + */ +public abstract class PyInjectorBase implements MultiHostInjector { + @Override + public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context) { + final Language language = getInjectedLanguage(context); + if (language != null) { + final PsiElement element = PyInjectionUtil.getLargestStringLiteral(context); + if (element != null) { + registrar.startInjecting(language); + PyInjectionUtil.registerStringLiteralInjection(element, registrar); + registrar.doneInjecting(); + } + } + } + + @NotNull + @Override + public List> elementsToInjectIn() { + return PyInjectionUtil.ELEMENTS_TO_INJECT_IN; + } + + @Nullable + public abstract Language getInjectedLanguage(@NotNull PsiElement context); +} diff --git a/python/src/com/jetbrains/python/codeInsight/regexp/PythonRegexpInjector.java b/python/src/com/jetbrains/python/codeInsight/regexp/PythonRegexpInjector.java index 39c9aff38410..bb23c475a576 100644 --- a/python/src/com/jetbrains/python/codeInsight/regexp/PythonRegexpInjector.java +++ b/python/src/com/jetbrains/python/codeInsight/regexp/PythonRegexpInjector.java @@ -67,7 +67,7 @@ public class PythonRegexpInjector implements MultiHostInjector { @Override public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context) { final PsiElement contextParent = context.getParent(); - if (PyInjectionUtil.isLargestStringLiteral(context) && contextParent instanceof PyArgumentList) { + if (PyInjectionUtil.getLargestStringLiteral(context) == context && contextParent instanceof PyArgumentList) { final PyExpression[] args = ((PyArgumentList)contextParent).getArguments(); int index = ArrayUtil.indexOf(args, context); PyCallExpression call = PsiTreeUtil.getParentOfType(context, PyCallExpression.class); diff --git a/xml/tests/src/com/intellij/codeInsight/SoftWrapTest.java b/xml/tests/src/com/intellij/codeInsight/SoftWrapTest.java new file mode 100644 index 000000000000..4de8e700def3 --- /dev/null +++ b/xml/tests/src/com/intellij/codeInsight/SoftWrapTest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight; + +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.impl.SoftWrapModelImpl; +import com.intellij.openapi.editor.impl.softwrap.mapping.SoftWrapApplianceManager; +import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase; + +/** + * @author Dmitry Avdeev + * Date: 25.11.13 + */ +public class SoftWrapTest extends LightPlatformCodeInsightFixtureTestCase { + + public void testSoftWrapInInjection() throws Exception { + + myFixture.configureByFile("softWrap.html"); + Editor editor = myFixture.getEditor(); + editor.getSettings().setUseSoftWraps(true); + ((SoftWrapModelImpl)editor.getSoftWrapModel()).getApplianceManager().setWidthProvider(new SoftWrapApplianceManager.VisibleAreaWidthProvider() { + @Override + public int getVisibleAreaWidth() { + return 600; + } + }); + + myFixture.type('j'); + myFixture.checkResultByFile("softWrap_after.html"); + } + + @Override + protected String getBasePath() { + return "/xml/tests/testData/softWrap"; + } + + @Override + protected boolean isCommunity() { + return true; + } +} diff --git a/xml/tests/testData/softWrap/softWrap.html b/xml/tests/testData/softWrap/softWrap.html new file mode 100644 index 000000000000..dfddbbf95281 --- /dev/null +++ b/xml/tests/testData/softWrap/softWrap.html @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/xml/tests/testData/softWrap/softWrap_after.html b/xml/tests/testData/softWrap/softWrap_after.html new file mode 100644 index 000000000000..7d5c77a43e9a --- /dev/null +++ b/xml/tests/testData/softWrap/softWrap_after.html @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file