Merge remote branch 'origin/master'

This commit is contained in:
Dmitry Jemerov
2011-12-19 16:19:13 +01:00
46 changed files with 537 additions and 213 deletions
@@ -1,63 +0,0 @@
package com.intellij.refactoring;
import com.intellij.codeInsight.CodeInsightTestCase;
import com.intellij.openapi.application.ex.PathManagerEx;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.projectRoots.impl.JavaSdkImpl;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiPackage;
import com.intellij.refactoring.rename.RenameProcessor;
import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.testFramework.PsiTestUtil;
import java.io.File;
public class RenamePackageTest extends CodeInsightTestCase {
public void testJspImport() throws Exception {
doTest("jspImport", "pack1", "pack2");
}
public void testNonJava() throws Exception {
doTest("nonJava", "com.foo", "fooNew");
}
public void testInBrokenXml() throws Exception {
doTest("inBrokenXml", "somepckg", "somepckg1");
}
public void testJsp() throws Exception {
doTest("jsp", "pack1", "pack2");
}
private void doTest(String testName, String packageName, String newPackageName) throws Exception {
String root = PathManagerEx.getTestDataPath()+ "/refactoring/renamePackage/" + testName;
String rootBefore = root + "/before";
PsiTestUtil.removeAllRoots(myModule, JavaSdkImpl.getMockJdk17());
VirtualFile rootDir = PsiTestUtil.createTestProjectStructure(myProject, myModule, rootBefore, myFilesToDelete);
performAction(packageName, newPackageName);
String rootAfter = root + "/after";
VirtualFile rootDir2 = LocalFileSystem.getInstance().findFileByPath(rootAfter.replace(File.separatorChar, '/'));
IdeaTestUtil.assertDirectoriesEqual(rootDir2, rootDir, IdeaTestUtil.CVS_FILE_FILTER);
}
private void performAction(String packageName, String newPackageName) throws Exception {
PsiPackage aPackage = JavaPsiFacade.getInstance(myPsiManager.getProject()).findPackage(packageName);
assertNotNull("Package " + packageName + " not found", aPackage);
//PsiDirectory dir = aPackage.getDirectories()[0];
//it is now impossible to rename dir without renaming corresponding package via rename processor - move processor would be used instead
new RenameProcessor(myProject, aPackage, newPackageName, true, true).run();
FileDocumentManager.getInstance().saveAllDocuments();
}
@Override
protected boolean isRunInWriteAction() {
return false;
}
}
@@ -20,7 +20,11 @@
package com.intellij.lang;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class LanguageImportStatements extends LanguageExtension<ImportOptimizer> {
public static final LanguageImportStatements INSTANCE = new LanguageImportStatements();
@@ -29,9 +33,15 @@ public class LanguageImportStatements extends LanguageExtension<ImportOptimizer>
super("com.intellij.lang.importOptimizer");
}
@Nullable
public ImportOptimizer forFile(PsiFile file) {
ImportOptimizer optimizer = forLanguage(file.getLanguage());
return optimizer != null && optimizer.supports(file) ? optimizer : null;
public List<Runnable> forFile(PsiFile file) {
List<Runnable> runnables = new ArrayList<Runnable>();
Set<ImportOptimizer> optimizers = new HashSet<ImportOptimizer>();
for (PsiFile psiFile : file.getViewProvider().getAllFiles()) {
ImportOptimizer optimizer = forLanguage(psiFile.getLanguage());
if (optimizer != null && optimizer.supports(psiFile) && optimizers.add(optimizer)) {
runnables.add(optimizer.processFile(psiFile));
}
}
return runnables;
}
}
@@ -103,7 +103,7 @@ public class LayoutCodeDialog extends DialogWrapper {
myCbIncludeSubdirs.setEnabled(myRbDirectory.isSelected());
myCbOptimizeImports.setEnabled(
!myRbSelectedText.isSelected() &&
!(myFile != null && LanguageImportStatements.INSTANCE.forFile(myFile) == null && myRbFile.isSelected()));
!(myFile != null && LanguageImportStatements.INSTANCE.forFile(myFile).isEmpty() && myRbFile.isSelected()));
final boolean canTargetVcsChanges = canTargetVcsRegions();
myCbOnlyVcsChangedRegions.setEnabled(canTargetVcsChanges);
@@ -193,6 +193,6 @@ public class OptimizeImportsAction extends AnAction {
}
private static boolean isOptimizeImportsAvailable(final PsiFile file) {
return LanguageImportStatements.INSTANCE.forFile(file) != null;
return !LanguageImportStatements.INSTANCE.forFile(file).isEmpty();
}
}
@@ -17,7 +17,6 @@
package com.intellij.codeInsight.actions;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.lang.ImportOptimizer;
import com.intellij.lang.LanguageImportStatements;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
@@ -27,6 +26,7 @@ import com.intellij.psi.PsiFile;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.concurrent.FutureTask;
public class OptimizeImportsProcessor extends AbstractLayoutCodeProcessor {
@@ -58,11 +58,16 @@ public class OptimizeImportsProcessor extends AbstractLayoutCodeProcessor {
}
@NotNull
protected FutureTask<Boolean> preprocessFile(@NotNull final PsiFile file, boolean processChangedTextOnly)
throws IncorrectOperationException
{
final ImportOptimizer optimizer = LanguageImportStatements.INSTANCE.forFile(file);
Runnable runnable = optimizer != null ? optimizer.processFile(file) : EmptyRunnable.getInstance();
protected FutureTask<Boolean> preprocessFile(@NotNull final PsiFile file, boolean processChangedTextOnly) throws IncorrectOperationException {
final List<Runnable> optimizers = LanguageImportStatements.INSTANCE.forFile(file);
Runnable runnable = optimizers.isEmpty() ? EmptyRunnable.getInstance() : new Runnable() {
@Override
public void run() {
for (Runnable optimizer : optimizers) {
optimizer.run();
}
}
};
return new FutureTask<Boolean>(runnable, true);
}
}
@@ -22,6 +22,7 @@ import com.intellij.ide.projectView.impl.ProjectRootsUtil;
import com.intellij.ide.structureView.StructureView;
import com.intellij.ide.structureView.StructureViewBuilder;
import com.intellij.ide.structureView.StructureViewWrapper;
import com.intellij.ide.structureView.impl.StructureViewComposite;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ModalityState;
@@ -43,6 +44,9 @@ import com.intellij.openapi.wm.ToolWindowId;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentFactory;
import com.intellij.ui.content.ContentManager;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.update.MergingUpdateQueue;
import com.intellij.util.ui.update.Update;
@@ -59,13 +63,14 @@ import java.awt.event.HierarchyListener;
*/
public class StructureViewWrapperImpl implements StructureViewWrapper, Disposable {
private final Project myProject;
private final ToolWindow myToolWindow;
private VirtualFile myFile;
private StructureView myStructureView;
private ModuleStructureComponent myModuleStructureComponent;
private final JPanel myPanel;
private JPanel[] myPanels = new JPanel[0];
private final MergingUpdateQueue myUpdateQueue;
private final String myKey = new String("DATA_SELECTOR");
@@ -75,17 +80,16 @@ public class StructureViewWrapperImpl implements StructureViewWrapper, Disposabl
private Runnable myPendingSelection;
public StructureViewWrapperImpl(Project project) {
public StructureViewWrapperImpl(Project project, ToolWindow toolWindow) {
myProject = project;
myPanel = new ContentPanel();
myPanel.setBackground(UIUtil.getTreeTextBackground());
myUpdateQueue = new MergingUpdateQueue("StructureView", Registry.intValue("structureView.coalesceTime"), false, myPanel, this, myPanel, true);
myToolWindow = toolWindow;
myUpdateQueue = new MergingUpdateQueue("StructureView", Registry.intValue("structureView.coalesceTime"), false, myToolWindow.getComponent(), this, myToolWindow.getComponent(), true);
myUpdateQueue.setRestartTimerOnAdd(true);
ActionManager.getInstance().addTimerListener(500, new TimerListener() {
public ModalityState getModalityState() {
return ModalityState.stateForComponent(myPanel);
return ModalityState.stateForComponent(myToolWindow.getComponent());
}
public void run() {
@@ -93,20 +97,21 @@ public class StructureViewWrapperImpl implements StructureViewWrapper, Disposabl
}
});
getComponent().addHierarchyListener(new HierarchyListener() {
myToolWindow.getComponent().addHierarchyListener(new HierarchyListener() {
public void hierarchyChanged(HierarchyEvent e) {
if ((e.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0) {
scheduleRebuild();
}
}
});
Disposer.register(myToolWindow.getContentManager(), this);
}
private void checkUpdate() {
if (myProject.isDisposed()) return;
final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
if (SwingUtilities.isDescendingFrom(myPanel, owner) || JBPopupFactory.getInstance().isPopupActive()) return;
if (SwingUtilities.isDescendingFrom(myToolWindow.getComponent(), owner) || JBPopupFactory.getInstance().isPopupActive()) return;
final DataContext dataContext = DataManager.getInstance().getDataContext(owner);
if (dataContext.getData(myKey) == this) return;
@@ -133,10 +138,6 @@ public class StructureViewWrapperImpl implements StructureViewWrapper, Disposabl
// StructureView interface implementation
// -------------------------------------------------------------------------
public JComponent getComponent() {
return myPanel;
}
public void dispose() {
rebuild();
}
@@ -202,8 +203,8 @@ public class StructureViewWrapperImpl implements StructureViewWrapper, Disposabl
myModuleStructureComponent = null;
}
myPanel.removeAll();
final ContentManager contentManager = myToolWindow.getContentManager();
contentManager.removeAllContents(true);
if (!isStructureViewShowing()) {
return;
}
@@ -216,19 +217,16 @@ public class StructureViewWrapperImpl implements StructureViewWrapper, Disposabl
}
}
String[] names = new String[] {""};
JComponent focusedComponent = null;
if (file != null && file.isValid()) {
if (file.isDirectory()) {
if (ProjectRootsUtil.isModuleContentRoot(file, myProject)) {
Module module = ModuleUtil.findModuleForFile(file, myProject);
if (module != null) {
myModuleStructureComponent = new ModuleStructureComponent(module);
myPanel.add(myModuleStructureComponent, BorderLayout.CENTER);
if (hadFocus) {
JComponent focusedComponent = IdeFocusTraversalPolicy.getPreferredFocusedComponent(myModuleStructureComponent);
if (focusedComponent != null) {
IdeFocusManager.getInstance(myProject).requestFocus(focusedComponent, true);
}
}
focusedComponent = hadFocus ? IdeFocusTraversalPolicy.getPreferredFocusedComponent(myModuleStructureComponent) : null;
createSinglePanel(myModuleStructureComponent.getComponent());
}
}
}
@@ -246,13 +244,22 @@ public class StructureViewWrapperImpl implements StructureViewWrapper, Disposabl
if (myStructureView instanceof StructureView.Scrollable) {
((StructureView.Scrollable)myStructureView).setReferenceSizeWhileInitializing(referenceSize);
}
myPanel.add(myStructureView.getComponent(), BorderLayout.CENTER);
if (hadFocus) {
JComponent focusedComponent = IdeFocusTraversalPolicy.getPreferredFocusedComponent(myStructureView.getComponent());
if (focusedComponent != null) {
IdeFocusManager.getInstance(myProject).requestFocus(focusedComponent, true);
final StructureViewComposite.StructureViewDescriptor[] views;
if (myStructureView instanceof StructureViewComposite) {
final StructureViewComposite composite = (StructureViewComposite)myStructureView;
views = composite.getStructureViews();
myPanels = new JPanel[views.length];
names = new String[views.length];
for (int i = 0; i < myPanels.length; i++) {
myPanels[i] = createContentPanel(views[i].structureView.getComponent());
names[i] = views[i].title;
}
} else {
createSinglePanel(myStructureView.getComponent());
}
focusedComponent = hadFocus ? IdeFocusTraversalPolicy.getPreferredFocusedComponent(myStructureView.getComponent()) : null;
myStructureView.restoreState();
myStructureView.centerSelectedRow();
}
@@ -264,11 +271,19 @@ public class StructureViewWrapperImpl implements StructureViewWrapper, Disposabl
}
if (myModuleStructureComponent == null && myStructureView == null) {
myPanel.add(new JLabel(IdeBundle.message("message.nothing.to.show.in.structure.view"), SwingConstants.CENTER), BorderLayout.CENTER);
createSinglePanel(new JLabel(IdeBundle.message("message.nothing.to.show.in.structure.view"), SwingConstants.CENTER));
}
myPanel.validate();
myPanel.repaint();
for (int i = 0; i < myPanels.length; i++) {
final Content content = ContentFactory.SERVICE.getInstance().createContent(myPanels[i], names[i], false);
contentManager.addContent(content);
if (i == 0 && myStructureView != null) {
Disposer.register(content, myStructureView);
}
}
if (hadFocus && focusedComponent != null) {
IdeFocusManager.getInstance(myProject).requestFocus(focusedComponent, true);
}
if (myPendingSelection != null) {
Runnable selection = myPendingSelection;
@@ -277,6 +292,18 @@ public class StructureViewWrapperImpl implements StructureViewWrapper, Disposabl
}
}
private void createSinglePanel(final JComponent component) {
myPanels = new JPanel[1];
myPanels[0] = createContentPanel(component);
}
private ContentPanel createContentPanel(JComponent component) {
final ContentPanel panel = new ContentPanel();
panel.setBackground(UIUtil.getTreeTextBackground());
panel.add(component, BorderLayout.CENTER);
return panel;
}
@Nullable
private FileEditor createTempFileEditor(VirtualFile file) {
FileEditorProviderManager editorProviderManager = FileEditorProviderManager.getInstance();
@@ -20,12 +20,9 @@ import com.intellij.ide.structureView.StructureView;
import com.intellij.ide.structureView.StructureViewModel;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.util.Disposer;
import com.intellij.ui.TabbedPaneWrapper;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
* @author cdr
@@ -33,8 +30,6 @@ import javax.swing.event.ChangeListener;
public class StructureViewComposite implements StructureView {
@NotNull private final StructureViewDescriptor[] myStructureViews;
@NotNull private StructureViewDescriptor mySelectedViewDescriptor;
@NotNull private final TabbedPaneWrapper myTabbedPaneWrapper;
public static class StructureViewDescriptor {
public final String title;
public final StructureView structureView;
@@ -53,17 +48,6 @@ public class StructureViewComposite implements StructureView {
Disposer.register(this, descriptor.structureView);
}
mySelectedViewDescriptor = views[0];
myTabbedPaneWrapper = new TabbedPaneWrapper(this);
for (StructureViewDescriptor descriptor : views) {
myTabbedPaneWrapper.addTab(descriptor.title, descriptor.icon, descriptor.structureView.getComponent(), null);
}
myTabbedPaneWrapper.setSelectedIndex(0);
myTabbedPaneWrapper.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
int index = myTabbedPaneWrapper.getSelectedIndex();
mySelectedViewDescriptor = myStructureViews[index];
}
});
}
public StructureView getSelectedStructureView() {
@@ -84,7 +68,7 @@ public class StructureViewComposite implements StructureView {
}
public JComponent getComponent() {
return myTabbedPaneWrapper.getComponent();
return mySelectedViewDescriptor.structureView.getComponent();
}
public void dispose() {
@@ -25,14 +25,11 @@ import com.intellij.openapi.components.Storage;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.MultiValuesMap;
import com.intellij.openapi.util.NotNullLazyValue;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.psi.PsiElement;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentFactory;
import com.intellij.util.ReflectionCache;
import org.jetbrains.annotations.NotNull;
@@ -96,11 +93,7 @@ public final class StructureViewFactoryImpl extends StructureViewFactoryEx imple
}
public void initToolWindow(ToolWindow toolWindow) {
myStructureViewWrapperImpl = new StructureViewWrapperImpl(myProject);
final Content content = ContentFactory.SERVICE.getInstance().createContent(myStructureViewWrapperImpl.getComponent(), "", false);
Disposer.register(content, myStructureViewWrapperImpl);
toolWindow.getContentManager().addContent(content);
myStructureViewWrapperImpl = new StructureViewWrapperImpl(myProject, toolWindow);
if (myRunWhenInitialized != null) {
myRunWhenInitialized.run();
myRunWhenInitialized = null;
@@ -130,12 +130,12 @@ public class LogMessageEx extends LogMessage {
};
}
public static void error(@NotNull Logger logger, @NotNull String message, @NotNull String... details) {
public static void error(@NotNull Logger logger, @NotNull String message, @NotNull String... attachmentText) {
StringBuilder detailsBuffer = new StringBuilder();
for (String detail : details) {
for (String detail : attachmentText) {
detailsBuffer.append(detail).append(",");
}
if (details.length > 0 && detailsBuffer.length() > 0) {
if (attachmentText.length > 0 && detailsBuffer.length() > 0) {
detailsBuffer.setLength(detailsBuffer.length() - 1);
}
Attachment attachment = detailsBuffer.length() > 0 ? new Attachment("current-context.txt", detailsBuffer.toString()) : null;
@@ -15,7 +15,9 @@
*/
package com.intellij.openapi.editor.impl.softwrap.mapping;
import com.intellij.diagnostic.LogMessageEx;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.event.DocumentListener;
@@ -24,10 +26,7 @@ import com.intellij.openapi.editor.event.VisibleAreaListener;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.ex.ScrollingModelEx;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.editor.impl.EditorTextRepresentationHelper;
import com.intellij.openapi.editor.impl.FontInfo;
import com.intellij.openapi.editor.impl.IterationState;
import com.intellij.openapi.editor.impl.TextChangeImpl;
import com.intellij.openapi.editor.impl.*;
import com.intellij.openapi.editor.impl.softwrap.*;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.util.text.StringUtil;
@@ -54,7 +53,9 @@ import java.util.List;
* @since Jul 5, 2010 10:01:27 AM
*/
public class SoftWrapApplianceManager implements SoftWrapFoldingListener, DocumentListener {
private static final Logger LOG = Logger.getInstance("#" + SoftWrapApplianceManager.class.getName());
/** Enumerates possible type of soft wrap indents to use. */
enum IndentType {
/** Don't apply special indent to soft-wrapped line at all. */
@@ -90,14 +91,15 @@ public class SoftWrapApplianceManager implements SoftWrapFoldingListener, Docume
*/
private int myLastTopLeftCornerOffset = -1;
private VisibleAreaWidthProvider myWidthProvider;
private LineWrapPositionStrategy myLineWrapPositionStrategy;
private boolean myVisualAreaListenerAttached;
private boolean myCustomIndentUsedLastTime;
private int myCustomIndentValueUsedLastTime;
private int myVisibleAreaWidth;
private boolean myInProgress;
private boolean myHasLinesWithFailedWrap;
private VisibleAreaWidthProvider myWidthProvider;
private LineWrapPositionStrategy myLineWrapPositionStrategy;
private IncrementalCacheUpdateEvent myEventBeingProcessed;
private boolean myVisualAreaListenerAttached;
private boolean myCustomIndentUsedLastTime;
private int myCustomIndentValueUsedLastTime;
private int myVisibleAreaWidth;
private boolean myInProgress;
private boolean myHasLinesWithFailedWrap;
public SoftWrapApplianceManager(@NotNull SoftWrapsStorage storage,
@NotNull EditorEx editor,
@@ -172,16 +174,28 @@ public class SoftWrapApplianceManager implements SoftWrapFoldingListener, Docume
List<IncrementalCacheUpdateEvent> events = new ArrayList<IncrementalCacheUpdateEvent>(myEventsStorage.getEvents());
myActiveEvents.addAll(events);
myEventsStorage.release();
if (myInProgress && !events.isEmpty()) {
String state = "";
if (myEditor instanceof EditorImpl) {
state = ((EditorImpl)myEditor).dumpState();
}
LogMessageEx.error(LOG, "Detected race condition at soft wraps recalculation", String.format(
"Current events: %s. Concurrent events: %s, event being processed: %s%n%s",
events, myActiveEvents, myEventBeingProcessed, state
));
}
myInProgress = true;
myHasLinesWithFailedWrap = false;
try {
for (IncrementalCacheUpdateEvent event : events) {
myEventBeingProcessed = event;
recalculateSoftWraps(event);
}
}
finally {
myInProgress = false;
myActiveEvents.clear();
myEventBeingProcessed = null;
}
updateLastTopLeftCornerOffset();
return true;
@@ -896,7 +910,8 @@ public class SoftWrapApplianceManager implements SoftWrapFoldingListener, Docume
@Override
public String toString() {
return String.format(
"recalculation in progress: %b; stored update events: %s; active update events: %s", myInProgress, myEventsStorage, myActiveEvents
"recalculation in progress: %b; stored update events: %s; active update events: %s, event being processed: %s",
myInProgress, myEventsStorage, myActiveEvents, myEventBeingProcessed
);
}
@@ -565,8 +565,8 @@ action.structureview.show.non.public=Show non-public
node.structureview.invalid=<invalid>
action.structureview.group.methods.by.defining.type=Group Methods by Defining Type
action.structureview.sort.by.visibility=Sort by Visibility
tab.structureview.jsp.view=JSP View
tab.structureview.baselanguage.view={0} View
tab.structureview.jsp.view=JSP
tab.structureview.baselanguage.view={0}
action.next.todo=Next TODO
action.previous.todo=Previous TODO
action.filter.todo.items=Filter TODO Items
@@ -415,12 +415,16 @@ public abstract class VcsVFSListener implements Disposable {
checkMovedAddedSourceBack();
if (!myAddedFiles.isEmpty()) {
executeAdd();
myAddedFiles.clear();
}
if (!myDeletedFiles.isEmpty() || !myDeletedWithoutConfirmFiles.isEmpty()) {
executeDelete();
myDeletedFiles.clear();
myDeletedWithoutConfirmFiles.clear();
}
if (!myMovedFiles.isEmpty()) {
executeMoveRename();
myMovedFiles.clear();
}
if (! myDirtyFiles.isEmpty()) {
final List<VirtualFile> files = new ArrayList<VirtualFile>();
@@ -28,6 +28,9 @@ import com.intellij.openapi.vcs.VcsDirectoryMapping;
import java.util.Arrays;
public class StartUseVcsAction extends AnAction implements DumbAware {
public StartUseVcsAction() {
super(VcsBundle.message("action.enable.version.control.integration.text"));
}
@Override
public void update(final AnActionEvent e) {
@@ -391,14 +391,14 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec
myUpdateException = null;
myAdditionalInfo.clear();
}
debugLogging("refresh procedure started, everything = " + wasEverythingDirty + " dirty scope: " +
StringUtil.join(scopes, new Function<VcsDirtyScope, String>() {
@Override
public String fun(VcsDirtyScope scope) {
return scope.toString();
}
}, "->\n"));
}
final String scopeInString = (! LOG.isDebugEnabled()) ? "" : StringUtil.join(scopes, new Function<VcsDirtyScope, String>() {
@Override
public String fun(VcsDirtyScope scope) {
return scope.toString();
}
}, "->\n");
debugLogging("refresh procedure started, everything = " + wasEverythingDirty + " dirty scope: " + scopeInString);
dataHolder.notifyStart();
myChangesViewManager.scheduleRefresh();
@@ -29,6 +29,7 @@ import git4idea.remote.GitRememberedInputs;
import git4idea.repo.GitRemote;
import git4idea.repo.GitRepository;
import git4idea.update.GitFetchResult;
import git4idea.util.NetrcData;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.InvalidRemoteException;
import org.eclipse.jgit.api.errors.JGitInternalException;
@@ -43,6 +44,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.ProxySelector;
import java.net.URISyntaxException;
@@ -65,9 +67,27 @@ public final class GitHttpAdapter {
public static boolean isHttpUrlWithoutUserCredentials(@NotNull String url) {
// if username & password are specified in the url, give it to the native Git
return url.startsWith("http") && !HTTP_URL_WITH_USERNAME_AND_PASSWORD.matcher(url).matches();
if (!url.startsWith("http")) {
return false;
}
if (HTTP_URL_WITH_USERNAME_AND_PASSWORD.matcher(url).matches()) {
return false;
}
try {
NetrcData netrcData = NetrcData.parse();
return !netrcData.hasAuthDataForUrl(url);
}
catch (FileNotFoundException e) {
return false;
}
catch (IOException e) {
LOG.warn("Couldn't read netrc file", e);
return false;
}
}
private enum GeneralResult {
SUCCESS,
CANCELLED,
@@ -0,0 +1,188 @@
/*
* 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 git4idea.util;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.SystemProperties;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
/**
* Parses {@code .netrc} file and stores the parsed authentication information in {@link AuthRecord} objects.
* Parse only {@code machine}, {@code login} and {@code password} fields.
*
* @author Kirill Likhodedov
*/
public class NetrcData {
private static final String NETRC_FILE = SystemInfo.isWindows ? "_netrc" : ".netrc";
private final Map<String, AuthRecord> myAuthDataMap;
private NetrcData(@NotNull Map<String, AuthRecord> authData) {
myAuthDataMap = authData;
}
/**
* Parses the {@code .netrc} file (which is {@code _netrc} on Windows) and returns the NetrcData object which can be analyzed further.
*/
@NotNull
public static NetrcData parse() throws IOException {
String home = SystemProperties.getUserHome();
File netrc = new File(home, NETRC_FILE);
return parse(netrc);
}
@NotNull
static NetrcData parse(@NotNull File netrcFile) throws IOException {
if (!netrcFile.exists()) {
throw new FileNotFoundException(netrcFile.getPath());
}
String netrcContent = FileUtil.loadFile(netrcFile);
return parse(netrcContent);
}
/**
* @return true iff the host of the given url is contained in {@code .netrc}, and both login and password are set for this host.
*/
public boolean hasAuthDataForUrl(@NotNull String url) {
String host = getHostFromUrl(url);
AuthRecord authRecord = myAuthDataMap.get(host);
return authRecord != null && authRecord.getLogin() != null && authRecord.getPassword() != null;
}
@NotNull
Collection<AuthRecord> getAuthData() {
return myAuthDataMap.values();
}
@NotNull
private static String getHostFromUrl(@NotNull String url) {
final String schemaSeparator = "://";
String urlWithoutSchema = url.substring(url.indexOf(schemaSeparator) + schemaSeparator.length());
return urlWithoutSchema.substring(0, urlWithoutSchema.indexOf('/'));
}
@NotNull
private static NetrcData parse(@NotNull String content) {
Map<String, AuthRecord> result = new HashMap<String, AuthRecord>();
StringTokenizer tokenizer = new StringTokenizer(content);
AuthRecord currentRecord = null;
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
if (token.equalsIgnoreCase("machine")) {
if (!tokenizer.hasMoreTokens()) {
break;
}
String host = tokenizer.nextToken();
host = host.toLowerCase(); // hosts are case insensitive
if (result.containsKey(host)) {
currentRecord = result.get(host);
} else {
currentRecord = new AuthRecord(host);
result.put(host, currentRecord);
}
}
else if (token.equalsIgnoreCase("login")) {
if (!tokenizer.hasMoreTokens()) {
break;
}
if (currentRecord != null && currentRecord.getLogin() == null) {
currentRecord.setLogin(tokenizer.nextToken());
}
}
else if (token.equalsIgnoreCase("password")) {
if (!tokenizer.hasMoreTokens()) {
break;
}
if (currentRecord != null && currentRecord.getPassword() == null) {
currentRecord.setPassword(tokenizer.nextToken());
}
}
}
return new NetrcData(result);
}
static class AuthRecord {
private final String myHost;
private String myLogin;
private String myPassword;
AuthRecord(String host, String login, String password) {
myHost = host;
myLogin = login;
myPassword = password;
}
private AuthRecord(String host) {
myHost = host;
}
@Nullable
String getLogin() {
return myLogin;
}
@Nullable
String getPassword() {
return myPassword;
}
private void setLogin(String login) {
myLogin = login;
}
private void setPassword(String password) {
myPassword = password;
}
@Override
public String toString() {
return String.format("AuthRecord{host=%s, login=%s, password=%s}", myHost, myLogin, myPassword);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AuthRecord record = (AuthRecord)o;
if (myHost != null ? !myHost.equalsIgnoreCase(record.myHost) : record.myHost != null) return false; // NB: host is case insensitive
if (myLogin != null ? !myLogin.equals(record.myLogin) : record.myLogin != null) return false;
if (myPassword != null ? !myPassword.equals(record.myPassword) : record.myPassword != null) return false;
return true;
}
@Override
public int hashCode() {
int result = myHost != null ? myHost.hashCode() : 0;
result = 31 * result + (myLogin != null ? myLogin.hashCode() : 0);
result = 31 * result + (myPassword != null ? myPassword.hashCode() : 0);
return result;
}
}
}
@@ -0,0 +1 @@
n0 empty netrc
@@ -0,0 +1,2 @@
machine bitbucket.org
login john
@@ -0,0 +1 @@
n1 just login for machine
@@ -0,0 +1 @@
bitbucket.org john
@@ -0,0 +1,3 @@
machine bitbucket.org
login john
password pass
@@ -0,0 +1 @@
n2 login & password defined
@@ -0,0 +1 @@
bitbucket.org john pass
@@ -0,0 +1,6 @@
machine bitbucket.org
login john
password pass
machine github.com
login bob
password pswd
@@ -0,0 +1 @@
n3 two full standard hosts
@@ -0,0 +1,2 @@
bitbucket.org john pass
github.com bob pswd
@@ -0,0 +1,4 @@
machine bitbucket.org
login john
machine bitbucket.org
password pass
@@ -0,0 +1,2 @@
n4 two supplementary records for one host
One host defined twice, login is set in the first record, password - in the second => complete auth data should be read
@@ -0,0 +1 @@
bitbucket.org john pass
@@ -0,0 +1,5 @@
machine bitbucket.org
login john
password pass
machine bitbucket.org
password doesnt_override
@@ -0,0 +1 @@
n5 overriding records for 1 host
@@ -0,0 +1 @@
bitbucket.org john pass
@@ -0,0 +1,4 @@
machine
bitbucket.org
login
john password pass
@@ -0,0 +1 @@
n6 poorly formatted netrc for 1 host
@@ -0,0 +1 @@
bitbucket.org john pass
@@ -15,71 +15,34 @@
*/
package git4idea.repo;
import com.intellij.openapi.application.PluginPathManager;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import git4idea.tests.GitTestUtil;
import git4idea.test.GitTestUtil;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.*;
import static org.testng.Assert.assertNotNull;
/**
* @author Kirill Likhodedov
*/
public class GitConfigTest {
@DataProvider(name = "remote")
public Object[][] loadRemotes() throws IOException {
return loadData("remote");
return GitTestUtil.loadConfigData(getTestDataFolder("remote"));
}
@DataProvider(name = "branch")
public Object[][] loadBranches() throws IOException {
return loadData("branch");
return GitTestUtil.loadConfigData(getTestDataFolder("branch"));
}
public static Object[][] loadData(String subfolder) throws IOException {
File pluginRoot = new File(PluginPathManager.getPluginHomePath("git4idea"));
File dataDir = new File(new File(new File(pluginRoot, "testData"), "config"), subfolder);
File[] tests = dataDir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return !name.startsWith(".");
}
});
Object[][] data = new Object[tests.length][];
for (int i = 0; i < tests.length; i++) {
File testDir = tests[i];
File descriptionFile = null;
File configFile = null;
File resultFile = null;
for (File file : testDir.listFiles()) {
if (file.getName().endsWith("_desc.txt")) {
descriptionFile = file;
}
else if (file.getName().endsWith("_config.txt")) {
configFile = file;
}
else if (file.getName().endsWith("_result.txt")) {
resultFile = file;
}
}
assertNotNull(descriptionFile, String.format("description file not found in %s among %s", testDir, Arrays.toString(testDir.list())));
assertNotNull(configFile, String.format("config file file not found in %s among %s", testDir, Arrays.toString(testDir.list())));
assertNotNull(resultFile, String.format("result file file not found in %s among %s", testDir, Arrays.toString(testDir.list())));
String testName = FileUtil.loadFile(descriptionFile).split("\n")[0]; // description is in the first line of the desc-file
data[i] = new Object[]{
testName, configFile, resultFile
};
}
return data;
private static File getTestDataFolder(String subfolder) {
File testData = GitTestUtil.getTestDataFolder();
return new File(new File(testData, "config"), subfolder);
}
@Test(dataProvider = "remote")
@@ -21,7 +21,7 @@ import com.intellij.testFramework.LightIdeaTestCase;
import com.intellij.util.Processor;
import git4idea.GitBranch;
import git4idea.branch.GitBranchesCollection;
import git4idea.tests.GitTestUtil;
import git4idea.test.GitTestUtil;
import org.jetbrains.annotations.NotNull;
import java.io.File;
@@ -13,21 +13,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.tests;
package git4idea.test;
import com.intellij.openapi.application.PluginPathManager;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.tests.GitTestRepository;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.fail;
/**
@@ -171,6 +178,47 @@ public class GitTestUtil {
return false;
}
public static Object[][] loadConfigData(@NotNull File dataFolder) throws IOException {
File[] tests = dataFolder.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return !name.startsWith(".");
}
});
Object[][] data = new Object[tests.length][];
for (int i = 0; i < tests.length; i++) {
File testDir = tests[i];
File descriptionFile = null;
File configFile = null;
File resultFile = null;
for (File file : testDir.listFiles()) {
if (file.getName().endsWith("_desc.txt")) {
descriptionFile = file;
}
else if (file.getName().endsWith("_config.txt")) {
configFile = file;
}
else if (file.getName().endsWith("_result.txt")) {
resultFile = file;
}
}
assertNotNull(descriptionFile, String.format("description file not found in %s among %s", testDir, Arrays.toString(testDir.list())));
assertNotNull(configFile, String.format("config file file not found in %s among %s", testDir, Arrays.toString(testDir.list())));
assertNotNull(resultFile, String.format("result file file not found in %s among %s", testDir, Arrays.toString(testDir.list())));
String testName = FileUtil.loadFile(descriptionFile).split("\n")[0]; // description is in the first line of the desc-file
data[i] = new Object[]{
testName, configFile, resultFile
};
}
return data;
}
public static File getTestDataFolder() {
File pluginRoot = new File(PluginPathManager.getPluginHomePath("git4idea"));
return new File(pluginRoot, "testData");
}
public interface EqualityChecker<T, E> {
boolean areEqual(T actual, E expected);
}
@@ -18,6 +18,7 @@ package git4idea.tests;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.GitBranch;
import git4idea.test.GitTestUtil;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@@ -26,6 +26,7 @@ import com.intellij.testFramework.vcs.MockChangelistBuilder;
import com.intellij.testFramework.vcs.MockDirtyScope;
import git4idea.GitVcs;
import git4idea.status.GitChangeProvider;
import git4idea.test.GitTestUtil;
import org.testng.annotations.BeforeMethod;
import java.util.Arrays;
@@ -20,6 +20,7 @@ import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vcs.changes.LocalChangeList;
import git4idea.test.GitTestUtil;
import org.testng.annotations.Test;
import java.io.IOException;
@@ -0,0 +1,72 @@
/*
* 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 git4idea.util;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import git4idea.test.GitTestUtil;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
/**
* @author Kirill Likhodedov
*/
public class NetrcDataTest {
@DataProvider(name = "netrc")
public Object[][] loadBranches() throws IOException {
return GitTestUtil.loadConfigData(new File(GitTestUtil.getTestDataFolder(), "netrc"));
}
@Test(dataProvider = "netrc")
public void testBranches(String testName, File netrcFile, File resultFile) throws IOException {
NetrcData netrcData = NetrcData.parse(netrcFile);
Collection<NetrcData.AuthRecord> expectedRecord = readAuthData(resultFile);
GitTestUtil.assertEqualCollections(netrcData.getAuthData(), expectedRecord);
if (!testName.startsWith("n0") && !testName.startsWith("n1")) { // auth data not defined for these tests
assertTrue(netrcData.hasAuthDataForUrl("http://bitbucket.org"));
assertTrue(netrcData.hasAuthDataForUrl("https://bitbucket.org"));
assertTrue(netrcData.hasAuthDataForUrl("bitbucket.org"));
assertTrue(netrcData.hasAuthDataForUrl("https://bitbucket.org/user/repo.git"));
assertTrue(netrcData.hasAuthDataForUrl("https://bitbucket.org/user/repo"));
}
assertFalse(netrcData.hasAuthDataForUrl("http://example.com"));
assertFalse(netrcData.hasAuthDataForUrl("https://example.com"));
assertFalse(netrcData.hasAuthDataForUrl("example.com"));
}
private static Collection<NetrcData.AuthRecord> readAuthData(File file) throws IOException {
Collection<NetrcData.AuthRecord> authRecords = new ArrayList<NetrcData.AuthRecord>();
for (String authLine : FileUtil.loadFile(file).split("\n")) {
if (StringUtil.isEmptyOrSpaces(authLine)) {
continue;
}
String[] data = authLine.split(" ");
authRecords.add(new NetrcData.AuthRecord(data[0], data[1], data.length > 2 ? data[2] : null));
}
return authRecords;
}
}
@@ -15,7 +15,7 @@ package org.zmlx.hg4idea.execution;
import com.intellij.execution.process.CapturingProcessHandler;
import com.intellij.execution.process.ProcessOutput;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
@@ -44,8 +44,12 @@ public final class ShellCommand {
if (myRunViaBash) {
// run via bash -cl <hg command> => need to escape bash special symbols
// '-l' makes bash execute as a login shell thus reading .bash_profile
String hgCommand = StringUtil.join(commandLine, " ");
hgCommand = escapeBashControlCharacters(hgCommand);
StringBuilder hgCommandBuilder = new StringBuilder();
for (String command : commandLine) {
hgCommandBuilder.append(escapeSpacesIfNeeded(command));
hgCommandBuilder.append(" ");
}
String hgCommand = escapeBashControlCharacters(hgCommandBuilder.toString());
commandLine = new ArrayList<String>(3);
commandLine.add("bash");
commandLine.add("-cl");
@@ -100,4 +104,9 @@ public final class ShellCommand {
return sb.toString();
}
@NotNull
private String escapeSpacesIfNeeded(@NotNull String s) {
return myRunViaBash ? s.replace(" ", "\\ ") : s;
}
}
@@ -73,21 +73,21 @@ public class XmlUnusedNamespaceInspection extends XmlSuppressableInspectionTool
XmlAttributeValue value = attribute.getValueElement();
assert value != null;
holder.registerProblem(attribute, "Namespace declaration is never used", ProblemHighlightType.LIKE_UNUSED_SYMBOL,
new RemoveNamespaceDeclarationFix(declaredPrefix));
new RemoveNamespaceDeclarationFix(declaredPrefix, false));
XmlTag parent = attribute.getParent();
if (declaredPrefix.length() == 0) {
XmlAttribute location = getDefaultLocation(parent);
if (location != null) {
holder.registerProblem(location, NAMESPACE_LOCATION_IS_NEVER_USED, ProblemHighlightType.LIKE_UNUSED_SYMBOL,
new RemoveNamespaceDeclarationFix(declaredPrefix));
new RemoveNamespaceDeclarationFix(declaredPrefix, true));
}
}
else {
for (PsiReference reference : getLocationReferences(namespace, parent)) {
if (!XmlHighlightVisitor.hasBadResolve(reference, false))
holder.registerProblemForReference(reference, ProblemHighlightType.LIKE_UNUSED_SYMBOL, NAMESPACE_LOCATION_IS_NEVER_USED,
new RemoveNamespaceDeclarationFix(declaredPrefix));
new RemoveNamespaceDeclarationFix(declaredPrefix, true));
}
}
}
@@ -234,9 +234,11 @@ public class XmlUnusedNamespaceInspection extends XmlSuppressableInspectionTool
public static final String NAME = "Remove unused namespace declaration";
protected final String myPrefix;
private final boolean myLocationFix;
private RemoveNamespaceDeclarationFix(@Nullable String prefix) {
private RemoveNamespaceDeclarationFix(@Nullable String prefix, boolean locationFix) {
myPrefix = prefix;
myLocationFix = locationFix;
}
@NotNull
@@ -328,7 +330,9 @@ public class XmlUnusedNamespaceInspection extends XmlSuppressableInspectionTool
@Override
public boolean equals(Object obj) {
return obj instanceof RemoveNamespaceDeclarationFix && Comparing.equal(myPrefix, ((RemoveNamespaceDeclarationFix)obj).myPrefix);
return obj instanceof RemoveNamespaceDeclarationFix &&
Comparing.equal(myPrefix, ((RemoveNamespaceDeclarationFix)obj).myPrefix) &&
(myLocationFix || ((RemoveNamespaceDeclarationFix)obj).myLocationFix);
}
@Override
@@ -342,7 +346,7 @@ public class XmlUnusedNamespaceInspection extends XmlSuppressableInspectionTool
public static final String NAME = "Remove unused namespace location";
private RemoveNamespaceLocationFix(String namespace) {
super(namespace);
super(namespace, true);
}
@NotNull