Merge branch 'master' of git.labs.intellij.net:idea/community

This commit is contained in:
Maxim Medvedev
2011-04-08 19:49:32 +04:00
184 changed files with 4008 additions and 2166 deletions
+2 -2
View File
@@ -1,11 +1,11 @@
<component name="libraryTable">
<library name="Guava">
<CLASSES>
<root url="jar://$PROJECT_DIR$/lib/guava-r08.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/guava-r09.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$PROJECT_DIR$/lib/src/guava-src-r08.zip!/" />
<root url="jar://$PROJECT_DIR$/lib/src/guava-src-r09.zip!/" />
</SOURCES>
</library>
</component>
+3 -2
View File
@@ -150,7 +150,7 @@ libraryLicense(name: "Apache Lucene", libraryName: "lucene-core-2.4.1.jar", vers
libraryLicense(name: "Automaton", libraryName: "automaton.jar", version: "1.11", license: "BSD", url: "http://www.brics.dk/automaton/", licenseUrl: "http://www.opensource.org/licenses/bsd-license.php")
libraryLicense(name: "DTDParser", version: "1.13", license: "LGPL", url: "http://sourceforge.net/projects/dtdparser/", licenseUrl: "http://www.opensource.org/licenses/lgpl-2.1")
libraryLicense(name: "Ganymed", version: "bundled with SVNKit", libraryName: "svnkit.jar", license: "BSD", url: "http://www.ganymed.ethz.ch/ssh2/", licenseUrl: "http://www.ganymed.ethz.ch/ssh2/LICENSE.txt")
libraryLicense(name: "Guava", version: "R6", license: "Apache 2.0", url: "http://code.google.com/p/guava-libraries/", licenseUrl: "http://ant.apache.org/license.html")
libraryLicense(name: "Guava", version: "R8", license: "Apache 2.0", url: "http://code.google.com/p/guava-libraries/", licenseUrl: "http://ant.apache.org/license.html")
libraryLicense(name: "Gradle API", libraryName: "gradle-open-api-0.8.jar", version: "0.8", license: "Apache 2.0", url: "http://www.gradle.org/")
libraryLicense(name: "Groovy", version: "1.7.3", license: "Apache 2.0", url: "http://groovy.codehaus.org/")
libraryLicense(name: "Gson", libraryName: "gson-1.3.jar", license: "Apache 2.0", url: "http://code.google.com/p/google-gson/")
@@ -170,6 +170,7 @@ libraryLicense(name: "JUnit", libraryName: "JUnit4", version: "4.8", license: "C
libraryLicense(name: "Log4j", libraryName: "Log4J", version: "1.2", license: "Apache 2.0", url: "http://logging.apache.org/log4j/1.2/index.html", licenseUrl: "http://logging.apache.org/license.html")
libraryLicense(name: "Maven", version: "2.2.1", license: "Apache 2.0", url: "http://maven.apache.org/", licenseUrl: "http://maven.apache.org/license.html")
libraryLicense(name: "Maven3", libraryNames: ["Maven3", "maven-dependency-tree-1.2.jar"], version: "3.0.3", license: "Apache 2.0", url: "http://maven.apache.org/", licenseUrl: "http://maven.apache.org/license.html")
libraryLicense(name: "mercurial_prompthooks", attachedTo: "hg4idea", version: "", license: "GPLv2 (used as hg extension called from hg executable)", url: "https://github.com/willemv/mercurial_prompthooks", licenseUrl: "https://github.com/willemv/mercurial_prompthooks/blob/master/LICENSE.txt")
libraryLicense(name: "Microba", libraryName: "microba", version: "0.4.2", license: "BSD", url: "http://microba.sourceforge.net/", licenseUrl: "http://microba.sourceforge.net/license.txt")
libraryLicense(name: "NanoXML", version: "2.2.3", license: "zlib/libpng", url: "http://nanoxml.cyberelf.be/", licenseUrl: "http://devkix.com/nanoxml.php")
libraryLicense(name: "Eclipse JDT Core", libraryName: "Eclipse", version: "3.3", license: "CPL 1.0", url: "http://www.eclipse.org/jdt/core/index.php")
@@ -191,7 +192,7 @@ libraryLicense(name: "XML Commons (xml-apis.jar, resolver.jar)", version: "", li
libraryLicense(name: "XMLBeans", libraryName: "XmlBeans", version: "2.3.0", license: "Apache 2.0", url: "http://xmlbeans.apache.org/", licenseUrl: "http://svn.jetbrains.org/idea/Trunk/bundled/WebServices/resources/lib/xmlbeans-2.3.0/xmlbeans.LICENSE")
libraryLicense(name: "XML-RPC", libraryName: "XmlRPC", version: "2.0", license: "Apache 2.0", url: "http://ws.apache.org/xmlrpc/xmlrpc2/", licenseUrl: "http://ws.apache.org/xmlrpc/xmlrpc2/license.html")
libraryLicense(name: "XStream", version: "1.2.1", license: "BSD", url: "http://xstream.codehaus.org/", licenseUrl: "http://xstream.codehaus.org/license.html")
libraryLicense(name: "YourKit Java Profiler", libraryName: "yjp-controller-api-redist.jar", version: "7.0.2", license: "link (commercial license)", url: "http://yourkit.com/", licenseUrl: "http://www.yourkit.com/purchase/license.html")
libraryLicense(name: "YourKit Java Profiler", libraryName: "yjp-controller-api-redist.jar", version: "8.0.x", license: "link (commercial license)", url: "http://yourkit.com/", licenseUrl: "http://www.yourkit.com/purchase/license.html")
jetbrainsLibrary("JPS")
jetbrainsLibrary("Maven Embedder")
jetbrainsLibrary("tcServiceMessages")
@@ -0,0 +1,29 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.packaging.impl.elements;
import com.intellij.facet.Facet;
import com.intellij.packaging.elements.PackagingElementResolvingContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author nik
*/
public interface FacetBasedPackagingElement {
@Nullable
Facet findFacet(@NotNull PackagingElementResolvingContext context);
}
@@ -47,11 +47,6 @@ public class ArtifactElementPresentation extends TreeNodePresentation {
return findArtifact() != null;
}
@Override
public Object getSourceObject() {
return myArtifactPointer;
}
@Override
public void navigateToSource() {
final Artifact artifact = findArtifact();
@@ -57,11 +57,6 @@ public class DelegatedPackagingElementPresentation extends PackagingElementPrese
myDelegate.navigateToSource();
}
@Nullable
public Object getSourceObject() {
return myDelegate.getSourceObject();
}
public int getWeight() {
return myDelegate.getWeight();
}
@@ -57,11 +57,6 @@ public class LibraryElementPresentation extends PackagingElementPresentation {
return myLibrary != null;
}
@Override
public Object getSourceObject() {
return myLibrary;
}
@Override
public void navigateToSource() {
myContext.selectLibrary(myLibrary);
@@ -51,11 +51,6 @@ public class ModuleElementPresentation extends TreeNodePresentation {
return findModule() != null;
}
@Override
public Object getSourceObject() {
return findModule();
}
@Nullable
private Module findModule() {
return myModulePointer != null ? myModulePointer.getModule() : null;
@@ -45,6 +45,9 @@ public abstract class TreeNodePresentation {
public void navigateToSource() {
}
/**
* @deprecated this method is not used and will be removed in the future
*/
@Nullable
public Object getSourceObject() {
return null;
@@ -15,6 +15,7 @@
*/
package com.intellij.openapi.roots.ui.configuration.artifacts;
import com.intellij.facet.Facet;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
@@ -25,9 +26,11 @@ import com.intellij.packaging.impl.artifacts.ArtifactUtil;
import com.intellij.packaging.impl.artifacts.PackagingElementPath;
import com.intellij.packaging.impl.artifacts.PackagingElementProcessor;
import com.intellij.packaging.impl.elements.ArtifactPackagingElement;
import com.intellij.packaging.impl.elements.FacetBasedPackagingElement;
import com.intellij.packaging.impl.elements.LibraryPackagingElement;
import com.intellij.packaging.impl.elements.ModuleOutputPackagingElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
@@ -64,25 +67,10 @@ public class ArtifactProjectStructureElement extends ProjectStructureElement {
ArtifactUtil.processPackagingElements(myArtifactsStructureContext.getRootElement(artifact), null, new PackagingElementProcessor<PackagingElement<?>>() {
@Override
public boolean process(@NotNull PackagingElement<?> packagingElement, @NotNull PackagingElementPath path) {
if (packagingElement instanceof ModuleOutputPackagingElement) {
final Module module = ((ModuleOutputPackagingElement)packagingElement).findModule(myArtifactsStructureContext);
if (module != null) {
usages.add(createUsage(packagingElement, path, new ModuleProjectStructureElement(myContext, module)));
}
}
else if (packagingElement instanceof LibraryPackagingElement) {
final Library library = ((LibraryPackagingElement)packagingElement).findLibrary(myArtifactsStructureContext);
if (library != null) {
usages.add(createUsage(packagingElement, path,
new LibraryProjectStructureElement(ArtifactProjectStructureElement.this.myContext, library)));
}
}
else if (packagingElement instanceof ArtifactPackagingElement) {
final Artifact usedArtifact = ((ArtifactPackagingElement)packagingElement).findArtifact(myArtifactsStructureContext);
if (usedArtifact != null) {
final ArtifactProjectStructureElement artifactElement = myArtifactsStructureContext.getOrCreateArtifactElement(usedArtifact);
usages.add(createUsage(packagingElement, path, artifactElement));
}
ProjectStructureElement element = getProjectStructureElementFor(packagingElement, ArtifactProjectStructureElement.this.myContext,
ArtifactProjectStructureElement.this.myArtifactsStructureContext);
if (element != null) {
usages.add(createUsage(packagingElement, path, element));
}
return true;
}
@@ -90,6 +78,37 @@ public class ArtifactProjectStructureElement extends ProjectStructureElement {
return usages;
}
@Nullable
public static ProjectStructureElement getProjectStructureElementFor(PackagingElement<?> packagingElement,
final StructureConfigurableContext context,
final ArtifactsStructureConfigurableContext artifactsStructureContext) {
if (packagingElement instanceof ModuleOutputPackagingElement) {
final Module module = ((ModuleOutputPackagingElement)packagingElement).findModule(artifactsStructureContext);
if (module != null) {
return new ModuleProjectStructureElement(context, module);
}
}
else if (packagingElement instanceof LibraryPackagingElement) {
final Library library = ((LibraryPackagingElement)packagingElement).findLibrary(artifactsStructureContext);
if (library != null) {
return new LibraryProjectStructureElement(context, library);
}
}
else if (packagingElement instanceof ArtifactPackagingElement) {
final Artifact usedArtifact = ((ArtifactPackagingElement)packagingElement).findArtifact(artifactsStructureContext);
if (usedArtifact != null) {
return artifactsStructureContext.getOrCreateArtifactElement(usedArtifact);
}
}
else if (packagingElement instanceof FacetBasedPackagingElement) {
Facet facet = ((FacetBasedPackagingElement)packagingElement).findFacet(artifactsStructureContext);
if (facet != null) {
return new FacetProjectStructureElement(context, facet);
}
}
return null;
}
private UsageInArtifact createUsage(PackagingElement<?> packagingElement, PackagingElementPath path,
final ProjectStructureElement element) {
return new UsageInArtifact(myOriginalArtifact, myArtifactsStructureContext, element, this, path.getPathString(), packagingElement);
@@ -15,21 +15,11 @@
*/
package com.intellij.openapi.roots.ui.configuration.artifacts.actions;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactsStructureConfigurableContext;
import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.ArtifactsTreeNode;
import com.intellij.openapi.roots.ui.configuration.projectRoot.FindUsagesInProjectStructureActionBase;
import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.LibraryProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ModuleProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
import com.intellij.packaging.artifacts.Artifact;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.treeStructure.Tree;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
@@ -38,7 +28,7 @@ import java.awt.*;
*/
public abstract class ArtifactEditorFindUsagesActionBase extends FindUsagesInProjectStructureActionBase {
private final Tree myTree;
private final ArtifactsStructureConfigurableContext myArtifactContext;
protected final ArtifactsStructureConfigurableContext myArtifactContext;
public ArtifactEditorFindUsagesActionBase(Tree tree, Project project, ArtifactsStructureConfigurableContext artifactContext) {
super(tree, project);
@@ -47,33 +37,9 @@ public abstract class ArtifactEditorFindUsagesActionBase extends FindUsagesInPro
}
protected boolean isEnabled() {
ArtifactsTreeNode node = getSelectedNode();
return node != null && node.getElementPresentation().getSourceObject() != null;
return getSelectedElement() != null;
}
protected ProjectStructureElement getSelectedElement() {
ArtifactsTreeNode node = getSelectedNode();
if (node == null) {
return null;
}
final Object sourceObject = node.getElementPresentation().getSourceObject();
final StructureConfigurableContext context = ProjectStructureConfigurable.getInstance(getContext().getProject()).getContext();
if (sourceObject instanceof Module) {
return new ModuleProjectStructureElement(context, (Module)sourceObject);
}
else if (sourceObject instanceof Library) {
return new LibraryProjectStructureElement(context, (Library)sourceObject);
}
else if (sourceObject instanceof Artifact) {
return myArtifactContext.getOrCreateArtifactElement((Artifact)sourceObject);
}
return null;
}
@Nullable
protected abstract ArtifactsTreeNode getSelectedNode();
protected RelativePoint getPointToShowResults() {
final int selectedRow = myTree.getSelectionRows()[0];
final Rectangle rowBounds = myTree.getRowBounds(selectedRow);
@@ -16,10 +16,11 @@
package com.intellij.openapi.roots.ui.configuration.artifacts.actions;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactsStructureConfigurableContext;
import com.intellij.openapi.roots.ui.configuration.artifacts.LayoutTreeComponent;
import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.ArtifactsTreeNode;
import org.jetbrains.annotations.Nullable;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
import com.intellij.packaging.elements.PackagingElement;
/**
* @author nik
@@ -32,10 +33,10 @@ public class LayoutTreeFindUsagesAction extends ArtifactEditorFindUsagesActionBa
myLayoutTreeComponent = layoutTreeComponent;
}
@Override
@Nullable
protected ArtifactsTreeNode getSelectedNode() {
return myLayoutTreeComponent.getSelection().getNodeIfSingle();
}
protected ProjectStructureElement getSelectedElement() {
PackagingElement<?> packagingElement = myLayoutTreeComponent.getSelection().getElementIfSingle();
if (packagingElement == null) return null;
return ArtifactProjectStructureElement.getProjectStructureElementFor(packagingElement, getContext(), myArtifactContext);
}
}
@@ -57,11 +57,6 @@ public class DelegatedSourceItemPresentation extends SourceItemPresentation {
myPresentation.navigateToSource();
}
@Nullable
public Object getSourceObject() {
return myPresentation.getSourceObject();
}
public int getWeight() {
return myPresentation.getWeight();
}
@@ -90,11 +90,6 @@ public class LibrarySourceItem extends PackagingSourceItem {
myContext.selectLibrary(myLibrary);
}
@Override
public Object getSourceObject() {
return myLibrary;
}
@Override
public String getPresentableName() {
final String name = myLibrary.getName();
@@ -38,6 +38,10 @@ public class ModuleOutputSourceItem extends PackagingSourceItem {
myModule = module;
}
public Module getModule() {
return myModule;
}
public boolean equals(Object obj) {
return obj instanceof ModuleOutputSourceItem && myModule.equals(((ModuleOutputSourceItem)obj).myModule);
}
@@ -140,11 +140,6 @@ public class ModuleSourceItemGroup extends PackagingSourceItem {
myContext.selectModule(myModule);
}
@Override
public Object getSourceObject() {
return myModule;
}
@Override
public int getWeight() {
return SourceItemWeights.MODULE_WEIGHT;
@@ -47,7 +47,7 @@ public class SourceItemNode extends SourceItemNodeBase {
}
}
protected PackagingSourceItem getSourceItem() {
public PackagingSourceItem getSourceItem() {
return mySourceItem;
}
}
@@ -19,8 +19,12 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactsStructureConfigurableContext;
import com.intellij.openapi.roots.ui.configuration.artifacts.actions.ArtifactEditorFindUsagesActionBase;
import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.ArtifactsTreeNode;
import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.SourceItemNode;
import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.SourceItemsTree;
import com.intellij.openapi.roots.ui.configuration.artifacts.sourceItems.*;
import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.LibraryProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ModuleProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
import com.intellij.packaging.ui.PackagingSourceItem;
import java.util.List;
@@ -35,9 +39,27 @@ public class SourceItemFindUsagesAction extends ArtifactEditorFindUsagesActionBa
myTree = tree;
}
@Override
protected ArtifactsTreeNode getSelectedNode() {
protected ProjectStructureElement getSelectedElement() {
final List<SourceItemNode> nodes = myTree.getSelectedSourceItemNodes();
return nodes.size() == 1 ? nodes.get(0) : null;
if (nodes.size() != 1) return null;
ArtifactsTreeNode node = nodes.get(0);
if (!(node instanceof SourceItemNode)) {
return null;
}
PackagingSourceItem sourceItem = ((SourceItemNode)node).getSourceItem();
if (sourceItem == null) return null;
final StructureConfigurableContext context = getContext();
if (sourceItem instanceof ModuleOutputSourceItem) {
return new ModuleProjectStructureElement(context, ((ModuleOutputSourceItem)sourceItem).getModule());
}
else if (sourceItem instanceof LibrarySourceItem) {
return new LibraryProjectStructureElement(context, ((LibrarySourceItem)sourceItem).getLibrary());
}
else if (sourceItem instanceof ArtifactSourceItem) {
return myArtifactContext.getOrCreateArtifactElement(((ArtifactSourceItem)sourceItem).getArtifact());
}
return null;
}
}
@@ -23,7 +23,8 @@ import com.intellij.facet.impl.ui.FacetEditorImpl;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator;
import com.intellij.openapi.ui.NamedConfigurable;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.FacetProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
@@ -33,16 +34,18 @@ import javax.swing.*;
/**
* @author nik
*/
public class FacetConfigurable extends NamedConfigurable<Facet> {
public class FacetConfigurable extends ProjectStructureElementConfigurable<Facet> {
private final Facet myFacet;
private final ModulesConfigurator myModulesConfigurator;
private String myFacetName;
private FacetProjectStructureElement myProjectStructureElement;
public FacetConfigurable(final Facet facet, final ModulesConfigurator modulesConfigurator, final Runnable updateTree) {
public FacetConfigurable(final Facet facet, final StructureConfigurableContext context, final Runnable updateTree) {
super(!facet.getType().isOnlyOneFacetAllowed() && !(facet instanceof InvalidFacet), updateTree);
myFacet = facet;
myModulesConfigurator = modulesConfigurator;
myModulesConfigurator = context.getModulesConfigurator();
myFacetName = myFacet.getName();
myProjectStructureElement = new FacetProjectStructureElement(context, facet);
}
@@ -54,6 +57,11 @@ public class FacetConfigurable extends NamedConfigurable<Facet> {
}
}
@Override
public ProjectStructureElement getProjectStructureElement() {
return myProjectStructureElement;
}
private ProjectFacetsConfigurator getFacetsConfigurator() {
return myModulesConfigurator.getFacetsConfigurator();
}
@@ -87,7 +87,7 @@ public class FacetEditorFacadeImpl implements FacetEditorFacade {
public FacetConfigurable getOrCreateConfigurable(final Facet facet) {
FacetConfigurable configurable = myConfigurables.get(facet);
if (configurable == null) {
configurable = new FacetConfigurable(facet, myStructureConfigurable.getContext().getModulesConfigurator(), myTreeUpdater);
configurable = new FacetConfigurable(facet, myStructureConfigurable.getContext(), myTreeUpdater);
myConfigurables.put(facet, configurable);
}
return configurable;
@@ -36,6 +36,8 @@ import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.FacetProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
import com.intellij.openapi.ui.DetailsComponent;
import com.intellij.openapi.ui.NamedConfigurable;
import com.intellij.openapi.util.IconLoader;
@@ -99,6 +101,19 @@ public class FacetStructureConfigurable extends BaseStructureConfigurable {
}
}
@NotNull
@Override
protected Collection<? extends ProjectStructureElement> getProjectStructureElements() {
List<ProjectStructureElement> elements = new ArrayList<ProjectStructureElement>();
for (Module module : myModuleManager.getModules()) {
Facet[] facets = FacetManager.getInstance(module).getAllFacets();
for (Facet facet : facets) {
elements.add(new FacetProjectStructureElement(myContext, facet));
}
}
return elements;
}
public MyNode findFacetTypeNode(FacetType facetType) {
return findNodeByObject(myRoot, facetType);
}
@@ -106,6 +121,7 @@ public class FacetStructureConfigurable extends BaseStructureConfigurable {
public void addFacetNode(@NotNull MyNode facetTypeNode, @NotNull Facet facet, @NotNull FacetEditorFacadeImpl editorFacade) {
FacetConfigurable facetConfigurable = editorFacade.getOrCreateConfigurable(facet);
addNode(new FacetConfigurableNode(facetConfigurable), facetTypeNode);
myContext.getDaemonAnalyzer().queueUpdate(new FacetProjectStructureElement(myContext, facet));
}
@Nullable
@@ -177,6 +193,9 @@ public class FacetStructureConfigurable extends BaseStructureConfigurable {
protected List<Facet> removeFacet(final Facet facet) {
List<Facet> removed = super.removeFacet(facet);
ModuleStructureConfigurable.getInstance(myProject).removeFacetNodes(removed);
for (Facet removedFacet : removed) {
myContext.getDaemonAnalyzer().removeElement(new FacetProjectStructureElement(myContext, removedFacet));
}
return removed;
}
@@ -0,0 +1,64 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.roots.ui.configuration.projectRoot.daemon;
import com.intellij.facet.Facet;
import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
/**
* @author nik
*/
public class FacetProjectStructureElement extends ProjectStructureElement {
private final Facet myFacet;
public FacetProjectStructureElement(@NotNull StructureConfigurableContext context, @NotNull Facet facet) {
super(context);
myFacet = facet;
}
@Override
public void check(ProjectStructureProblemsHolder problemsHolder) {
}
@Override
public List<ProjectStructureElementUsage> getUsagesInElement() {
return Collections.emptyList();
}
@Override
public boolean highlightIfUnused() {
return false;
}
@Override
public boolean equals(Object obj) {
return obj instanceof FacetProjectStructureElement && myFacet.equals(((FacetProjectStructureElement)obj).myFacet);
}
@Override
public int hashCode() {
return myFacet.hashCode();
}
@Override
public String toString() {
return "facet:" + myFacet.getName() + " in " + myFacet.getModule().getName();
}
}
@@ -74,7 +74,7 @@ public class NullableStuffInspection extends BaseLocalInspectionTool {
final PsiParameterList parameterList = psiMethod.getParameterList();
final PsiParameter[] parameters = parameterList.getParameters();
final PsiExpression[] expressions = expression.getArgumentList().getExpressions();
for (int i = 0, expressionsLength = expressions.length; i < Math.max(expressionsLength, parameters.length); i++) {
for (int i = 0, expressionsLength = expressions.length; i < Math.min(expressionsLength, parameters.length); i++) {
PsiExpression psiExpression = expressions[i];
if (psiExpression.getType() == PsiType.NULL) {
if (!AnnotationUtil.isNullable(parameters[i]) && !AnnotationUtil.isNotNull(parameters[i])) {
@@ -52,12 +52,12 @@ class AnchorElementInfo extends SelfElementInfo {
@Nullable
public PsiElement restoreElement() {
if (stubId != -1) {
PsiFile file = SelfElementInfo.restoreFileFromVirtual(myVirtualFile, myProject);
PsiFile file = SelfElementInfo.restoreFileFromVirtual(getVirtualFile(), myProject);
if (!(file instanceof PsiFileWithStubSupport)) return null;
return PsiAnchor.restoreFromStubIndex((PsiFileWithStubSupport)file, stubId, myStubElementType);
}
if (!mySyncMarkerIsValid) return null;
PsiFile file = SelfElementInfo.restoreFileFromVirtual(myVirtualFile, myProject);
PsiFile file = SelfElementInfo.restoreFileFromVirtual(getVirtualFile(), myProject);
if (file == null) return null;
PsiElement anchor = file.findElementAt(getSyncStartOffset());
if (anchor == null) return null;
@@ -77,7 +77,7 @@ public class ImplicitVariableElementInfoFactory implements SmartPointerElementIn
}
@Override
public boolean pointsToTheSameElementAs(SmartPointerElementInfo other) {
public boolean pointsToTheSameElementAs(@NotNull SmartPointerElementInfo other) {
if (other instanceof ImplicitVariableInfo) {
return myVar == ((ImplicitVariableInfo)other).myVar;
}
@@ -19,7 +19,13 @@ import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.intention.impl.TypeExpression;
import com.intellij.codeInsight.template.impl.TemplateManagerImpl;
import com.intellij.codeInsight.template.impl.TemplateState;
import com.intellij.ide.ui.ListCellRendererWrapper;
import com.intellij.ide.ui.UISettings;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CustomShortcutSet;
import com.intellij.openapi.actionSystem.KeyboardShortcut;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.editor.Editor;
@@ -28,6 +34,7 @@ import com.intellij.openapi.editor.ScrollType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
@@ -42,11 +49,11 @@ import com.intellij.refactoring.ui.TypeSelectorManagerImpl;
import com.intellij.refactoring.util.occurences.OccurenceManager;
import com.intellij.ui.StateRestoringCheckBox;
import com.intellij.ui.TitlePanel;
import com.intellij.util.VisibilityUtil;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
@@ -62,7 +69,7 @@ public class InplaceIntroduceConstantPopup {
private PsiExpression myExpr;
private final PsiLocalVariable myLocalVariable;
private final PsiExpression[] myOccurrences;
private final TypeSelectorManagerImpl myTypeSelectorManager;
private TypeSelectorManagerImpl myTypeSelectorManager;
private PsiElement myAnchorElement;
private int myAnchorIdx = -1;
private PsiElement myAnchorElementIfAll;
@@ -83,7 +90,7 @@ public class InplaceIntroduceConstantPopup {
private JCheckBox myMoveToAnotherClassCb;
private JavaVisibilityPanel myVisibilityPanel;
private JComboBox myVisibilityCombo;
private JPanel myWholePanel;
@@ -122,31 +129,36 @@ public class InplaceIntroduceConstantPopup {
myLocalName = localVariable != null ? localVariable.getName() : null;
myWholePanel = new JPanel(new GridBagLayout());
GridBagConstraints gc = new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0 , 0, 0), 0, 0);
GridBagConstraints gc = new GridBagConstraints(0, 0, 1, 1, 1, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0 , 0, 0), 0, 0);
final TitlePanel titlePanel = new TitlePanel();
titlePanel.setBorder(null);
titlePanel.setText(IntroduceConstantHandler.REFACTORING_NAME);
gc.gridwidth = 2;
myWholePanel.add(titlePanel, gc);
gc.insets = new Insets(5, 5, 5, 5);
gc.gridwidth = 1;
gc.gridy = 1;
myWholePanel.add(createLeftPanel(), gc);
myVisibilityPanel = new JavaVisibilityPanel(false, false);
myVisibilityPanel.setVisibility(JavaRefactoringSettings.getInstance().INTRODUCE_CONSTANT_VISIBILITY);
myWholePanel.add(myVisibilityPanel, gc);
gc.gridx = 1;
gc.insets.left = 6;
myWholePanel.add(createRightPanel(), gc);
}
private JPanel createRightPanel() {
final JPanel right = new JPanel(new GridBagLayout());
final GridBagConstraints rgc = new GridBagConstraints(0, GridBagConstraints.RELATIVE,1,1,0,0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(1,0,0,0), 0, 0);
myReplaceAllCb = new JCheckBox("Replace all occurrences");
myReplaceAllCb.setMnemonic('a');
myReplaceAllCb.setFocusable(false);
myWholePanel.add(myReplaceAllCb, gc);
myReplaceAllCb.setVisible(myOccurrences.length > 1);
right.add(myReplaceAllCb, rgc);
myCbDeleteVariable = new StateRestoringCheckBox("Delete variable declaration");
myCbDeleteVariable.setMnemonic('d');
myCbDeleteVariable.setFocusable(false);
myWholePanel.add(myCbDeleteVariable, gc);
if (myLocalVariable != null) {
if (myReplaceAllCb != null) {
myReplaceAllCb.setEnabled(false);
@@ -157,15 +169,15 @@ public class InplaceIntroduceConstantPopup {
} else {
myCbDeleteVariable.setVisible(false);
}
right.add(myCbDeleteVariable, rgc);
myAnnotateNonNls = new JCheckBox("Annotate field as @NonNls");
myAnnotateNonNls.setMnemonic('f');
myAnnotateNonNls.setFocusable(false);
myWholePanel.add(myAnnotateNonNls, gc);
final PsiManager psiManager = PsiManager.getInstance(myProject);
if ((myTypeSelectorManager.isSuggestedType("java.lang.String") || (myLocalVariable != null && AnnotationUtil.isAnnotated(myLocalVariable, AnnotationUtil.NON_NLS, false)))&&
LanguageLevelProjectExtension.getInstance(psiManager.getProject()).getLanguageLevel().hasEnumKeywordAndAutoboxing() &&
JavaPsiFacade.getInstance(psiManager.getProject()).findClass(AnnotationUtil.NON_NLS, myParentClass.getResolveScope()) != null) {
if ((myTypeSelectorManager.isSuggestedType("java.lang.String") || (myLocalVariable != null && AnnotationUtil
.isAnnotated(myLocalVariable, AnnotationUtil.NON_NLS, false)))&&
LanguageLevelProjectExtension.getInstance(myProject).getLanguageLevel().hasEnumKeywordAndAutoboxing() &&
JavaPsiFacade.getInstance(myProject).findClass(AnnotationUtil.NON_NLS, myParentClass.getResolveScope()) != null) {
final PropertiesComponent component = PropertiesComponent.getInstance(myProject);
myAnnotateNonNls.setSelected(component.isTrueValue(IntroduceConstantDialog.NONNLS_SELECTED_PROPERTY));
myAnnotateNonNls.addItemListener(new ItemListener() {
@@ -176,13 +188,69 @@ public class InplaceIntroduceConstantPopup {
} else {
myAnnotateNonNls.setVisible(false);
}
right.add(myAnnotateNonNls, rgc);
return right;
}
private JPanel createLeftPanel() {
final JPanel left = new JPanel(new GridBagLayout());
final GridBagConstraints lgc = new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(5,5,0,0), 0, 0);
final JLabel label = new JLabel("Visibility:");
label.setDisplayedMnemonic('V');
left.add(label, lgc);
myVisibilityCombo = new JComboBox(new String[]{PsiModifier.PUBLIC, PsiModifier.PACKAGE_LOCAL, PsiModifier.PROTECTED, PsiModifier.PRIVATE});
myVisibilityCombo.setRenderer(new ListCellRendererWrapper<String>(myVisibilityCombo.getRenderer()){
@Override
public void customize(JList list, String value, int index, boolean selected, boolean hasFocus) {
setText(PsiBundle.visibilityPresentation(value));
}
});
label.setLabelFor(myVisibilityCombo);
myVisibilityCombo.setSelectedItem(JavaRefactoringSettings.getInstance().INTRODUCE_CONSTANT_VISIBILITY);
myVisibilityCombo.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
ToolWindowManager.getInstance(myProject).activateEditorComponent();
}
}
});
final AnAction arrow = new AnAction() {
@Override
public void actionPerformed(AnActionEvent e) {
if (e.getInputEvent() instanceof KeyEvent) {
final int code = ((KeyEvent)e.getInputEvent()).getKeyCode();
final int delta = code == KeyEvent.VK_DOWN ? 1 : code == KeyEvent.VK_UP ? -1 : 0;
if (delta == 0) return;
final int size = myVisibilityCombo.getModel().getSize();
int next = myVisibilityCombo.getSelectedIndex() + delta;
if (next < 0 || next >= size) {
if (!UISettings.getInstance().CYCLE_SCROLLING) {
return;
}
next = (next + size) % size;
}
myVisibilityCombo.setSelectedIndex(next);
}
}
};
arrow.registerCustomShortcutSet(new CustomShortcutSet(new KeyboardShortcut(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), null),
new KeyboardShortcut(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), null)), myVisibilityCombo);
lgc.gridx = 1;
lgc.insets.top = 2;
lgc.insets.left = 0;
left.add(myVisibilityCombo, lgc);
myMoveToAnotherClassCb = new JCheckBox("Move to another class");
myMoveToAnotherClassCb.setMnemonic('m');
myMoveToAnotherClassCb.setFocusable(false);
myWholePanel.add(myMoveToAnotherClassCb, gc);
lgc.gridx = 0;
lgc.gridy = 1;
lgc.gridwidth = 2;
lgc.insets.top = 0;
left.add(myMoveToAnotherClassCb, lgc);
return left;
}
public void performInplaceIntroduce() {
@@ -220,7 +288,7 @@ public class InplaceIntroduceConstantPopup {
PsiField field = elementFactory.createFieldFromText(psiType.getCanonicalText() + " " + (myConstantName != null ? myConstantName : names[0]) + " = " + myExprText + ";", myParentClass);
PsiUtil.setModifierProperty(field, PsiModifier.FINAL, true);
PsiUtil.setModifierProperty(field, PsiModifier.STATIC, true);
final String visibility = myVisibilityPanel.getVisibility();
final String visibility = getSelectedVisibility();
if (visibility != null) {
PsiUtil.setModifierProperty(field, visibility, true);
}
@@ -230,6 +298,10 @@ public class InplaceIntroduceConstantPopup {
});
}
private String getSelectedVisibility() {
return (String)myVisibilityCombo.getSelectedItem();
}
public List<RangeMarker> getOccurrenceMarkers() {
if (myOccurrenceMarkers == null) {
myOccurrenceMarkers = new ArrayList<RangeMarker>();
@@ -301,7 +373,7 @@ public class InplaceIntroduceConstantPopup {
@Override
protected void saveSettings(PsiVariable psiVariable) {
TypeSelectorManagerImpl.typeSelected(psiVariable.getType(), myDefaultParameterTypePointer.getType());
JavaRefactoringSettings.getInstance().INTRODUCE_CONSTANT_VISIBILITY = myVisibilityPanel.getVisibility();
JavaRefactoringSettings.getInstance().INTRODUCE_CONSTANT_VISIBILITY = getSelectedVisibility();
}
@Override
@@ -321,7 +393,7 @@ public class InplaceIntroduceConstantPopup {
isReplaceAllOccurrences(), true,
true,
BaseExpressionToFieldHandler.InitializationPlace.IN_FIELD_DECLARATION,
myVisibilityPanel.getVisibility(), myLocalVariable,
getSelectedVisibility(), myLocalVariable,
myFieldTypePointer.getType(),
isDeleteVariable(),
myParentClass, isAnnotateNonNls(), false);
@@ -358,10 +430,16 @@ public class InplaceIntroduceConstantPopup {
protected JComponent getComponent() {
if (!myInitListeners) {
myInitListeners = true;
myVisibilityPanel.addListener(new VisibilityListener(myProject, myEditor){
final VisibilityListener visibilityListener = new VisibilityListener(myProject, myEditor) {
@Override
protected String getVisibility() {
return myVisibilityPanel.getVisibility();
return getSelectedVisibility();
}
};
myVisibilityCombo.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
visibilityListener.stateChanged(null);
}
});
myReplaceAllCb.addItemListener(new ItemListener() {
@@ -370,6 +448,7 @@ public class InplaceIntroduceConstantPopup {
final TemplateState templateState = TemplateManagerImpl.getTemplateState(myEditor);
if (templateState != null) {
templateState.gotoEnd(true);
myTypeSelectorManager = new TypeSelectorManagerImpl(myProject, myFieldTypePointer.getType(), null, myExpr, myOccurrences);
startIntroduceTemplate(isReplaceAllOccurrences());
}
}
@@ -59,7 +59,7 @@ public class InplaceIntroduceFieldPopup {
private final Editor myEditor;
private Project myProject;
private final TypeSelectorManagerImpl myTypeSelectorManager;
private TypeSelectorManagerImpl myTypeSelectorManager;
private final PsiElement myAnchorElement;
private int myAnchorIdx = -1;
private final PsiElement myAnchorElementIfAll;
@@ -136,16 +136,7 @@ public class InplaceIntroduceFieldPopup {
myWholePanel.add(myIntroduceFieldPanel.createCenterPanel(), gc);
myIntroduceFieldPanel.initializeControls(initializerExpression, ourLastInitializerPlace);
myIntroduceFieldPanel.addOccurrenceListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
final TemplateState templateState = TemplateManagerImpl.getTemplateState(myEditor);
if (templateState != null) {
templateState.gotoEnd(true);
startTemplate(myIntroduceFieldPanel.isReplaceAllOccurrences());
}
}
});
}
@@ -298,6 +289,17 @@ public class InplaceIntroduceFieldPopup {
finalListener.perform(myIntroduceFieldPanel.isDeclareFinal());
}
});
myIntroduceFieldPanel.addOccurrenceListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
final TemplateState templateState = TemplateManagerImpl.getTemplateState(myEditor);
if (templateState != null) {
templateState.gotoEnd(true);
myTypeSelectorManager = new TypeSelectorManagerImpl(myProject, myFieldTypePointer.getType(), null, myInitializerExpression, myOccurrences);
startTemplate(myIntroduceFieldPanel.isReplaceAllOccurrences());
}
}
});
}
return myWholePanel;
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -10,7 +10,7 @@ commons-logging-1.1.1.jar
commons-net-2.0-patched.jar
dtdparser113.jar
groovy-all-1.7.3.jar
guava-r08.jar
guava-r09.jar
jaxen-1.1.3.jar
jcip-annotations.jar
jdom.jar
Binary file not shown.
Binary file not shown.
@@ -16,6 +16,7 @@
package com.intellij.psi;
import com.intellij.openapi.util.Segment;
import org.jetbrains.annotations.Nullable;
/**
* pointer to a PsiFile + range inside the file.
@@ -25,5 +26,6 @@ public interface SmartPsiFileRange extends SmartPsiElementPointer<PsiFile> {
/**
* @return the range inside the PsiFile, or null if the range or PsiFile became invalid
*/
@Nullable
Segment getRange();
}
@@ -97,11 +97,11 @@ public abstract class ElementBase extends UserDataHolderBase implements Iconable
baseIcon = myBaseIcon.get(flags);
}
if (!psiElement.isValid()) return null;
if (isToDeferIconLoading()) {
return IconDeferrer.getInstance().defer(baseIcon, new ElementIconRequest(psiElement, flags), ICON_COMPUTE);
}
else {
if (!psiElement.isValid()) return null;
return computeIconNow(psiElement, flags);
}
}
@@ -160,7 +160,7 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware
int newY = start.y;
SoftWrapModel softWrapModel = editor.getSoftWrapModel();
int lineHeight = editor.getLineHeight();
for (int i = startLine + lineShift; i < endLine && newY < maxY; i++) {
for (int i = Math.max(0, startLine + lineShift); i < endLine && newY < maxY; i++) {
List<? extends SoftWrap> softWraps = softWrapModel.getSoftWrapsForLine(i);
int logicalLineHeight = softWraps.size() * lineHeight;
if (i > startLine + lineShift) {
@@ -28,6 +28,7 @@ import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.editor.ScrollType;
@@ -42,8 +43,10 @@ import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import static com.intellij.codeInsight.editorActions.JoinLinesHandlerDelegate.CANNOT_JOIN;
@@ -160,7 +163,7 @@ public class JoinLinesHandler extends EditorWriteActionHandler {
if (rc != CANNOT_JOIN) break;
}
}
docManager.doPostponedOperationsAndUnblockDocument(doc);
doPostponedOperationsAndUnblockDocument(docManager, doc);
if (rc != CANNOT_JOIN) {
if (caretRestoreOffset == CANNOT_JOIN) caretRestoreOffset = rc;
@@ -209,7 +212,7 @@ public class JoinLinesHandler extends EditorWriteActionHandler {
}
if (prevLineCount < doc.getLineCount()) {
docManager.doPostponedOperationsAndUnblockDocument(doc);
doPostponedOperationsAndUnblockDocument(docManager, doc);
end = doc.getLineEndOffset(startLine) + doc.getLineSeparatorLength(startLine);
start = end - doc.getLineSeparatorLength(startLine);
int addedLinesCount = doc.getLineCount() - prevLineCount - 1;
@@ -235,6 +238,19 @@ public class JoinLinesHandler extends EditorWriteActionHandler {
}
}
private static void doPostponedOperationsAndUnblockDocument(@NotNull PsiDocumentManager docManager, @NotNull DocumentEx document) {
boolean restore = CodeEditUtil.isSuspendedNodesReformattingAllowed();
CodeEditUtil.setAllowSuspendNodesReformatting(false);
try {
docManager.doPostponedOperationsAndUnblockDocument(document);
}
finally {
if (restore) {
CodeEditUtil.setAllowSuspendNodesReformatting(true);
}
}
}
private static boolean isCommentElement(final PsiElement element) {
return element != null && PsiTreeUtil.getParentOfType(element, PsiComment.class, false) != null;
}
@@ -45,8 +45,11 @@ class FoldingUtil {
}
public static FoldRegion findFoldRegionStartingAtLine(Editor editor, int line){
FoldRegion[] regions = editor.getFoldingModel().getAllFoldRegions();
FoldRegion result = null;
if (line >= editor.getDocument().getLineCount()) {
return result;
}
FoldRegion[] regions = editor.getFoldingModel().getAllFoldRegions();
for (FoldRegion region : regions) {
if (region.getDocument().getLineNumber(region.getStartOffset()) == line) {
if (result != null) return null;
@@ -0,0 +1,604 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.execution.impl;
import com.intellij.execution.filters.HyperlinkInfo;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import gnu.trove.TIntArrayList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import static com.intellij.execution.impl.ConsoleViewImpl.HyperlinkTokenInfo;
import static com.intellij.execution.impl.ConsoleViewImpl.TokenInfo;
/**
* IJ user may want the console to use cyclic buffer, i.e. don't keep more than particular amount of symbols. So, we need
* to have a data structure that allow to achieve that. This class serves for that purpose.
* <p/>
* Not thread-safe.
* <p/>
* <b>Note:</b> basically this class consists of functionality that is cut from {@link ConsoleViewImpl} in order to make it possible
* to cover it by tests.
*
* @author Denis Zhdanov
* @since 4/5/11 5:26 PM
*/
public class ConsoleBuffer {
private static final int DEFAULT_CYCLIC_BUFFER_UNIT_SIZE = 256;
private static final boolean DEBUG_PROCESSING = false;
/**
* Buffer for deferred stdout, stderr and stdin output.
* <p/>
* Feel free to check rationale for using this approach at {@link #myCyclicBufferSize} contract.
*/
private final Deque<StringBuilder> myDeferredOutput = new ArrayDeque<StringBuilder>();
private final Set<ConsoleViewContentType> myContentTypesToNotStripOnCycling = new HashSet<ConsoleViewContentType>();
/**
* Main console usage scenario assumes the following:
* <pre>
* <ul>
* <li>
* console may be {@link ConsoleViewImpl#print(String, ConsoleViewContentType) provided} with the new text from any thread
* (e.g. separate thread is charged for reading output of java application launched under IJ. That output is provided
* to the console);
* </li>
* <li>current class flushes provided text to {@link Editor editor} used for representing it to end-user from EDT;</li>
* <li>
* dedicated buffer is kept to hold console text between the moment when it's provided to the current class
* and flush to the editor;</li>
* </ul>
* </pre>
* <p/>
* It's also possible to configure console to use cyclic buffer in order to avoid unnecessary memory consumption.
* However, that implies possibility of the following situation - console user provides it with the great number
* of small chunks of text (that is the case for junit processing). It's inappropriate to use single {@link StringBuilder} as
* a buffer then because every time we see that cyclic buffer size is exceeded and we need to cut exceeding text from buffer
* start, trailing part is moved to the zero offset. That produces extensive CPU usage in case of great number of small messages
* where every such message exceeds cyclic buffer size.
* <p/>
* That is the reason why we use data structure similar to STL deque here - we hold number of string buffers of small size instead
* of the single big buffer. That means that every 'cut at the start' operation requires much less number of trailing symbols
* to be moved. Current constant defines default size of that small buffers.
*/
private final int myCyclicBufferSize;
private final int myCyclicBufferUnitSize;
private final boolean myUseCyclicBuffer;
/**
* Holds information about number of symbols stored at {@link #myDeferredOutput} collection.
*/
private int myDeferredOutputLength;
/** Buffer for deferred stdin and stderr output. */
private StringBuffer myDeferredUserInput = new StringBuffer();
/**
* Holds information about lexical division by offsets of the text that is not yet pushed to document.
* <p/>
* Target offsets are anchored to the {@link #myDeferredOutput deferred buffer}.
*/
private final List<TokenInfo> myDeferredTokens = new ArrayList<TokenInfo>();
private final Set<ConsoleViewContentType> myDeferredTypes = new HashSet<ConsoleViewContentType>();
public ConsoleBuffer() {
this(useCycleBuffer(), getCycleBufferSize(), DEFAULT_CYCLIC_BUFFER_UNIT_SIZE);
}
public ConsoleBuffer(boolean useCyclicBuffer, int cyclicBufferSize, int cyclicBufferUnitSize) {
myUseCyclicBuffer = useCyclicBuffer;
myCyclicBufferSize = cyclicBufferSize;
myCyclicBufferUnitSize = cyclicBufferUnitSize;
myContentTypesToNotStripOnCycling.add(ConsoleViewContentType.USER_INPUT);
}
private static boolean useCycleBuffer() {
final String useCycleBufferProperty = System.getProperty("idea.cycle.buffer.size");
return useCycleBufferProperty == null || !"disabled".equalsIgnoreCase(useCycleBufferProperty);
}
private static int getCycleBufferSize() {
final String cycleBufferSizeProperty = System.getProperty("idea.cycle.buffer.size");
if (cycleBufferSizeProperty == null) return 1024 * 1024;
try {
return Integer.parseInt(cycleBufferSizeProperty) * 1024;
}
catch (NumberFormatException e) {
return 1024 * 1024;
}
}
public boolean isUseCyclicBuffer() {
return myUseCyclicBuffer;
}
public int getCyclicBufferSize() {
return myCyclicBufferSize;
}
public boolean isEmpty() {
return myDeferredOutput.isEmpty() || (myDeferredOutput.size() == 1 && myDeferredOutput.getFirst().length() <= 0);
}
public int getLength() {
return myDeferredOutputLength;
}
public int getUserInputLength() {
return myDeferredUserInput.length();
}
public String getUserInput() {
return myDeferredUserInput.toString();
}
public List<TokenInfo> getDeferredTokens() {
return myDeferredTokens;
}
public Set<ConsoleViewContentType> getDeferredTokenTypes() {
return myDeferredTypes;
}
public Deque<StringBuilder> getDeferredOutput() {
return myDeferredOutput;
}
public String getText() {
if (myDeferredOutput.size() > 1) {
final StringBuilder buffer = new StringBuilder();
for (StringBuilder builder : myDeferredOutput) {
buffer.append(builder);
}
return buffer.toString();
}
else if (myDeferredOutput.size() == 1) {
return myDeferredOutput.getFirst().substring(0);
}
else {
return "";
}
}
/**
* This buffer automatically strips text that exceeds {@link #getCycleBufferSize() cyclic buffer size}. However, we may want
* to avoid 'significant text' stripping, i.e. don't strip the text of particular type.
* <p/>
* {@link ConsoleViewContentType#USER_INPUT} is considered to be such a type by default, however, it's possible to overwrite that
* via the current method.
*
* @param types content types that should not be stripped during the buffer's cycling
*/
public void setContentTypesToNotStripOnCycling(@NotNull Collection<ConsoleViewContentType> types) {
myContentTypesToNotStripOnCycling.clear();
myContentTypesToNotStripOnCycling.addAll(types);
}
public void clear() {
if (myUseCyclicBuffer) {
myDeferredOutput.clear();
myDeferredOutput.add(new StringBuilder(myCyclicBufferUnitSize));
}
else {
for (StringBuilder builder : myDeferredOutput) {
builder.setLength(0);
}
}
myDeferredOutputLength = 0;
myDeferredTypes.clear();
myDeferredUserInput = new StringBuffer();
myDeferredTokens.clear();
}
@Nullable
public String cutFirstUserInputLine() {
final String text = myDeferredUserInput.substring(0, myDeferredUserInput.length());
final int index = Math.max(text.lastIndexOf('\n'), text.lastIndexOf('\r'));
if (index < 0) {
return null;
}
final String result = text.substring(0, index + 1);
myDeferredUserInput.setLength(0);
myDeferredUserInput.append(text.substring(index + 1));
return result;
}
public void addUserText(int offset, String text) {
myDeferredUserInput.insert(offset, text);
}
public void removeUserText(int startOffset, int endOffset) {
myDeferredUserInput.delete(startOffset, endOffset);
}
public void replaceUserText(int startOffset, int endOffset, String text) {
myDeferredUserInput.replace(startOffset, endOffset, text);
}
/**
* Asks current buffer to store given text of the given type.
*
* @param s text to store
* @param contentType type of the given text
* @param info hyperlink info for the given text (if any)
* @return text that is actually stored (there is a possible case that the buffer is full and given text's type
* is considered to have lower priority than the stored one, hence, it's better to drop given text completely
* or partially) and number of existed symbols removed during storing the given data
*/
@NotNull
public Pair<String, Integer> print(@NotNull String s, @NotNull ConsoleViewContentType contentType, @Nullable HyperlinkInfo info) {
int numberOfSymbolsToProceed = s.length();
int trimmedSymbolsNumber = myDeferredOutputLength;
if (contentType != ConsoleViewContentType.USER_INPUT) {
numberOfSymbolsToProceed = trimDeferredOutputIfNecessary(s.length());
trimmedSymbolsNumber -= myDeferredOutputLength;
}
else {
trimmedSymbolsNumber = 0;
}
if (numberOfSymbolsToProceed <= 0) {
return new Pair<String, Integer>("", 0);
}
if (numberOfSymbolsToProceed < s.length()) {
s = s.substring(s.length() - numberOfSymbolsToProceed);
}
myDeferredTypes.add(contentType);
s = StringUtil.convertLineSeparators(s, !SystemInfo.isMac);
myDeferredOutputLength += s.length();
StringBuilder bufferToUse;
if (myDeferredOutput.isEmpty()) {
myDeferredOutput.add(bufferToUse = new StringBuilder(myCyclicBufferUnitSize));
}
else {
bufferToUse = myDeferredOutput.getLast();
}
int offset = 0;
while (offset < s.length()) {
if (bufferToUse.length() >= myCyclicBufferUnitSize) {
myDeferredOutput.add(bufferToUse = new StringBuilder(myCyclicBufferUnitSize));
}
if (bufferToUse.length() < myCyclicBufferUnitSize) {
int numberOfSymbolsToAdd = Math.min(myCyclicBufferUnitSize - bufferToUse.length(), s.length() - offset);
bufferToUse.append(s.substring(offset, offset + numberOfSymbolsToAdd));
offset += numberOfSymbolsToAdd;
}
}
if (contentType == ConsoleViewContentType.USER_INPUT) {
myDeferredUserInput.append(s);
}
ConsoleUtil.addToken(s.length(), info, contentType, myDeferredTokens);
return new Pair<String, Integer>(s, trimmedSymbolsNumber);
}
//private void checkState() {
// int bufferOffset = 0;
// Iterator<StringBuilder> iterator = myDeferredOutput.iterator();
// StringBuilder currentBuffer = null;
// int prevTokenEnd = 0;
// for (TokenInfo token : myDeferredTokens) {
// if (prevTokenEnd != token.startOffset) {
// try {
// System.out.println("Problem detected!");
// System.in.read();
// }
// catch (IOException e) {
// e.printStackTrace();
// }
// }
// prevTokenEnd = token.endOffset;
// char c = token.contentType == ConsoleViewContentType.ERROR_OUTPUT ? '2' : '1';
// int length = token.getLength();
// if (currentBuffer == null) {
// currentBuffer = iterator.next();
// }
//
// while (length > 0) {
// if (bufferOffset == currentBuffer.length()) {
// if (!iterator.hasNext()) {
// try {
// System.out.println("Problem detected!");
// System.in.read();
// }
// catch (IOException e) {
// e.printStackTrace();
// }
// }
// currentBuffer = iterator.next();
// bufferOffset = 0;
// }
// else {
// int endOffset = Math.min(bufferOffset + length, currentBuffer.length());
// if (token.contentType == ConsoleViewContentType.NORMAL_OUTPUT || token.contentType == ConsoleViewContentType.ERROR_OUTPUT) {
// for (int i = bufferOffset; i < endOffset; i++) {
// char c1 = currentBuffer.charAt(i);
// if (c1 != c && c1 != '\n') {
// try {
// System.out.println("Problem detected!");
// System.in.read();
// }
// catch (IOException e) {
// e.printStackTrace();
// }
// }
// }
// }
// length -= endOffset - bufferOffset;
// bufferOffset = endOffset;
// }
// }
// }
//}
/**
* IJ console works as follows - it receives managed process outputs from dedicated thread that serves that process and
* pushes it to the {@link Document document} of editor used to represent process console. Important point here is that process
* output is received in a control flow of the thread over than EDT but push to the document is performed from EDT. Hence, we
* have a potential situation when particular process outputs a lot and EDT is busy or push to the document is performed slowly.
* <p/>
* We don't want to keep too many information from the underlying process then and want to trim text buffer that holds text
* to push to the document then. Current method serves exactly that purpose, i.e. it's expected to be called when new chunk of
* text is received from the underlying process and trims existing text buffer if necessary.
*
* @param numberOfNewSymbols number of symbols read from the managed process output
* @return number of newly read symbols that should be accepted
*/
@SuppressWarnings({"ForLoopReplaceableByForEach"})
private int trimDeferredOutputIfNecessary(final int numberOfNewSymbols) {
if (!myUseCyclicBuffer || myDeferredOutputLength + numberOfNewSymbols <= myCyclicBufferSize) {
return numberOfNewSymbols;
}
final int numberOfSymbolsToRemove = Math.min(myDeferredOutputLength, myDeferredOutputLength + numberOfNewSymbols - myCyclicBufferSize);
myDeferredTypes.clear();
if (DEBUG_PROCESSING) {
log("Starting console trimming. Need to delete %d symbols (deferred output length: %d, number of new symbols: %d, "
+ "cyclic buffer size: %d). Current state:",
numberOfSymbolsToRemove, myDeferredOutputLength, numberOfNewSymbols, myCyclicBufferSize
);
dumpDeferredOutput();
}
Context context = new Context(numberOfSymbolsToRemove);
TIntArrayList indicesOfTokensToRemove = new TIntArrayList();
for (int i = 0; i < myDeferredTokens.size(); i++) {
TokenInfo tokenInfo = myDeferredTokens.get(i);
tokenInfo.startOffset -= context.removedSymbolsNumber;
tokenInfo.endOffset -= context.removedSymbolsNumber;
if (!context.canContinueProcessing()) {
// Just update token offsets.
myDeferredTypes.add(tokenInfo.contentType);
if (context.removedSymbolsNumber == 0) {
break;
}
continue;
}
int tokenLength = tokenInfo.getLength();
// Don't remove input text.
if (myContentTypesToNotStripOnCycling.contains(tokenInfo.contentType)) {
skip(context, tokenLength);
myDeferredTypes.add(tokenInfo.contentType);
continue;
}
int removedTokenSymbolsNumber = remove(context, tokenLength);
if (removedTokenSymbolsNumber == tokenLength) {
indicesOfTokensToRemove.add(i);
}
else {
tokenInfo.endOffset -= removedTokenSymbolsNumber;
myDeferredTypes.add(tokenInfo.contentType);
}
}
for (int i = indicesOfTokensToRemove.size() - 1; i >= 0; i--) {
myDeferredTokens.remove(indicesOfTokensToRemove.get(i));
}
if (!myDeferredTokens.isEmpty()) {
TokenInfo tokenInfo = myDeferredTokens.get(0);
if (tokenInfo.startOffset > 0) {
final HyperlinkInfo hyperlinkInfo = tokenInfo.getHyperlinkInfo();
myDeferredTokens.add(0, hyperlinkInfo != null ? new HyperlinkTokenInfo(ConsoleViewContentType.USER_INPUT, 0, tokenInfo.startOffset, hyperlinkInfo)
: new TokenInfo(ConsoleViewContentType.USER_INPUT, 0, tokenInfo.startOffset));
myDeferredTypes.add(ConsoleViewContentType.USER_INPUT);
}
}
if (numberOfNewSymbols + myDeferredOutputLength > myCyclicBufferSize) {
int result = myCyclicBufferSize - myDeferredOutputLength;
if (result < 0) {
return 0;
}
return result;
}
return numberOfNewSymbols;
}
private static void skip(@NotNull Context context, int symbolsToSkipNumber) {
int remainingNumberOfBufferSymbols = context.currentBuffer.length() - context.bufferOffset;
if (remainingNumberOfBufferSymbols < symbolsToSkipNumber) {
symbolsToSkipNumber -= remainingNumberOfBufferSymbols;
while (context.iterator.hasNext()) {
context.currentBuffer = context.iterator.next();
context.bufferOffset = 0;
if (DEBUG_PROCESSING) {
log("Switching to the next buffer. Number of token symbols to skip: %d", symbolsToSkipNumber);
}
if (symbolsToSkipNumber <= 0) {
break;
}
if (context.currentBuffer.length() > symbolsToSkipNumber) {
context.bufferOffset = symbolsToSkipNumber;
symbolsToSkipNumber = 0;
break;
}
else {
symbolsToSkipNumber -= context.currentBuffer.length();
}
}
assert symbolsToSkipNumber <= 0;
}
else {
context.bufferOffset += symbolsToSkipNumber;
if (DEBUG_PROCESSING) {
log("All symbols to skip are processed. Current buffer offset is %d, text: '%s'", context.bufferOffset, context.currentBuffer);
}
}
}
private int remove(@NotNull Context context, int tokenLength) {
int removedSymbolsNumber = 0;
int remainingTotalNumberOfSymbolsToRemove = context.numberOfSymbolsToRemove - context.removedSymbolsNumber;
int numberOfTokenSymbolsToRemove = Math.min(remainingTotalNumberOfSymbolsToRemove, tokenLength);
while (numberOfTokenSymbolsToRemove > 0 && context.currentBuffer != null) {
int diff = numberOfTokenSymbolsToRemove - (context.currentBuffer.length() - context.bufferOffset);
int endDeleteBufferOffset = Math.min(context.bufferOffset + numberOfTokenSymbolsToRemove, context.currentBuffer.length());
int numberOfSymbolsRemovedFromCurrentBuffer = endDeleteBufferOffset - context.bufferOffset;
if (DEBUG_PROCESSING) {
log("About to delete %d symbols from the current buffer (offset is %d). Removed symbols number: %d. Current buffer: %d: '%s'",
numberOfSymbolsRemovedFromCurrentBuffer, context.bufferOffset, context.removedSymbolsNumber, context.currentBuffer.length(),
StringUtil.convertLineSeparators(context.currentBuffer.toString()));
}
numberOfTokenSymbolsToRemove -= numberOfSymbolsRemovedFromCurrentBuffer;
removedSymbolsNumber += numberOfSymbolsRemovedFromCurrentBuffer;
context.removedSymbolsNumber += numberOfSymbolsRemovedFromCurrentBuffer;
myDeferredOutputLength -= numberOfSymbolsRemovedFromCurrentBuffer;
if (context.bufferOffset == 0 && (diff >= 0 || endDeleteBufferOffset == context.currentBuffer.length())) {
context.iterator.remove();
context.nextBuffer();
}
else {
context.currentBuffer.delete(context.bufferOffset, endDeleteBufferOffset);
if (DEBUG_PROCESSING) {
log("Removed symbols at range [%d; %d). Buffer offset: %d, buffer length: %d, text: '%s'",
context.bufferOffset, endDeleteBufferOffset, context.bufferOffset, context.currentBuffer.length(), context.currentBuffer);
}
if (context.bufferOffset == context.currentBuffer.length()) {
context.nextBuffer();
}
}
}
return removedSymbolsNumber;
}
private final class Context {
public final int numberOfSymbolsToRemove;
public StringBuilder currentBuffer;
public Iterator<StringBuilder> iterator;
public int bufferOffset;
public int removedSymbolsNumber;
Context(int numberOfSymbolsToRemove) {
this.numberOfSymbolsToRemove = numberOfSymbolsToRemove;
iterator = myDeferredOutput.iterator();
if (iterator.hasNext()) {
currentBuffer = iterator.next();
}
else {
currentBuffer = null;
}
}
public boolean canContinueProcessing() {
return removedSymbolsNumber < numberOfSymbolsToRemove && currentBuffer != null;
}
public boolean nextBuffer() {
if (iterator.hasNext()) {
currentBuffer = iterator.next();
bufferOffset = 0;
return true;
}
return false;
}
}
@SuppressWarnings({"PointlessBooleanExpression", "ConstantConditions"})
private void dumpDeferredOutput() {
if (!DEBUG_PROCESSING) {
return;
}
log("Tokens:");
for (TokenInfo token : myDeferredTokens) {
log("\t" + token);
}
log("Data:");
for (StringBuilder buffer : myDeferredOutput) {
log("\t%d: '%s'", buffer.length(), StringUtil.convertLineSeparators(buffer.toString()));
}
log("-----------------------------------------------------------------------------------------------------");
}
@SuppressWarnings({"UnusedDeclaration", "CallToPrintStackTrace"})
private static void log(Object o) {
//try {
// doLog(o);
//}
//catch (Exception e) {
// e.printStackTrace();
//}
}
@SuppressWarnings({"UnusedDeclaration", "CallToPrintStackTrace"})
private static void log(String message, Object... formatData) {
//try {
// doLog(String.format(message, formatData));
//}
//catch (Exception e) {
// e.printStackTrace();
//}
}
//private static BufferedWriter myWriter;
//private static void doLog(Object o) throws Exception {
// if (!DEBUG_PROCESSING) {
// return;
// }
// File file = new File("/home/denis/log/console.log");
// if (myWriter == null || !file.exists()) {
// myWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
// }
// myWriter.write(o.toString());
// myWriter.newLine();
// myWriter.flush();
//}
}
@@ -0,0 +1,54 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.execution.impl;
import static com.intellij.execution.impl.ConsoleViewImpl.TokenInfo;
import static com.intellij.execution.impl.ConsoleViewImpl.HyperlinkTokenInfo;
import com.intellij.execution.filters.HyperlinkInfo;
import com.intellij.execution.ui.ConsoleViewContentType;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* Holds utility methods for console processing.
*
* @author Denis Zhdanov
* @since 4/6/11 3:50 PM
*/
public class ConsoleUtil {
private ConsoleUtil() {
}
public static void addToken(int length, @Nullable HyperlinkInfo info, ConsoleViewContentType contentType, List<TokenInfo> tokens) {
int startOffset = 0;
if (!tokens.isEmpty()) {
final TokenInfo lastToken = tokens.get(tokens.size() - 1);
if (lastToken.contentType == contentType && info == lastToken.getHyperlinkInfo()) {
lastToken.endOffset += length; // optimization
return;
}
else {
startOffset = lastToken.endOffset;
}
}
tokens.add(info != null ? new HyperlinkTokenInfo(contentType, startOffset, startOffset + length, info)
: new TokenInfo(contentType, startOffset, startOffset + length));
}
}
@@ -68,7 +68,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.LineTokenizer;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.Navigatable;
@@ -102,6 +102,7 @@ import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;
public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableConsoleView, DataProvider, OccurenceNavigator {
private @NonNls String CONSOLE_VIEW_POPUP_MENU = "ConsoleView.PopupMenu";
private static final Logger LOG = Logger.getInstance("#com.intellij.execution.impl.ConsoleViewImpl");
@@ -115,35 +116,6 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
typedAction.setupHandler(new MyTypedHandler(typedAction.getHandler()));
}
private final int CYCLIC_BUFFER_SIZE = getCycleBufferSize();
/**
* Main console usage scenario assumes the following:
* <pre>
* <ul>
* <li>
* console may be {@link #print(String, ConsoleViewContentType) provided} with the new text from any thread
* (e.g. separate thread is charged for reading output of java application launched under IJ. That output is provided
* to the console);
* </li>
* <li>current class flushes provided text to {@link Editor editor} used for representing it to end-user from EDT;</li>
* <li>
* dedicated buffer is kept to hold console text between the moment when it's provided to the current class
* and flush to the editor;</li>
* </ul>
* </pre>
* <p/>
* It's also possible to configure console to use cyclic buffer in order to avoid unnecessary memory consumption.
* However, that implies possibility of the following situation - console user provides it with the great number
* of small chunks of text (that is the case for junit processing). It's inappropriate to use single {@link StringBuilder} as
* a buffer then because every time we see that cyclic buffer size is exceeded and we need to cut exceeding text from buffer
* start, trailing part is moved to the zero offset. That produces extensive CPU usage in case of great number of small messages
* where every such message exceeds cyclic buffer size.
* <p/>
* That is the reason why we use data structure similar to STL deque here - we hold number of string buffers of small size instead
* of the single big buffer. That means that every 'cut at the start' operation requires much less number of trailing symbols
* to be moved. Current constant defines desired size of that small buffers.
*/
private static final int CYCLIC_BUFFER_UNIT_SIZE = 256;
private final CommandLineFolding myCommandLineFolding = new CommandLineFolding();
private final DisposedPsiManagerCheck myPsiDisposedCheck;
@@ -153,30 +125,12 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
private Computable<ModalityState> myStateForUpdate;
private static int getCycleBufferSize() {
final String cycleBufferSizeProperty = System.getProperty("idea.cycle.buffer.size");
if (cycleBufferSizeProperty == null) return 1024 * 1024;
try {
return Integer.parseInt(cycleBufferSizeProperty) * 1024;
}
catch (NumberFormatException e) {
return 1024 * 1024;
}
}
private final boolean USE_CYCLIC_BUFFER = useCycleBuffer();
private static boolean useCycleBuffer() {
final String useCycleBufferProperty = System.getProperty("idea.cycle.buffer.size");
return useCycleBufferProperty == null || !"disabled".equalsIgnoreCase(useCycleBufferProperty);
}
private static final int HYPERLINK_LAYER = HighlighterLayer.SELECTION - 123;
private final Alarm mySpareTimeAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, this);
private final CopyOnWriteArraySet<ChangeListener> myListeners = new CopyOnWriteArraySet<ChangeListener>();
private final Set<ConsoleViewContentType> myDeferredTypes = new HashSet<ConsoleViewContentType>();
private final ArrayList<AnAction> customActions = new ArrayList<AnAction>();
private final ConsoleBuffer myBuffer = new ConsoleBuffer();
@TestOnly
public Editor getEditor() {
@@ -211,10 +165,10 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
updateFoldings(0, myEditor.getDocument().getLineCount() - 1, true);
}
private static class TokenInfo {
private final ConsoleViewContentType contentType;
private int startOffset;
private int endOffset;
static class TokenInfo {
final ConsoleViewContentType contentType;
int startOffset;
int endOffset;
private final TextAttributes attributes;
TokenInfo(final ConsoleViewContentType contentType, final int startOffset, final int endOffset) {
@@ -239,7 +193,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
}
}
private static class HyperlinkTokenInfo extends TokenInfo {
static class HyperlinkTokenInfo extends TokenInfo {
private HyperlinkInfo myHyperlinkInfo;
HyperlinkTokenInfo(final ConsoleViewContentType contentType, final int startOffset, final int endOffset, HyperlinkInfo hyperlinkInfo) {
@@ -268,21 +222,6 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
*/
private int myContentSize;
/**
* Buffer for deferred stdout, stderr and stdin output.
* <p/>
* Feel free to check rationale for using this approach at {@link #CYCLIC_BUFFER_UNIT_SIZE} contract.
*/
private final Deque<StringBuilder> myDeferredOutput = new ArrayDeque<StringBuilder>();
/**
* Holds information about number of symbols stored at {@link #myDeferredOutput} collection.
*/
private int myDeferredOutputLength;
/** Buffer for deferred stdin and stderr output. */
private StringBuffer myDeferredUserInput = new StringBuffer();
/**
* Holds information about lexical division by offsets of the text already pushed to document.
* <p/>
@@ -290,13 +229,6 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
*/
private ArrayList<TokenInfo> myTokens = new ArrayList<TokenInfo>();
/**
* Holds information about lexical division by offsets of the text that is not yet pushed to document.
* <p/>
* Target offsets are anchored to the {@link #myDeferredOutput deferred buffer}.
*/
private final List<TokenInfo> myDeferredTokens = new ArrayList<TokenInfo>();
private final Hyperlinks myHyperlinks = new Hyperlinks();
private final TIntObjectHashMap<ConsoleFolding> myFolding = new TIntObjectHashMap<ConsoleFolding>();
@@ -416,14 +348,8 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
// code and alarm clear request (in EDT) my occur print event in non-EDT thread
// and unfortunately it is a real usecase and happens when switching active test in
// tests console.
myContentSize = Math.max(0, myContentSize - myDeferredOutputLength);
myDeferredOutput.clear();
myDeferredOutput.add(new StringBuilder(CYCLIC_BUFFER_UNIT_SIZE));
myDeferredOutputLength = 0;
myDeferredTypes.clear();
myDeferredUserInput = new StringBuffer();
myDeferredTokens.clear();
myContentSize = Math.max(0, myContentSize - myBuffer.getLength());
myBuffer.clear();
}
myFlushAlarm.cancelAllRequests();
myFlushAlarm.addRequest(myClearRequest, 0, getStateForUpdate());
@@ -435,7 +361,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
flushDeferredText(false);
if (myEditor == null) return;
int moveOffset = offset;
if (USE_CYCLIC_BUFFER && moveOffset >= myEditor.getDocument().getTextLength()) {
if (myBuffer.isUseCyclicBuffer() && moveOffset >= myEditor.getDocument().getTextLength()) {
moveOffset = 0;
}
myEditor.getCaretModel().moveToOffset(moveOffset);
@@ -462,7 +388,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
public boolean hasDeferredOutput() {
synchronized (LOCK) {
return myDeferredOutputLength > 0;
return myBuffer.getLength() > 0;
}
}
@@ -531,9 +457,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
mySpareTimeAlarm.cancelAllRequests();
disposeEditor();
synchronized (LOCK) {
myDeferredOutput.clear();
myDeferredOutput.add(new StringBuilder());
myDeferredOutputLength = 0;
myBuffer.clear();
}
myEditor = null;
}
@@ -551,44 +475,9 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
private void printHyperlink(String s, ConsoleViewContentType contentType, HyperlinkInfo info) {
synchronized (LOCK) {
int numberOfSymbolsToProceed = s.length();
if (contentType != ConsoleViewContentType.USER_INPUT) {
numberOfSymbolsToProceed = trimDeferredOutputIfNecessary(s.length());
}
if (numberOfSymbolsToProceed <= 0) {
return;
}
if (numberOfSymbolsToProceed < s.length()) {
s = s.substring(s.length() - numberOfSymbolsToProceed);
}
myDeferredTypes.add(contentType);
s = StringUtil.convertLineSeparators(s, !SystemInfo.isMac);
myContentSize += s.length();
myDeferredOutputLength += s.length();
StringBuilder bufferToUse;
if (myDeferredOutput.isEmpty()) {
myDeferredOutput.add(bufferToUse = new StringBuilder(s.length()));
}
else {
StringBuilder lastBuffer = myDeferredOutput.getLast();
bufferToUse = lastBuffer;
if (USE_CYCLIC_BUFFER) {
if (lastBuffer.length() + s.length() > CYCLIC_BUFFER_UNIT_SIZE) {
myDeferredOutput.addLast(bufferToUse = new StringBuilder(CYCLIC_BUFFER_UNIT_SIZE));
}
}
}
bufferToUse.append(s);
if (contentType == ConsoleViewContentType.USER_INPUT) {
myDeferredUserInput.append(s);
}
addToken(s.length(), info, contentType, myDeferredTokens);
Pair<String,Integer> pair = myBuffer.print(s, contentType, info);
s = pair.first;
myContentSize += s.length() - pair.second;
if (s.indexOf('\n') >= 0 || s.indexOf('\r') >= 0) {
if (contentType == ConsoleViewContentType.USER_INPUT) {
@@ -596,146 +485,19 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
}
}
if (myFlushAlarm.getActiveRequestCount() == 0 && myEditor != null && !myFlushAlarm.isDisposed()) {
final boolean shouldFlushNow = USE_CYCLIC_BUFFER && myDeferredOutputLength > CYCLIC_BUFFER_SIZE;
final boolean shouldFlushNow = myBuffer.isUseCyclicBuffer() && myBuffer.getLength() >= myBuffer.getCyclicBufferSize();
myFlushAlarm.addRequest(myFlushDeferredRunnable, shouldFlushNow ? 0 : FLUSH_DELAY, getStateForUpdate());
}
}
}
/**
* IJ console works as follows - it receives managed process outputs from dedicated thread that serves that process and
* pushes it to the {@link Document document} of editor used to represent process console. Important point here is that process
* output is received in a control flow of the thread over than EDT but push to the document is performed from EDT. Hence, we
* have a potential situation when particular process outputs a lot and EDT is busy or push to the document is performed slowly.
* <p/>
* We don't want to keep too many information from the underlying process then and want to trim text buffer that holds text
* to push to the document then. Current method serves exactly that purpose, i.e. it's expected to be called when new chunk of
* text is received from the underlying process and trims existing text buffer if necessary.
*
* @param numberOfNewSymbols number of symbols read from the managed process output
* @return number of newly read symbols that should be accepted
*/
@SuppressWarnings({"ForLoopReplaceableByForEach"})
private int trimDeferredOutputIfNecessary(final int numberOfNewSymbols) {
if (!USE_CYCLIC_BUFFER || myDeferredOutputLength <= 0 || myDeferredOutputLength + numberOfNewSymbols <= CYCLIC_BUFFER_SIZE) {
return numberOfNewSymbols;
}
final int numberOfSymbolsToRemove = myDeferredOutputLength + numberOfNewSymbols - CYCLIC_BUFFER_SIZE;
myDeferredTypes.clear();
int removedSymbolsNumber = 0;
int endIndex = -1;
Iterator<StringBuilder> iterator = myDeferredOutput.iterator();
StringBuilder currentBuffer = iterator.next();
int bufferOffset = 0;
for (int i = 0; i < myDeferredTokens.size(); i++) {
TokenInfo tokenInfo = myDeferredTokens.get(i);
if (removedSymbolsNumber < numberOfSymbolsToRemove) {
int tokenLength = tokenInfo.getLength();
// Don't remove input text.
if (tokenInfo.contentType == ConsoleViewContentType.USER_INPUT) {
tokenInfo.startOffset -= removedSymbolsNumber;
tokenInfo.endOffset -= removedSymbolsNumber;
removedSymbolsNumber += tokenLength;
bufferOffset += tokenLength;
continue;
}
int remainingNumberOfSymbolsToRemove = numberOfSymbolsToRemove - removedSymbolsNumber;
int numberOfSymbolsToRemoveNow = Math.min(remainingNumberOfSymbolsToRemove, tokenLength);
while (numberOfSymbolsToRemoveNow > 0) {
int diff = numberOfSymbolsToRemoveNow - currentBuffer.length() - bufferOffset;
int endDeleteOffset = Math.min(bufferOffset + numberOfSymbolsToRemoveNow, currentBuffer.length());
int numberOfSymbolsRemovedFromCurrentBuffer = endDeleteOffset - bufferOffset;
numberOfSymbolsToRemoveNow -= numberOfSymbolsRemovedFromCurrentBuffer;
removedSymbolsNumber += numberOfSymbolsRemovedFromCurrentBuffer;
myDeferredOutputLength -= numberOfSymbolsRemovedFromCurrentBuffer;
currentBuffer.delete(bufferOffset, endDeleteOffset);
if (diff >= 0) {
iterator.remove();
if (iterator.hasNext()) {
currentBuffer = iterator.next();
}
else if (numberOfSymbolsToRemove > 0) {
assert false;
}
bufferOffset = 0;
}
}
if (remainingNumberOfSymbolsToRemove < tokenLength) {
tokenInfo.startOffset += remainingNumberOfSymbolsToRemove;
endIndex = i;
}
else {
endIndex = i + 1;
}
}
if (removedSymbolsNumber < numberOfSymbolsToRemove) {
continue;
}
tokenInfo.startOffset -= numberOfSymbolsToRemove;
tokenInfo.endOffset -= numberOfSymbolsToRemove;
myDeferredTypes.add(tokenInfo.contentType);
}
if (endIndex < 0) {
myDeferredTokens.clear();
}
else if (endIndex > 0) {
myDeferredTokens.subList(0, endIndex).clear();
}
if (!myDeferredTokens.isEmpty()) {
TokenInfo tokenInfo = myDeferredTokens.get(0);
if (tokenInfo.startOffset > 0) {
final HyperlinkInfo hyperlinkInfo = tokenInfo.getHyperlinkInfo();
myDeferredTokens.add(0, hyperlinkInfo != null ? new HyperlinkTokenInfo(ConsoleViewContentType.USER_INPUT, 0, tokenInfo.startOffset, hyperlinkInfo)
: new TokenInfo(ConsoleViewContentType.USER_INPUT, 0, tokenInfo.startOffset));
myDeferredTypes.add(ConsoleViewContentType.USER_INPUT);
}
}
myContentSize -= removedSymbolsNumber;
if (numberOfNewSymbols + myDeferredOutputLength > CYCLIC_BUFFER_SIZE) {
int result = CYCLIC_BUFFER_SIZE - myDeferredOutputLength;
if (result < 0) {
return 0;
}
return result;
}
return numberOfNewSymbols;
}
protected void beforeExternalAddContentToDocument(int length, ConsoleViewContentType contentType) {
myContentSize+=length;
addToken(length, null, contentType);
}
private void addToken(int length, @Nullable HyperlinkInfo info, ConsoleViewContentType contentType) {
addToken(length, info, contentType, myTokens);
}
private static void addToken(int length, @Nullable HyperlinkInfo info, ConsoleViewContentType contentType, List<TokenInfo> tokens) {
int startOffset = 0;
if (!tokens.isEmpty()) {
final TokenInfo lastToken = tokens.get(tokens.size() - 1);
if (lastToken.contentType == contentType && info == lastToken.getHyperlinkInfo()) {
lastToken.endOffset += length; // optimization
return;
}
else {
startOffset = lastToken.endOffset;
}
}
tokens.add(info != null ? new HyperlinkTokenInfo(contentType, startOffset, startOffset + length, info)
: new TokenInfo(contentType, startOffset, startOffset + length));
ConsoleUtil.addToken(length, info, contentType, myTokens);
}
private ModalityState getStateForUpdate() {
@@ -783,50 +545,29 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
}, null, DocCommandGroupId.noneGroupId(document));
}
final StringBuilder buffer = new StringBuilder();
final String text;
final Collection<ConsoleViewContentType> contentTypes;
int deferredTokensSize;
synchronized (LOCK) {
if (myOutputPaused) return;
if (myDeferredOutput.isEmpty() || (myDeferredOutput.size() == 1 && myDeferredOutput.getFirst().length() <= 0)) return;
if (myBuffer.isEmpty()) return;
if (myEditor == null) return;
if (myDeferredOutput.size() > 1) {
for (StringBuilder builder : myDeferredOutput) {
buffer.append(builder);
}
text = buffer.substring(0);
}
else if (myDeferredOutput.size() == 1) {
text = myDeferredOutput.getFirst().substring(0);
}
else {
return;
}
text = myBuffer.getText();
contentTypes = Collections.unmodifiableCollection(new HashSet<ConsoleViewContentType>(myDeferredTypes));
for (TokenInfo deferredToken : myDeferredTokens) {
contentTypes = Collections.unmodifiableCollection(new HashSet<ConsoleViewContentType>(myBuffer.getDeferredTokenTypes()));
List<TokenInfo> deferredTokens = myBuffer.getDeferredTokens();
for (TokenInfo deferredToken : deferredTokens) {
addToken(deferredToken.getLength(), deferredToken.getHyperlinkInfo(), deferredToken.contentType);
}
deferredTokensSize = myDeferredTokens.size();
myDeferredTokens.clear();
myDeferredTypes.clear();
if (USE_CYCLIC_BUFFER) {
myDeferredOutput.clear();
myDeferredOutput.add(new StringBuilder(CYCLIC_BUFFER_SIZE));
}
else {
for (StringBuilder builder : myDeferredOutput) {
builder.setLength(0);
}
}
myDeferredOutputLength = 0;
deferredTokensSize = deferredTokens.size();
myBuffer.clear();
}
final Document document = myEditor.getDocument();
final int oldLineCount = document.getLineCount();
final boolean isAtEndOfDocument = myEditor.getCaretModel().getOffset() == document.getTextLength();
boolean cycleUsed = USE_CYCLIC_BUFFER && document.getTextLength() + text.length() > CYCLIC_BUFFER_SIZE;
boolean cycleUsed = myBuffer.isUseCyclicBuffer() && document.getTextLength() + text.length() > myBuffer.getCyclicBufferSize();
CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
public void run() {
int offset = myEditor.getCaretModel().getOffset();
@@ -887,12 +628,10 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
}
private void flushDeferredUserInput() {
final String text = myDeferredUserInput.substring(0, myDeferredUserInput.length());
final int index = Math.max(text.lastIndexOf('\n'), text.lastIndexOf('\r'));
if (index < 0) return;
final String textToSend = text.substring(0, index + 1);
myDeferredUserInput.setLength(0);
myDeferredUserInput.append(text.substring(index + 1));
final String textToSend = myBuffer.cutFirstUserInputLine();
if (textToSend == null) {
return;
}
myFlushUserInputAlarm.addRequest(new Runnable() {
public void run() {
if (myState.isRunning()) {
@@ -980,7 +719,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
});
final int bufferSize = USE_CYCLIC_BUFFER ? CYCLIC_BUFFER_SIZE : 0;
final int bufferSize = myBuffer.isUseCyclicBuffer() ? myBuffer.getCyclicBufferSize() : 0;
editor.getDocument().setCyclicBufferSize(bufferSize);
editor.putUserData(CONSOLE_VIEW_IN_EDITOR_VIEW, this);
@@ -1589,7 +1328,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
public void execute(final ConsoleViewImpl consoleView, final DataContext context) {
final int maxSize = UISettings.getInstance().CONSOLE_COMMAND_HISTORY_LIMIT;
synchronized (consoleView.LOCK) {
String str = consoleView.myDeferredUserInput.toString();
String str = consoleView.myBuffer.getUserInput();
if (StringUtil.isNotEmpty(str)) {
consoleView.myHistory.remove(str);
consoleView.myHistory.add(str);
@@ -1917,6 +1656,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
*/
private void insertUserText(final String s, int offset) {
final ConsoleViewImpl consoleView = this;
final ConsoleBuffer buffer = consoleView.myBuffer;
final Editor editor = consoleView.myEditor;
final Document document = editor.getDocument();
final int startOffset;
@@ -1938,7 +1678,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
return;
}
final int deferredOffset = myContentSize - consoleView.myDeferredOutputLength - consoleView.myDeferredUserInput.length();
final int deferredOffset = myContentSize - buffer.getLength() - buffer.getUserInputLength();
if (offset > info.endOffset) {
startOffset = info.endOffset;
}
@@ -1946,7 +1686,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
startOffset = Math.max(deferredOffset, Math.max(info.startOffset, offset));
}
consoleView.myDeferredUserInput.insert(startOffset - deferredOffset, s);
buffer.addUserText(startOffset - deferredOffset, s);
int charCountToAdd = s.length();
info.endOffset += charCountToAdd;
@@ -1972,6 +1712,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
return;
}
final ConsoleViewImpl consoleView = this;
final ConsoleBuffer buffer = consoleView.myBuffer;
final Editor editor = consoleView.myEditor;
final Document document = editor.getDocument();
final int startOffset;
@@ -1987,9 +1728,9 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
editor.getSelectionModel().removeSelection();
return;
}
if (consoleView.myDeferredUserInput.length() == 0) return;
if (buffer.isEmpty()) return;
final int deferredOffset = myContentSize - consoleView.myDeferredOutputLength - consoleView.myDeferredUserInput.length();
final int deferredOffset = myContentSize - buffer.getLength() - buffer.getUserInputLength();
startOffset = getStartOffset(start, info, deferredOffset);
endOffset = getEndOffset(end, info);
@@ -2003,7 +1744,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
}
int charCountToReplace = s.length() - endOffset + startOffset;
consoleView.myDeferredUserInput.replace(startOffset - deferredOffset, endOffset - deferredOffset, s);
buffer.replaceUserText(startOffset - deferredOffset, endOffset - deferredOffset, s);
info.endOffset += charCountToReplace;
if (info.startOffset == info.endOffset) {
@@ -2026,6 +1767,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
*/
private void deleteUserText(int offset, int length) {
ConsoleViewImpl consoleView = this;
ConsoleBuffer buffer = consoleView.myBuffer;
final Editor editor = consoleView.myEditor;
final Document document = editor.getDocument();
final int startOffset;
@@ -2035,9 +1777,9 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
if (consoleView.myTokens.isEmpty()) return;
final TokenInfo info = consoleView.myTokens.get(consoleView.myTokens.size() - 1);
if (info.contentType != ConsoleViewContentType.USER_INPUT) return;
if (consoleView.myDeferredUserInput.length() == 0) return;
if (myBuffer.getUserInputLength() == 0) return;
final int deferredOffset = myContentSize - consoleView.myDeferredOutputLength - consoleView.myDeferredUserInput.length();
final int deferredOffset = myContentSize - buffer.getLength() - buffer.getUserInputLength();
startOffset = getStartOffset(offset, info, deferredOffset);
endOffset = getEndOffset(offset + length, info);
if (startOffset == -1 ||
@@ -2049,7 +1791,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo
return;
}
consoleView.myDeferredUserInput.delete(startOffset - deferredOffset, endOffset - deferredOffset);
buffer.removeUserText(startOffset - deferredOffset, endOffset - deferredOffset);
int charCountToDelete = endOffset - startOffset;
info.endOffset -= charCountToDelete;
@@ -51,7 +51,7 @@ import java.util.List;
* This class provides basic functionality for running consoles.
* It launches external process and handles line input with history
*/
public abstract class AbstractConsoleRunnerWithHistory {
public abstract class AbstractConsoleRunnerWithHistory<T extends LanguageConsoleViewImpl> {
private final Project myProject;
private final String myConsoleTitle;
@@ -59,7 +59,7 @@ public abstract class AbstractConsoleRunnerWithHistory {
private final CommandLineArgumentsProvider myProvider;
private final String myWorkingDir;
private LanguageConsoleViewImpl myConsoleView;
private T myConsoleView;
private ConsoleExecuteActionHandler myConsoleExecuteActionHandler;
@@ -194,7 +194,7 @@ public abstract class AbstractConsoleRunnerWithHistory {
myConsoleView.getConsole().setEditable(false);
}
protected abstract LanguageConsoleViewImpl createConsoleView();
protected abstract T createConsoleView();
@Nullable
protected abstract Process createProcess(CommandLineArgumentsProvider provider) throws ExecutionException;
@@ -288,7 +288,7 @@ public abstract class AbstractConsoleRunnerWithHistory {
}
}
public LanguageConsoleViewImpl getConsoleView() {
public T getConsoleView() {
return myConsoleView;
}
@@ -27,6 +27,7 @@ public abstract class EditorHeaderToggleAction extends CheckboxAction {
public JComponent createCustomComponent(Presentation presentation) {
final JComponent customComponent = super.createCustomComponent(presentation);
getCheckBox().setFocusable(false);
getCheckBox().setOpaque(false);
return customComponent;
}
@@ -1,9 +1,14 @@
package com.intellij.find.editorHeaderActions;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.PopupChooserBuilder;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.vfs.ReadonlyStatusHandler;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import javax.swing.*;
import java.awt.*;
@@ -61,4 +66,20 @@ public class Utils {
}
}
}
public static boolean okToWrite(Editor e) {
final PsiFile psiFile = PsiDocumentManager.getInstance(e.getProject()).getPsiFile(e.getDocument());
boolean okWritable;
if (psiFile != null) {
final VirtualFile virtualFile = psiFile.getVirtualFile();
if (virtualFile != null) {
okWritable = ReadonlyStatusHandler.ensureFilesWritable(e.getProject(), virtualFile);
} else {
okWritable = psiFile.isWritable();
}
} else {
okWritable = e.getDocument().isWritable();
}
return okWritable;
}
}
@@ -3,6 +3,7 @@ package com.intellij.find.impl.livePreview;
import com.intellij.find.FindManager;
import com.intellij.find.FindModel;
import com.intellij.find.FindUtil;
import com.intellij.find.editorHeaderActions.Utils;
import com.intellij.find.impl.FindResultImpl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
@@ -194,7 +195,7 @@ public class LivePreviewControllerBase implements LivePreview.Delegate, FindUtil
@Nullable
@Override
public TextRange performReplace(final LiveOccurrence occurrence, final String replacement, final Editor editor) {
if (myReplaceDenied) return null;
if (myReplaceDenied || !Utils.okToWrite(editor)) return null;
TextRange range = occurrence.getPrimaryRange();
FindModel findModel = mySearchResults.getFindModel();
TextRange result = null;
@@ -216,6 +217,7 @@ public class LivePreviewControllerBase implements LivePreview.Delegate, FindUtil
@Override
public void performReplaceAll(Editor e) {
if (!Utils.okToWrite(e)) return;
if (mySearchResults.getFindModel() != null) {
FindUtil.replace(e.getProject(), e,
mySearchResults.getFindModel().isGlobal() ? 0 : mySearchResults.getEditor().getSelectionModel().getSelectionStart(),
@@ -362,7 +362,7 @@ public class ReplaceInProjectManager {
for (final Usage usage : selectedUsages) {
final VirtualFile file = ((UsageInFile)usage).getFile();
if (!file.isWritable()) {
if (file != null && !file.isWritable()) {
if (readOnlyFiles == null) readOnlyFiles = new HashSet<VirtualFile>();
readOnlyFiles.add(file);
}
@@ -20,6 +20,7 @@ import com.intellij.find.EditorSearchComponent;
import com.intellij.find.FindManager;
import com.intellij.find.FindModel;
import com.intellij.find.FindUtil;
import com.intellij.find.editorHeaderActions.Utils;
import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
@@ -69,7 +70,7 @@ public class IncrementalFindAction extends EditorAction {
public boolean isEnabled(Editor editor, DataContext dataContext) {
Project project = PlatformDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(editor.getComponent()));
return project != null && !editor.isOneLineMode();
return project != null && !editor.isOneLineMode() && (!myReplace || Utils.okToWrite(editor));
}
}
@@ -69,7 +69,7 @@ class FileElementInfo implements SmartPointerElementInfo {
}
@Override
public boolean pointsToTheSameElementAs(SmartPointerElementInfo other) {
public boolean pointsToTheSameElementAs(@NotNull SmartPointerElementInfo other) {
if (other instanceof FileElementInfo) {
return myVirtualFile == ((FileElementInfo)other).myVirtualFile;
}
@@ -65,7 +65,7 @@ class HardElementInfo implements SmartPointerElementInfo {
}
@Override
public boolean pointsToTheSameElementAs(SmartPointerElementInfo other) {
public boolean pointsToTheSameElementAs(@NotNull SmartPointerElementInfo other) {
return Comparing.equal(myElement, other.restoreElement());
}
@@ -17,24 +17,73 @@ package com.intellij.psi.impl.smartPointers;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.Segment;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* User: cdr
*/
class InjectedSelfElementInfo extends SelfElementInfo {
private final SmartPsiFileRange myPsiFileRangeInHostElement;
private final Class<? extends PsiElement> myClass;
InjectedSelfElementInfo(@NotNull Project project,
@NotNull PsiElement anchor,
@NotNull PsiFile containingFile) {
super(project, InjectedLanguageManager.getInstance(project).injectedToHost(anchor, anchor.getTextRange()), anchor.getClass(), InjectedLanguageUtil.getTopLevelFile(containingFile));
assert containingFile.getContext() != null;
@NotNull PsiElement context) {
super(project, context);
SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(project);
myPsiFileRangeInHostElement = smartPointerManager.createSmartPsiFileRangePointer(context.getContainingFile(), InjectedLanguageManager.getInstance(
project).injectedToHost(anchor, anchor.getTextRange()));
myClass = anchor.getClass();
}
@Override
protected PsiElement findAnchorAt(@NotNull PsiFile file, int syncStartOffset) {
return InjectedLanguageUtil.findInjectedElementNoCommitWithOffset(file, syncStartOffset);
public VirtualFile getVirtualFile() {
PsiElement element = restoreElement();
if (element == null) return null;
return element.getContainingFile().getVirtualFile();
}
@Override
public PsiElement restoreElement() {
PsiElement host = super.restoreElement();
if (host == null) return null;
Segment segment = myPsiFileRangeInHostElement.getRange();
if (segment == null) return null;
final TextRange rangeInHostElement = TextRange.create(segment);
final Ref<PsiElement> result = new Ref<PsiElement>();
InjectedLanguageUtil.enumerate(host, host.getContainingFile(), new PsiLanguageInjectionHost.InjectedPsiVisitor() {
@Override
public void visit(@NotNull PsiFile injectedPsi, @NotNull List<PsiLanguageInjectionHost.Shred> places) {
if (result.get() != null) return;
TextRange hostRange =
InjectedLanguageManager.getInstance(getProject()).injectedToHost(injectedPsi, new TextRange(0, injectedPsi.getTextLength()));
if (hostRange.contains(rangeInHostElement)) {
TextRange rangeInside = rangeInHostElement.shiftRight(-hostRange.getStartOffset());
PsiElement element = findElementInside(injectedPsi, rangeInside.getStartOffset(), rangeInside.getEndOffset(), myClass);
result.set(element);
}
}
}, false);
return result.get();
}
@Override
public boolean pointsToTheSameElementAs(@NotNull SmartPointerElementInfo other) {
if (getClass() != other.getClass()) return false;
if (!super.pointsToTheSameElementAs(other)) return false;
SmartPointerElementInfo myElementInfo = ((SmartPsiElementPointerImpl)myPsiFileRangeInHostElement).getElementInfo();
SmartPointerElementInfo oElementInfo = ((SmartPsiElementPointerImpl)((InjectedSelfElementInfo)other).myPsiFileRangeInHostElement).getElementInfo();
return myElementInfo.pointsToTheSameElementAs(oElementInfo);
}
}
@@ -34,7 +34,7 @@ import java.lang.ref.SoftReference;
* User: cdr
*/
public class SelfElementInfo implements SmartPointerElementInfo {
protected final VirtualFile myVirtualFile;
private final VirtualFile myVirtualFile;
private Reference<RangeMarker> myMarkerRef; // create marker only in case of live document
private int mySyncStartOffset;
private int mySyncEndOffset;
@@ -44,6 +44,9 @@ public class SelfElementInfo implements SmartPointerElementInfo {
@SuppressWarnings({"UnusedDeclaration"})
private RangeMarker myRangeMarker; //maintain hard reference during modification
protected SelfElementInfo(@NotNull Project project, @NotNull PsiElement anchor) {
this(project, anchor.getTextRange(), anchor.getClass(), anchor.getContainingFile());
}
public SelfElementInfo(@NotNull Project project, @NotNull TextRange anchor, @NotNull Class anchorClass, @NotNull PsiFile containingFile) {
myVirtualFile = containingFile.getVirtualFile();
myType = anchorClass;
@@ -154,7 +157,11 @@ public class SelfElementInfo implements SmartPointerElementInfo {
final int syncStartOffset = getSyncStartOffset();
final int syncEndOffset = getSyncEndOffset();
PsiElement anchor = findAnchorAt(file, syncStartOffset);
return findElementInside(file, syncStartOffset, syncEndOffset, myType);
}
protected static PsiElement findElementInside(PsiFile file, int syncStartOffset, int syncEndOffset, Class type) {
PsiElement anchor = file.getViewProvider().findElementAt(syncStartOffset, file.getLanguage());
if (anchor == null) return null;
TextRange range = anchor.getTextRange();
@@ -166,7 +173,7 @@ public class SelfElementInfo implements SmartPointerElementInfo {
range = anchor.getTextRange();
}
while (range.getEndOffset() == syncEndOffset && anchor != null && !myType.equals(anchor.getClass())) {
while (range.getEndOffset() == syncEndOffset && anchor != null && !type.equals(anchor.getClass())) {
anchor = anchor.getParent();
if (anchor == null || anchor.getTextRange() == null) break;
range = anchor.getTextRange();
@@ -176,10 +183,6 @@ public class SelfElementInfo implements SmartPointerElementInfo {
return null;
}
protected PsiElement findAnchorAt(@NotNull PsiFile file, int syncStartOffset) {
return file.getViewProvider().findElementAt(syncStartOffset, file.getLanguage());
}
@Override
public void dispose() {
RangeMarker marker = getMarker();
@@ -251,7 +254,7 @@ public class SelfElementInfo implements SmartPointerElementInfo {
}
@Override
public boolean pointsToTheSameElementAs(SmartPointerElementInfo other) {
public boolean pointsToTheSameElementAs(@NotNull SmartPointerElementInfo other) {
if (other instanceof SelfElementInfo) {
return myVirtualFile == ((SelfElementInfo)other).myVirtualFile
&& myType == ((SelfElementInfo)other).myType
@@ -38,7 +38,7 @@ interface SmartPointerElementInfo {
void dispose();
int elementHashCode(); // must be immutable
boolean pointsToTheSameElementAs(SmartPointerElementInfo other);
boolean pointsToTheSameElementAs(@NotNull SmartPointerElementInfo other);
VirtualFile getVirtualFile();
@@ -85,7 +85,7 @@ class SmartPsiElementPointerImpl<E extends PsiElement> implements SmartPointerEx
if (element != null && !element.isValid()) {
element = null;
}
if (element == null && myElementInfo != null) {
if (element == null) {
element = (E)myElementInfo.restoreElement();
if (element != null && (!element.getClass().equals(myElementClass) || !element.isValid())) {
element = null;
@@ -153,7 +153,8 @@ class SmartPsiElementPointerImpl<E extends PsiElement> implements SmartPointerEx
FileViewProvider viewProvider = containingFile.getViewProvider();
if (viewProvider instanceof InjectedFileViewProvider) {
return new InjectedSelfElementInfo(project, element, containingFile);
PsiElement context = containingFile.getContext();
if (context != null) return new InjectedSelfElementInfo(project, element, context);
}
if (element instanceof PsiFile) {
@@ -167,17 +168,13 @@ class SmartPsiElementPointerImpl<E extends PsiElement> implements SmartPointerEx
}
public void documentAndPsiInSync() {
if (myElementInfo != null) {
myElementInfo.documentAndPsiInSync();
}
myElementInfo.documentAndPsiInSync();
}
@Override
public void dispose() {
if (myElementInfo != null) {
myElementInfo.dispose();
myElement = null;
}
myElementInfo.dispose();
myElement = null;
}
@Override
@@ -189,6 +186,7 @@ class SmartPsiElementPointerImpl<E extends PsiElement> implements SmartPointerEx
myElementInfo.fastenBelt(offset);
}
@NotNull
SmartPointerElementInfo getElementInfo() {
return myElementInfo;
}
@@ -221,7 +221,7 @@ public class PostprocessReformattingAspect implements PomModelAspect, Disposable
LOG.error(e);
}
finally {
LOG.assertTrue(myReformatElements.isEmpty());
LOG.assertTrue(myReformatElements.isEmpty(), myReformatElements);
}
}
});
@@ -22,13 +22,11 @@ import com.intellij.formatting.FormattingModelBuilder;
import com.intellij.formatting.IndentInfo;
import com.intellij.lang.*;
import com.intellij.openapi.command.AbnormalCommandTerminationException;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.TokenType;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.impl.source.tree.Factory;
@@ -53,7 +51,13 @@ public class CodeEditUtil {
return Boolean.TRUE;
}
};
private static final ThreadLocal<Boolean> ALLOW_NODES_REFORMATTING = new ThreadLocal<Boolean>() {
@Override
protected Boolean initialValue() {
return Boolean.TRUE;
}
};
public static final Key<Boolean> OUTER_OK = new Key<Boolean>("OUTER_OK");
private CodeEditUtil() {
@@ -425,7 +429,7 @@ public class CodeEditUtil {
* @return <code>true</code> if given node is configured to be reformatted; <code>false</code> otherwise
*/
public static boolean isMarkedToReformat(final ASTNode node) {
return node.getCopyableUserData(REFORMAT_KEY) != null;
return node.getCopyableUserData(REFORMAT_KEY) != null && ALLOW_NODES_REFORMATTING.get();
}
/**
@@ -454,9 +458,9 @@ public class CodeEditUtil {
* to {@link #markToReformat(ASTNode, boolean) mark node for postponed formatting} will have no effect until current method is
* called with <code>'true'</code> as an argument. Hence, following usage scenario is expected:
* <ol>
* <li>this method is called with <code>'false'</code> argument;</li>
* <li>formatting is performed at dedicated <code>'try'</code> block;</li>
* <li>this method is called with <code>'false'</code> argument from <code>'finally'</code> section;</li>
* <li>This method is called with <code>'false'</code> argument;</li>
* <li>Formatting is performed at dedicated <code>'try'</code> block;</li>
* <li>This method is called with <code>'false'</code> argument from <code>'finally'</code> section;</li>
* </ol>
*
* @param allow flag that defines if new reformat markers can be added from the current thread
@@ -465,4 +469,32 @@ public class CodeEditUtil {
public static void allowToMarkNodesForPostponedFormatting(boolean allow) {
ALLOW_TO_MARK_NODES_TO_REFORMAT.set(allow);
}
/**
* @return <code>'allow suspended formatting'</code> flag value
* @see #setAllowSuspendNodesReformatting(boolean)
*/
public static boolean isSuspendedNodesReformattingAllowed() {
return ALLOW_NODES_REFORMATTING.get();
}
/**
* There is a possible case that particular PSI tree node is {@link #markToReformat(ASTNode, boolean) marked for reformatting}.
* That means that there is a big chance that the node will be re-formatted during corresponding document processing
* (e.g. on call to {@link PsiDocumentManager#doPostponedOperationsAndUnblockDocument(Document)}).
* <p/>
* However, there is a possible case that particular activity that triggers such document processing is not ready to the
* situation when the document is modified because of postponed formatting. Hence, it may ask to suspend postponed formatting
* for a while. This method allows to do that at thread-local manner. I.e. it's expected to be called as follows:
* <pre>
* <ol>
* <li>This method is called with <code>'false'</code> argument;</li>
* <li>Document is processed at dedicated <code>'try'</code> block;</li>
* <li>This method is called with <code>'false'</code> argument from <code>'finally'</code> section;</li>
* </ol>
</pre>
*/
public static void setAllowSuspendNodesReformatting(boolean allow) {
ALLOW_NODES_REFORMATTING.set(allow);
}
}
@@ -202,7 +202,7 @@ public class FileReference implements FileReferenceOwner, PsiPolyVariantReferenc
public boolean execute(PsiFileSystemItem element) {
result.add(new PsiElementResolveResult(getOriginalFile(element)));
return false;
return true;
}
});
}
@@ -0,0 +1,148 @@
package com.intellij.execution.impl;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.openapi.editor.markup.TextAttributes;
import org.jetbrains.annotations.NotNull;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
import static com.intellij.execution.impl.ConsoleViewImpl.TokenInfo;
import static org.junit.Assert.assertEquals;
/**
* @author Denis Zhdanov
* @since 04/06/2011
*/
public class ConsoleBufferTest {
private static final ConsoleViewContentType IMPORTANT_OUTPUT = new ConsoleViewContentType("IMPORTANT_OUTPUT", (TextAttributes)null);
private static final ConsoleViewContentType NORMAL_OUTPUT = new ConsoleViewContentType("NORMAL_OUTPUT", (TextAttributes)null);
private static final ConsoleViewContentType BORING_OUTPUT = new ConsoleViewContentType("BORING_OUTPUT", (TextAttributes)null);
private ConsoleBuffer myBuffer;
private int myBufferSize;
private int myBufferUnitSize;
@Before
public void setUp() throws Exception {
myBufferSize = 10;
myBufferUnitSize = 3;
init();
}
@Test
public void nonExceedingBufferSize() {
myBuffer.print("a", NORMAL_OUTPUT, null);
checkState(s("a"), new TokenInfo(NORMAL_OUTPUT, 0, 1));
myBuffer.print("b", BORING_OUTPUT, null);
checkState(s("ab"), new TokenInfo(NORMAL_OUTPUT, 0, 1), new TokenInfo(BORING_OUTPUT, 1, 2));
myBuffer.print("cd", BORING_OUTPUT, null);
checkState(s("abc", "d"), new TokenInfo(NORMAL_OUTPUT, 0, 1), new TokenInfo(BORING_OUTPUT, 1, 4));
myBuffer.print("ef", BORING_OUTPUT, null);
checkState(s("abc", "def"), new TokenInfo(NORMAL_OUTPUT, 0, 1), new TokenInfo(BORING_OUTPUT, 1, 6));
myBuffer.print("ghij", NORMAL_OUTPUT, null);
checkState(
s("abc", "def", "ghi", "j"),
new TokenInfo(NORMAL_OUTPUT, 0, 1), new TokenInfo(BORING_OUTPUT, 1, 6), new TokenInfo(NORMAL_OUTPUT, 6, 10)
);
}
@Test
public void cycling() {
myBuffer.print("abcdefghi", NORMAL_OUTPUT, null);
myBuffer.print("jklm", BORING_OUTPUT, null);
checkState(s("def", "ghi", "jkl", "m"), new TokenInfo(NORMAL_OUTPUT, 0, 6), new TokenInfo(BORING_OUTPUT, 6, 10));
}
@Test
public void removedTokenOnCyclingIsNotShownAtDeferredTypes() {
myBuffer.print("a", BORING_OUTPUT, null);
myBuffer.print("bcdefghijklmn", NORMAL_OUTPUT, null);
checkState(s("efg", "hij", "klm", "n"), new TokenInfo(NORMAL_OUTPUT, 0, 10));
}
@Test
public void tokenDataIsTrimmed() {
myBuffer.print("1234567890", NORMAL_OUTPUT, null);
myBuffer.print("123456789012", NORMAL_OUTPUT, null);
checkState(s("345", "678", "901", "2"), new TokenInfo(NORMAL_OUTPUT, 0, 10));
}
@Test
public void removingOfGreatNumberOfTokens() {
myBuffer.print("a", NORMAL_OUTPUT, null);
myBuffer.print("b", BORING_OUTPUT, null);
myBuffer.print("a", NORMAL_OUTPUT, null);
myBuffer.print("b", BORING_OUTPUT, null);
myBuffer.print("a", NORMAL_OUTPUT, null);
myBuffer.print("1234567890", BORING_OUTPUT, null);
checkState(s("123", "456", "789", "0"), new TokenInfo(BORING_OUTPUT, 0, 10));
}
@Test
public void removeFromBufferEnd() {
myBuffer.print("1234567", IMPORTANT_OUTPUT, null);
myBuffer.print("890", NORMAL_OUTPUT, null);
myBuffer.print("abc", BORING_OUTPUT, null);
checkState(
s("123", "456", "7ab", "c"),
new TokenInfo(IMPORTANT_OUTPUT, 0, 7), new TokenInfo(BORING_OUTPUT, 7, 10)
);
}
@Test
public void smallRemoveAtStart() {
myBuffer.print("abc", NORMAL_OUTPUT, null);
myBuffer.print("def", BORING_OUTPUT, null);
myBuffer.print("ghi", NORMAL_OUTPUT, null);
myBuffer.print("k", BORING_OUTPUT, null);
myBuffer.print("l", NORMAL_OUTPUT, null);
checkState(
s("bc", "def", "ghi", "kl"),
new TokenInfo(NORMAL_OUTPUT, 0, 2), new TokenInfo(BORING_OUTPUT, 2, 5), new TokenInfo(NORMAL_OUTPUT, 5, 8),
new TokenInfo(BORING_OUTPUT, 8, 9), new TokenInfo(NORMAL_OUTPUT, 9, 10)
);
}
private static List<String> s(String ... strings) {
return Arrays.asList(strings);
}
private void checkState(@NotNull List<String> expectedBuffers, @NotNull TokenInfo ... expectedTokens) {
Deque<StringBuilder> actualBuffers = myBuffer.getDeferredOutput();
assertEquals(expectedBuffers.size(), actualBuffers.size());
int i = 0;
for (StringBuilder actual : actualBuffers) {
String expected = expectedBuffers.get(i++);
assertEquals(expected, actual.toString());
}
List<TokenInfo> actualTokens = myBuffer.getDeferredTokens();
assertEquals(expectedTokens.length, actualTokens.size());
i = 0;
Set<ConsoleViewContentType> contentTypes = new HashSet<ConsoleViewContentType>();
for (TokenInfo actual : actualTokens) {
TokenInfo expected = expectedTokens[i++];
assertEquals(expected.contentType, actual.contentType);
assertEquals(expected.startOffset, actual.startOffset);
assertEquals(expected.endOffset, actual.endOffset);
contentTypes.add(expected.contentType);
}
assertEquals(contentTypes, myBuffer.getDeferredTokenTypes());
}
private void init() throws Exception {
myBuffer = new ConsoleBuffer(true, myBufferSize, myBufferUnitSize);
myBuffer.setContentTypesToNotStripOnCycling(Collections.singleton(IMPORTANT_OUTPUT));
}
}
@@ -71,7 +71,7 @@ public class GenericLineWrapPositionStrategy implements LineWrapPositionStrategy
if (rule.condition == WrapCondition.BOTH || rule.condition == WrapCondition.AFTER) {
int target = i+1;
if (rule.symbol != ' ') {
while(i < maxPreferredOffsetToUse && text.charAt(target) == ' ') {
while(target < maxPreferredOffsetToUse && text.charAt(target) == ' ') {
target++;
}
}
@@ -27,6 +27,7 @@ public abstract class FileDocumentManager {
return ApplicationManager.getApplication().getComponent(FileDocumentManager.class);
}
@Nullable
public abstract Document getDocument(@NotNull VirtualFile file);
@Nullable
@@ -434,6 +434,9 @@ public class IdeEventQueue extends EventQueue {
if (e instanceof KeyEvent) {
myKeyboardBusy = e.getID() != KeyEvent.KEY_RELEASED || ((KeyEvent)e).getModifiers() != 0;
if (e.getID() == KeyEvent.KEY_RELEASED && ((KeyEvent)e).getKeyCode() == KeyEvent.VK_SHIFT) {
myMouseEventDispatcher.resetHorScrollingTracker();
}
}
if (!typeaheadFlushing && typeAheadDispatchToFocusManager(e)) return;
@@ -30,12 +30,13 @@ import org.jetbrains.annotations.NotNull;
import java.awt.*;
import java.awt.event.MouseWheelEvent;
import java.util.Arrays;
import java.util.List;
public class EditorUtil {
private EditorUtil() { }
public static int getLastVisualLineColumnNumber(Editor editor, int line) {
public static int getLastVisualLineColumnNumber(Editor editor, final int line) {
Document document = editor.getDocument();
int lastLine = document.getLineCount() - 1;
if (lastLine < 0) {
@@ -128,8 +129,9 @@ public class EditorUtil {
assert false : String.format(
"Target visual line: %d, mapped logical line: %d, visual lines range for the mapped logical line: [%s]-[%s], soft wraps for "
+ "the target logical line: %s%nDocument text: '%s'",
line, resultLogLine, resVisStart, resVisEnd, softWraps, editor.getDocument().getText()
+ "the target logical line: %s, all soft wraps: %s, fold regions: %s%nDocument text: '%s'",
line, resultLogLine, resVisStart, resVisEnd, softWraps, editor.getSoftWrapModel().getSoftWrapsForRange(0, document.getTextLength()),
Arrays.toString(editor.getFoldingModel().getAllFoldRegions()), editor.getDocument().getText()
);
return resVisEnd.column;
}
@@ -372,7 +374,9 @@ public class EditorUtil {
for (int i = start; i < offset; i++) {
char c = text.charAt(i);
assert c != '\n' && c != '\r';
assert c != '\n' && c != '\r' :
String.format("Symbol: %c, its index: %d, given start: %d, given offset: %d, given tab size: %d, text: '%s'",
c, i, start, offset, tabSize, text);
if (c == '\t') {
shift += getTabLength(i + shift - start, tabSize) - 1;
}
@@ -635,6 +635,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, PrioritizedDocumentLi
+ "%s%nDocument:%n%s%nFold regions: %s",
task, myDataMapper, myEditor.getDocument().getText(), Arrays.toString(myEditor.getFoldingModel().fetchTopLevel())), e);
}
myEditor.getFoldingModel().rebuild();
myDataMapper.release();
myApplianceManager.reset();
myStorage.removeAll();
@@ -107,6 +107,7 @@ public class FileDocumentManagerImpl extends FileDocumentManager implements Appl
public void disposeComponent() {
}
@Nullable
public Document getDocument(@NotNull final VirtualFile file) {
DocumentEx document = (DocumentEx)getCachedDocument(file);
if (document == null) {
@@ -47,6 +47,7 @@ public final class IdeMouseEventDispatcher {
private final PresentationFactory myPresentationFactory = new PresentationFactory();
private final ArrayList<AnAction> myActions = new ArrayList<AnAction>(1);
private final Map<Container, Integer> myRootPane2BlockedId = new HashMap<Container, Integer>();
private int myLastHorScrolledComponentHash = 0;
public IdeMouseEventDispatcher() {
}
@@ -187,16 +188,23 @@ public final class IdeMouseEventDispatcher {
return false;
}
private static boolean doHorizontalScrolling(Component c, MouseWheelEvent me) {
FeatureUsageTracker.getInstance().triggerFeatureUsed("ui.horizontal.scrolling");
private boolean doHorizontalScrolling(Component c, MouseWheelEvent me) {
final JScrollBar scrollBar = findHorizontalScrollBar(c);
if (scrollBar != null) {
if (scrollBar.hashCode() != myLastHorScrolledComponentHash) {
FeatureUsageTracker.getInstance().triggerFeatureUsed("ui.horizontal.scrolling");
myLastHorScrolledComponentHash = scrollBar.hashCode();
}
scrollBar.setValue(scrollBar.getValue() + getScrollAmount(c, me, scrollBar));
return true;
}
return false;
}
public void resetHorScrollingTracker() {
myLastHorScrolledComponentHash = 0;
}
private static int getScrollAmount(Component c, MouseWheelEvent me, JScrollBar scrollBar) {
final int ratio = Registry.is("ide.smart.horizontal.scrolling")
? Math.max((int)Math.pow(c.getWidth() / scrollBar.getWidth() , 2), 10) : 10; // do annoying scrolling faster if smart scrolling is on
@@ -0,0 +1,17 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<body style="font-family: Verdana; font-size: 13;" LEFTMARGIN="25" TOPMARGIN="25">
<table cellpadding="15" width="98%" border="0">
<tr>
<td>
Version control annotations shows latest change of each line in one click!<br/>
Use right-click on annotation and
<font style="font-family: Verdana; font-weight:bold;" size="3" color="#005555">Show Diff</font> popup,
in context menu.<br/><br/>
<p><img hspace="20" src="images/annotationShowDiff.png"></p>
</td>
</tr>
</table>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

@@ -15,6 +15,7 @@
*/
package com.intellij.usages;
import com.intellij.injected.editor.DocumentWindow;
import com.intellij.lexer.Lexer;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
@@ -26,6 +27,7 @@ import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.PlainSyntaxHighlighter;
import com.intellij.openapi.fileTypes.SyntaxHighlighter;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Segment;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDocumentManager;
@@ -101,10 +103,11 @@ public class ChunkExtractor {
private ChunkExtractor(@NotNull PsiFile file) {
myColorsScheme = UsageTreeColorsScheme.getInstance().getScheme();
myDocument = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
Project project = file.getProject();
myDocument = PsiDocumentManager.getInstance(project).getDocument(file);
LOG.assertTrue(myDocument != null);
final FileType fileType = file.getFileType();
final SyntaxHighlighter highlighter = SyntaxHighlighter.PROVIDER.create(fileType, file.getProject(), file.getVirtualFile());
final SyntaxHighlighter highlighter = SyntaxHighlighter.PROVIDER.create(fileType, project, file.getVirtualFile());
myHighlighter = highlighter == null ? new PlainSyntaxHighlighter() : highlighter;
myLexer = myHighlighter.getHighlightingLexer();
myLexer.start(myDocument.getCharsSequence());
@@ -125,8 +128,15 @@ public class ChunkExtractor {
int absoluteStartOffset = usageInfo2UsageAdapter.getNavigationOffset();
if (absoluteStartOffset == -1) return TextChunk.EMPTY_ARRAY;
final int lineNumber = myDocument.getLineNumber(absoluteStartOffset);
final int columnNumber = absoluteStartOffset - myDocument.getLineStartOffset(lineNumber);
Document visibleDocument = myDocument instanceof DocumentWindow ? ((DocumentWindow)myDocument).getDelegate() : myDocument;
int visibleStartOffset = myDocument instanceof DocumentWindow ? ((DocumentWindow)myDocument).injectedToHost(absoluteStartOffset) : absoluteStartOffset;
int lineNumber = myDocument.getLineNumber(absoluteStartOffset);
//int columnNumber = absoluteStartOffset - myDocument.getLineStartOffset(lineNumber);
int visibleLineNumber = visibleDocument.getLineNumber(visibleStartOffset);
int visibleColumnNumber = visibleStartOffset - visibleDocument.getLineStartOffset(visibleLineNumber);
final List<TextChunk> result = new ArrayList<TextChunk>();
appendPrefix(result, visibleLineNumber, visibleColumnNumber);
int lineStartOffset = myDocument.getLineStartOffset(lineNumber);
int lineEndOffset = lineStartOffset < myDocument.getTextLength() ? myDocument.getLineEndOffset(lineNumber) : 0;
@@ -136,8 +146,6 @@ public class ChunkExtractor {
if (myLexer.getTokenStart() > absoluteStartOffset) {
myLexer.start(chars);
}
final List<TextChunk> result = new ArrayList<TextChunk>();
appendPrefix(result, lineNumber, columnNumber);
if (lineEndOffset - lineStartOffset > MAX_LINE_TO_SHOW) {
lineStartOffset = Math.max(lineStartOffset, absoluteStartOffset - OFFSET_BEFORE_TO_SHOW_WHEN_LONG_LINE);
lineEndOffset = Math.min(lineEndOffset, absoluteStartOffset + OFFSET_AFTER_TO_SHOW_WHEN_LONG_LINE);
@@ -41,7 +41,7 @@ public class UsageDataUtil {
for (Usage usage : usages) {
if (usage instanceof UsageInFile) {
VirtualFile file = ((UsageInFile)usage).getFile();
if (file.isValid()) {
if (file != null && file.isValid()) {
result.add(file);
}
}
@@ -16,6 +16,7 @@
package com.intellij.usages;
import com.intellij.ide.SelectInEditorManager;
import com.intellij.injected.editor.VirtualFileWindow;
import com.intellij.openapi.actionSystem.DataKey;
import com.intellij.openapi.actionSystem.DataSink;
import com.intellij.openapi.actionSystem.TypeSafeDataProvider;
@@ -79,7 +80,7 @@ public class UsageInfo2UsageAdapter implements UsageInModule,
@Override
public void run() {
PsiElement element = getElement();
Document document = getDocument();
Document document = PsiDocumentManager.getInstance(getProject()).getDocument(element.getContainingFile());
int startOffset = myUsageInfo.getNavigationOffset();
if (document != null) {
@@ -197,7 +198,8 @@ public class UsageInfo2UsageAdapter implements UsageInModule,
}
public boolean canNavigate() {
return getFile().isValid();
VirtualFile file = getFile();
return file != null && file.isValid();
}
public boolean canNavigateToSource() {
@@ -218,7 +220,7 @@ public class UsageInfo2UsageAdapter implements UsageInModule,
int offset = getUsageInfo().getNavigationOffset();
if (offset == -1) offset = myOffset;
if (offset >= document.getTextLength()) {
int line = Math.max(0, Math.min(myLineNumber, document.getLineCount()));
int line = Math.max(0, Math.min(myLineNumber, document.getLineCount() - 1));
offset = document.getLineStartOffset(line);
}
return offset;
@@ -323,16 +325,27 @@ public class UsageInfo2UsageAdapter implements UsageInModule,
return myUsageInfo;
}
// by start offset
public int compareTo(final UsageInfo2UsageAdapter o) {
VirtualFile containingFile = getFile();
int shift1 = 0;
if (containingFile instanceof VirtualFileWindow) {
shift1 = ((VirtualFileWindow)containingFile).getDocumentWindow().injectedToHost(0);
containingFile = ((VirtualFileWindow)containingFile).getDelegate();
}
VirtualFile oContainingFile = o.getFile();
int shift2 = 0;
if (oContainingFile instanceof VirtualFileWindow) {
shift2 = ((VirtualFileWindow)oContainingFile).getDocumentWindow().injectedToHost(0);
oContainingFile = ((VirtualFileWindow)oContainingFile).getDelegate();
}
if (containingFile == null && oContainingFile == null || !Comparing.equal(containingFile, oContainingFile)) {
return 0;
}
Segment s1 = getFirstSegment();
Segment s2 = o.getFirstSegment();
if (s1 == null || s2 == null) return 0;
return s1.getStartOffset() - s2.getStartOffset();
return s1.getStartOffset() + shift1 - s2.getStartOffset() - shift2;
}
public void rename(String newName) throws IncorrectOperationException {
@@ -909,7 +909,8 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
for (Usage usage : usages) {
if (usage instanceof UsageInFile) {
UsageInFile usageInFile = (UsageInFile)usage;
result.add(usageInFile.getFile());
VirtualFile file = usageInFile.getFile();
if (file != null) result.add(file);
}
if (usage instanceof UsageInFiles) {
@@ -889,7 +889,7 @@ public class ContainerUtil {
return value;
}
public static <T, V> V getOrCreate(final Map<T, V> result, final T key, final Factory<V> factory) {
public static <T, V> V getOrCreate(@NotNull Map<T, V> result, final T key, @NotNull Factory<V> factory) {
V value = result.get(key);
if (value == null) {
result.put(key, value = factory.create());
@@ -28,6 +28,8 @@ import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.util.DefaultJDOMExternalizer;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -54,6 +56,10 @@ public final class VcsConfiguration implements PersistentStateComponent<Element>
@NonNls private static final String VALUE_ATTR = "value";
@NonNls private static final String CONFIRM_MOVE_TO_FAILED_COMMIT_ELEMENT = "confirmMoveToFailedCommit";
@NonNls private static final String CONFIRM_REMOVE_EMPTY_CHANGELIST_ELEMENT = "confirmRemoveEmptyChangelist";
@NonNls public static final String PATCH = "patch";
@NonNls public static final String DIFF = "diff";
private Project myProject;
public boolean OFFER_MOVE_TO_ANOTHER_CHANGELIST_ON_PARTIAL_COMMIT = true;
@@ -73,6 +79,7 @@ public final class VcsConfiguration implements PersistentStateComponent<Element>
public int CHANGED_ON_SERVER_INTERVAL = 60;
public boolean SHOW_ONLY_CHANGED_IN_SELECTION_DIFF = true;
public boolean CHECK_COMMIT_MESSAGE_SPELLING = true;
public String DEFAULT_PATCH_EXTENSION = PATCH;
public enum StandardOption {
ADD(VcsBundle.message("vcs.command.name.add")),
@@ -346,4 +353,17 @@ public final class VcsConfiguration implements PersistentStateComponent<Element>
}
public String getPatchFileExtension() {
return DEFAULT_PATCH_EXTENSION;
}
public void acceptLastCreatedPatchName(final String string) {
if (StringUtil.isEmptyOrSpaces(string)) return;
final String extension = FileUtil.getExtension(string);
if (DIFF.equalsIgnoreCase(extension)) {
DEFAULT_PATCH_EXTENSION = DIFF;
} else if (PATCH.equalsIgnoreCase(extension)) {
DEFAULT_PATCH_EXTENSION = PATCH;
}
}
}
@@ -47,78 +47,6 @@ public interface ContinuationContext {
void suspend();
void ping();
class GatheringContinuationContext implements ContinuationContext {
private final List<TaskDescriptor> myList;
public GatheringContinuationContext() {
myList = new ArrayList<TaskDescriptor>();
}
public List<TaskDescriptor> getList() {
return myList;
}
@Override
public void cancelEverything() {
}
@Override
public <T extends Exception> void addExceptionHandler(Class<T> clazz, Consumer<T> consumer) {
}
@Override
public boolean handleException(Exception e) {
return false;
}
@Override
public void throwDisaster(Object disaster, final Object cure) {
}
@Override
public void keepExisting(Object disaster, Object cure) {
}
@Override
public void next(TaskDescriptor... next) {
myList.addAll(0, Arrays.asList(next));
}
@Override
public void next(List<TaskDescriptor> next) {
myList.addAll(0, next);
}
@Override
public void last(TaskDescriptor... next) {
myList.addAll(Arrays.asList(next));
}
@Override
public void last(List<TaskDescriptor> next) {
myList.addAll(next);
}
@Override
public void after(@NotNull TaskDescriptor inQueue, TaskDescriptor... next) {
int idx = -1;
for (int i = 0; i < myList.size(); i++) {
final TaskDescriptor descriptor = myList.get(i);
if (inQueue == descriptor) {
idx = i;
break;
}
}
assert idx != -1;
myList.addAll(idx, Arrays.asList(next));
}
@Override
public void suspend() {
}
@Override
public void ping() {
}
}
}
void removeNewTasksPatcher(@NotNull final Consumer<TaskDescriptor> consumer);
void addNewTasksPatcher(@NotNull final Consumer<TaskDescriptor> consumer);
}
@@ -0,0 +1,46 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util.continuation;
import com.intellij.util.Consumer;
/**
* @author irengrig
* Date: 4/7/11
* Time: 7:25 PM
*/
public class ContinuationFinalTasksInserter {
private final ContinuationContext myContext;
private final Consumer<TaskDescriptor> myPatcher;
public ContinuationFinalTasksInserter(ContinuationContext context) {
myContext = context;
myPatcher = new Consumer<TaskDescriptor>() {
@Override
public void consume(TaskDescriptor taskDescriptor) {
taskDescriptor.setHaveMagicCure(true);
}
};
}
public void allNextAreFinal() {
myContext.addNewTasksPatcher(myPatcher);
}
public void removeFinalPropertyAdder() {
myContext.removeNewTasksPatcher(myPatcher);
}
}
@@ -0,0 +1,111 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util.continuation;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author irengrig
* Date: 4/7/11
* Time: 6:43 PM
*/
public class GatheringContinuationContext implements ContinuationContext {
private final List<TaskDescriptor> myList;
public GatheringContinuationContext() {
myList = new ArrayList<TaskDescriptor>();
}
public List<TaskDescriptor> getList() {
return myList;
}
@Override
public void cancelEverything() {
}
@Override
public <T extends Exception> void addExceptionHandler(Class<T> clazz, Consumer<T> consumer) {
}
@Override
public boolean handleException(Exception e) {
return false;
}
@Override
public void throwDisaster(Object disaster, final Object cure) {
}
@Override
public void keepExisting(Object disaster, Object cure) {
}
@Override
public void next(TaskDescriptor... next) {
myList.addAll(0, Arrays.asList(next));
}
@Override
public void next(List<TaskDescriptor> next) {
myList.addAll(0, next);
}
@Override
public void last(TaskDescriptor... next) {
myList.addAll(Arrays.asList(next));
}
@Override
public void last(List<TaskDescriptor> next) {
myList.addAll(next);
}
@Override
public void after(@NotNull TaskDescriptor inQueue, TaskDescriptor... next) {
int idx = -1;
for (int i = 0; i < myList.size(); i++) {
final TaskDescriptor descriptor = myList.get(i);
if (inQueue == descriptor) {
idx = i;
break;
}
}
assert idx != -1;
myList.addAll(idx, Arrays.asList(next));
}
@Override
public void suspend() {
}
@Override
public void ping() {
}
@Override
public void addNewTasksPatcher(@NotNull Consumer<TaskDescriptor> consumer) {
}
@Override
public void removeNewTasksPatcher(@NotNull Consumer<TaskDescriptor> consumer) {
}
}
@@ -22,6 +22,7 @@ import java.util.HashMap;
import java.util.Map;
public abstract class TaskDescriptor {
// this also means that it would be called in case of chain cancel()
private boolean myHaveMagicCure;
private final String myName;
@NotNull
@@ -47,10 +47,7 @@ import com.intellij.openapi.vfs.newvfs.RefreshSession;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.util.Consumer;
import com.intellij.util.WaitForProgressToShow;
import com.intellij.util.continuation.Continuation;
import com.intellij.util.continuation.ContinuationContext;
import com.intellij.util.continuation.TaskDescriptor;
import com.intellij.util.continuation.Where;
import com.intellij.util.continuation.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -111,9 +108,9 @@ public class PatchApplier<BinaryType extends FilePatch> {
@AsynchronousExecution
public void execute(boolean showSuccessNotification) {
final Continuation continuation = new Continuation(myProject, true);
final ContinuationContext.GatheringContinuationContext initContext =
new ContinuationContext.GatheringContinuationContext();
final Continuation continuation = Continuation.createForCurrentProgress(myProject, true, "Apply patch");
final GatheringContinuationContext initContext =
new GatheringContinuationContext();
scheduleSelf(showSuccessNotification, initContext);
continuation.run(initContext.getList());
}
@@ -17,6 +17,7 @@ package com.intellij.openapi.vcs.actions;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.AbstractVcs;
@@ -89,7 +90,10 @@ public abstract class AbstractShowDiffAction extends AbstractVcsAction{
final Project project = vcsContext.getProject();
final VirtualFile selectedFile = vcsContext.getSelectedFiles()[0];
final FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
fileDocumentManager.saveDocument(fileDocumentManager.getDocument(selectedFile));
final Document document = fileDocumentManager.getDocument(selectedFile);
if (document != null) {
fileDocumentManager.saveDocument(document);
}
final ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(project);
final AbstractVcs vcs = vcsManager.getVcsFor(selectedFile);
@@ -29,6 +29,7 @@ import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.JDOMExternalizable;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.vcs.VcsBundle;
import com.intellij.openapi.vcs.VcsConfiguration;
import com.intellij.openapi.vcs.changes.*;
import com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager;
import com.intellij.util.WaitForProgressToShow;
@@ -118,7 +119,7 @@ public class CreatePatchCommitExecutor implements CommitExecutorWithHelp, Projec
if (PATCH_PATH.length() == 0) {
PATCH_PATH = myProject.getBaseDir().getPresentableUrl();
}
myPanel.setFileName(ShelveChangesManager.suggestPatchName(commitMessage, new File(PATCH_PATH)));
myPanel.setFileName(ShelveChangesManager.suggestPatchName(myProject, commitMessage, new File(PATCH_PATH), null));
myPanel.setReversePatch(REVERSE_PATCH);
return myPanel.getPanel();
}
@@ -150,6 +151,7 @@ public class CreatePatchCommitExecutor implements CommitExecutorWithHelp, Projec
try {
final String fileName = myPanel.getFileName();
final File file = new File(fileName).getAbsoluteFile();
VcsConfiguration.getInstance(myProject).acceptLastCreatedPatchName(file.getName());
PATCH_PATH = file.getParent();
REVERSE_PATCH = myPanel.isReversePatch();
@@ -141,7 +141,7 @@ public class CreatePatchConfigurationPanel {
}
public boolean isOkToExecute() {
return myErrorLabel.getText() == null || myErrorLabel.getText().length() == 0;
return myErrorLabel.getText() == null || myErrorLabel.getText().length() == 0;
}
public String getError() {
@@ -36,11 +36,12 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
public class PatchFileType implements FileType {
public static final String NAME = "PATCH";
@NotNull
@NonNls
public String getName() {
return "PATCH";
return NAME;
}
@NotNull
@@ -21,8 +21,10 @@ public class PatchNameChecker {
public final static int MAX = 100;
private final String myName;
private String myError;
private final String myPath;
public PatchNameChecker(final String name) {
myPath = name;
myName = new File(name).getName();
}
@@ -33,6 +35,9 @@ public class PatchNameChecker {
} else if (myName.length() > MAX) {
myError = "File name length cannot exceed " + MAX + " characters";
return false;
} else if (new File(myPath).exists()) {
myError = "File with the same name already exists";
return false;
}
return true;
}
@@ -24,7 +24,9 @@ import com.intellij.openapi.fileChooser.FileChooserFactory;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vcs.AbstractVcsHelper;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
@@ -60,28 +62,49 @@ public class ImportIntoShelfAction extends DumbAwareAction {
@Override
public void consume(final VirtualFile[] virtualFiles) {
if (virtualFiles.length == 0) return;
ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
@Override
public void run() {
final List<VcsException> exceptions = new ArrayList<VcsException>();
final List<ShelvedChangeList> lists =
ShelveChangesManager.getInstance(project).importChangeLists(Arrays.asList(virtualFiles), new Consumer<VcsException>() {
@Override
public void consume(VcsException e) {
exceptions.add(e);
}
});
if (! lists.isEmpty()) {
ShelvedChangesViewManager.getInstance(project).activateView(lists.get(lists.size() - 1));
//gatherPatchFiles
final ProgressManager pm = ProgressManager.getInstance();
final ShelveChangesManager shelveChangesManager = ShelveChangesManager.getInstance(project);
final List<VirtualFile> asList = Arrays.asList(virtualFiles);
final List<VirtualFile> patchTypeFiles = new ArrayList<VirtualFile>();
final boolean filesFound = pm.runProcessWithProgressSynchronously(new Runnable() {
@Override
public void run() {
patchTypeFiles.addAll(shelveChangesManager.gatherPatchFiles(asList));
}
if (! exceptions.isEmpty()) {
AbstractVcsHelper.getInstance(project).showErrors(exceptions, "Import patches into shelf");
}, "Looking for patch files...", true, project);
if (! filesFound || patchTypeFiles.isEmpty()) return;
if (! patchTypeFiles.equals(asList)) {
final int toImport = Messages.showYesNoDialog(project, "Found " +
(patchTypeFiles.size() == 1
? "one patch file (" + patchTypeFiles.get(0).getPath() + ")."
: (patchTypeFiles.size() + " patch files.")) +
"\nContinue with import?", "Import patches", Messages.getQuestionIcon());
if (DialogWrapper.CANCEL_EXIT_CODE == toImport) return;
}
pm.runProcessWithProgressSynchronously(new Runnable() {
@Override
public void run() {
final List<VcsException> exceptions = new ArrayList<VcsException>();
final List<ShelvedChangeList> lists =
shelveChangesManager.importChangeLists(patchTypeFiles, new Consumer<VcsException>() {
@Override
public void consume(VcsException e) {
exceptions.add(e);
}
});
if (!lists.isEmpty()) {
ShelvedChangesViewManager.getInstance(project).activateView(lists.get(lists.size() - 1));
}
if (!exceptions.isEmpty()) {
AbstractVcsHelper.getInstance(project).showErrors(exceptions, "Import patches into shelf");
}
if (lists.isEmpty() && exceptions.isEmpty()) {
VcsBalloonProblemNotifier.showOverChangesView(project, "No patches found", MessageType.WARNING);
}
}
if (lists.isEmpty() && exceptions.isEmpty()) {
VcsBalloonProblemNotifier.showOverChangesView(project, "No patches found", MessageType.WARNING);
}
}
}, "Import patches into shelf", true, project);
}, "Import patches into shelf", true, project);
}
});
}
@@ -37,21 +37,17 @@ import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.FilePathImpl;
import com.intellij.openapi.vcs.VcsBundle;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.changes.*;
import com.intellij.openapi.vcs.changes.patch.PatchFileType;
import com.intellij.openapi.vcs.changes.patch.PatchNameChecker;
import com.intellij.openapi.vcs.changes.ui.RollbackWorker;
import com.intellij.openapi.vcs.impl.VcsFileTypeFactory;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import com.intellij.util.PathUtil;
import com.intellij.util.SmartList;
import com.intellij.util.continuation.Continuation;
import com.intellij.util.continuation.ContinuationContext;
import com.intellij.util.continuation.TaskDescriptor;
import com.intellij.util.continuation.Where;
import com.intellij.util.continuation.*;
import com.intellij.util.messages.MessageBus;
import com.intellij.util.messages.Topic;
import com.intellij.util.text.CharArrayCharSequence;
@@ -68,8 +64,6 @@ import java.util.*;
public class ShelveChangesManager implements ProjectComponent, JDOMExternalizable {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager");
@NonNls private static final String PATCH_EXTENSION = "patch";
public static ShelveChangesManager getInstance(Project project) {
return PeriodicalTasksCloser.getInstance().safeGetComponent(project, ShelveChangesManager.class);
}
@@ -221,17 +215,30 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl
}
}
public List<VirtualFile> gatherPatchFiles(final Collection<VirtualFile> files) {
final List<VirtualFile> result = new ArrayList<VirtualFile>();
final LinkedList<VirtualFile> filesQueue = new LinkedList<VirtualFile>(files);
while (! filesQueue.isEmpty()) {
ProgressManager.checkCanceled();
final VirtualFile file = filesQueue.removeFirst();
if (file.isDirectory()) {
filesQueue.addAll(Arrays.asList(file.getChildren()));
continue;
}
if (PatchFileType.NAME.equals(file.getFileType().getName())) {
result.add(file);
}
}
return result;
}
public List<ShelvedChangeList> importChangeLists(final Collection<VirtualFile> files, final Consumer<VcsException> exceptionConsumer) {
final List<ShelvedChangeList> result = new ArrayList<ShelvedChangeList>(files.size());
try {
final LinkedList<VirtualFile> filesQueue = new LinkedList<VirtualFile>(files);
while (! filesQueue.isEmpty()) {
for (VirtualFile file : files) {
ProgressManager.checkCanceled();
final VirtualFile file = filesQueue.removeFirst();
if (file.isDirectory()) {
filesQueue.addAll(Arrays.asList(file.getChildren()));
continue;
}
final String description = file.getNameWithoutExtension().replace('_', ' ');
final File patchPath = getPatchPath(description);
@@ -289,16 +296,17 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl
file.mkdirs();
}
return suggestPatchName(commitMessage.length() > PatchNameChecker.MAX ? (commitMessage.substring(0, PatchNameChecker.MAX)) :
commitMessage, file);
return suggestPatchName(myProject, commitMessage.length() > PatchNameChecker.MAX ? (commitMessage.substring(0, PatchNameChecker.MAX)) :
commitMessage, file, VcsConfiguration.PATCH);
}
public static File suggestPatchName(final String commitMessage, final File file) {
public static File suggestPatchName(Project project, final String commitMessage, final File file, String extension) {
@NonNls String defaultPath = PathUtil.suggestFileName(commitMessage);
if (defaultPath.length() == 0) {
defaultPath = "unnamed";
}
return FileUtil.findSequentNonexistentFile(file, defaultPath, PATCH_EXTENSION);
return FileUtil.findSequentNonexistentFile(file, defaultPath,
extension == null ? VcsConfiguration.getInstance(project).getPatchFileExtension() : extension);
}
public void unshelveChangeList(final ShelvedChangeList changeList, @Nullable final List<ShelvedChange> changes,
@@ -312,8 +320,8 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl
@Nullable final List<ShelvedBinaryFile> binaryFiles,
@Nullable final LocalChangeList targetChangeList,
boolean showSuccessNotification) {
final Continuation continuation = new Continuation(myProject, true);
final ContinuationContext.GatheringContinuationContext initContext = new ContinuationContext.GatheringContinuationContext();
final Continuation continuation = Continuation.createForCurrentProgress(myProject, true, "Unshelve changes");
final GatheringContinuationContext initContext = new GatheringContinuationContext();
scheduleUnshelveChangeList(changeList, changes, binaryFiles, targetChangeList, showSuccessNotification, initContext);
continuation.run(initContext.getList());
}
@@ -27,11 +27,16 @@ import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.VcsBundle;
import com.intellij.openapi.vcs.changes.BackgroundFromStartOption;
import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vcs.changes.LocalChangeList;
import com.intellij.openapi.vcs.changes.ui.ChangeListChooser;
import org.jetbrains.annotations.NotNull;
import java.util.List;
@@ -65,7 +70,7 @@ public class UnshelveChangesAction extends AnAction {
}
}
defaultName = changeLists.length == 1 ? changeLists [0].DESCRIPTION : null;
ChangeListChooser chooser = new ChangeListChooser(project, allChangeLists, list,
final ChangeListChooser chooser = new ChangeListChooser(project, allChangeLists, list,
VcsBundle.message("unshelve.changelist.chooser.title"), defaultName);
chooser.show();
if (!chooser.isOK()) {
@@ -74,9 +79,16 @@ public class UnshelveChangesAction extends AnAction {
FileDocumentManager.getInstance().saveAllDocuments();
for(ShelvedChangeList changeList: changeLists) {
ShelveChangesManager.getInstance(project).unshelveChangeList(changeList, changes, binaryFiles, chooser.getSelectedList());
}
final List<ShelvedBinaryFile> finalBinaryFiles = binaryFiles;
final List<ShelvedChange> finalChanges = changes;
ProgressManager.getInstance().run(new Task.Backgroundable(project, "Unshelve changes", true, BackgroundFromStartOption.getInstance()) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
for(ShelvedChangeList changeList: changeLists) {
ShelveChangesManager.getInstance(project).unshelveChangeList(changeList, finalChanges, finalBinaryFiles, chooser.getSelectedList());
}
}
});
}
@Override
@@ -17,6 +17,7 @@ package com.intellij.openapi.vcs.impl;
import com.intellij.openapi.fileTypes.FileTypeFactory;
import com.intellij.openapi.fileTypes.FileTypeConsumer;
import com.intellij.openapi.vcs.VcsConfiguration;
import com.intellij.openapi.vcs.changes.patch.PatchFileType;
import org.jetbrains.annotations.NotNull;
@@ -25,6 +26,6 @@ import org.jetbrains.annotations.NotNull;
*/
public class VcsFileTypeFactory extends FileTypeFactory {
public void createFileTypes(@NotNull FileTypeConsumer consumer) {
consumer.consume(new PatchFileType(), "patch;diff");
consumer.consume(new PatchFileType(), VcsConfiguration.PATCH + ";" + VcsConfiguration.DIFF);
}
}
@@ -0,0 +1,160 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.vcs.update;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.progress.PerformInBackgroundOption;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.util.Icons;
import com.intellij.util.continuation.*;
import org.jetbrains.annotations.NotNull;
/**
* @author irengrig
* Date: 4/7/11
* Time: 7:06 PM
*/
public class TestContinuationAction extends AnAction {
public TestContinuationAction() {
super("Test Continuation", "Test Continuation", Icons.ADVICE_ICON);
}
@Override
public void actionPerformed(final AnActionEvent e) {
final Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext());
if (project == null) return;
ProgressManager.getInstance().run(new Task.Backgroundable(project, "Test Continuation", true,
new PerformInBackgroundOption() {
@Override
public boolean shouldStartInBackground() {
return false;
}
@Override
public void processSentToBackground() {
}
}) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
final Continuation continuation = Continuation.createForCurrentProgress(project, true, e.getPresentation().getText());
final ReportTask finalBlock = new ReportTask("I'm finally block!");
finalBlock.setHaveMagicCure(true);
continuation.run(new TaskDescriptor[] {new LongTaskDescriptor("First"), new ReportTask("First complete"),
new TaskDescriptor("Adding task", Where.POOLED) {
@Override
public void run(final ContinuationContext context) {
addMore(context);
try {
Thread.sleep(10000);
}
catch (InterruptedException e1) {
//
}
}
},
new LongTaskDescriptor("Second"), new ReportTask("Second complete"),
new TaskDescriptor("Adding task 2", Where.POOLED) {
@Override
public void run(final ContinuationContext context) {
addMoreSurviving(context);
try {
Thread.sleep(10000);
}
catch (InterruptedException e1) {
//
}
throw new IllegalStateException();
/*context.suspend();
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
context.ping();
}
}, ModalityState.NON_MODAL);*/
}
},
new LongTaskDescriptor("Third"), new ReportTask("Third complete"),
finalBlock});
}
@Override
public void onCancel() {
Messages.showInfoMessage("cancel!", myTitle);
}
@Override
public void onSuccess() {
Messages.showInfoMessage("success!", myTitle);
}
});
}
private void addMore(ContinuationContext context) {
context.next(new LongTaskDescriptor("Inside killable"), new ReportTask("Inside killable complete"));
}
private void addMoreSurviving(ContinuationContext context) {
final ContinuationFinalTasksInserter finalTasksInserter = new ContinuationFinalTasksInserter(context);
finalTasksInserter.allNextAreFinal();
context.next(new LongTaskDescriptor("Inside surviving"), new ReportTask("Inside surviving complete"));
finalTasksInserter.removeFinalPropertyAdder();
}
@Override
public void update(AnActionEvent e) {
final Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext());
e.getPresentation().setEnabled(project != null);
}
private static class ReportTask extends TaskDescriptor {
private ReportTask(String name) {
super(name, Where.AWT);
}
@Override
public void run(ContinuationContext context) {
Messages.showInfoMessage(getName(), "Result");
}
}
private static class LongTaskDescriptor extends TaskDescriptor {
private LongTaskDescriptor(final String name) {
super(name, Where.POOLED);
}
@Override
public void run(ContinuationContext context) {
final ProgressIndicator pi = ProgressManager.getInstance().getProgressIndicator();
pi.setText(getName());
try {
Thread.sleep(10000);
}
catch (InterruptedException e) {
//
}
pi.setText("");
}
}
}
@@ -15,91 +15,50 @@
*/
package com.intellij.util.continuation;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator;
import com.intellij.openapi.progress.impl.ProgressManagerImpl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.CalledInAny;
import com.intellij.openapi.vcs.CalledInAwt;
import com.intellij.openapi.vcs.changes.BackgroundFromStartOption;
import com.intellij.util.Consumer;
import com.intellij.util.Processor;
import com.intellij.util.WaitForProgressToShow;
import com.intellij.util.concurrency.Semaphore;
import com.sun.tools.javac.code.Attribute;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
public class Continuation {
private GeneralRunner myGeneralRunner;
public Continuation(final Project project, final boolean cancellable) {
myGeneralRunner = new GeneralRunner(project, cancellable);
private Continuation(GeneralRunner generalRunner) {
myGeneralRunner = generalRunner;
}
public static Continuation createForCurrentProgress(final Project project, final boolean cancellable, final String commonTitle) {
return new Continuation(new SameProgressRunner(project, cancellable, commonTitle));
}
public static Continuation createFragmented(final Project project, final boolean cancellable) {
return new Continuation(new SeparatePiecesRunner(project, cancellable));
}
public void run(final TaskDescriptor... tasks) {
if (tasks.length == 0) return;
myGeneralRunner.next(tasks);
pingRunnerInCorrectThread();
myGeneralRunner.ping();
}
public void run(final List<TaskDescriptor> tasks) {
if (tasks.isEmpty()) return;
myGeneralRunner.next(tasks);
pingRunnerInCorrectThread();
}
public void runAndWait(final TaskDescriptor... tasks) {
runAndWait(Arrays.asList(tasks));
}
public void runAndWait(final List<TaskDescriptor> tasks) {
final Semaphore semaphore = new Semaphore();
semaphore.down();
tasks.add(new TaskDescriptor("", Where.AWT) {
@Override
public void run(ContinuationContext context) {
semaphore.up();
}
});
run(tasks);
semaphore.waitFor();
myGeneralRunner.ping();
}
public <T extends Exception> void addExceptionHandler(final Class<T> clazz, final Consumer<T> consumer) {
myGeneralRunner.addExceptionHandler(clazz, consumer);
}
public void runIndirect(final Consumer<ContinuationContext> consumer) {
consumer.consume(myGeneralRunner);
if (myGeneralRunner.isEmpty()) return;
pingRunnerInCorrectThread();
}
public void resume() {
myGeneralRunner.ping();
}
private void pingRunnerInCorrectThread() {
if (! ApplicationManager.getApplication().isDispatchThread()) {
WaitForProgressToShow.runOrInvokeLaterAboveProgress(new Runnable() {
public void run() {
myGeneralRunner.ping();
}
}, null, myGeneralRunner.getProject());
} else {
myGeneralRunner.ping();
}
}
public void clearQueue() {
myGeneralRunner.cancelEverything();
}
@@ -116,224 +75,7 @@ public class Continuation {
return myGeneralRunner.isEmpty();
}
private static class TaskWrapper extends Task.Backgroundable {
private final TaskDescriptor myTaskDescriptor;
private final GeneralRunner myGeneralRunner;
private TaskWrapper(@Nullable Project project,
@NotNull String title,
boolean canBeCancelled,
TaskDescriptor taskDescriptor,
GeneralRunner generalRunner) {
super(project, title, canBeCancelled, BackgroundFromStartOption.getInstance());
myTaskDescriptor = taskDescriptor;
myGeneralRunner = generalRunner;
}
@Override
public void run(@NotNull ProgressIndicator indicator) {
myTaskDescriptor.run(myGeneralRunner);
}
@Override
public void onSuccess() {
myGeneralRunner.ping();
}
}
private static class GeneralRunner implements ContinuationContext {
private final Project myProject;
private final boolean myCancellable;
private final List<TaskDescriptor> myQueue;
private final Object myQueueLock;
private volatile boolean myTriggerSuspend;
private ProgressIndicator myIndicator;
private final Map<Object, Object> myDisasters;
private final Map<Class<? extends Exception>, Consumer<Exception>> myHandlersMap;
private GeneralRunner(final Project project, boolean cancellable) {
myProject = project;
myCancellable = cancellable;
myQueueLock = new Object();
myQueue = new LinkedList<TaskDescriptor>();
myDisasters = new HashMap<Object, Object>();
myHandlersMap = new HashMap<Class<? extends Exception>, Consumer<Exception>>();
}
public <T extends Exception> void addExceptionHandler(final Class<T> clazz, final Consumer<T> consumer) {
synchronized (myQueueLock) {
myHandlersMap.put(clazz, new Consumer<Exception>() {
@Override
public void consume(Exception e) {
if (! clazz.isAssignableFrom(e.getClass())) {
throw new RuntimeException(e);
}
consumer.consume((T) e);
}
});
}
}
public Project getProject() {
return myProject;
}
public void clearDisasters() {
synchronized (myQueueLock) {
myDisasters.clear();
}
}
@Override
public boolean handleException(Exception e) {
synchronized (myQueueLock) {
final Class<? extends Exception> aClass = e.getClass();
Consumer<Exception> consumer = myHandlersMap.get(e.getClass());
if (consumer != null) {
consumer.consume(e);
return true;
}
for (Map.Entry<Class<? extends Exception>, Consumer<Exception>> entry : myHandlersMap.entrySet()) {
if (entry.getKey().isAssignableFrom(aClass)) {
entry.getValue().consume(e);
return true;
}
}
}
return false;
}
@CalledInAny
public void cancelEverything() {
synchronized (myQueueLock) {
myQueue.clear();
}
}
public void cancelCurrent() {
if (myIndicator != null) {
myIndicator.cancel();
}
}
public void suspend() {
myTriggerSuspend = true;
}
@Override
public void keepExisting(Object disaster, Object cure) {
synchronized (myQueueLock) {
for (TaskDescriptor taskDescriptor : myQueue) {
taskDescriptor.addCure(disaster, cure);
}
}
}
@Override
public void throwDisaster(@NotNull Object disaster, @NotNull final Object cure) {
synchronized (myQueueLock) {
final Iterator<TaskDescriptor> iterator = myQueue.iterator();
while (iterator.hasNext()) {
final TaskDescriptor taskDescriptor = iterator.next();
if (taskDescriptor.isHaveMagicCure()) continue;
final Object taskCure = taskDescriptor.hasCure(disaster);
if (! cure.equals(taskCure)) {
iterator.remove();
}
}
myDisasters.put(disaster, cure);
}
}
@Override
public void after(@NotNull TaskDescriptor inQueue, TaskDescriptor... next) {
synchronized (myQueueLock) {
int idx = -1;
int i = 0;
for (TaskDescriptor descriptor : myQueue) {
if (descriptor == inQueue) {
idx = i;
break;
}
++ i;
}
assert idx != -1;
myQueue.addAll(idx + 1, Arrays.asList(next));
}
}
@CalledInAny
public void next(TaskDescriptor... next) {
synchronized (myQueueLock) {
myQueue.addAll(0, Arrays.asList(next));
}
}
public void next(List<TaskDescriptor> next) {
synchronized (myQueueLock) {
myQueue.addAll(0, next);
}
}
@Override
public void last(List<TaskDescriptor> next) {
synchronized (myQueueLock) {
myQueue.addAll(next);
}
}
@Override
public void last(TaskDescriptor... next) {
synchronized (myQueueLock) {
myQueue.addAll(Arrays.asList(next));
}
}
public boolean isEmpty() {
synchronized (myQueueLock) {
return myQueue.isEmpty();
}
}
@CalledInAwt
public void ping() {
ApplicationManager.getApplication().assertIsDispatchThread();
while (true) {
// stop if project is being disposed
if (! myProject.isOpen()) return;
TaskDescriptor current;
synchronized (myQueueLock) {
if (myQueue.isEmpty()) return;
if (myTriggerSuspend) {
myTriggerSuspend = false;
return;
}
current = myQueue.remove(0);
// check if some tasks were scheduled after disaster was thrown, anyway, they should also be checked for cure
if (! current.isHaveMagicCure()) {
for (Map.Entry<Object, Object> entry : myDisasters.entrySet()) {
if (! entry.getValue().equals(current.hasCure(entry.getKey()))) {
current = null;
break;
}
}
}
if (current == null) continue;
}
if (Where.AWT.equals(current.getWhere())) {
myIndicator = null;
current.run(this);
} else {
final TaskWrapper task = new TaskWrapper(myProject, current.getName(), myCancellable, current, this);
myIndicator = ApplicationManager.getApplication().isUnitTestMode() ? new EmptyProgressIndicator() :
new BackgroundableProcessIndicator(task);
ProgressManagerImpl.runProcessWithProgressAsynchronously(task, myIndicator);
return;
}
}
}
public void onCancel() {
myGeneralRunner.onCancel();
}
}
@@ -0,0 +1,272 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util.continuation;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator;
import com.intellij.openapi.progress.impl.ProgressManagerImpl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.CalledInAny;
import com.intellij.openapi.vcs.CalledInAwt;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author irengrig
* Date: 4/7/11
* Time: 2:44 PM
*/
abstract class GeneralRunner implements ContinuationContext {
protected final Project myProject;
protected final boolean myCancellable;
protected final List<TaskDescriptor> myQueue;
protected final Object myQueueLock;
protected volatile boolean myTriggerSuspend;
protected ProgressIndicator myIndicator;
protected final Map<Object, Object> myDisasters;
private final List<Consumer<TaskDescriptor>> myTasksPatchers;
private final Map<Class<? extends Exception>, Consumer<Exception>> myHandlersMap;
GeneralRunner(final Project project, boolean cancellable) {
myProject = project;
myCancellable = cancellable;
myQueueLock = new Object();
myQueue = new LinkedList<TaskDescriptor>();
myDisasters = new HashMap<Object, Object>();
myHandlersMap = new HashMap<Class<? extends Exception>, Consumer<Exception>>();
myTasksPatchers = new ArrayList<Consumer<TaskDescriptor>>();
}
public <T extends Exception> void addExceptionHandler(final Class<T> clazz, final Consumer<T> consumer) {
synchronized (myQueueLock) {
myHandlersMap.put(clazz, new Consumer<Exception>() {
@Override
public void consume(Exception e) {
if (!clazz.isAssignableFrom(e.getClass())) {
throw new RuntimeException(e);
}
consumer.consume((T)e);
}
});
}
}
public Project getProject() {
return myProject;
}
public void clearDisasters() {
synchronized (myQueueLock) {
myDisasters.clear();
}
}
@Override
public boolean handleException(Exception e) {
synchronized (myQueueLock) {
final Class<? extends Exception> aClass = e.getClass();
Consumer<Exception> consumer = myHandlersMap.get(e.getClass());
if (consumer != null) {
consumer.consume(e);
return true;
}
for (Map.Entry<Class<? extends Exception>, Consumer<Exception>> entry : myHandlersMap.entrySet()) {
if (entry.getKey().isAssignableFrom(aClass)) {
entry.getValue().consume(e);
return true;
}
}
}
return false;
}
@CalledInAny
public void cancelEverything() {
synchronized (myQueueLock) {
myQueue.clear();
myIndicator = null;
}
}
public void cancelCurrent() {
if (myIndicator != null) {
myIndicator.cancel();
}
}
public void suspend() {
myTriggerSuspend = true;
}
@Override
public void keepExisting(Object disaster, Object cure) {
synchronized (myQueueLock) {
for (TaskDescriptor taskDescriptor : myQueue) {
taskDescriptor.addCure(disaster, cure);
}
}
}
@Override
public void throwDisaster(@NotNull Object disaster, @NotNull final Object cure) {
synchronized (myQueueLock) {
final Iterator<TaskDescriptor> iterator = myQueue.iterator();
while (iterator.hasNext()) {
final TaskDescriptor taskDescriptor = iterator.next();
if (taskDescriptor.isHaveMagicCure()) continue;
final Object taskCure = taskDescriptor.hasCure(disaster);
if (! cure.equals(taskCure)) {
iterator.remove();
}
}
myDisasters.put(disaster, cure);
}
}
@Override
public void after(@NotNull TaskDescriptor inQueue, TaskDescriptor... next) {
synchronized (myQueueLock) {
int idx = -1;
int i = 0;
for (TaskDescriptor descriptor : myQueue) {
if (descriptor == inQueue) {
idx = i;
break;
}
++ i;
}
assert idx != -1;
final List<TaskDescriptor> asList = Arrays.asList(next);
patchTasks(asList);
myQueue.addAll(idx + 1, asList);
}
}
private void patchTasks(final List<TaskDescriptor> next) {
for (TaskDescriptor descriptor : next) {
for (Consumer<TaskDescriptor> tasksPatcher : myTasksPatchers) {
tasksPatcher.consume(descriptor);
}
}
}
@CalledInAny
public void next(TaskDescriptor... next) {
synchronized (myQueueLock) {
final List<TaskDescriptor> asList = Arrays.asList(next);
patchTasks(asList);
myQueue.addAll(0, asList);
}
}
public void next(List<TaskDescriptor> next) {
synchronized (myQueueLock) {
patchTasks(next);
myQueue.addAll(0, next);
}
}
@Override
public void last(List<TaskDescriptor> next) {
synchronized (myQueueLock) {
patchTasks(next);
myQueue.addAll(next);
}
}
@Override
public void last(TaskDescriptor... next) {
synchronized (myQueueLock) {
final List<TaskDescriptor> asList = Arrays.asList(next);
patchTasks(asList);
myQueue.addAll(asList);
}
}
public boolean isEmpty() {
synchronized (myQueueLock) {
return myQueue.isEmpty();
}
}
public abstract void ping();
@Override
public void addNewTasksPatcher(@NotNull Consumer<TaskDescriptor> consumer) {
synchronized (myQueueLock) {
myTasksPatchers.add(consumer);
}
}
@Override
public void removeNewTasksPatcher(@NotNull Consumer<TaskDescriptor> consumer) {
synchronized (myQueueLock) {
myTasksPatchers.remove(consumer);
}
}
@CalledInAwt
public void onCancel() {
// left only "final" tasks
synchronized (myQueueLock) {
if (myQueue.isEmpty()) return;
final Iterator<TaskDescriptor> iterator = myQueue.iterator();
while (iterator.hasNext()) {
final TaskDescriptor next = iterator.next();
if (! next.isHaveMagicCure()) {
iterator.remove();
}
}
}
ping();
}
// null - no more tasks or suspend
@Nullable
protected TaskDescriptor getNextMatching() {
while (true) {
synchronized (myQueueLock) {
if (myQueue.isEmpty()) return null;
if (myTriggerSuspend) {
return null;
}
TaskDescriptor current = myQueue.remove(0);
// check if some tasks were scheduled after disaster was thrown, anyway, they should also be checked for cure
if (! current.isHaveMagicCure()) {
if (myIndicator != null && myIndicator.isCanceled()) {
current = null;
continue;
} else {
for (Map.Entry<Object, Object> entry : myDisasters.entrySet()) {
if (! entry.getValue().equals(current.hasCure(entry.getKey()))) {
current = null;
break;
}
}
}
}
if (current != null) return current;
}
}
}
}
@@ -0,0 +1,106 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util.continuation;
import com.intellij.CommonBundle;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vcs.changes.BackgroundFromStartOption;
import com.intellij.util.WaitForProgressToShow;
import com.intellij.util.concurrency.Semaphore;
import sun.util.LocaleServiceProviderPool;
/**
* @author irengrig
* Date: 4/7/11
* Time: 2:54 PM
*/
public class SameProgressRunner extends GeneralRunner {
private final Thread myInitThread;
private final Semaphore mySemaphore;
private final static Logger LOG = Logger.getInstance("#com.intellij.util.continuation.SameProgressRunner");
public SameProgressRunner(Project project, boolean cancellable, final String commonName) {
super(project, cancellable);
final Application application = ApplicationManager.getApplication();
if (! application.isUnitTestMode()) {
assert ! application.isDispatchThread();
}
myIndicator = ProgressManager.getInstance().getProgressIndicator();
if (myIndicator == null) {
myIndicator = new EmptyProgressIndicator();
}
myInitThread = Thread.currentThread();
mySemaphore = new Semaphore();
}
@Override
public void ping() {
myTriggerSuspend = false;
if (Thread.currentThread().equals(myInitThread)) {
pingInSourceThread();
} else {
mySemaphore.up();
}
}
private void pingInSourceThread() {
while (true) {
try {
// stop if project is being disposed
if (! myProject.isOpen()) return;
final TaskDescriptor current = getNextMatching();
if (current == null) {
synchronized (myQueueLock) {
if (myTriggerSuspend) {
mySemaphore.down();
while (myTriggerSuspend) {
mySemaphore.waitFor(500);
}
continue;
} else {
return;
}
}
}
if (Where.AWT.equals(current.getWhere())) {
WaitForProgressToShow.runOrInvokeAndWaitAboveProgress(new Runnable() {
@Override
public void run() {
current.run(SameProgressRunner.this);
}
});
} else {
current.run(this);
}
} catch (ProcessCanceledException ignored) {
} catch (Throwable t) {
LOG.error(t);
myIndicator.cancel();
}
}
}
}
@@ -0,0 +1,108 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util.continuation;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator;
import com.intellij.openapi.progress.impl.ProgressManagerImpl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.CalledInAwt;
import com.intellij.openapi.vcs.changes.BackgroundFromStartOption;
import com.intellij.util.WaitForProgressToShow;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author irengrig
* Date: 4/7/11
* Time: 2:46 PM
*/
public class SeparatePiecesRunner extends GeneralRunner {
public SeparatePiecesRunner(Project project, boolean cancellable) {
super(project, cancellable);
}
@CalledInAwt
public void ping() {
if (! ApplicationManager.getApplication().isDispatchThread()) {
WaitForProgressToShow.runOrInvokeLaterAboveProgress(new Runnable() {
public void run() {
pingImpl();
}
}, null, myProject);
} else {
pingImpl();
}
}
private void pingImpl() {
while (true) {
// stop if project is being disposed
if (! myProject.isOpen()) return;
final TaskDescriptor current = getNextMatching();
if (current == null) {
synchronized (myQueueLock) {
myTriggerSuspend = false;
}
return;
}
if (Where.AWT.equals(current.getWhere())) {
myIndicator = null;
current.run(this);
} else {
final TaskWrapper task = new TaskWrapper(myProject, current.getName(), myCancellable, current, this);
myIndicator = ApplicationManager.getApplication().isUnitTestMode() ? new EmptyProgressIndicator() :
new BackgroundableProcessIndicator(task);
ProgressManagerImpl.runProcessWithProgressAsynchronously(task, myIndicator);
return;
}
}
}
static class TaskWrapper extends Task.Backgroundable {
private final TaskDescriptor myTaskDescriptor;
private final GeneralRunner myGeneralRunner;
TaskWrapper(@Nullable Project project,
@NotNull String title,
boolean canBeCancelled,
TaskDescriptor taskDescriptor,
GeneralRunner generalRunner) {
super(project, title, canBeCancelled, BackgroundFromStartOption.getInstance());
myTaskDescriptor = taskDescriptor;
myGeneralRunner = generalRunner;
}
@Override
public void run(@NotNull ProgressIndicator indicator) {
myTaskDescriptor.run(myGeneralRunner);
}
@Override
public void onSuccess() {
myGeneralRunner.ping();
}
@Override
public void onCancel() {
myGeneralRunner.onCancel();
}
}
}
@@ -20,7 +20,6 @@ import com.intellij.notification.NotificationType;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
@@ -37,7 +36,6 @@ import com.intellij.ui.CheckboxTree;
import com.intellij.ui.CheckedTreeNode;
import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.Consumer;
import com.intellij.util.continuation.ContinuationContext;
import com.intellij.util.text.DateFormatUtil;
import com.intellij.util.ui.UIUtil;
@@ -72,7 +70,8 @@ import java.util.*;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import static git4idea.ui.GitUIUtil.*;
import static git4idea.ui.GitUIUtil.notifyError;
import static git4idea.ui.GitUIUtil.notifyMessage;
/**
* The dialog that allows pushing active branches.
@@ -304,8 +303,8 @@ public class GitPushActiveBranchesDialog extends DialogWrapper {
final ArrayList<VcsException> errors = new ArrayList<VcsException>();
for (Root r : rootsToPush) {
GitLineHandler h = new GitLineHandler(myProject, r.root, GitCommand.PUSH);
String src = r.commitToPush != null ? r.commitToPush : r.branch;
h.addParameters("-v", r.remote, src + ":" + r.remoteBranch);
String src = r.commitToPush != null ? r.commitToPush : r.currentBranch;
h.addParameters("-v", r.remoteName, src + ":" + r.remoteBranch);
GitPushUtils.trackPushRejectedAsError(h, "Rejected push (" + r.root.getPresentableUrl() + "): ");
errors.addAll(GitHandlerUtil.doSynchronouslyWithExceptions(h));
}
@@ -321,7 +320,7 @@ public class GitPushActiveBranchesDialog extends DialogWrapper {
for (int i = 0; i < myTreeRoot.getChildCount(); i++) {
CheckedTreeNode node = (CheckedTreeNode) myTreeRoot.getChildAt(i);
Root r = (Root)node.getUserObject();
if (r.remote == null || r.commits.size() == 0) {
if (r.remoteName == null || r.commits.size() == 0) {
continue;
}
boolean topCommit = true;
@@ -646,7 +645,7 @@ public class GitPushActiveBranchesDialog extends DialogWrapper {
for (Commit c : r.commits) {
CheckedTreeNode child = new CheckedTreeNode(c);
rootNode.add(child);
child.setChecked(r.remote != null && !unchecked.contains(c.commitId()));
child.setChecked(r.remoteName != null && !unchecked.contains(c.commitId()));
}
myTreeRoot.add(rootNode);
}
@@ -704,7 +703,7 @@ public class GitPushActiveBranchesDialog extends DialogWrapper {
error = GitBundle.getString("push.active.error.reorder.needed");
}
}
if (r.branch == null) {
if (r.currentBranch == null) {
if (error == null) {
error = GitBundle.getString("push.active.error.no.branch");
}
@@ -770,20 +769,20 @@ public class GitPushActiveBranchesDialog extends DialogWrapper {
r.root = root;
GitBranch b = GitBranch.current(project, root);
if (b != null) {
r.branch = b.getFullName();
r.remote = b.getTrackedRemoteName(project, root);
r.currentBranch = b.getFullName();
r.remoteName = b.getTrackedRemoteName(project, root);
r.remoteBranch = b.getTrackedBranchName(project, root);
if (r.remote != null) {
if (fetchData && !r.remote.equals(".")) {
if (r.remoteName != null) {
if (fetchData && !r.remoteName.equals(".")) {
GitLineHandler fetch = new GitLineHandler(project, root, GitCommand.FETCH);
fetch.addParameters(r.remote, "-v");
fetch.addParameters(r.remoteName, "-v");
Collection<VcsException> exs = GitHandlerUtil.doSynchronouslyWithExceptions(fetch);
exceptions.addAll(exs);
}
GitBranch tracked = b.tracked(project, root);
assert tracked != null : "Tracked branch cannot be null here";
GitSimpleHandler unmerged = new GitSimpleHandler(project, root, GitCommand.LOG);
unmerged.addParameters("--pretty=format:%H", r.branch + ".." + tracked.getFullName());
unmerged.addParameters("--pretty=format:%H", r.currentBranch + ".." + tracked.getFullName());
unmerged.setNoSSH(true);
unmerged.setStdoutSuppressed(true);
StringScanner su = new StringScanner(unmerged.run());
@@ -793,7 +792,7 @@ public class GitPushActiveBranchesDialog extends DialogWrapper {
}
}
GitSimpleHandler toPush = new GitSimpleHandler(project, root, GitCommand.LOG);
toPush.addParameters("--pretty=format:%H%x20%ct%x20%at%x20%s%n%P", tracked.getFullName() + ".." + r.branch);
toPush.addParameters("--pretty=format:%H%x20%ct%x20%at%x20%s%n%P", tracked.getFullName() + ".." + r.currentBranch);
toPush.setNoSSH(true);
toPush.setStdoutSuppressed(true);
StringScanner sp = new StringScanner(toPush.run());
@@ -907,77 +906,59 @@ public class GitPushActiveBranchesDialog extends DialogWrapper {
static abstract class Node {
/**
* Render the node text
*
* @param renderer the renderer to use
*/
protected abstract void render(ColoredTreeCellRenderer renderer);
}
/**
* The commit descriptor
*/
static class Status extends Node {
/**
* The root
*/
Root root;
private String myMessage = "";
/**
* {@inheritDoc}
*/
@Override
protected void render(ColoredTreeCellRenderer renderer) {
renderer.append(GitBundle.getString("push.active.status.status"));
if (root.branch == null) {
renderer.append(GitBundle.message("push.active.status.no.branch"), SimpleTextAttributes.ERROR_ATTRIBUTES);
if (root.currentBranch == null) {
myMessage = GitBundle.message("push.active.status.no.branch");
renderer.append(myMessage, SimpleTextAttributes.ERROR_ATTRIBUTES);
}
else if (root.remote == null) {
renderer.append(GitBundle.message("push.active.status.no.tracked"), SimpleTextAttributes.GRAYED_BOLD_ATTRIBUTES);
else if (root.remoteName == null) {
myMessage = GitBundle.message("push.active.status.no.tracked");
renderer.append(myMessage, SimpleTextAttributes.GRAYED_BOLD_ATTRIBUTES);
}
else if (root.remoteCommits != 0 && root.commits.size() == 0) {
renderer.append(GitBundle.message("push.active.status.no.commits.behind", root.remoteCommits),
SimpleTextAttributes.GRAYED_BOLD_ATTRIBUTES);
myMessage = GitBundle.message("push.active.status.no.commits.behind", root.remoteCommits);
renderer.append(myMessage, SimpleTextAttributes.GRAYED_BOLD_ATTRIBUTES);
}
else if (root.commits.size() == 0) {
renderer.append(GitBundle.message("push.active.status.no.commits"), SimpleTextAttributes.GRAYED_BOLD_ATTRIBUTES);
myMessage = GitBundle.message("push.active.status.no.commits");
renderer.append(myMessage, SimpleTextAttributes.GRAYED_BOLD_ATTRIBUTES);
}
else if (root.remoteCommits != 0) {
renderer.append(GitBundle.message("push.active.status.behind", root.remoteCommits), SimpleTextAttributes.ERROR_ATTRIBUTES);
myMessage = GitBundle.message("push.active.status.behind", root.remoteCommits);
renderer.append(myMessage, SimpleTextAttributes.ERROR_ATTRIBUTES);
}
else {
renderer.append(GitBundle.message("push.active.status.push", root.commits.size()));
myMessage = GitBundle.message("push.active.status.push", root.commits.size());
renderer.append(myMessage);
}
}
@Override
public String toString() {
return GitBundle.getString("push.active.status.status") + myMessage;
}
}
/**
* The commit descriptor
*/
static class Commit extends Node {
/**
* The root
*/
Root root;
/**
* The revision
*/
GitRevisionNumber revision;
/**
* The message
*/
String message;
/**
* The author time
*/
String authorTime;
/**
* If true, the commit is a merge
*/
boolean isMerge;
boolean isMerge; // true if this commit is a merge commit
/**
* {@inheritDoc}
*/
@Override
protected void render(ColoredTreeCellRenderer renderer) {
renderer.append(revision.asString().substring(0, HASH_PREFIX_SIZE), SimpleTextAttributes.GRAYED_ATTRIBUTES);
@@ -994,53 +975,35 @@ public class GitPushActiveBranchesDialog extends DialogWrapper {
String commitId() {
return authorTime + ":" + message;
}
@Override
public String toString() {
String mergeCommitStr = isMerge ? " " + GitBundle.getString("push.active.commit.node.merge") : "";
return revision.toShortString() + " " + message + mergeCommitStr;
}
}
/**
* The root node
*/
static class Root extends Node {
/**
* if true, the update is required
*/
int remoteCommits;
/**
* the path to vcs root
*/
VirtualFile root;
/**
* the current branch
*/
String branch;
/**
* the remote name
*/
String remote;
/**
* the remote branch name
*/
String currentBranch;
String remoteName;
String remoteBranch;
/**
* The commit that will be actually pushed
*/
String commitToPush;
/**
* the commit
*/
String commitToPush; // The commit that will be actually pushed
List<Commit> commits = new ArrayList<Commit>();
/**
* {@inheritDoc}
*/
@Override
protected void render(ColoredTreeCellRenderer renderer) {
SimpleTextAttributes rootAttributes;
SimpleTextAttributes branchAttributes;
if (remote != null && commits.size() != 0 && remoteCommits != 0 || branch == null) {
if (remoteName != null && commits.size() != 0 && remoteCommits != 0 || currentBranch == null) {
rootAttributes = SimpleTextAttributes.ERROR_ATTRIBUTES.derive(SimpleTextAttributes.STYLE_BOLD, null, null, null);
branchAttributes = SimpleTextAttributes.ERROR_ATTRIBUTES;
}
else if (remote == null || commits.size() == 0) {
else if (remoteName == null || commits.size() == 0) {
rootAttributes = SimpleTextAttributes.GRAYED_BOLD_ATTRIBUTES;
branchAttributes = SimpleTextAttributes.GRAYED_ATTRIBUTES;
}
@@ -1049,13 +1012,27 @@ public class GitPushActiveBranchesDialog extends DialogWrapper {
rootAttributes = SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES;
}
renderer.append(root.getPresentableUrl(), rootAttributes);
if (branch != null) {
renderer.append(" [" + branch, branchAttributes);
if (remote != null) {
renderer.append(" -> " + remote + "#" + remoteBranch, branchAttributes);
if (currentBranch != null) {
renderer.append(" [" + currentBranch, branchAttributes);
if (remoteName != null) {
renderer.append(" -> " + remoteName + "#" + remoteBranch, branchAttributes);
}
renderer.append("]", branchAttributes);
}
}
@Override
public String toString() {
final StringBuilder string = new StringBuilder();
string.append(root.getPresentableUrl());
if (currentBranch != null) {
string.append(" [").append(currentBranch);
if (remoteName != null) {
string.append(" -> ").append(remoteName).append("#").append(remoteBranch);
}
string.append("]");
}
return string.toString();
}
}
}
@@ -36,7 +36,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.util.Consumer;
import com.intellij.util.continuation.Continuation;
import com.intellij.util.continuation.ContinuationContext;
import com.intellij.util.continuation.GatheringContinuationContext;
import com.intellij.util.ui.UIUtil;
import git4idea.GitRevisionNumber;
import git4idea.GitUtil;
@@ -578,7 +578,7 @@ public class GitCheckoutProcess {
progress.setText("Refreshing files before restoring shelve: " + shelve.DESCRIPTION);
final ShelvedChangeList finalShelve = shelve;
final Continuation continuation = new Continuation(myProject, true);
final Continuation continuation = Continuation.createForCurrentProgress(myProject, true, "Git: restore changes");
final Consumer<VcsException> exceptionConsumer = new Consumer<VcsException>() {
@Override
public void consume(VcsException e) {
@@ -590,8 +590,7 @@ public class GitCheckoutProcess {
}
};
continuation.addExceptionHandler(VcsException.class, exceptionConsumer);
final ContinuationContext.GatheringContinuationContext initContext =
new ContinuationContext.GatheringContinuationContext();
final GatheringContinuationContext initContext = new GatheringContinuationContext();
GitStashUtils.doSystemUnshelve(myProject, shelve, myShelveManager, new Runnable() {
@Override
public void run() {
@@ -15,10 +15,7 @@ package git4idea.history.wholeTree;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.CalledInAwt;
import com.intellij.util.containers.Convertor;
import com.intellij.util.continuation.Continuation;
import com.intellij.util.continuation.ContinuationContext;
import com.intellij.util.continuation.TaskDescriptor;
import com.intellij.util.continuation.Where;
import com.intellij.util.continuation.*;
import org.jetbrains.annotations.Nullable;
import java.util.List;
@@ -45,8 +42,7 @@ public class LoadAlgorithm {
}
public void fillContinuation() {
final ContinuationContext.GatheringContinuationContext initContext =
new ContinuationContext.GatheringContinuationContext();
final GatheringContinuationContext initContext = new GatheringContinuationContext();
for (LoaderAndRefresher<CommitHashPlusParents> loader : myLoaders) {
final LoaderFactory factory = new LoaderFactory(loader);
@@ -86,7 +86,7 @@ public class LoadController implements Loader {
continuation = myPreviousAlgorithm.getContinuation();
continuation.clearQueue();
} else {
continuation = new Continuation(myProject, true);
continuation = Continuation.createFragmented(myProject, true);
}
myPreviousAlgorithm = new LoadAlgorithm(myProject, list, shortLoaders, continuation);
myPreviousAlgorithm.fillContinuation();
@@ -18,12 +18,10 @@ package git4idea.update;
import com.intellij.ide.GeneralSettings;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ex.ProjectManagerEx;
import com.intellij.util.continuation.Continuation;
import com.intellij.util.continuation.ContinuationContext;
import com.intellij.util.continuation.TaskDescriptor;
import com.intellij.util.continuation.Where;
import com.intellij.util.continuation.*;
import com.intellij.util.ui.UIUtil;
/**
@@ -32,6 +30,7 @@ import com.intellij.util.ui.UIUtil;
* Time: 2:59 PM
*/
public abstract class GitUpdateLikeProcess {
public static final String GIT_UPDATING = "Git: updating";
private final Project myProject;
private GeneralSettings myGeneralSettings;
private ProjectManagerEx myProjectManager;
@@ -60,22 +59,25 @@ public abstract class GitUpdateLikeProcess {
}
});
final Continuation continuation = new Continuation(myProject, true);
final ContinuationContext.GatheringContinuationContext initContext = new ContinuationContext.GatheringContinuationContext();
initContext.next(new TaskDescriptor("Git: updating", Where.POOLED) {
@Override
public void run(final ContinuationContext context) {
runImpl(context);
}
}, new TaskDescriptor("", Where.AWT) {
final Continuation continuation = Continuation.createForCurrentProgress(myProject, true, GIT_UPDATING);
final GatheringContinuationContext initContext = new GatheringContinuationContext();
final TaskDescriptor returnFlagsBack = new TaskDescriptor("", Where.AWT) {
@Override
public void run(ContinuationContext context) {
myProjectManager.unblockReloadingProjectOnExternalChanges();
myGeneralSettings.setSaveOnFrameDeactivation(saveOnFrameDeactivation);
myGeneralSettings.setSyncOnFrameActivation(syncOnFrameDeactivation);
}
});
continuation.runAndWait(initContext.getList());
};
final TaskDescriptor[] next = {new TaskDescriptor(GIT_UPDATING, Where.POOLED) {
@Override
public void run(final ContinuationContext context) {
runImpl(context);
}
}, returnFlagsBack};
returnFlagsBack.setHaveMagicCure(true);
initContext.next(next);
continuation.run(initContext.getList());
}
protected abstract void runImpl(ContinuationContext context);
@@ -27,6 +27,8 @@ import com.intellij.openapi.vcs.update.UpdatedFiles;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import com.intellij.util.continuation.ContinuationContext;
import com.intellij.util.continuation.ContinuationFinalTasksInserter;
import com.intellij.util.continuation.TaskDescriptor;
import com.intellij.util.text.DateFormatUtil;
import git4idea.GitBranch;
import git4idea.branch.GitBranchPair;
@@ -189,7 +191,11 @@ public class GitUpdateProcess {
"Restoring changes saved before update failed with an error.<br/>" + e.getLocalizedMessage());
}
});
// try restore changes under all circumstances
final ContinuationFinalTasksInserter finalTasksInserter = new ContinuationFinalTasksInserter(context);
finalTasksInserter.allNextAreFinal();
mySaver.restoreLocalChanges(context);
finalTasksInserter.removeFinalPropertyAdder();
}
// fetch all roots. If an error happens, return false and notify about errors.
@@ -116,5 +116,6 @@ hg4idea.merge.please-commit=Merged heads, please commit repository \"{0}\"
hg4idea.error.invalidExecutable=\"{0}\" is not a valid mercurial executable
hg4idea.integrate.other.head=Other head: {0}
hgidea.dialog.login.password.required=Login and password required
hg4idea.dialog.login.password.required=Login and password required
hg4idea.dialog.login.description=Login to {0}
hg4idea.exception.file.not.under.hg=The file {0} is not under Mercurial.
@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
@@ -0,0 +1,8 @@
This Mercurial extension is meant to provide robust integration with
external tools.
Mercurial sometimes asks vital questions during several operations, for
instance during merging. Automatically choosing the default answer
for these questions is typically not what you want when working from,
for instance, an IDE. This extension forwards the prompts to another
process (i.e. the IDE) so that it can be answered by the user.
+102 -14
View File
@@ -1,24 +1,50 @@
#!/usr/bin/env python
from mercurial import filemerge, ui, util, dispatch
from mercurial.node import short
import sys, struct, socket
#Mercurial extension to robustly integrate prompts with other processes
#Copyright (C) 2010-2011 Willem Verstraeten
#
#This program is free software; you can redistribute it and/or
#modify it under the terms of the GNU General Public License
#as published by the Free Software Foundation; either version 2
#of the License, or (at your option) any later version.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License
#along with this program; if not, write to the Free Software
#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
import urllib2
from mercurial import ui, util
import struct, socket
from mercurial.i18n import _
try:
from mercurial.url import passwordmgr
except:
from mercurial.httprepo import passwordmgr
def sendInt( client, number):
length = struct.pack('>L', number)
client.sendall( length )
def send( client, data ):
sendInt(client, len(data))
client.sendall( data )
if data is None:
sendInt(client, 0)
else:
sendInt(client, len(data))
client.sendall( data )
def receiveInt(client):
def receiveIntWithMessage(client, message):
requiredLength = struct.calcsize('>L')
buffer = ''
while len(buffer)<requiredLength:
chunk = client.recv(requiredLength-len(buffer))
if chunk == '':
raise RuntimeError, "socket connection broken"
raise util.Abort( message )
buffer = buffer + chunk
# struct.unpack always returns a tuple, even if that tuple only contains a single
@@ -27,13 +53,20 @@ def receiveInt(client):
return intToReturn
def receiveInt(client):
return receiveIntWithMessage(client, "could not get information from server")
def receive( client ):
length = receiveInt(client)
receiveWithMessage(client, "could not get information from server")
def receiveWithMessage( client, message ):
length = receiveIntWithMessage(client, message)
buffer = ''
while len(buffer) < length :
chunk = client.recv(length - len(buffer))
if chunk == '':
raise RuntimeError, "socket connection broken"
raise util.Abort( message)
buffer = buffer+chunk
return buffer
@@ -45,14 +78,14 @@ def monkeypatch_method(cls):
return func
return decorator
def sendchangestoidea(ui, msg, choices, default):
def sendchoicestoidea(ui, msg, choices, default):
port = int(ui.config( 'hg4ideaprompt', 'port', None, True))
if not port:
raise util.Abort("No port was specified")
numOfChoices = len(choices)
if numOfChoices == 0:
if not numOfChoices:
return default
client = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
@@ -67,7 +100,6 @@ def sendchangestoidea(ui, msg, choices, default):
sendInt( client, default )
answer = receiveInt( client )
print "Received answer: %s" % answer
if answer == -1:
raise util.Abort("User cancelled")
else:
@@ -80,13 +112,13 @@ def sendchangestoidea(ui, msg, choices, default):
if getattr(ui.ui, 'promptchoice', None):
@monkeypatch_method(ui.ui)
def promptchoice(self, msg, choices=None, default=0):
return sendchangestoidea(self, msg, choices, default)
return sendchoicestoidea(self, msg, choices, default)
else:
@monkeypatch_method(ui.ui)
def prompt(self, msg, choices=None, default="y"):
resps = [s[s.index('&')+1].lower() for s in choices]
defaultIndex = resps.index( default )
responseIndex = sendchangestoidea( self, msg, choices, defaultIndex)
responseIndex = sendchoicestoidea( self, msg, choices, defaultIndex)
return resps[responseIndex]
original_warn = ui.ui.warn
@@ -110,3 +142,59 @@ def warn(self, *msg):
sendInt( client, len(msg) )
for message in msg:
send( client, message )
def retrieve_pass_from_server(ui, uri,path, proposed_user):
port = int(ui.config('hg4ideapass', 'port', None, True))
if port is None:
raise util.Abort("No port was specified")
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ui.debug("connecting ...")
client.connect(('127.0.0.1', port))
ui.debug("connected, sending data ...")
send(client, "getpass")
send(client, uri)
send(client, path)
send(client, proposed_user)
user = receiveWithMessage(client, "http authorization required")
password = receiveWithMessage(client, "http authorization required")
return user, password
original_retrievepass=passwordmgr.find_user_password
@monkeypatch_method(passwordmgr)
def find_user_password(self, realm, authuri):
try:
return original_retrievepass(self, realm, authuri)
except util.Abort:
# In mercurial 1.8 the readauthtoken method was replaced with
# the readauthforuri method, which has different semantics
if getattr(self, 'readauthtoken', None):
def read_hgrc_authtoken(ui, authuri):
return self.readauthtoken(authuri)
else:
def read_hgrc_authtoken(ui, authuri):
# hg 1.8
from mercurial.url import readauthforuri
res = readauthforuri(self.ui, authuri)
if res:
group, auth = res
return auth
else:
return None
user, password = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(self, realm, authuri)
if user is None:
auth = read_hgrc_authtoken(self.ui, authuri)
if auth:
user = auth.get("username")
reduced_uri, path= self.reduce_uri(authuri, False)
retrievedPass = retrieve_pass_from_server(self.ui, reduced_uri, path, user)
if retrievedPass is None:
raise util.Abort(_('http authorization required'))
user, passwd = retrievedPass
self.add_password(realm, authuri, user, passwd)
return retrievedPass
-2
View File
@@ -17,8 +17,6 @@
serviceImplementation="org.zmlx.hg4idea.HgGlobalSettings"/>
<projectService serviceInterface="org.zmlx.hg4idea.HgProjectSettings"
serviceImplementation="org.zmlx.hg4idea.HgProjectSettings"/>
<projectService serviceInterface="org.zmlx.hg4idea.command.HgCommandService"
serviceImplementation="org.zmlx.hg4idea.command.HgCommandService"/>
<projectService serviceInterface="org.zmlx.hg4idea.HgRootsHandler"
serviceImplementation="org.zmlx.hg4idea.HgRootsHandler" />
</extensions>

Some files were not shown because too many files have changed in this diff Show More