diff options
Diffstat (limited to 'platform/vcs-impl/src/com/intellij')
20 files changed, 616 insertions, 216 deletions
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java index 3008138c60a2..291289854fa7 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java @@ -169,6 +169,7 @@ public class AnnotateToggleAction extends ToggleAction implements DumbAware, Ann if (vcs == null) return; final AnnotationProvider annotationProvider = vcs.getCachingAnnotationProvider(); + assert annotationProvider != null; final Ref<FileAnnotation> fileAnnotationRef = new Ref<FileAnnotation>(); final Ref<VcsException> exceptionRef = new Ref<VcsException>(); @@ -190,7 +191,7 @@ public class AnnotateToggleAction extends ToggleAction implements DumbAware, Ann exceptionRef.set(e); } catch (Throwable t) { - handler.completed(file.getPath()); + exceptionRef.set(new VcsException(t)); } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/MapIgnoredFilesHolder.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/MapIgnoredFilesHolder.java index 297e46482135..5ba7ce8cab0e 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/MapIgnoredFilesHolder.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/MapIgnoredFilesHolder.java @@ -20,7 +20,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.AbstractVcs; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.hash.HashSet; +import gnu.trove.THashSet; import java.util.Collection; import java.util.Set; @@ -40,8 +40,8 @@ public class MapIgnoredFilesHolder extends AbstractIgnoredFilesHolder { public MapIgnoredFilesHolder(Project project) { super(project); myProject = project; - mySet = new HashSet<VirtualFile>(); - myVcsIgnoredSet = new HashSet<VirtualFile>(); //collect ignored files from VcsChangeProvider -> processIgnored + mySet = new THashSet<VirtualFile>(); + myVcsIgnoredSet = new THashSet<VirtualFile>(); //collect ignored files from VcsChangeProvider -> processIgnored } @Override diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/RemoteRevisionsCache.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/RemoteRevisionsCache.java index 0ff134eb723a..7870b7ddbe02 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/RemoteRevisionsCache.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/RemoteRevisionsCache.java @@ -16,13 +16,11 @@ package com.intellij.openapi.vcs.changes; import com.intellij.lifecycle.PeriodicalTasksCloser; -import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Couple; -import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Getter; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vcs.*; @@ -57,12 +55,12 @@ public class RemoteRevisionsCache implements PlusMinus<Pair<String, AbstractVcs> private final Object myLock; private final Map<String, RemoteDifferenceStrategy> myKinds; private final ControlledCycle myControlledCycle; - private final MessageBusConnection myConnection; public static RemoteRevisionsCache getInstance(final Project project) { return PeriodicalTasksCloser.getInstance().safeGetService(project, RemoteRevisionsCache.class); } + @SuppressWarnings("UnusedDeclaration") // initialized as a Service private RemoteRevisionsCache(final Project project) { myProject = project; myLock = new Object(); @@ -73,15 +71,11 @@ public class RemoteRevisionsCache implements PlusMinus<Pair<String, AbstractVcs> myChangeDecorator = new RemoteStatusChangeNodeDecorator(this); myVcsManager = ProjectLevelVcsManager.getInstance(project); - myConnection = myProject.getMessageBus().connect(); - myConnection.subscribe(ProjectLevelVcsManager.VCS_CONFIGURATION_CHANGED, this); - myConnection.subscribe(ProjectLevelVcsManager.VCS_CONFIGURATION_CHANGED_IN_PLUGIN, this); + MessageBusConnection connection = myProject.getMessageBus().connect(); + connection.subscribe(ProjectLevelVcsManager.VCS_CONFIGURATION_CHANGED, this); + connection.subscribe(ProjectLevelVcsManager.VCS_CONFIGURATION_CHANGED_IN_PLUGIN, this); myKinds = new HashMap<String, RemoteDifferenceStrategy>(); - Disposer.register(project, new Disposable() { - public void dispose() { - myConnection.disconnect(); - } - }); + final VcsConfiguration vcsConfiguration = VcsConfiguration.getInstance(myProject); myControlledCycle = new ControlledCycle(project, new Getter<Boolean>() { @Override @@ -92,7 +86,14 @@ public class RemoteRevisionsCache implements PlusMinus<Pair<String, AbstractVcs> boolean somethingChanged = myRemoteRevisionsNumbersCache.updateStep(); somethingChanged |= myRemoteRevisionsStateCache.updateStep(); if (somethingChanged) { - myProject.getMessageBus().syncPublisher(REMOTE_VERSION_CHANGED).run(); + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + if (!myProject.isDisposed()) { + myProject.getMessageBus().syncPublisher(REMOTE_VERSION_CHANGED).run(); + } + } + }); } } return shouldBeDone; diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/UpdatingChangeListBuilder.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/UpdatingChangeListBuilder.java index 93919fcb2bd0..fdc079641b68 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/UpdatingChangeListBuilder.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/UpdatingChangeListBuilder.java @@ -66,14 +66,14 @@ class UpdatingChangeListBuilder implements ChangelistBuilder { } public void processChange(final Change change, VcsKey vcsKey) { - processChangeInList( change, (ChangeList) null, vcsKey); + processChangeInList(change, (ChangeList)null, vcsKey); } public void processChangeInList(final Change change, @Nullable final ChangeList changeList, final VcsKey vcsKey) { checkIfDisposed(); LOG.debug("[processChangeInList-1] entering, cl name: " + ((changeList == null) ? null: changeList.getName()) + - " change: " + ChangesUtil.getFilePath(change).getPath()); + " change: " + ChangesUtil.getFilePath(change).getPath()); final String fileName = ChangesUtil.getFilePath(change).getName(); if (FileTypeManager.getInstance().isFileIgnored(fileName)) { LOG.debug("[processChangeInList-1] file type ignored"); @@ -86,11 +86,13 @@ class UpdatingChangeListBuilder implements ChangelistBuilder { if (changeList != null) { LOG.debug("[processChangeInList-1] to add change to cl"); myChangeListWorker.addChangeToList(changeList.getName(), change, vcsKey); - } else { + } + else { LOG.debug("[processChangeInList-1] to add to corresponding list"); myChangeListWorker.addChangeToCorrespondingList(change, vcsKey); } - } else { + } + else { LOG.debug("[processChangeInList-1] not under scope"); } } @@ -203,7 +205,7 @@ class UpdatingChangeListBuilder implements ChangelistBuilder { if (file == null) return; checkIfDisposed(); if (myScope.belongsTo(new FilePathImpl(file))) { - ((LogicallyLockedHolder) myComposite.get(FileHolder.HolderType.LOGICALLY_LOCKED)).add(file, logicalLock); + ((LogicallyLockedHolder)myComposite.get(FileHolder.HolderType.LOGICALLY_LOCKED)).add(file, logicalLock); } } @@ -220,7 +222,7 @@ class UpdatingChangeListBuilder implements ChangelistBuilder { if (file == null) return; checkIfDisposed(); if (myScope.belongsTo(new FilePathImpl(file))) { - ((SwitchedFileHolder) myComposite.get(FileHolder.HolderType.ROOT_SWITCH)).addFile(file, branch, false); + ((SwitchedFileHolder)myComposite.get(FileHolder.HolderType.ROOT_SWITCH)).addFile(file, branch, false); } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsGuess.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsGuess.java index f5abcf039ece..00ca9f2ebf4f 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsGuess.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsGuess.java @@ -22,6 +22,7 @@ import com.intellij.openapi.roots.FileIndexFacade; import com.intellij.openapi.util.Computable; import com.intellij.openapi.vcs.AbstractVcs; import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.ProjectLevelVcsManager; import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; @@ -34,7 +35,7 @@ public class VcsGuess { VcsGuess(final Project project) { myProject = project; - myVcsManager = (ProjectLevelVcsManagerImpl) ProjectLevelVcsManagerImpl.getInstance(myProject); + myVcsManager = (ProjectLevelVcsManagerImpl)ProjectLevelVcsManager.getInstance(myProject); myExcludedFileIndex = PeriodicalTasksCloser.getInstance().safeGetService(myProject, FileIndexFacade.class); } @@ -72,14 +73,14 @@ public class VcsGuess { final boolean inContent = myVcsManager.isFileInContent(validParent); if (inContent) return true; if (filePath != null) { - return isFileInBaseDir(filePath, myProject.getBaseDir()) && ! myExcludedFileIndex.isExcludedFile(validParent); + return isFileInBaseDir(filePath, myProject.getBaseDir()) && !myExcludedFileIndex.isExcludedFile(validParent); } return false; } }); } - private boolean isFileInBaseDir(final FilePath filePath, final VirtualFile baseDir) { + private static boolean isFileInBaseDir(final FilePath filePath, final VirtualFile baseDir) { final VirtualFile parent = filePath.getVirtualFileParent(); return !filePath.isDirectory() && parent != null && parent.equals(baseDir); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTreeList.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTreeList.java index 21ee42093193..2b4ea995448b 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTreeList.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTreeList.java @@ -27,6 +27,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.EmptyRunnable; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.FilePath; @@ -445,7 +446,7 @@ public abstract class ChangesTreeList<T> extends JPanel implements TypeSafeDataP ContentRevision afterRevision = change.getAfterRevision(); if (afterRevision == null) return false; FilePath file = afterRevision.getFile(); - return file.getName().equals(toSelect.getName()); + return FileUtil.pathsEqual(file.getPath(), toSelect.getPath()); } protected abstract DefaultTreeModel buildTreeModel(final List<T> changes, final ChangeNodeDecorator changeNodeDecorator); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/CopyLineStatusRangeAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/CopyLineStatusRangeAction.java index 83dc7ebd8b21..aaa512c4e674 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/CopyLineStatusRangeAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/CopyLineStatusRangeAction.java @@ -35,7 +35,7 @@ public class CopyLineStatusRangeAction extends BaseLineStatusRangeAction { } public void actionPerformed(final AnActionEvent e) { - final String content = myLineStatusTracker.getUpToDateContent(myRange); + final String content = myLineStatusTracker.getUpToDateContent(myRange).toString(); CopyPasteManager.getInstance().setContents(new StringSelection(content)); } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/DocumentWrapper.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/DocumentWrapper.java index d6362ffe19c9..1674b55ff3b6 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/DocumentWrapper.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/DocumentWrapper.java @@ -38,21 +38,14 @@ public class DocumentWrapper { @NotNull public List<String> getLines() { - return getLines(0, myDocument.getLineCount() - 1); + return getLines(0, getLineCount(myDocument) - 1); } @NotNull public List<String> getLines(int from, int to) { ArrayList<String> result = new ArrayList<String>(); for (int i = from; i <= to; i++) { - if (i >= myDocument.getLineCount()) break; - final String line = getLine(i); - /* - if (line.length() > 0 || i < to) { - result.add(line); - } - */ - result.add(line); + result.add(getLine(i)); } return result; } @@ -65,5 +58,9 @@ public class DocumentWrapper { } return myDocument.getText(range); } + + private static int getLineCount(@NotNull Document document) { + return Math.max(document.getLineCount(), 1); + } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java index ac0bc8608368..44a56e7df517 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java @@ -175,10 +175,10 @@ public class LineStatusTracker { LOG.assertTrue(!myReleased, "Already released"); int first = - range.getOffset1() >= myDocument.getLineCount() ? myDocument.getTextLength() : myDocument.getLineStartOffset(range.getOffset1()); + range.getOffset1() >= getLineCount(myDocument) ? myDocument.getTextLength() : myDocument.getLineStartOffset(range.getOffset1()); int second = - range.getOffset2() >= myDocument.getLineCount() ? myDocument.getTextLength() : myDocument.getLineStartOffset(range.getOffset2()); + range.getOffset2() >= getLineCount(myDocument) ? myDocument.getTextLength() : myDocument.getLineStartOffset(range.getOffset2()); final RangeHighlighter highlighter = DocumentMarkupModel.forDocument(myDocument, myProject, true) .addRangeHighlighter(first, second, HighlighterLayer.FIRST - 1, null, HighlighterTargetArea.LINES_IN_RANGE); @@ -259,6 +259,7 @@ public class LineStatusTracker { if (range.getHighlighter() != null) { range.getHighlighter().dispose(); } + range.invalidate(); } myRanges.clear(); } @@ -316,10 +317,10 @@ public class LineStatusTracker { if (myBulkUpdate || myAnathemaThrown || BaseLoadState.LOADED != myBaseLoaded) return; try { myFirstChangedLine = myDocument.getLineNumber(e.getOffset()); - myLastChangedLine = myDocument.getLineNumber(e.getOffset() + e.getOldLength()); - myChangedLines = myLastChangedLine - myFirstChangedLine; + myLastChangedLine = e.getOldLength() == 0 ? myFirstChangedLine : myDocument.getLineNumber(e.getOffset() + e.getOldLength() - 1); if (StringUtil.endsWithChar(e.getOldFragment(), '\n')) myLastChangedLine++; - myTotalLines = e.getDocument().getLineCount(); + myChangedLines = myLastChangedLine - myFirstChangedLine; + myTotalLines = getLineCount(e.getDocument()); } catch (ProcessCanceledException ignore) { } @@ -334,9 +335,14 @@ public class LineStatusTracker { if (myReleased) return; if (myBulkUpdate || myAnathemaThrown || BaseLoadState.LOADED != myBaseLoaded) return; try { - int currentChangedLines = myDocument.getLineNumber(e.getOffset() + e.getNewLength()) - myDocument.getLineNumber(e.getOffset()); + int currentFirstChangedLine = myFirstChangedLine; + int currentLastChangedLine = + e.getNewLength() == 0 ? currentFirstChangedLine : myDocument.getLineNumber(e.getOffset() + e.getNewLength() - 1); + if (StringUtil.endsWithChar(e.getNewFragment(), '\n')) currentLastChangedLine++; + int currentChangedLines = currentLastChangedLine - currentFirstChangedLine; + int upToDateTotalLine = getLineCount(myUpToDateDocument); + int linesShift = currentChangedLines - myChangedLines; - int upToDateTotalLine = myUpToDateDocument.getLineCount(); List<Range> rangesBeforeChange = new ArrayList<Range>(); List<Range> rangesAfterChange = new ArrayList<Range>(); @@ -355,13 +361,14 @@ public class LineStatusTracker { myLastChangedLine = lastChangedRange.getOffset2() - 1; } - int currentFirstLine = myFirstChangedLine; - int currentLastLine = myLastChangedLine + linesShift; + currentFirstChangedLine = myFirstChangedLine; + currentLastChangedLine = myLastChangedLine + linesShift; int upToDateFirstLine = getUpToDateLine1(lastRangeBefore, myFirstChangedLine); int upToDateLastLine = getUpToDateLine2(firstRangeAfter, myLastChangedLine, myTotalLines, upToDateTotalLine); - List<Range> newChangedRanges = getNewChangedRanges(currentFirstLine, currentLastLine, upToDateFirstLine, upToDateLastLine); + List<Range> newChangedRanges = + getNewChangedRanges(currentFirstChangedLine, currentLastChangedLine, upToDateFirstLine, upToDateLastLine); shiftRanges(rangesAfterChange, linesShift); @@ -426,6 +433,7 @@ public class LineStatusTracker { range.getHighlighter().dispose(); } range.setHighlighter(null); + range.invalidate(); } for (Range range : newRangesInChange) { range.setHighlighter(createHighlighter(range)); @@ -531,98 +539,207 @@ public class LineStatusTracker { } } - public void rollbackChanges(final Range range) { + public void rollbackChanges(@NotNull Range range) { myApplication.assertWriteAccessAllowed(); synchronized (myLock) { - TextRange currentTextRange = getCurrentTextRangeWithMagic(range); + if (!range.isValid()) { + LOG.warn("Rollback of invalid range"); + return; + } + + if (range.getType() == Range.MODIFIED) { + TextRange currentTextRange = getCurrentTextRange(range); + int offset1 = currentTextRange.getStartOffset(); + int offset2 = currentTextRange.getEndOffset(); - int offset1 = currentTextRange.getStartOffset(); - int offset2 = Math.min(currentTextRange.getEndOffset() + 1, myDocument.getTextLength()); - if (range.getType() == Range.INSERTED) { - myDocument.replaceString(offset1, offset2, ""); + CharSequence upToDateContent = getUpToDateContent(range); + myDocument.replaceString(offset1, offset2, upToDateContent); + } + else if (range.getType() == Range.INSERTED) { + TextRange currentTextRange = getCurrentTextRange(range); + int offset1 = currentTextRange.getStartOffset(); + int offset2 = currentTextRange.getEndOffset(); + + if (offset1 > 0) { + offset1--; + } + else if (offset2 < myDocument.getTextLength()) { + offset2++; + } + myDocument.deleteString(offset1, offset2); } else if (range.getType() == Range.DELETED) { - String upToDateContent = getUpToDateContentWithMagic(range); - myDocument.insertString(offset1, upToDateContent); + CharSequence content = getUpToDateContent(range); + if (range.getOffset2() == getLineCount(myDocument)) { + myDocument.insertString(myDocument.getTextLength(), "\n" + content); + } + else { + myDocument.insertString(myDocument.getLineStartOffset(range.getOffset2()), content + "\n"); + } } else { - String upToDateContent = getUpToDateContentWithMagic(range); - myDocument.replaceString(offset1, offset2, upToDateContent); + throw new IllegalArgumentException("Unknown range type: " + range.getType()); } } } - public String getUpToDateContentWithMagic(Range range) { - synchronized (myLock) { - TextRange textRange = getUpToDateRangeWithMagic(range); - final int startOffset = textRange.getStartOffset(); - final int endOffset = Math.min(textRange.getEndOffset() + 1, myUpToDateDocument.getTextLength()); - return myUpToDateDocument.getCharsSequence().subSequence(startOffset, endOffset).toString(); - } - } + public void rollbackChanges(@NotNull SegmentTree lines) { + myApplication.assertWriteAccessAllowed(); - public String getUpToDateContent(Range range) { synchronized (myLock) { - TextRange textRange = getUpToDateRange(range); - final int startOffset = textRange.getStartOffset(); - final int endOffset = Math.min(textRange.getEndOffset() + 1, myUpToDateDocument.getTextLength()); - return myUpToDateDocument.getCharsSequence().subSequence(startOffset, endOffset).toString(); + List<Range> affectedRanges = new ArrayList<Range>(); + + boolean wasEnd = false; + boolean simple = true; + for (Range range : myRanges) { + if (!range.isValid()) { + LOG.warn("Rollback of invalid range"); + return; + } + + boolean check; + if (range.getOffset1() == range.getOffset2()) { + check = lines.check(range.getOffset1()); + } + else { + check = lines.check(range.getOffset1(), range.getOffset2()); + } + if (check) { + if (wasEnd) simple = false; + affectedRanges.add(range); + } + else { + if (!affectedRanges.isEmpty()) wasEnd = true; + } + } + + if (simple) { + rollbackChangesSimple(affectedRanges); + } + else { + rollbackChangesComplex(affectedRanges); + } } } - Project getProject() { - return myProject; + private void rollbackChangesSimple(@NotNull List<Range> ranges) { + if (ranges.isEmpty()) return; + + Range first = ranges.get(0); + Range last = ranges.get(ranges.size() - 1); + + byte type = first == last ? first.getType() : Range.MODIFIED; + final Range merged = new Range(first.getOffset1(), last.getOffset2(), first.getUOffset1(), last.getUOffset2(), type); + + // We don't expect complex Insertion/Deletion operation - they shouldn't exist + assert type != Range.MODIFIED || (first.getOffset1() != last.getOffset2() && first.getUOffset1() != last.getUOffset2()); + + rollbackChanges(merged); } - @NotNull - TextRange getCurrentTextRangeWithMagic(@NotNull Range range) { - return getRangeWithMagic(range.getType(), range.getOffset1(), range.getOffset2(), Range.DELETED, myDocument); + private void rollbackChangesComplex(@NotNull List<Range> ranges) { + // We can't relay on assumption, that revert of a single change will not affect any other. + // This, among the others, is because of 'magic' ranges for revert, that will affect nearby lines implicitly. + // So it's dangerous to apply ranges ony-by-one and we have to create single atomic modification. + // Usage of Bulk mode will lead to full rebuild of tracker, and therefore will be slow.. + + if (ranges.isEmpty()) return; + if (ranges.size() == 1) { + rollbackChanges(ranges.get(0)); + return; + } + + Range first = ranges.get(0); + Range last = ranges.get(ranges.size() - 1); + + // We don't expect complex Insertion/Deletion operation - they shouldn't exist. + assert first != last && first.getOffset1() != last.getOffset2() && first.getUOffset1() != last.getUOffset2(); + + final int start = getCurrentTextRange(first).getStartOffset(); + final int end = getCurrentTextRange(last).getEndOffset(); + + StringBuilder builder = new StringBuilder(); + + int lastOffset = start; + for (Range range : ranges) { + TextRange textRange = getCurrentTextRange(range); + + builder.append(myDocument.getText(new TextRange(lastOffset, textRange.getStartOffset()))); + lastOffset = textRange.getEndOffset(); + + if (range.getType() == Range.MODIFIED) { + builder.append(getUpToDateContent(range)); + } + else if (range.getType() == Range.INSERTED) { + if (builder.length() > 0) { + builder.deleteCharAt(builder.length() - 1); + } + else { + lastOffset++; + } + } + else if (range.getType() == Range.DELETED) { + CharSequence content = getUpToDateContent(range); + if (range.getOffset2() == getLineCount(myDocument)) { + builder.append('\n').append(content); + } + else { + builder.append(content).append('\n'); + } + } + else { + throw new IllegalArgumentException("Unknown range type: " + range.getType()); + } + } + builder.append(myDocument.getText(new TextRange(lastOffset, end))); + + final String s = builder.toString(); + + myDocument.replaceString(start, end, s); } - @NotNull - TextRange getUpToDateRangeWithMagic(@NotNull Range range) { - return getRangeWithMagic(range.getType(), range.getUOffset1(), range.getUOffset2(), Range.INSERTED, myUpToDateDocument); + public CharSequence getUpToDateContent(@NotNull Range range) { + synchronized (myLock) { + TextRange textRange = getUpToDateRange(range); + final int startOffset = textRange.getStartOffset(); + final int endOffset = textRange.getEndOffset(); + return myUpToDateDocument.getCharsSequence().subSequence(startOffset, endOffset); + } } @NotNull TextRange getCurrentTextRange(@NotNull Range range) { - return getRange(range.getType(), range.getOffset1(), range.getOffset2(), Range.DELETED, myDocument); - } + synchronized (myLock) { + if (!range.isValid()) { + LOG.warn("Current TextRange of invalid range"); + } - @NotNull - TextRange getUpToDateRange(@NotNull Range range) { - return getRange(range.getType(), range.getUOffset1(), range.getUOffset2(), Range.INSERTED, myUpToDateDocument); + return getRange(range.getOffset1(), range.getOffset2(), myDocument); + } } @NotNull - private static TextRange getRangeWithMagic(byte rangeType, int offset1, int offset2, byte emptyRangeCondition, Document document) { - if (rangeType == emptyRangeCondition) { - int lineStartOffset; - if (offset1 == 0) { - lineStartOffset = 0; - } - else { - lineStartOffset = document.getLineEndOffset(offset1 - 1); - } - //if (lineStartOffset > 0) lineStartOffset--; - return new TextRange(lineStartOffset, lineStartOffset); - } - else { - int startOffset = document.getLineStartOffset(offset1); - int endOffset = document.getLineEndOffset(offset2 - 1); - if (startOffset > 0) { - --startOffset; - --endOffset; + TextRange getUpToDateRange(@NotNull Range range) { + synchronized (myLock) { + if (!range.isValid()) { + LOG.warn("UpToDate TextRange of invalid range"); } - return new TextRange(startOffset, endOffset); + + return getRange(range.getUOffset1(), range.getUOffset2(), myUpToDateDocument); } } + /** + * Return affected range, without non-internal '\n' + * so if last line is not empty, the last symbol will be not '\n' + * <p/> + * So we consider '\n' not as a part of line, but a separator between lines + */ @NotNull - private static TextRange getRange(byte rangeType, int offset1, int offset2, byte emptyRangeCondition, Document document) { - if (rangeType == emptyRangeCondition) { - int lineStartOffset = offset1 < document.getLineCount() ? document.getLineStartOffset(offset1) : document.getTextLength(); + private static TextRange getRange(int offset1, int offset2, @NotNull Document document) { + if (offset1 == offset2) { + int lineStartOffset = offset1 < getLineCount(document) ? document.getLineStartOffset(offset1) : document.getTextLength(); return new TextRange(lineStartOffset, lineStartOffset); } else { @@ -643,7 +760,11 @@ public class LineStatusTracker { } } - public static enum BaseLoadState { + Project getProject() { + return myProject; + } + + public enum BaseLoadState { LOADING, FAILED, LOADED @@ -705,4 +826,8 @@ public class LineStatusTracker { myLabel.setText("Can not highlight changed lines. File is too big and there are too many changes."); } } + + private static int getLineCount(@NotNull Document document) { + return Math.max(document.getLineCount(), 1); + } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTrackerDrawing.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTrackerDrawing.java index 24884b4a101c..6f1cd15ef018 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTrackerDrawing.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTrackerDrawing.java @@ -160,7 +160,7 @@ public class LineStatusTrackerDrawing { localShowPrevAction.copyFrom(globalShowPrevAction); final RollbackLineStatusRangeAction rollback = new RollbackLineStatusRangeAction(tracker, range, editor); - EmptyAction.setupAction(rollback, IdeActions.CHANGES_VIEW_ROLLBACK, editorComponent); + EmptyAction.setupAction(rollback, IdeActions.SELECTED_CHANGES_ROLLBACK, editorComponent); group.add(rollback); group.add(new ShowLineStatusRangeDiffAction(tracker, range, editor)); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/Range.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/Range.java index 5727c62f4d14..e166acd30fec 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/Range.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/Range.java @@ -42,6 +42,8 @@ public class Range { private final byte myType; @Nullable private RangeHighlighter myRangeHighlighter; + private boolean myValid = true; + public static Range createOn(@NotNull Diff.Change change, int shift, int upToDateShift) { byte type = getChangeTypeFrom(change); @@ -158,4 +160,12 @@ public class Range { public RangeHighlighter getHighlighter() { return myRangeHighlighter; } + + public boolean isValid() { + return myValid; + } + + public void invalidate() { + myValid = false; + } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RollbackLineStatusAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RollbackLineStatusAction.java new file mode 100644 index 000000000000..59697f1222dd --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RollbackLineStatusAction.java @@ -0,0 +1,136 @@ +/* + * Copyright 2000-2010 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.ex; + +import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.editor.Caret; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.DumbAwareAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vcs.VcsBundle; +import com.intellij.openapi.vcs.impl.LineStatusTrackerManager; +import com.intellij.openapi.vfs.ReadonlyStatusHandler; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class RollbackLineStatusAction extends DumbAwareAction { + public RollbackLineStatusAction() { + super("Rollback", "Rollback selected changes", AllIcons.Actions.Reset); + } + + @Override + public void update(AnActionEvent e) { + Project project = e.getProject(); + if (project == null) { + e.getPresentation().setEnabled(false); + return; + } + Editor editor = CommonDataKeys.EDITOR.getData(e.getDataContext()); + if (editor == null) { + e.getPresentation().setEnabled(false); + return; + } + LineStatusTracker tracker = LineStatusTrackerManager.getInstance(project).getLineStatusTracker(editor.getDocument()); + if (tracker == null) { + e.getPresentation().setEnabled(false); + return; + } + e.getPresentation().setEnabled(true); + } + + @Override + public void actionPerformed(AnActionEvent e) { + Project project = e.getProject(); + Editor editor = CommonDataKeys.EDITOR.getData(e.getDataContext()); + LineStatusTracker tracker = LineStatusTrackerManager.getInstance(project).getLineStatusTracker(editor.getDocument()); + if (tracker == null) return; + + rollback(tracker, editor, null); + } + + protected static void rollback(@NotNull LineStatusTracker tracker, @Nullable Editor editor, @Nullable Range range) { + if (range != null) { + doRollback(tracker, range); + return; + } + + if (editor == null) return; + Document document = editor.getDocument(); + int totalLines = getLineCount(document); + + SegmentTree lines = new SegmentTree(totalLines + 1); + + List<Caret> carets = editor.getCaretModel().getAllCarets(); + for (Caret caret : carets) { + if (caret.hasSelection()) { + int line1 = editor.offsetToLogicalPosition(caret.getSelectionStart()).line; + int line2 = editor.offsetToLogicalPosition(caret.getSelectionEnd()).line; + lines.mark(line1, line2 + 1); + if (caret.getSelectionEnd() == document.getTextLength()) lines.mark(totalLines); + } + else { + lines.mark(caret.getLogicalPosition().line); + if (caret.getOffset() == document.getTextLength()) lines.mark(totalLines); + } + } + + doRollback(tracker, lines); + } + + private static void doRollback(@NotNull final LineStatusTracker tracker, @NotNull final Range range) { + execute(tracker, new Runnable() { + @Override + public void run() { + tracker.rollbackChanges(range); + } + }); + } + + private static void doRollback(@NotNull final LineStatusTracker tracker, @NotNull final SegmentTree lines) { + execute(tracker, new Runnable() { + @Override + public void run() { + tracker.rollbackChanges(lines); + } + }); + } + + private static void execute(@NotNull final LineStatusTracker tracker, @NotNull final Runnable task) { + // TODO: is there possible data races? + CommandProcessor.getInstance().executeCommand(tracker.getProject(), new Runnable() { + public void run() { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void run() { + if (!tracker.getDocument().isWritable()) { + final ReadonlyStatusHandler.OperationStatus operationStatus = ReadonlyStatusHandler + .getInstance(tracker.getProject()).ensureFilesWritable(tracker.getVirtualFile()); + if (operationStatus.hasReadonlyFiles()) return; + } + task.run(); + } + }); + } + }, VcsBundle.message("command.name.rollback.change"), null); + } + + private static int getLineCount(@NotNull Document document) { + return Math.max(document.getLineCount(), 1); + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RollbackLineStatusRangeAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RollbackLineStatusRangeAction.java index ed5846d20ade..f1795b5efc3c 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RollbackLineStatusRangeAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RollbackLineStatusRangeAction.java @@ -12,41 +12,28 @@ */ package com.intellij.openapi.vcs.ex; -import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.vcs.VcsBundle; -import com.intellij.openapi.vfs.ReadonlyStatusHandler; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -/** -* @author irengrig -*/ -public class RollbackLineStatusRangeAction extends BaseLineStatusRangeAction { - public RollbackLineStatusRangeAction(final LineStatusTracker lineStatusTracker, final Range range, final Editor editor) { - super(VcsBundle.message("action.name.rollback"), AllIcons.Actions.Reset, lineStatusTracker, range); +public class RollbackLineStatusRangeAction extends RollbackLineStatusAction { + @NotNull private final LineStatusTracker myTracker; + @Nullable private final Editor myEditor; + @NotNull private final Range myRange; + + public RollbackLineStatusRangeAction(@NotNull LineStatusTracker tracker, @NotNull Range range, @Nullable Editor editor) { + myTracker = tracker; + myEditor = editor; + myRange = range; } - public boolean isEnabled() { - return true; + @Override + public void update(AnActionEvent e) { + e.getPresentation().setEnabled(true); } public void actionPerformed(final AnActionEvent e) { - CommandProcessor.getInstance().executeCommand(myLineStatusTracker.getProject(), new Runnable() { - public void run() { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - public void run() { - if (!myLineStatusTracker.getDocument().isWritable()) { - final ReadonlyStatusHandler.OperationStatus operationStatus = ReadonlyStatusHandler - .getInstance(myLineStatusTracker.getProject()).ensureFilesWritable(myLineStatusTracker.getVirtualFile()); - if (operationStatus.hasReadonlyFiles()) return; - } - myLineStatusTracker.rollbackChanges(myRange); - } - }); - } - }, VcsBundle.message("command.name.rollback.change"), null); - + rollback(myTracker, myEditor, myRange); } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/SegmentTree.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/SegmentTree.java new file mode 100644 index 000000000000..68307129c13e --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/SegmentTree.java @@ -0,0 +1,116 @@ +package com.intellij.openapi.vcs.ex; + +import org.jetbrains.annotations.Nullable; + +public class SegmentTree { + private final int myActualLength; + private final int myLength; + + private final Node myRoot; + + public SegmentTree(int length) { + myActualLength = length; + myLength = toUpperSquare(length); + myRoot = new Node(); + } + + public void mark(int pos) { + mark(pos, pos + 1); + } + + public void mark(int start, int end) { + start = correct(0, myActualLength, start); + end = correct(0, myActualLength, end); + + myRoot.mark(0, myLength, start, end); + } + + public boolean check(int pos) { + return check(pos, pos + 1); + } + + public boolean check(int start, int end) { + start = correct(0, myActualLength, start); + end = correct(0, myActualLength, end); + + return myRoot.check(0, myLength, start, end); + } + + private static int toUpperSquare(int value) { + int high = Integer.highestOneBit(value); + return high == value ? value : high * 2; + } + + private static class Node { + @Nullable + public Node myLeft; + + @Nullable + public Node myRight; + + public boolean myMarked; + + public boolean mark(int thisStart, int thisEnd, int start, int end) { + if (myLeft == null && myMarked) return true; + + if (start == end) return false; + + myMarked = true; + + if (thisStart == start && thisEnd == end) { + myLeft = null; + myRight = null; + return true; + } + + if (myLeft == null) { + myLeft = new Node(); + myRight = new Node(); + } + + int mid = thisStart + (thisEnd - thisStart) / 2; + int start1 = correct(thisStart, mid, start); + int end1 = correct(thisStart, mid, end); + int start2 = correct(mid, thisEnd, start); + int end2 = correct(mid, thisEnd, end); + + boolean marked = true; + marked &= myLeft.mark(thisStart, mid, start1, end1); + marked &= myRight.mark(mid, thisEnd, start2, end2); + + if (marked) { + myLeft = null; + myRight = null; + } + + return marked; + } + + public boolean check(int thisStart, int thisEnd, int start, int end) { + if (start == end) return false; + + if (thisStart == start && thisEnd == end) { + return myMarked; + } + + if (myLeft == null) return myMarked; + + int mid = thisStart + (thisEnd - thisStart) / 2; + int start1 = correct(thisStart, mid, start); + int end1 = correct(thisStart, mid, end); + int start2 = correct(mid, thisEnd, start); + int end2 = correct(mid, thisEnd, end); + + if (myLeft.check(thisStart, mid, start1, end1)) return true; + if (myRight.check(mid, thisEnd, start2, end2)) return true; + + return false; + } + } + + private static int correct(int start, int end, int value) { + if (value < start) return start; + if (value > end) return end; + return value; + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/ShowLineStatusRangeDiffAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/ShowLineStatusRangeDiffAction.java index c5dba0cab29c..38f65446a56f 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/ShowLineStatusRangeDiffAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/ShowLineStatusRangeDiffAction.java @@ -25,27 +25,22 @@ import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vcs.VcsBundle; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * @author irengrig */ public class ShowLineStatusRangeDiffAction extends BaseLineStatusRangeAction { - public ShowLineStatusRangeDiffAction(final LineStatusTracker lineStatusTracker, final Range range, final Editor editor) { + public ShowLineStatusRangeDiffAction(@NotNull LineStatusTracker lineStatusTracker, @NotNull Range range, @Nullable Editor editor) { super(VcsBundle.message("action.name.show.difference"), AllIcons.Actions.Diff, lineStatusTracker, range); } + @Override public boolean isEnabled() { - return isModifiedRange() || isDeletedRange(); - } - - private boolean isDeletedRange() { - return Range.DELETED == myRange.getType(); - } - - private boolean isModifiedRange() { - return Range.MODIFIED == myRange.getType(); + return true; } + @Override public void actionPerformed(final AnActionEvent e) { DiffManager.getInstance().getDiffTool().show(createDiffData()); } @@ -54,12 +49,13 @@ public class ShowLineStatusRangeDiffAction extends BaseLineStatusRangeAction { return new DiffRequest(myLineStatusTracker.getProject()) { @NotNull public DiffContent[] getContents() { + Range range = expand(myRange, myLineStatusTracker.getDocument(), myLineStatusTracker.getUpToDateDocument()); return new DiffContent[]{ createDiffContent(myLineStatusTracker.getUpToDateDocument(), - myLineStatusTracker.getUpToDateRange(myRange), + myLineStatusTracker.getUpToDateRange(range), null), createDiffContent(myLineStatusTracker.getDocument(), - myLineStatusTracker.getCurrentTextRange(myRange), + myLineStatusTracker.getCurrentTextRange(range), myLineStatusTracker.getVirtualFile())}; } @@ -74,9 +70,25 @@ public class ShowLineStatusRangeDiffAction extends BaseLineStatusRangeAction { }; } - private DiffContent createDiffContent(final Document uDocument, final TextRange textRange, final VirtualFile file) { + @NotNull + private DiffContent createDiffContent(@NotNull Document uDocument, @NotNull TextRange textRange, @Nullable VirtualFile file) { final Project project = myLineStatusTracker.getProject(); final DiffContent diffContent = new DocumentContent(project, uDocument); return new FragmentContent(diffContent, textRange, project, file); } + + @NotNull + private static Range expand(@NotNull Range range, @NotNull Document document, @NotNull Document uDocument) { + if (range.getType() == Range.MODIFIED) return range; + if (range.getType() == Range.INSERTED || range.getType() == Range.DELETED) { + boolean canExpandBefore = range.getOffset1() != 0 && range.getUOffset1() != 0; + boolean canExpandAfter = range.getOffset2() < document.getLineCount() && range.getUOffset2() < uDocument.getLineCount(); + int offset1 = range.getOffset1() - (canExpandBefore ? 1 : 0); + int uOffset1 = range.getUOffset1() - (canExpandBefore ? 1 : 0); + int offset2 = range.getOffset2() + (canExpandAfter ? 1 : 0); + int uOffset2 = range.getUOffset2() + (canExpandAfter ? 1 : 0); + return new Range(offset1, offset2, uOffset1, uOffset2, range.getType()); + } + throw new IllegalArgumentException("Unknown range type: " + range.getType()); + } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java index a1fa2baeb1d2..172edc75d02a 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java @@ -56,6 +56,7 @@ import com.intellij.openapi.vcs.ui.ReplaceFileConfirmationDialog; import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; import com.intellij.openapi.vcs.vfs.VcsFileSystem; import com.intellij.openapi.vcs.vfs.VcsVirtualFile; +import com.intellij.openapi.vcs.vfs.VcsVirtualFolder; import com.intellij.openapi.vfs.ReadonlyStatusHandler; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.*; @@ -1123,8 +1124,9 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { VcsFileRevision revision = e.getData( VcsDataKeys.VCS_FILE_REVISION ); final Boolean nonLocal = e.getData(VcsDataKeys.VCS_NON_LOCAL_HISTORY_SESSION); - FileType fileType = revVFile == null ? null : revVFile.getFileType(); - boolean enabled = revision != null && revVFile != null && !fileType.isBinary() && ! Boolean.TRUE.equals(nonLocal); + boolean isFile = revVFile != null && !revVFile.isDirectory(); + FileType fileType = isFile ? revVFile.getFileType() : null; + boolean enabled = revision != null && isFile && !fileType.isBinary() && !Boolean.TRUE.equals(nonLocal); if (enabled) { final ProjectLevelVcsManager plVcsManager = ProjectLevelVcsManager.getInstance(myVcs.getProject()); @@ -1309,7 +1311,9 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { private VirtualFile createVirtualFileForRevision(VcsFileRevision revision) { if (!myRevisionToVirtualFile.containsKey(revision)) { FilePath filePath = (revision instanceof VcsFileRevisionEx ? ((VcsFileRevisionEx)revision).getPath() : myFilePath); - myRevisionToVirtualFile.put(revision, new VcsVirtualFile(filePath.getPath(), revision, VcsFileSystem.getInstance())); + myRevisionToVirtualFile.put(revision, filePath.isDirectory() + ? new VcsVirtualFolder(filePath.getPath(), null, VcsFileSystem.getInstance()) + : new VcsVirtualFile(filePath.getPath(), revision, VcsFileSystem.getInstance())); } return myRevisionToVirtualFile.get(revision); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ModuleDefaultVcsRootPolicy.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ModuleDefaultVcsRootPolicy.java index 4f4efe5137a3..4b057f49b282 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ModuleDefaultVcsRootPolicy.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ModuleDefaultVcsRootPolicy.java @@ -21,7 +21,6 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.StorageScheme; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; -import com.intellij.openapi.module.ModuleUtil; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ex.ProjectEx; @@ -35,7 +34,6 @@ import com.intellij.openapi.vcs.changes.FilePathUnderVcs; import com.intellij.openapi.vcs.changes.VcsGuess; import com.intellij.openapi.vcs.ex.ProjectLevelVcsManagerEx; import com.intellij.openapi.vcs.impl.projectlevelman.NewMappings; -import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; @@ -102,10 +100,7 @@ public class ModuleDefaultVcsRootPolicy extends DefaultVcsRootPolicy { if (matchContext != null) { return true; } - if (myBaseDir != null && VfsUtilCore.isAncestor(myBaseDir, file, false)) { - return !ProjectRootManager.getInstance(myProject).getFileIndex().isIgnored(file); - } - return false; + return myBaseDir != null && VfsUtilCore.isAncestor(myBaseDir, file, false); } @Override diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ProjectLevelVcsManagerImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ProjectLevelVcsManagerImpl.java index cbf9d2b1889a..5cc3a8a9ca92 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ProjectLevelVcsManagerImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ProjectLevelVcsManagerImpl.java @@ -135,7 +135,7 @@ public class ProjectLevelVcsManagerImpl extends ProjectLevelVcsManagerEx impleme myMappings = new NewMappings(myProject, myMessageBus, this, manager, excludedFileIndex); myMappingsToRoots = new MappingsToRoots(myMappings, myProject); - if (! myProject.isDefault()) { + if (!myProject.isDefault()) { myVcsEventListenerManager = new VcsEventsListenerManagerImpl(); } @@ -203,7 +203,7 @@ public class ProjectLevelVcsManagerImpl extends ProjectLevelVcsManagerEx impleme } public boolean haveVcses() { - return ! AllVcses.getInstance(myProject).isEmpty(); + return !AllVcses.getInstance(myProject).isEmpty(); } @Override @@ -356,7 +356,7 @@ public class ProjectLevelVcsManagerImpl extends ProjectLevelVcsManagerEx impleme } public void unregisterVcs(AbstractVcs vcs) { - if (! ApplicationManager.getApplication().isUnitTestMode() && myMappings.haveActiveVcs(vcs.getName())) { + if (!ApplicationManager.getApplication().isUnitTestMode() && myMappings.haveActiveVcs(vcs.getName())) { // unlikely LOG.warn("Active vcs '" + vcs.getName() + "' is being unregistered. Remove from mappings first."); } @@ -391,7 +391,7 @@ public class ProjectLevelVcsManagerImpl extends ProjectLevelVcsManagerEx impleme @Override public boolean hasAnyMappings() { - return ! myMappings.isEmpty(); + return !myMappings.isEmpty(); } @Override @@ -452,7 +452,7 @@ public class ProjectLevelVcsManagerImpl extends ProjectLevelVcsManagerEx impleme private void releaseEditor() { if (myEditorAdapter != null) { final Editor editor = myEditorAdapter.getEditor(); - if (! editor.isDisposed()) { + if (!editor.isDisposed()) { EditorFactory.getInstance().releaseEditor(editor); } } @@ -472,7 +472,7 @@ public class ProjectLevelVcsManagerImpl extends ProjectLevelVcsManagerEx impleme @Override @NotNull public VcsShowSettingOption getStandardOption(@NotNull VcsConfiguration.StandardOption option, @NotNull AbstractVcs vcs) { - final VcsShowOptionsSettingImpl options = (VcsShowOptionsSettingImpl) getOptions(option); + final VcsShowOptionsSettingImpl options = (VcsShowOptionsSettingImpl)getOptions(option); options.addApplicableVcs(vcs); return options; } @@ -490,7 +490,7 @@ public class ProjectLevelVcsManagerImpl extends ProjectLevelVcsManagerEx impleme @Override public UpdateInfoTree showUpdateProjectInfo(UpdatedFiles updatedFiles, String displayActionName, ActionInfo actionInfo, boolean canceled) { - if (! myProject.isOpen() || myProject.isDisposed()) return null; + if (!myProject.isOpen() || myProject.isDisposed()) return null; ContentManager contentManager = getContentManager(); if (contentManager == null) { return null; // content manager is made null during dispose; flag is set later @@ -537,7 +537,7 @@ public class ProjectLevelVcsManagerImpl extends ProjectLevelVcsManagerEx impleme public boolean hasExplicitMapping(final VirtualFile vFile) { final VcsDirectoryMapping mapping = myMappings.getMappingFor(vFile); - return mapping != null && ! mapping.isDefaultMapping(); + return mapping != null && !mapping.isDefaultMapping(); } @Override @@ -687,7 +687,7 @@ public class ProjectLevelVcsManagerImpl extends ProjectLevelVcsManagerEx impleme final AbstractVcs[] vcses = myMappings.getActiveVcses(); for (AbstractVcs vcs : vcses) { final VirtualFile[] roots = getRootsUnderVcs(vcs); - for(VirtualFile root: roots) { + for (VirtualFile root : roots) { vcsRoots.add(new VcsRoot(vcs, root)); } } @@ -708,10 +708,8 @@ public class ProjectLevelVcsManagerImpl extends ProjectLevelVcsManagerEx impleme myMappings.clear(); final List<VcsDirectoryMapping> mappingsList = new ArrayList<VcsDirectoryMapping>(); - final List list = element.getChildren(ELEMENT_MAPPING); boolean haveNonEmptyMappings = false; - for(Object childObj: list) { - Element child = (Element) childObj; + for (Element child : element.getChildren(ELEMENT_MAPPING)) { final String vcs = child.getAttributeValue(ATTRIBUTE_VCS); if (vcs != null && !vcs.isEmpty()) { haveNonEmptyMappings = true; @@ -729,8 +727,9 @@ public class ProjectLevelVcsManagerImpl extends ProjectLevelVcsManagerEx impleme try { rootSettings.readExternal(rootSettingsElement); mapping.setRootSettings(rootSettings); - } catch (InvalidDataException e) { - LOG.error("Failed to load VCS root settings class "+ className + " for VCS " + vcsInstance.getClass().getName(), e); + } + catch (InvalidDataException e) { + LOG.error("Failed to load VCS root settings class " + className + " for VCS " + vcsInstance.getClass().getName(), e); } } } @@ -748,7 +747,7 @@ public class ProjectLevelVcsManagerImpl extends ProjectLevelVcsManagerEx impleme if (myProject.isDefault()) { element.setAttribute(ATTRIBUTE_DEFAULT_PROJECT, Boolean.TRUE.toString()); } - for(VcsDirectoryMapping mapping: getDirectoryMappings()) { + for (VcsDirectoryMapping mapping : getDirectoryMappings()) { Element child = new Element(ELEMENT_MAPPING); child.setAttribute(ATTRIBUTE_DIRECTORY, mapping.getDirectory()); child.setAttribute(ATTRIBUTE_VCS, mapping.getVcs()); @@ -803,7 +802,7 @@ public class ProjectLevelVcsManagerImpl extends ProjectLevelVcsManagerEx impleme @Override public void fireDirectoryMappingsChanged() { - if (myProject.isOpen() && ! myProject.isDisposed()) { + if (myProject.isOpen() && !myProject.isDisposed()) { myMappings.mappingsChanged(); } } @@ -844,7 +843,7 @@ public class ProjectLevelVcsManagerImpl extends ProjectLevelVcsManagerEx impleme @Override public Boolean compute() { return vf != null && (myExcludedIndex.isInContent(vf) || isFileInBaseDir(vf) || vf.equals(myProject.getBaseDir()) || - hasExplicitMapping(vf) || isInDirectoryBasedRoot(vf)) && ! myExcludedIndex.isExcludedFile(vf); + hasExplicitMapping(vf) || isInDirectoryBasedRoot(vf)) && !myExcludedIndex.isExcludedFile(vf); } }); } @@ -862,7 +861,7 @@ public class ProjectLevelVcsManagerImpl extends ProjectLevelVcsManagerEx impleme private boolean isInDirectoryBasedRoot(final VirtualFile file) { if (file == null) return false; - final StorageScheme storageScheme = ((ProjectEx) myProject).getStateStore().getStorageScheme(); + final StorageScheme storageScheme = ((ProjectEx)myProject).getStateStore().getStorageScheme(); if (StorageScheme.DIRECTORY_BASED.equals(storageScheme)) { final VirtualFile baseDir = myProject.getBaseDir(); if (baseDir == null) return false; diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/VcsRootIterator.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/VcsRootIterator.java index 8caa6f2bc981..122988896cb2 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/VcsRootIterator.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/VcsRootIterator.java @@ -54,12 +54,10 @@ public class VcsRootIterator { public boolean acceptFolderUnderVcs(final VirtualFile vcsRoot, final VirtualFile file) { final String vcsUrl = vcsRoot.getUrl(); final MyRootFilter rootFilter = myOtherVcsFolders.get(vcsUrl); - if ((rootFilter != null) && (! rootFilter.accept(file))) { + if ((rootFilter != null) && (!rootFilter.accept(file))) { return false; } - final Boolean excluded = isExcluded(myExcludedFileIndex, file); - if (excluded) return false; - return true; + return !isExcluded(myExcludedFileIndex, file); } private static boolean isExcluded(final FileIndexFacade indexFacade, final VirtualFile file) { @@ -128,9 +126,9 @@ public class VcsRootIterator { } public static void iterateVcsRoot(final Project project, - final VirtualFile root, - final Processor<FilePath> processor, - @Nullable VirtualFileFilter directoryFilter) { + final VirtualFile root, + final Processor<FilePath> processor, + @Nullable VirtualFileFilter directoryFilter) { final MyRootIterator rootIterator = new MyRootIterator(project, root, processor, null, directoryFilter); rootIterator.iterate(); } @@ -177,9 +175,9 @@ public class VcsRootIterator { @Override public Result visitFileEx(@NotNull VirtualFile file) { if (isExcluded(myExcludedFileIndex, file)) return SKIP_CHILDREN; - if (myRootPresentFilter != null && ! myRootPresentFilter.accept(file)) return SKIP_CHILDREN; - if (myProject.isDisposed() || ! process(file)) return skipTo(myRoot); - if (myDirectoryFilter != null && file.isDirectory() && ! myDirectoryFilter.shouldGoIntoDirectory(file)) return SKIP_CHILDREN; + if (myRootPresentFilter != null && !myRootPresentFilter.accept(file)) return SKIP_CHILDREN; + if (myProject.isDisposed() || !process(file)) return skipTo(myRoot); + if (myDirectoryFilter != null && file.isDirectory() && !myDirectoryFilter.shouldGoIntoDirectory(file)) return SKIP_CHILDREN; return CONTINUE; } }); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/projectlevelman/NewMappings.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/projectlevelman/NewMappings.java index de1a40d80f36..32fe920fc4db 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/projectlevelman/NewMappings.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/projectlevelman/NewMappings.java @@ -53,30 +53,31 @@ public class NewMappings { private final DefaultVcsRootPolicy myDefaultVcsRootPolicy; private final MessageBus myMessageBus; private final FileStatusManager myFileStatusManager; - private final FileIndexFacade myExcludedFileIndex; + private final FileIndexFacade myFileIndexFacade; private final Project myProject; private boolean myActivated; public NewMappings(final Project project, final MessageBus messageBus, - final ProjectLevelVcsManagerImpl vcsManager, FileStatusManager fileStatusManager, FileIndexFacade excludedFileIndex) { + final ProjectLevelVcsManagerImpl vcsManager, FileStatusManager fileStatusManager, FileIndexFacade fileIndexFacade) { myProject = project; myMessageBus = messageBus; myFileStatusManager = fileStatusManager; - myExcludedFileIndex = excludedFileIndex; + myFileIndexFacade = fileIndexFacade; myLock = new Object(); myVcsToPaths = new HashMap<String, List<VcsDirectoryMapping>>(); myFileWatchRequestsManager = new FileWatchRequestsManager(myProject, this, LocalFileSystem.getInstance()); myDefaultVcsRootPolicy = DefaultVcsRootPolicy.getInstance(project); myActiveVcses = new AbstractVcs[0]; - if (! myProject.isDefault()) { + if (!myProject.isDefault()) { final ArrayList<VcsDirectoryMapping> listStr = new ArrayList<VcsDirectoryMapping>(); final VcsDirectoryMapping mapping = new VcsDirectoryMapping("", ""); listStr.add(mapping); myVcsToPaths.put("", listStr); - mySortedMappings = new VcsDirectoryMapping[] {mapping}; - } else { + mySortedMappings = new VcsDirectoryMapping[]{mapping}; + } + else { mySortedMappings = VcsDirectoryMapping.EMPTY_ARRAY; } myActivated = false; @@ -131,7 +132,6 @@ public class NewMappings { return; } } - } final Ref<Boolean> switched = new Ref<Boolean>(Boolean.FALSE); @@ -139,7 +139,7 @@ public class NewMappings { public void run() { // sorted -> map. sorted mappings are NOT changed; switched.set(trySwitchVcs(path, activeVcsName)); - if (! switched.get().booleanValue()) { + if (!switched.get().booleanValue()) { final List<VcsDirectoryMapping> newList = listForVcsFromMap(newMapping.getVcs()); newList.add(newMapping); sortedMappingsByMap(); @@ -153,7 +153,7 @@ public class NewMappings { private void keepActiveVcs(@NotNull Runnable runnable) { final MyVcsActivator activator; synchronized (myLock) { - if (! myActivated) { + if (!myActivated) { runnable.run(); return; } @@ -204,7 +204,8 @@ public class NewMappings { final List<VcsDirectoryMapping> itemsCopy; if (items.isEmpty()) { itemsCopy = Collections.singletonList(new VcsDirectoryMapping("", "")); - } else { + } + else { itemsCopy = items; } @@ -224,7 +225,7 @@ public class NewMappings { @Nullable public VcsDirectoryMapping getMappingFor(@Nullable VirtualFile file) { if (file == null) return null; - if (! file.isInLocalFileSystem()) { + if (!file.isInLocalFileSystem()) { return null; } @@ -232,17 +233,22 @@ public class NewMappings { } @Nullable - public VcsDirectoryMapping getMappingFor(final VirtualFile file, final Object matchContext) { + public VcsDirectoryMapping getMappingFor(final VirtualFile file, final Object parentModule) { + // if parentModule is not null it means that file belongs to the module so it isn't excluded + if (parentModule == null && myFileIndexFacade.isExcludedFile(file)) { + return null; + } + // performance: calculate file path just once, rather than once per mapping String path = file.getPath(); - final String systemIndependentPath = FileUtil.toSystemIndependentName((file.isDirectory() && (! path.endsWith("/"))) ? (path + "/") : path); + final String systemIndependentPath = FileUtil.toSystemIndependentName((file.isDirectory() && (!path.endsWith("/"))) ? (path + "/") : path); final VcsDirectoryMapping[] mappings; synchronized (myLock) { mappings = mySortedMappings; } - for (int i = mappings.length - 1; i >= 0; -- i) { + for (int i = mappings.length - 1; i >= 0; --i) { final VcsDirectoryMapping mapping = mappings[i]; - if (fileMatchesMapping(file, matchContext, systemIndependentPath, mapping)) { + if (fileMatchesMapping(file, parentModule, systemIndependentPath, mapping)) { return mapping; } } @@ -258,12 +264,14 @@ public class NewMappings { return mapping.getVcs(); } - private boolean fileMatchesMapping(final VirtualFile file, final Object matchContext, final String systemIndependentPath, final VcsDirectoryMapping mapping) { + private boolean fileMatchesMapping(final VirtualFile file, + final Object matchContext, + final String systemIndependentPath, + final VcsDirectoryMapping mapping) { if (mapping.getDirectory().length() == 0) { return myDefaultVcsRootPolicy.matchesDefaultMapping(file, matchContext); } - return FileUtil.startsWith(systemIndependentPath, mapping.systemIndependentPath()) && - ! myExcludedFileIndex.isExcludedFile(file); + return FileUtil.startsWith(systemIndependentPath, mapping.systemIndependentPath()); } public List<VirtualFile> getMappingsAsFilesUnderVcs(final AbstractVcs vcs) { @@ -281,7 +289,8 @@ public class NewMappings { if (mapping.isDefaultMapping()) { // todo callback here; don't like it myDefaultVcsRootPolicy.addDefaultVcsRoots(this, vcsName, result); - } else { + } + else { final VirtualFile file = LocalFileSystem.getInstance().findFileByPath(mapping.getDirectory()); if (file != null) { result.add(file); @@ -307,7 +316,7 @@ public class NewMappings { private void clearImpl() { // if vcses were not mapped, there's nothing to clear - if ((myActiveVcses == null) || (myActiveVcses.length == 0)) return; + if ((myActiveVcses == null) || (myActiveVcses.length == 0)) return; keepActiveVcs(new Runnable() { public void run() { @@ -374,7 +383,7 @@ public class NewMappings { final LocalFileSystem lfs = LocalFileSystem.getInstance(); final AllVcsesI allVcses = AllVcses.getInstance(myProject); - for (Iterator<String> iterator = myVcsToPaths.keySet().iterator(); iterator.hasNext();) { + for (Iterator<String> iterator = myVcsToPaths.keySet().iterator(); iterator.hasNext(); ) { final String vcsName = iterator.next(); final List<VcsDirectoryMapping> mappings = myVcsToPaths.get(vcsName); @@ -399,8 +408,9 @@ public class NewMappings { }; if (StringUtil.isEmptyOrSpaces(vcsName)) { filteredFiles = AbstractVcs.filterUniqueRootsDefault(objects, fileConvertor); - } else { - final AbstractVcs vcs = allVcses.getByName(vcsName); + } + else { + final AbstractVcs<?> vcs = allVcses.getByName(vcsName); if (vcs == null) { VcsBalloonProblemNotifier.showOverChangesView(myProject, "VCS plugin not found for mapping to : '" + vcsName + "'", MessageType.ERROR); continue; @@ -420,7 +430,8 @@ public class NewMappings { if (filteredMappings.isEmpty()) { iterator.remove(); - } else { + } + else { mappings.clear(); mappings.addAll(filteredMappings); } @@ -434,7 +445,7 @@ public class NewMappings { for (VcsDirectoryMapping mapping : mySortedMappings) { if (mapping.systemIndependentPath().equals(fixedPath)) { final String oldVcs = mapping.getVcs(); - if (! oldVcs.equals(activeVcsName)) { + if (!oldVcs.equals(activeVcsName)) { migrateVcs(activeVcsName, mapping, oldVcs); } return true; @@ -489,7 +500,7 @@ public class NewMappings { return ourInstance; } - public int compare(VcsDirectoryMapping m1, VcsDirectoryMapping m2) { + public int compare(@NotNull VcsDirectoryMapping m1, @NotNull VcsDirectoryMapping m2) { return m1.getDirectory().compareTo(m2.getDirectory()); } } @@ -514,7 +525,8 @@ public class NewMappings { catch (VcsException e) { // actually is not thrown (AbstractVcs#actualActivate()) } - } else { + } + else { LOG.info("Error: activating non existing vcs: " + s); } } @@ -529,7 +541,8 @@ public class NewMappings { catch (VcsException e) { // actually is not thrown (AbstractVcs#actualDeactivate()) } - } else { + } + else { LOG.info("Error: removing non existing vcs: " + s); } } @@ -543,7 +556,7 @@ public class NewMappings { // omit empty vcs: not a vcs if (topItem.trim().length() == 0) continue; - if (! bottom.contains(topItem)) { + if (!bottom.contains(topItem)) { if (notInBottom == null) { notInBottom = new HashSet<String>(); } @@ -589,7 +602,8 @@ public class NewMappings { public void invoke() { if (myItems.isEmpty()) { myItemsCopy = Collections.singletonList(new VcsDirectoryMapping("", "")); - } else { + } + else { myItemsCopy = myItems; } } @@ -606,8 +620,9 @@ public class NewMappings { myDefaultVcsRootPolicy.addDefaultVcsRoots(this, defaultVcs, list); if (StringUtil.isEmptyOrSpaces(defaultVcs)) { return AbstractVcs.filterUniqueRootsDefault(list, Convertor.SELF); - } else { - final AbstractVcs vcs = AllVcses.getInstance(myProject).getByName(defaultVcs); + } + else { + final AbstractVcs<?> vcs = AllVcses.getInstance(myProject).getByName(defaultVcs); if (vcs == null) { return AbstractVcs.filterUniqueRootsDefault(list, Convertor.SELF); } |