diff options
Diffstat (limited to 'platform/vcs-impl')
29 files changed, 745 insertions, 612 deletions
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/CommonCheckinFilesAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/CommonCheckinFilesAction.java index 89d1f5695e63..1829413f0f7e 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/CommonCheckinFilesAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/CommonCheckinFilesAction.java @@ -23,12 +23,14 @@ import com.intellij.openapi.vcs.changes.LocalChangeList; import com.intellij.openapi.vcs.checkin.CheckinEnvironment; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.HashSet; +import org.jetbrains.annotations.NotNull; import java.util.Collection; import java.util.Iterator; import java.util.Set; public class CommonCheckinFilesAction extends AbstractCommonCheckinAction { + @Override protected String getActionName(final VcsContext dataContext) { final String checkinActionName = getCheckinActionName(dataContext); return modifyCheckinActionName(dataContext, checkinActionName); @@ -125,14 +127,16 @@ public class CommonCheckinFilesAction extends AbstractCommonCheckinAction { return false; } - protected boolean isApplicableRoot(VirtualFile file, FileStatus status, VcsContext dataContext) { + protected boolean isApplicableRoot(@NotNull VirtualFile file, @NotNull FileStatus status, @NotNull VcsContext dataContext) { return status != FileStatus.UNKNOWN && status != FileStatus.IGNORED; } + @Override protected FilePath[] getRoots(final VcsContext context) { return context.getSelectedFilePaths(); } + @Override protected boolean filterRootsBeforeAction() { return true; } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/AbstractRefreshablePanel.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/AbstractRefreshablePanel.java index 070a4e95587a..fe20806a8ed4 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/AbstractRefreshablePanel.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/AbstractRefreshablePanel.java @@ -49,7 +49,7 @@ public abstract class AbstractRefreshablePanel<T> implements RefreshablePanel<Ch private final DetailsPanel myDetailsPanel; private final GenericDetailsLoader<Ticket, T> myDetailsLoader; private final BackgroundTaskQueue myQueue; - private boolean myDisposed; + private volatile boolean myDisposed; protected AbstractRefreshablePanel(final Project project, final String loadingTitle, final BackgroundTaskQueue queue) { myQueue = queue; diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesFragmentedDiffPanel.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesFragmentedDiffPanel.java index 025795eb08ed..731634b85080 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesFragmentedDiffPanel.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesFragmentedDiffPanel.java @@ -549,7 +549,7 @@ public class ChangesFragmentedDiffPanel implements Disposable { } private static final int[] ourMarks = {1,2,4,8,-1}; - public static final Hashtable<Integer,JLabel> LABELS = new Hashtable<Integer, JLabel>(); + public static final Hashtable<Integer, JComponent> LABELS = new Hashtable<Integer, JComponent>(); public static final int ALL_VALUE = 5; static { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/FragmentedDiffRequestFromChange.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/FragmentedDiffRequestFromChange.java index 77dd9b3f42b3..ce6ca72ab996 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/FragmentedDiffRequestFromChange.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/FragmentedDiffRequestFromChange.java @@ -218,8 +218,8 @@ public class FragmentedDiffRequestFromChange { final List<BeforeAfter<TextRange>> result = new ArrayList<BeforeAfter<TextRange>>(); if (myRanges == null || myRanges.isEmpty()) return Collections.emptyList(); for (Range range : myRanges) { - final TextRange before = new TextRange(range.getUOffset1(), range.getUOffset2()); - final TextRange after = new TextRange(range.getOffset1(), range.getOffset2()); + final TextRange before = new TextRange(range.getVcsLine1(), range.getVcsLine2()); + final TextRange after = new TextRange(range.getLine1(), range.getLine2()); result.add(new BeforeAfter<TextRange>(before, after)); } return result; diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/CommittedChangesCache.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/CommittedChangesCache.java index 2da25f6e92c1..6619b98ea5e9 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/CommittedChangesCache.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/CommittedChangesCache.java @@ -44,6 +44,7 @@ import com.intellij.util.Consumer; import com.intellij.util.MessageBusUtil; import com.intellij.util.NotNullFunction; import com.intellij.util.containers.ConcurrentHashMap; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import com.intellij.util.messages.MessageBus; import com.intellij.util.messages.MessageBusConnection; @@ -772,16 +773,21 @@ public class CommittedChangesCache implements PersistentStateComponent<Committed } public void processUpdatedFiles(final UpdatedFiles updatedFiles) { + processUpdatedFiles(updatedFiles, null); + } + + public void processUpdatedFiles(final UpdatedFiles updatedFiles, + @Nullable final Consumer<List<CommittedChangeList>> incomingChangesConsumer) { final Runnable task = new Runnable() { @Override public void run() { debug("Processing updated files"); final Collection<ChangesCacheFile> caches = myCachesHolder.getAllCaches(); + myPendingUpdateCount += caches.size(); for(final ChangesCacheFile cache: caches) { - myPendingUpdateCount++; try { if (cache.isEmpty()) { - pendingUpdateProcessed(); + pendingUpdateProcessed(incomingChangesConsumer); continue; } debug("Processing updated files in " + cache.getLocation()); @@ -789,12 +795,12 @@ public class CommittedChangesCache implements PersistentStateComponent<Committed if (needRefresh) { debug("Found unaccounted files, requesting refresh"); // todo do we need double-queueing here??? - processUpdatedFilesAfterRefresh(cache, updatedFiles); + processUpdatedFilesAfterRefresh(cache, updatedFiles, incomingChangesConsumer); } else { debug("Clearing cached incoming changelists"); myCachedIncomingChangeLists = null; - pendingUpdateProcessed(); + pendingUpdateProcessed(incomingChangesConsumer); } } catch (IOException e) { @@ -806,15 +812,20 @@ public class CommittedChangesCache implements PersistentStateComponent<Committed myTaskQueue.run(task); } - private void pendingUpdateProcessed() { + private void pendingUpdateProcessed(@Nullable Consumer<List<CommittedChangeList>> incomingChangesConsumer) { myPendingUpdateCount--; if (myPendingUpdateCount == 0) { notifyIncomingChangesUpdated(myNewIncomingChanges); + if (incomingChangesConsumer != null) { + incomingChangesConsumer.consume(ContainerUtil.newArrayList(myNewIncomingChanges)); + } myNewIncomingChanges.clear(); } } - private void processUpdatedFilesAfterRefresh(final ChangesCacheFile cache, final UpdatedFiles updatedFiles) { + private void processUpdatedFilesAfterRefresh(final ChangesCacheFile cache, + final UpdatedFiles updatedFiles, + @Nullable final Consumer<List<CommittedChangeList>> incomingChangesConsumer) { refreshCacheAsync(cache, false, new RefreshResultConsumer() { @Override public void receivedChanges(final List<CommittedChangeList> committedChangeLists) { @@ -833,7 +844,7 @@ public class CommittedChangesCache implements PersistentStateComponent<Committed debug("Clearing cached incoming changelists"); myCachedIncomingChangeLists = null; } - pendingUpdateProcessed(); + pendingUpdateProcessed(incomingChangesConsumer); } catch (IOException e) { LOG.error(e); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserNode.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserNode.java index 7d4156873d7c..b7a42218e3e6 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserNode.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowserNode.java @@ -150,7 +150,7 @@ public class ChangesBrowserNode<T> extends DefaultMutableTreeNode { public <T> List<T> getAllObjectsUnder(final Class<T> clazz) { List<T> changes = new ArrayList<T>(); - final Enumeration enumeration = depthFirstEnumeration(); + final Enumeration enumeration = preorderEnumeration(); while (enumeration.hasMoreElements()) { ChangesBrowserNode child = (ChangesBrowserNode)enumeration.nextElement(); final Object value = child.getUserObject(); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesModuleGroupingPolicy.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesModuleGroupingPolicy.java index 2ed36d79947f..1efc7b44d66b 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesModuleGroupingPolicy.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesModuleGroupingPolicy.java @@ -21,6 +21,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.Nullable; @@ -55,8 +56,9 @@ public class ChangesModuleGroupingPolicy implements ChangesGroupingPolicy { if (vFile == null) { vFile = LocalFileSystem.getInstance().findFileByIoFile(new File(node.getPath())); } - if (vFile != null && Comparing.equal(vFile, index.getContentRootForFile(vFile))) { - Module module = index.getModuleForFile(vFile); + boolean hideExcludedFiles = Registry.is("ide.hide.excluded.files"); + if (vFile != null && Comparing.equal(vFile, index.getContentRootForFile(vFile, hideExcludedFiles))) { + Module module = index.getModuleForFile(vFile, hideExcludedFiles); return getNodeForModule(module, rootNode); } return null; diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsBackgroundOperationsConfigurable.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsBackgroundOperationsConfigurable.java new file mode 100644 index 000000000000..695f8b9143db --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsBackgroundOperationsConfigurable.java @@ -0,0 +1,77 @@ +/* + * Copyright 2000-2014 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.configurable; + + +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.options.SearchableConfigurable; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class VcsBackgroundOperationsConfigurable implements SearchableConfigurable { + private VcsBackgroundOperationsConfigurationPanel myPanel; + private final Project myProject; + + public VcsBackgroundOperationsConfigurable(Project project) { + myProject = project; + } + + @Override + public void reset() { + myPanel.reset(); + } + + @Override + public void apply() throws ConfigurationException { + myPanel.apply(); + } + + @Override + public boolean isModified() { + return myPanel.isModified(); + } + + @Nls + public String getDisplayName() { + return "Background"; + } + + public String getHelpTopic() { + return "project.propVCSSupport.Background"; + } + + @NotNull + public String getId() { + return getHelpTopic(); + } + + public Runnable enableSearch(String option) { + return null; + } + + public JComponent createComponent() { + myPanel = new VcsBackgroundOperationsConfigurationPanel(myProject); + return myPanel.getPanel(); + } + + public void disposeUIResources() { + myPanel = null; + } + +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsBackgroundOperationsConfigurationPanel.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsBackgroundOperationsConfigurationPanel.java index 1a9884ee003b..6b23766b9038 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsBackgroundOperationsConfigurationPanel.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsBackgroundOperationsConfigurationPanel.java @@ -31,7 +31,7 @@ import java.awt.event.ActionListener; import java.util.LinkedHashMap; import java.util.Map; -public class VcsBackgroundOperationsConfigurationPanel implements SearchableConfigurable { +public class VcsBackgroundOperationsConfigurationPanel { private JPanel myPanel; @@ -151,30 +151,6 @@ public class VcsBackgroundOperationsConfigurationPanel implements SearchableConf return myPanel; } - @Nls - public String getDisplayName() { - return "Background"; - } - - public String getHelpTopic() { - return "project.propVCSSupport.Background"; - } - - @NotNull - public String getId() { - return getHelpTopic(); - } - - public Runnable enableSearch(String option) { - return null; - } - - public JComponent createComponent() { - return getPanel(); - } - - public void disposeUIResources() { - } private void createUIComponents() { myCacheSettingsPanel = new CacheSettingsPanel(); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsGeneralConfigurationConfigurable.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsGeneralConfigurationConfigurable.java new file mode 100644 index 000000000000..03368a82464c --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsGeneralConfigurationConfigurable.java @@ -0,0 +1,111 @@ +/* + * Copyright 2000-2014 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.configurable; + +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.options.SearchableConfigurable; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vcs.AbstractVcs; +import com.intellij.openapi.vcs.ProjectLevelVcsManager; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.Arrays; +import java.util.Collection; + +public class VcsGeneralConfigurationConfigurable implements SearchableConfigurable { + private VcsGeneralConfigurationPanel myPanel; + private Project myProject; + private VcsManagerConfigurable myMainConfigurable; + + public VcsGeneralConfigurationConfigurable(Project project, VcsManagerConfigurable configurable) { + myProject = project; + myMainConfigurable = configurable; + } + + @Nullable + @Override + public JComponent createComponent() { + myPanel = new VcsGeneralConfigurationPanel(myProject); + if (getMappings() != null) { + myPanel.updateAvailableOptions(getMappings().getActiveVcses()); + addListenerToGeneralPanel(); + } + else { + myPanel.updateAvailableOptions(Arrays.asList(ProjectLevelVcsManager.getInstance(myProject).getAllActiveVcss())); + } + addListenerToGeneralPanel(); + + return myPanel.getPanel(); + } + + private VcsDirectoryConfigurationPanel getMappings() { + return myMainConfigurable.getMappings(); + } + + private void addListenerToGeneralPanel() { + VcsDirectoryConfigurationPanel mappings = getMappings(); + if (mappings != null) { + mappings.addVcsListener(new ModuleVcsListener() { + @Override + public void activeVcsSetChanged(Collection<AbstractVcs> activeVcses) { + myPanel.updateAvailableOptions(activeVcses); + } + }); + } + } + + @Override + public void apply() throws ConfigurationException { + myPanel.apply(); + } + + @Override + public boolean isModified() { + return myPanel.isModified(); + } + + @Override + public void reset() { + myPanel.reset(); + } + + @Override + public void disposeUIResources() { + myPanel = null; + } + @Nls + public String getDisplayName() { + return "Confirmation"; + } + + @NotNull + public String getHelpTopic() { + return "project.propVCSSupport.Confirmation"; + } + + @NotNull + public String getId() { + return getHelpTopic(); + } + + public Runnable enableSearch(String option) { + return null; + } + +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsGeneralConfigurationPanel.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsGeneralConfigurationPanel.java index 781e4fd949a6..45a7267d7587 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsGeneralConfigurationPanel.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsGeneralConfigurationPanel.java @@ -36,7 +36,7 @@ import java.awt.*; import java.util.*; import java.util.List; -public class VcsGeneralConfigurationPanel implements SearchableConfigurable { +public class VcsGeneralConfigurationPanel { private JCheckBox myForceNonEmptyComment; private JCheckBox myShowReadOnlyStatusDialog; @@ -294,29 +294,4 @@ public class VcsGeneralConfigurationPanel implements SearchableConfigurable { return StringUtil.join(result, ", "); } - @Nls - public String getDisplayName() { - return "Confirmation"; - } - - @NotNull - public String getHelpTopic() { - return "project.propVCSSupport.Confirmation"; - } - - public JComponent createComponent() { - return getPanel(); - } - - public void disposeUIResources() { - } - - @NotNull - public String getId() { - return getHelpTopic(); - } - - public Runnable enableSearch(String option) { - return null; - } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsManagerConfigurable.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsManagerConfigurable.java index 304275d732d7..a392ff1f5db2 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsManagerConfigurable.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsManagerConfigurable.java @@ -40,7 +40,7 @@ import java.util.List; public class VcsManagerConfigurable extends SearchableConfigurable.Parent.Abstract implements Configurable.NoScroll { private final Project myProject; private VcsDirectoryConfigurationPanel myMappings; - private VcsGeneralConfigurationPanel myGeneralPanel; + private VcsGeneralConfigurationConfigurable myGeneralPanel; public VcsManagerConfigurable(Project project) { myProject = project; @@ -49,9 +49,6 @@ public class VcsManagerConfigurable extends SearchableConfigurable.Parent.Abstra @Override public JComponent createComponent() { myMappings = new VcsDirectoryConfigurationPanel(myProject); - if (myGeneralPanel != null) { - addListenerToGeneralPanel(); - } return myMappings; } @@ -118,26 +115,12 @@ public class VcsManagerConfigurable extends SearchableConfigurable.Parent.Abstra @Override protected Configurable[] buildConfigurables() { - myGeneralPanel = new VcsGeneralConfigurationPanel(myProject){ - @Override - public void disposeUIResources() { - super.disposeUIResources(); - myGeneralPanel = null; - } - }; - - if (myMappings != null) { - myGeneralPanel.updateAvailableOptions(myMappings.getActiveVcses()); - addListenerToGeneralPanel(); - } - else { - myGeneralPanel.updateAvailableOptions(Arrays.asList(ProjectLevelVcsManager.getInstance(myProject).getAllActiveVcss())); - } + myGeneralPanel = new VcsGeneralConfigurationConfigurable(myProject, this); List<Configurable> result = new ArrayList<Configurable>(); result.add(myGeneralPanel); - result.add(new VcsBackgroundOperationsConfigurationPanel(myProject)); + result.add(new VcsBackgroundOperationsConfigurable(myProject)); if (!myProject.isDefault()) { result.add(new IgnoredSettingsPanel(myProject)); @@ -165,13 +148,8 @@ public class VcsManagerConfigurable extends SearchableConfigurable.Parent.Abstra return result.toArray(new Configurable[result.size()]); } - private void addListenerToGeneralPanel() { - myMappings.addVcsListener(new ModuleVcsListener() { - @Override - public void activeVcsSetChanged(Collection<AbstractVcs> activeVcses) { - myGeneralPanel.updateAvailableOptions(activeVcses); - } - }); + public VcsDirectoryConfigurationPanel getMappings() { + return myMappings; } private Configurable createVcsConfigurableWrapper(final VcsDescriptor vcs) { @@ -237,5 +215,4 @@ public class VcsManagerConfigurable extends SearchableConfigurable.Parent.Abstra } }; } - } 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 aaa512c4e674..a9f761a2b5e0 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).toString(); + final String content = myLineStatusTracker.getVcsContent(myRange).toString(); CopyPasteManager.getInstance().setContents(new StringSelection(content)); } } 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 44a56e7df517..9d8d73a92be9 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 @@ -32,21 +32,21 @@ import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsBundle; import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.EditorNotificationPanel; +import com.intellij.util.ExceptionUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.diff.FilesTooBigForDiffException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.util.ArrayList; -import java.util.List; -import java.util.ListIterator; +import java.util.*; /** * @author irengrig @@ -61,7 +61,7 @@ public class LineStatusTracker { private BaseLoadState myBaseLoaded; private final Document myDocument; - private final Document myUpToDateDocument; + private final Document myVcsDocument; private List<Range> myRanges; @@ -69,34 +69,35 @@ public class LineStatusTracker { private MyDocumentListener myDocumentListener; + private boolean mySuppressUpdate; private boolean myBulkUpdate; private final Application myApplication; @Nullable private RevisionPack myBaseRevisionNumber; private String myPreviousBaseRevision; private boolean myAnathemaThrown; private FileEditorManager myFileEditorManager; + private final VcsDirtyScopeManager myVcsDirtyScopeManager; private final VirtualFile myVirtualFile; private boolean myReleased = false; private LineStatusTracker(@NotNull final Document document, - @NotNull final Document upToDateDocument, + @NotNull final Document vcsDocument, final Project project, @Nullable final VirtualFile virtualFile) { myVirtualFile = virtualFile; myApplication = ApplicationManager.getApplication(); myDocument = document; - myUpToDateDocument = upToDateDocument; - myUpToDateDocument.putUserData(UndoConstants.DONT_RECORD_UNDO, Boolean.TRUE); + myVcsDocument = vcsDocument; + myVcsDocument.putUserData(UndoConstants.DONT_RECORD_UNDO, Boolean.TRUE); myProject = project; myBaseLoaded = BaseLoadState.LOADING; - synchronized (myLock) { - myRanges = new ArrayList<Range>(); - } + myRanges = new ArrayList<Range>(); myAnathemaThrown = false; myFileEditorManager = FileEditorManager.getInstance(myProject); + myVcsDirtyScopeManager = VcsDirtyScopeManager.getInstance(myProject); } - public void initialize(@NotNull final String upToDateContent, @NotNull RevisionPack baseRevisionNumber) { + public void initialize(@NotNull final String vcsContent, @NotNull RevisionPack baseRevisionNumber) { ApplicationManager.getApplication().assertIsDispatchThread(); synchronized (myLock) { @@ -107,9 +108,9 @@ public class LineStatusTracker { myBaseRevisionNumber = baseRevisionNumber; myPreviousBaseRevision = null; - myUpToDateDocument.setReadOnly(false); - myUpToDateDocument.replaceString(0, myUpToDateDocument.getTextLength(), upToDateContent); - myUpToDateDocument.setReadOnly(true); + myVcsDocument.setReadOnly(false); + myVcsDocument.replaceString(0, myVcsDocument.getTextLength(), vcsContent); + myVcsDocument.setReadOnly(true); reinstallRanges(); if (myDocumentListener == null) { @@ -144,7 +145,7 @@ public class LineStatusTracker { removeAnathema(); removeHighlightersFromMarkupModel(); try { - myRanges = new RangesBuilder(myDocument, myUpToDateDocument).getRanges(); + myRanges = new RangesBuilder(myDocument, myVcsDocument).getRanges(); } catch (FilesTooBigForDiffException e) { myRanges.clear(); @@ -175,10 +176,10 @@ public class LineStatusTracker { LOG.assertTrue(!myReleased, "Already released"); int first = - range.getOffset1() >= getLineCount(myDocument) ? myDocument.getTextLength() : myDocument.getLineStartOffset(range.getOffset1()); + range.getLine1() >= getLineCount(myDocument) ? myDocument.getTextLength() : myDocument.getLineStartOffset(range.getLine1()); int second = - range.getOffset2() >= getLineCount(myDocument) ? myDocument.getTextLength() : myDocument.getLineStartOffset(range.getOffset2()); + range.getLine2() >= getLineCount(myDocument) ? myDocument.getTextLength() : myDocument.getLineStartOffset(range.getLine2()); final RangeHighlighter highlighter = DocumentMarkupModel.forDocument(myDocument, myProject, true) .addRangeHighlighter(first, second, HighlighterLayer.FIRST - 1, null, HighlighterTargetArea.LINES_IN_RANGE); @@ -192,19 +193,19 @@ public class LineStatusTracker { highlighter.setEditorFilter(MarkupEditorFilterFactory.createIsNotDiffFilter()); final String tooltip; - if (range.getOffset1() == range.getOffset2()) { - if (range.getUOffset1() + 1 == range.getUOffset2()) { - tooltip = VcsBundle.message("tooltip.text.line.before.deleted", range.getOffset1() + 1); + if (range.getLine1() == range.getLine2()) { + if (range.getVcsLine1() + 1 == range.getVcsLine2()) { + tooltip = VcsBundle.message("tooltip.text.line.before.deleted", range.getLine1() + 1); } else { - tooltip = VcsBundle.message("tooltip.text.lines.before.deleted", range.getOffset1() + 1, range.getUOffset2() - range.getUOffset1()); + tooltip = VcsBundle.message("tooltip.text.lines.before.deleted", range.getLine1() + 1, range.getVcsLine2() - range.getVcsLine1()); } } - else if (range.getOffset1() + 1 == range.getOffset2()) { - tooltip = VcsBundle.message("tooltip.text.line.changed", range.getOffset1() + 1); + else if (range.getLine1() + 1 == range.getLine2()) { + tooltip = VcsBundle.message("tooltip.text.line.changed", range.getLine1() + 1); } else { - tooltip = VcsBundle.message("tooltip.text.lines.changed", range.getOffset1() + 1, range.getOffset2()); + tooltip = VcsBundle.message("tooltip.text.lines.changed", range.getLine1() + 1, range.getLine2()); } highlighter.setErrorStripeTooltip(tooltip); @@ -238,9 +239,9 @@ public class LineStatusTracker { } } - public Document getUpToDateDocument() { + public Document getVcsDocument() { myApplication.assertIsDispatchThread(); - return myUpToDateDocument; + return myVcsDocument; } public void startBulkUpdate() { @@ -285,11 +286,11 @@ public class LineStatusTracker { // there can be multiple resets before init -> take from document only firts time -> when right after install(), // where myPreviousBaseRevision become null if (BaseLoadState.LOADED.equals(myBaseLoaded) && myPreviousBaseRevision == null) { - myPreviousBaseRevision = myUpToDateDocument.getText(); + myPreviousBaseRevision = myVcsDocument.getText(); } - myUpToDateDocument.setReadOnly(false); - myUpToDateDocument.setText(""); - myUpToDateDocument.setReadOnly(true); + myVcsDocument.setReadOnly(false); + myVcsDocument.setText(""); + myVcsDocument.setReadOnly(true); removeAnathema(); removeHighlightersFromMarkupModel(); myBaseLoaded = BaseLoadState.LOADING; @@ -298,15 +299,13 @@ public class LineStatusTracker { private class MyDocumentListener extends DocumentAdapter { // We have 3 document versions: - // * VCS version - upToDate* - // * before change - my* - // * after change - current* + // * VCS version + // * before change + // * after change - private int myFirstChangedLine; - private int myLastChangedLine; - private int myChangedLines; - private int myTotalLines; - private final VcsDirtyScopeManager myVcsDirtyScopeManager = VcsDirtyScopeManager.getInstance(myProject); + private int myLine1; + private int myBeforeChangedLines; + private int myBeforeTotalLines; @Override public void beforeDocumentChange(DocumentEvent e) { @@ -314,13 +313,21 @@ public class LineStatusTracker { synchronized (myLock) { if (myReleased) return; - if (myBulkUpdate || myAnathemaThrown || BaseLoadState.LOADED != myBaseLoaded) return; + if (myBulkUpdate || mySuppressUpdate || myAnathemaThrown || BaseLoadState.LOADED != myBaseLoaded) return; + assert myDocument == e.getDocument(); + try { - myFirstChangedLine = myDocument.getLineNumber(e.getOffset()); - myLastChangedLine = e.getOldLength() == 0 ? myFirstChangedLine : myDocument.getLineNumber(e.getOffset() + e.getOldLength() - 1); - if (StringUtil.endsWithChar(e.getOldFragment(), '\n')) myLastChangedLine++; - myChangedLines = myLastChangedLine - myFirstChangedLine; - myTotalLines = getLineCount(e.getDocument()); + myLine1 = myDocument.getLineNumber(e.getOffset()); + if (e.getOldLength() == 0) { + myBeforeChangedLines = 1; + } + else { + int line1 = myLine1; + int line2 = myDocument.getLineNumber(e.getOffset() + e.getOldLength()); + myBeforeChangedLines = line2 - line1 + 1; + } + + myBeforeTotalLines = getLineCount(myDocument); } catch (ProcessCanceledException ignore) { } @@ -333,138 +340,307 @@ public class LineStatusTracker { synchronized (myLock) { if (myReleased) return; - if (myBulkUpdate || myAnathemaThrown || BaseLoadState.LOADED != myBaseLoaded) return; - try { - 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; - - List<Range> rangesBeforeChange = new ArrayList<Range>(); - List<Range> rangesAfterChange = new ArrayList<Range>(); - List<Range> changedRanges = new ArrayList<Range>(); - sortRanges(myRanges, myFirstChangedLine, myLastChangedLine, rangesBeforeChange, changedRanges, rangesAfterChange); - - Range firstChangedRange = ContainerUtil.getFirstItem(changedRanges); - Range lastChangedRange = ContainerUtil.getLastItem(changedRanges); - Range lastRangeBefore = ContainerUtil.getLastItem(rangesBeforeChange); - Range firstRangeAfter = ContainerUtil.getFirstItem(rangesAfterChange); - - if (firstChangedRange != null && firstChangedRange.getOffset1() < myFirstChangedLine) { - myFirstChangedLine = firstChangedRange.getOffset1(); - } - if (lastChangedRange != null && lastChangedRange.getOffset2() > myLastChangedLine) { - myLastChangedLine = lastChangedRange.getOffset2() - 1; - } + if (myBulkUpdate || mySuppressUpdate || myAnathemaThrown || BaseLoadState.LOADED != myBaseLoaded) return; + assert myDocument == e.getDocument(); - currentFirstChangedLine = myFirstChangedLine; - currentLastChangedLine = myLastChangedLine + linesShift; + int afterChangedLines; + if (e.getNewLength() == 0) { + afterChangedLines = 1; + } + else { + int line1 = myLine1; + int line2 = myDocument.getLineNumber(e.getOffset() + e.getNewLength()); + afterChangedLines = line2 - line1 + 1; + } - int upToDateFirstLine = getUpToDateLine1(lastRangeBefore, myFirstChangedLine); - int upToDateLastLine = getUpToDateLine2(firstRangeAfter, myLastChangedLine, myTotalLines, upToDateTotalLine); + int linesShift = afterChangedLines - myBeforeChangedLines; - List<Range> newChangedRanges = - getNewChangedRanges(currentFirstChangedLine, currentLastChangedLine, upToDateFirstLine, upToDateLastLine); + int line1 = myLine1; + int line2 = line1 + myBeforeChangedLines; // TODO: optimize some whole-line-changed cases - shiftRanges(rangesAfterChange, linesShift); + int[] fixed = fixRanges(e, line1, line2); + line1 = fixed[0]; + line2 = fixed[1]; - if (!changedRanges.equals(newChangedRanges)) { - replaceRanges(changedRanges, newChangedRanges); + doUpdateRanges(line1, line2, linesShift, myBeforeTotalLines); + } + } + } - myRanges = new ArrayList<Range>(rangesBeforeChange.size() + newChangedRanges.size() + rangesAfterChange.size()); + @NotNull + private int[] fixRanges(@NotNull DocumentEvent e, int line1, int line2) { + CharSequence document = myDocument.getCharsSequence(); + int offset = e.getOffset(); - myRanges.addAll(rangesBeforeChange); - myRanges.addAll(newChangedRanges); - myRanges.addAll(rangesAfterChange); + if (e.getOldLength() == 0 && e.getNewLength() != 0) { + if (StringUtil.endsWithChar(e.getNewFragment(), '\n') && isNewline(offset - 1, document)) { + return new int[]{line1, line2 - 1}; + } + if (StringUtil.startsWithChar(e.getNewFragment(), '\n') && isNewline(offset + e.getNewLength(), document)) { + return new int[]{line1 + 1, line2}; + } + } + if (e.getOldLength() != 0 && e.getNewLength() == 0) { + if (StringUtil.endsWithChar(e.getOldFragment(), '\n') && isNewline(offset - 1, document)) { + return new int[]{line1, line2 - 1}; + } + if (StringUtil.startsWithChar(e.getOldFragment(), '\n') && isNewline(offset + e.getNewLength(), document)) { + return new int[]{line1 + 1, line2}; + } + } - for (Range range : myRanges) { - if (!range.hasHighlighter()) range.setHighlighter(createHighlighter(range)); - } + return new int[]{line1, line2}; + } - if (myRanges.isEmpty() && myVirtualFile != null) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - FileDocumentManager.getInstance().saveDocument(e.getDocument()); - boolean[] stillEmpty = new boolean[1]; - synchronized (myLock) { - stillEmpty[0] = myRanges.isEmpty(); - } - if (stillEmpty[0]) { - // file was modified, and now it's not -> dirty local change - myVcsDirtyScopeManager.fileDirty(myVirtualFile); - } - } - }); - } - } - } - catch (ProcessCanceledException ignore) { + private static boolean isNewline(int offset, @NotNull CharSequence sequence) { + if (offset < 0) return false; + if (offset >= sequence.length()) return false; + return sequence.charAt(offset) == '\n'; + } + + private void doUpdateRanges(int beforeChangedLine1, + int beforeChangedLine2, + int linesShift, + int beforeTotalLines) { + List<Range> rangesBeforeChange = new ArrayList<Range>(); + List<Range> rangesAfterChange = new ArrayList<Range>(); + List<Range> changedRanges = new ArrayList<Range>(); + + sortRanges(beforeChangedLine1, beforeChangedLine2, linesShift, rangesBeforeChange, changedRanges, rangesAfterChange); + + Range firstChangedRange = ContainerUtil.getFirstItem(changedRanges); + Range lastChangedRange = ContainerUtil.getLastItem(changedRanges); + + if (firstChangedRange != null && firstChangedRange.getLine1() < beforeChangedLine1) { + beforeChangedLine1 = firstChangedRange.getLine1(); + } + if (lastChangedRange != null && lastChangedRange.getLine2() > beforeChangedLine2) { + beforeChangedLine2 = lastChangedRange.getLine2(); + } + + doUpdateRanges(beforeChangedLine1, beforeChangedLine2, linesShift, beforeTotalLines, + rangesBeforeChange, changedRanges, rangesAfterChange); + } + + private void doUpdateRanges(int beforeChangedLine1, + int beforeChangedLine2, + int linesShift, // before -> after + int beforeTotalLines, + @NotNull List<Range> rangesBefore, + @NotNull List<Range> changedRanges, + @NotNull List<Range> rangesAfter) { + try { + int vcsTotalLines = getLineCount(myVcsDocument); + + Range lastRangeBefore = ContainerUtil.getLastItem(rangesBefore); + Range firstRangeAfter = ContainerUtil.getFirstItem(rangesAfter); + + int afterChangedLine1 = beforeChangedLine1; + int afterChangedLine2 = beforeChangedLine2 + linesShift; + + int vcsLine1 = getVcsLine1(lastRangeBefore, beforeChangedLine1); + int vcsLine2 = getVcsLine2(firstRangeAfter, beforeChangedLine2, beforeTotalLines, vcsTotalLines); + + List<Range> newChangedRanges = getNewChangedRanges(afterChangedLine1, afterChangedLine2, vcsLine1, vcsLine2); + + shiftRanges(rangesAfter, linesShift); + + if (!changedRanges.equals(newChangedRanges)) { + replaceRanges(changedRanges, newChangedRanges); + + myRanges = new ArrayList<Range>(rangesBefore.size() + newChangedRanges.size() + rangesAfter.size()); + + myRanges.addAll(rangesBefore); + myRanges.addAll(newChangedRanges); + myRanges.addAll(rangesAfter); + + for (Range range : myRanges) { + if (!range.hasHighlighter()) range.setHighlighter(createHighlighter(range)); } - catch (FilesTooBigForDiffException e1) { - installAnathema(); - removeHighlightersFromMarkupModel(); + + if (myRanges.isEmpty() && myVirtualFile != null) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + FileDocumentManager.getInstance().saveDocument(myDocument); + boolean stillEmpty; + synchronized (myLock) { + stillEmpty = myRanges.isEmpty(); + } + if (stillEmpty) { + // file was modified, and now it's not -> dirty local change + myVcsDirtyScopeManager.fileDirty(myVirtualFile); + } + } + }); } } } + catch (ProcessCanceledException ignore) { + } + catch (FilesTooBigForDiffException e1) { + installAnathema(); + removeHighlightersFromMarkupModel(); + } + } - private int getUpToDateLine1(@Nullable Range range, int line) { - return range == null ? line : line + range.getUOffset2() - range.getOffset2(); + private static int getVcsLine1(@Nullable Range range, int line) { + return range == null ? line : line + range.getVcsLine2() - range.getLine2(); + } + + private static int getVcsLine2(@Nullable Range range, int line, int totalLinesBefore, int totalLinesAfter) { + return range == null ? totalLinesAfter - totalLinesBefore + line : line + range.getVcsLine1() - range.getLine1(); + } + + private List<Range> getNewChangedRanges(int changedLine1, int changedLine2, int vcsLine1, int vcsLine2) + throws FilesTooBigForDiffException { + + if (changedLine1 == changedLine2 && vcsLine1 == vcsLine2) { + return Collections.emptyList(); + } + if (changedLine1 == changedLine2) { + return Collections.singletonList(new Range(changedLine1, changedLine2, vcsLine1, vcsLine2, Range.DELETED)); } + if (vcsLine1 == vcsLine2) { + return Collections.singletonList(new Range(changedLine1, changedLine2, vcsLine1, vcsLine2, Range.INSERTED)); + } + + List<String> lines = new DocumentWrapper(myDocument).getLines(changedLine1, changedLine2 - 1); + List<String> vcsLines = new DocumentWrapper(myVcsDocument).getLines(vcsLine1, vcsLine2 - 1); - private int getUpToDateLine2(@Nullable Range range, int line, int totalLinesBefore, int totalLinesAfter) { - return range == null ? totalLinesAfter - totalLinesBefore + line : line + range.getUOffset1() - range.getOffset1(); + return new RangesBuilder(lines, vcsLines, changedLine1, vcsLine1).getRanges(); + } + + private void replaceRanges(@NotNull List<Range> rangesInChange, @NotNull List<Range> newRangesInChange) { + for (Range range : rangesInChange) { + if (range.getHighlighter() != null) { + range.getHighlighter().dispose(); + } + range.setHighlighter(null); + range.invalidate(); } + for (Range range : newRangesInChange) { + range.setHighlighter(createHighlighter(range)); + } + } - private List<Range> getNewChangedRanges(int firstChangedLine, int lastChangedLine, int upToDateFirstLine, int upToDateLastLine) - throws FilesTooBigForDiffException { - List<String> lines = new DocumentWrapper(myDocument).getLines(firstChangedLine, lastChangedLine); - List<String> uLines = new DocumentWrapper(myUpToDateDocument).getLines(upToDateFirstLine, upToDateLastLine); - return new RangesBuilder(lines, uLines, firstChangedLine, upToDateFirstLine).getRanges(); + private static void shiftRanges(@NotNull List<Range> rangesAfterChange, int shift) { + for (final Range range : rangesAfterChange) { + range.shift(shift); } + } - private void replaceRanges(@NotNull List<Range> rangesInChange, @NotNull List<Range> newRangesInChange) { - for (Range range : rangesInChange) { - if (range.getHighlighter() != null) { - range.getHighlighter().dispose(); + private void sortRanges(int beforeChangedLine1, + int beforeChangedLine2, + int linesShift, + @NotNull List<Range> rangesBeforeChange, + @NotNull List<Range> changedRanges, + @NotNull List<Range> rangesAfterChange) { + if (!Registry.is("diff.status.tracker.skip.spaces")) { + for (Range range : myRanges) { + if (range.getLine2() < beforeChangedLine1) { + rangesBeforeChange.add(range); + } + else if (range.getLine1() > beforeChangedLine2) { + rangesAfterChange.add(range); + } + else { + changedRanges.add(range); } - range.setHighlighter(null); - range.invalidate(); - } - for (Range range : newRangesInChange) { - range.setHighlighter(createHighlighter(range)); } } + else { + int lastBefore = -1; + int firstAfter = myRanges.size(); + for (int i = 0; i < myRanges.size(); i++) { + Range range = myRanges.get(i); - private void shiftRanges(@NotNull List<Range> rangesAfterChange, int shift) { - for (final Range aRangesAfterChange : rangesAfterChange) { - aRangesAfterChange.shift(shift); + if (range.getLine2() < beforeChangedLine1) { + lastBefore = i; + } + else if (range.getLine1() > beforeChangedLine2) { + firstAfter = i; + break; + } } - } - } - public static void sortRanges(@NotNull List<Range> ranges, - int firstChangedLine, - int lastChangedLine, - @NotNull List<Range> rangesBeforeChange, - @NotNull List<Range> changedRanges, - @NotNull List<Range> rangesAfterChange) { - for (Range range : ranges) { - int offset1 = range.getOffset1() - 1; - int offset2 = range.getOffset2(); - if (offset2 < firstChangedLine) { - rangesBeforeChange.add(range); + // Expand on ranges, that are separated from changes only by empty/whitespaces lines + // This is needed to reduce amount of confusing cases, when changed blocks are matched wrong due to matched empty lines between them + CharSequence sequence = myDocument.getCharsSequence(); + + while (true) { + if (lastBefore == -1) break; + + if (lastBefore < myRanges.size() - 1 && firstAfter - lastBefore > 1) { + Range firstChangedRange = myRanges.get(lastBefore + 1); + if (firstChangedRange.getLine1() < beforeChangedLine1) { + beforeChangedLine1 = firstChangedRange.getLine1(); + } + } + + if (beforeChangedLine1 >= getLineCount(myDocument)) break; + int offset1 = myDocument.getLineStartOffset(beforeChangedLine1) - 2; + + int deltaLines = 0; + while (offset1 > 0) { + char c = sequence.charAt(offset1); + if (!StringUtil.isWhiteSpace(c)) break; + if (c == '\n') deltaLines++; + offset1--; + } + + if (deltaLines == 0) break; + beforeChangedLine1 -= deltaLines; + + if (myRanges.get(lastBefore).getLine2() < beforeChangedLine1) break; + while (lastBefore != -1 && myRanges.get(lastBefore).getLine2() >= beforeChangedLine1) { + lastBefore--; + } } - else if (offset1 > lastChangedLine) { - rangesAfterChange.add(range); + + while (true) { + if (firstAfter == myRanges.size()) break; + + if (firstAfter > 0 && firstAfter - lastBefore > 1) { + Range lastChangedRange = myRanges.get(firstAfter - 1); + if (lastChangedRange.getLine2() > beforeChangedLine2) { + beforeChangedLine2 = lastChangedRange.getLine2(); + } + } + + if (beforeChangedLine2 + linesShift < 1) break; + int offset2 = myDocument.getLineEndOffset(beforeChangedLine2 + linesShift - 1) + 1; + + int deltaLines = 0; + while (offset2 < sequence.length()) { + char c = sequence.charAt(offset2); + if (!StringUtil.isWhiteSpace(c)) break; + if (c == '\n') deltaLines++; + offset2++; + } + + if (deltaLines == 0) break; + beforeChangedLine2 += deltaLines; + + if (myRanges.get(firstAfter).getLine1() > beforeChangedLine2) break; + while (firstAfter != myRanges.size() && myRanges.get(firstAfter).getLine1() <= beforeChangedLine2) { + firstAfter++; + } } - else { - changedRanges.add(range); + + + for (int i = 0; i < myRanges.size(); i++) { + Range range = myRanges.get(i); + if (i <= lastBefore) { + rangesBeforeChange.add(range); + } + else if (i >= firstAfter) { + rangesAfterChange.add(range); + } + else { + changedRanges.add(range); + } } } } @@ -496,7 +672,7 @@ public class LineStatusTracker { } for (final Range range : myRanges) { - if (line > range.getOffset1() || line > range.getOffset2()) { + if (line > range.getLine1() || line > range.getLine2()) { continue; } return range; @@ -515,7 +691,7 @@ public class LineStatusTracker { for (ListIterator<Range> iterator = myRanges.listIterator(myRanges.size()); iterator.hasPrevious(); ) { final Range range = iterator.previous(); - if (range.getOffset1() > line) { + if (range.getLine1() > line) { continue; } return range; @@ -528,10 +704,10 @@ public class LineStatusTracker { public Range getRangeForLine(final int line) { synchronized (myLock) { for (final Range range : myRanges) { - if (range.getType() == Range.DELETED && line == range.getOffset1()) { + if (range.getType() == Range.DELETED && line == range.getLine1()) { return range; } - else if (line >= range.getOffset1() && line < range.getOffset2()) { + else if (line >= range.getLine1() && line < range.getLine2()) { return range; } } @@ -539,172 +715,127 @@ public class LineStatusTracker { } } - public void rollbackChanges(@NotNull Range range) { - myApplication.assertWriteAccessAllowed(); + private void doRollbackRange(@NotNull Range range) { + if (range.getType() == Range.MODIFIED) { + TextRange currentTextRange = getCurrentTextRange(range); + int offset1 = currentTextRange.getStartOffset(); + int offset2 = currentTextRange.getEndOffset(); - synchronized (myLock) { - 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(); + CharSequence vcsContent = getVcsContent(range); + myDocument.replaceString(offset1, offset2, vcsContent); + } + else if (range.getType() == Range.INSERTED) { + TextRange currentTextRange = getCurrentTextRange(range); + int offset1 = currentTextRange.getStartOffset(); + int offset2 = currentTextRange.getEndOffset(); - CharSequence upToDateContent = getUpToDateContent(range); - myDocument.replaceString(offset1, offset2, upToDateContent); + if (offset1 > 0) { + offset1--; } - 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 (offset2 < myDocument.getTextLength()) { + offset2++; } - else if (range.getType() == Range.DELETED) { - CharSequence content = getUpToDateContent(range); - if (range.getOffset2() == getLineCount(myDocument)) { - myDocument.insertString(myDocument.getTextLength(), "\n" + content); - } - else { - myDocument.insertString(myDocument.getLineStartOffset(range.getOffset2()), content + "\n"); - } + myDocument.deleteString(offset1, offset2); + } + else if (range.getType() == Range.DELETED) { + CharSequence content = getVcsContent(range); + if (range.getLine2() == getLineCount(myDocument)) { + myDocument.insertString(myDocument.getTextLength(), "\n" + content); } else { - throw new IllegalArgumentException("Unknown range type: " + range.getType()); + myDocument.insertString(myDocument.getLineStartOffset(range.getLine2()), content + "\n"); } } + else { + throw new IllegalArgumentException("Unknown range type: " + range.getType()); + } } - public void rollbackChanges(@NotNull SegmentTree lines) { + public void rollbackChanges(@NotNull Range range) { myApplication.assertWriteAccessAllowed(); synchronized (myLock) { - List<Range> affectedRanges = new ArrayList<Range>(); + if (myBulkUpdate) return; - 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 (!range.isValid()) { + LOG.warn("Rollback of invalid range"); + return; } - if (simple) { - rollbackChangesSimple(affectedRanges); - } - else { - rollbackChangesComplex(affectedRanges); - } + doRollbackRange(range); } } - private void rollbackChangesSimple(@NotNull List<Range> ranges) { - if (ranges.isEmpty()) return; - - Range first = ranges.get(0); - Range last = ranges.get(ranges.size() - 1); + public void rollbackChanges(@NotNull BitSet lines) { + myApplication.assertWriteAccessAllowed(); - byte type = first == last ? first.getType() : Range.MODIFIED; - final Range merged = new Range(first.getOffset1(), last.getOffset2(), first.getUOffset1(), last.getUOffset2(), type); + synchronized (myLock) { + if (myBulkUpdate) return; - // We don't expect complex Insertion/Deletion operation - they shouldn't exist - assert type != Range.MODIFIED || (first.getOffset1() != last.getOffset2() && first.getUOffset1() != last.getUOffset2()); + try { + mySuppressUpdate = true; - rollbackChanges(merged); - } + Range first = null; + Range last = null; - 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.. + int shift = 0; + for (Range range : myRanges) { + if (!range.isValid()) { + LOG.warn("Rollback of invalid range"); + break; + } - if (ranges.isEmpty()) return; - if (ranges.size() == 1) { - rollbackChanges(ranges.get(0)); - return; - } + boolean check; + if (range.getLine1() == range.getLine2()) { + check = lines.get(range.getLine1()); + } + else { + int next = lines.nextSetBit(range.getLine1()); + check = next != -1 && next < range.getLine2(); + } - Range first = ranges.get(0); - Range last = ranges.get(ranges.size() - 1); + if (check) { + if (first == null) { + first = range; + } + last = range; - // We don't expect complex Insertion/Deletion operation - they shouldn't exist. - assert first != last && first.getOffset1() != last.getOffset2() && first.getUOffset1() != last.getUOffset2(); + Range shiftedRange = new Range(range); + shiftedRange.shift(shift); - final int start = getCurrentTextRange(first).getStartOffset(); - final int end = getCurrentTextRange(last).getEndOffset(); + doRollbackRange(shiftedRange); - StringBuilder builder = new StringBuilder(); + shift += (range.getVcsLine2() - range.getVcsLine1()) - (range.getLine2() - range.getLine1()); + } + } - int lastOffset = start; - for (Range range : ranges) { - TextRange textRange = getCurrentTextRange(range); + if (first != null) { + int beforeChangedLine1 = first.getLine1(); + int beforeChangedLine2 = last.getLine2(); - builder.append(myDocument.getText(new TextRange(lastOffset, textRange.getStartOffset()))); - lastOffset = textRange.getEndOffset(); + int beforeTotalLines = getLineCount(myDocument) - shift; - 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++; + doUpdateRanges(beforeChangedLine1, beforeChangedLine2, shift, beforeTotalLines); } } - 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'); - } + catch (Throwable e) { + reinstallRanges(); + if (e instanceof Error) throw ((Error)e); + if (e instanceof RuntimeException) throw ((RuntimeException)e); + throw new RuntimeException(e); } - else { - throw new IllegalArgumentException("Unknown range type: " + range.getType()); + finally { + mySuppressUpdate = false; } } - builder.append(myDocument.getText(new TextRange(lastOffset, end))); - - final String s = builder.toString(); - - myDocument.replaceString(start, end, s); } - public CharSequence getUpToDateContent(@NotNull Range range) { + public CharSequence getVcsContent(@NotNull Range range) { synchronized (myLock) { - TextRange textRange = getUpToDateRange(range); + TextRange textRange = getVcsRange(range); final int startOffset = textRange.getStartOffset(); final int endOffset = textRange.getEndOffset(); - return myUpToDateDocument.getCharsSequence().subSequence(startOffset, endOffset); + return myVcsDocument.getCharsSequence().subSequence(startOffset, endOffset); } } @@ -715,18 +846,18 @@ public class LineStatusTracker { LOG.warn("Current TextRange of invalid range"); } - return getRange(range.getOffset1(), range.getOffset2(), myDocument); + return getRange(range.getLine1(), range.getLine2(), myDocument); } } @NotNull - TextRange getUpToDateRange(@NotNull Range range) { + TextRange getVcsRange(@NotNull Range range) { synchronized (myLock) { if (!range.isValid()) { - LOG.warn("UpToDate TextRange of invalid range"); + LOG.warn("Vcs TextRange of invalid range"); } - return getRange(range.getUOffset1(), range.getUOffset2(), myUpToDateDocument); + return getRange(range.getVcsLine1(), range.getVcsLine2(), myVcsDocument); } } @@ -737,14 +868,14 @@ public class LineStatusTracker { * So we consider '\n' not as a part of line, but a separator between lines */ @NotNull - private static TextRange getRange(int offset1, int offset2, @NotNull Document document) { - if (offset1 == offset2) { - int lineStartOffset = offset1 < getLineCount(document) ? document.getLineStartOffset(offset1) : document.getTextLength(); + private static TextRange getRange(int line1, int line2, @NotNull Document document) { + if (line1 == line2) { + int lineStartOffset = line1 < getLineCount(document) ? document.getLineStartOffset(line1) : document.getTextLength(); return new TextRange(lineStartOffset, lineStartOffset); } else { - int startOffset = document.getLineStartOffset(offset1); - int endOffset = document.getLineEndOffset(offset2 - 1); + int startOffset = document.getLineStartOffset(line1); + int endOffset = document.getLineEndOffset(line2 - 1); return new TextRange(startOffset, endOffset); } } 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 d332d1faa792..2230f9c4ff59 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 @@ -208,13 +208,13 @@ public class LineStatusTrackerDrawing { component.add(toolbarPanel, BorderLayout.NORTH); if (range.getType() != Range.INSERTED) { - final DocumentEx doc = (DocumentEx) tracker.getUpToDateDocument(); + final DocumentEx doc = (DocumentEx) tracker.getVcsDocument(); final EditorEx uEditor = (EditorEx)EditorFactory.getInstance().createViewer(doc, tracker.getProject()); final EditorHighlighter highlighter = EditorHighlighterFactory.getInstance().createEditorHighlighter(tracker.getProject(), getFileName(tracker.getDocument())); uEditor.setHighlighter(highlighter); final EditorFragmentComponent editorFragmentComponent = - EditorFragmentComponent.createEditorFragmentComponent(uEditor, range.getUOffset1(), range.getUOffset2(), false, false); + EditorFragmentComponent.createEditorFragmentComponent(uEditor, range.getVcsLine1(), range.getVcsLine2(), false, false); component.add(editorFragmentComponent, BorderLayout.CENTER); @@ -252,7 +252,7 @@ public class LineStatusTrackerDrawing { public static void moveToRange(final Range range, final Editor editor, final LineStatusTracker tracker) { final Document document = tracker.getDocument(); - final int lastOffset = document.getLineStartOffset(Math.min(range.getOffset2(), document.getLineCount() - 1)); + final int lastOffset = document.getLineStartOffset(Math.min(range.getLine2(), document.getLineCount() - 1)); editor.getCaretModel().moveToOffset(lastOffset); editor.getScrollingModel().scrollToCaret(ScrollType.CENTER); 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 e166acd30fec..e788b1849ce3 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 @@ -31,29 +31,28 @@ public class Range { public static final byte INSERTED = 2; public static final byte DELETED = 3; - // offset1/offset2 - line numbers // (2,3) - modified 2nd line // (2,2) - empty range between 1 and 2 lines // index of first line is 0 - private int myOffset1; - private int myOffset2; - private final int myUpToDateOffset1; - private final int myUpToDateOffset2; + private int myLine1; + private int myLine2; + private final int myVcsLine1; + private final int myVcsLine2; private final byte myType; @Nullable private RangeHighlighter myRangeHighlighter; private boolean myValid = true; - public static Range createOn(@NotNull Diff.Change change, int shift, int upToDateShift) { + public static Range createOn(@NotNull Diff.Change change, int shift, int vcsShift) { byte type = getChangeTypeFrom(change); - int offset1 = shift + change.line1; - int offset2 = offset1 + change.inserted; + int line1 = shift + change.line1; + int line2 = line1 + change.inserted; - int uOffset1 = upToDateShift + change.line0; - int uOffset2 = uOffset1 + change.deleted; + int vcsLine1 = vcsShift + change.line0; + int vcsLine2 = vcsLine1 + change.deleted; - return new Range(offset1, offset2, uOffset1, uOffset2, type); + return new Range(line1, line2, vcsLine1, vcsLine2, type); } private static byte getChangeTypeFrom(@NotNull Diff.Change change) { @@ -64,31 +63,35 @@ public class Range { return 0; } - public Range(int offset1, int offset2, int uOffset1, int uOffset2, byte type) { - myOffset1 = offset1; - myOffset2 = offset2; - myUpToDateOffset1 = uOffset1; - myUpToDateOffset2 = uOffset2; + public Range(@NotNull Range range) { + this(range.getLine1(), range.getLine2(), range.getVcsLine1(), range.getVcsLine2(), range.getType()); + } + + public Range(int line1, int line2, int vcsLine1, int vcsLine2, byte type) { + myLine1 = line1; + myLine2 = line2; + myVcsLine1 = vcsLine1; + myVcsLine2 = vcsLine2; myType = type; } public int hashCode() { - return myUpToDateOffset1 ^ myUpToDateOffset2 ^ myType ^ myOffset1 ^ myOffset2; + return myVcsLine1 ^ myVcsLine2 ^ myType ^ myLine1 ^ myLine2; } public boolean equals(Object object) { if (!(object instanceof Range)) return false; Range other = (Range)object; return - (myUpToDateOffset1 == other.myUpToDateOffset1) - && (myUpToDateOffset2 == other.myUpToDateOffset2) - && (myOffset1 == other.myOffset1) - && (myOffset2 == other.myOffset2) + (myVcsLine1 == other.myVcsLine1) + && (myVcsLine2 == other.myVcsLine2) + && (myLine1 == other.myLine1) + && (myLine2 == other.myLine2) && (myType == other.myType); } public String toString() { - return String.format("%s, %s, %s, %s, %s", myOffset1, myOffset2, myUpToDateOffset1, myUpToDateOffset2, getTypeName()); + return String.format("%s, %s, %s, %s, %s", myLine1, myLine2, myVcsLine1, myVcsLine2, getTypeName()); } @NonNls @@ -109,43 +112,32 @@ public class Range { } public int getUpToDateRangeLength() { - return myUpToDateOffset2 - myUpToDateOffset1; + return myVcsLine2 - myVcsLine1; } public void shift(int shift) { - myOffset1 += shift; - myOffset2 += shift; - } - - public int getOffset1() { - return myOffset1; - } - - public int getOffset2() { - return myOffset2; + myLine1 += shift; + myLine2 += shift; } - public int getUOffset1() { - return myUpToDateOffset1; + public int getLine1() { + return myLine1; } - public int getUOffset2() { - return myUpToDateOffset2; + public int getLine2() { + return myLine2; } - public boolean canBeMergedWith(@NotNull Range range) { - return myOffset2 == range.myOffset1; + public int getVcsLine1() { + return myVcsLine1; } - @NotNull - public Range mergeWith(@NotNull Range range) { - return new Range(myOffset1, range.myOffset2, myUpToDateOffset1, range.myUpToDateOffset2, mergedStatusWith(range)); + public int getVcsLine2() { + return myVcsLine2; } - private byte mergedStatusWith(@NotNull Range range) { - byte type = myType; - if (myType != range.myType) type = MODIFIED; - return type; + public boolean rightBefore(@NotNull Range range) { + return myLine2 == range.myLine1; } public boolean hasHighlighter() { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RangesBuilder.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RangesBuilder.java index 74f9e43d08ad..509a9168de42 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RangesBuilder.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RangesBuilder.java @@ -31,17 +31,17 @@ import java.util.List; public class RangesBuilder { private List<Range> myRanges; - public RangesBuilder(@NotNull Document current, @NotNull Document upToDate) throws FilesTooBigForDiffException { - this(new DocumentWrapper(current).getLines(), new DocumentWrapper(upToDate).getLines(), 0, 0); + public RangesBuilder(@NotNull Document current, @NotNull Document vcs) throws FilesTooBigForDiffException { + this(new DocumentWrapper(current).getLines(), new DocumentWrapper(vcs).getLines(), 0, 0); } - public RangesBuilder(@NotNull List<String> current, @NotNull List<String> upToDate, int shift, int uShift) throws FilesTooBigForDiffException { + public RangesBuilder(@NotNull List<String> current, @NotNull List<String> vcs, int shift, int vcsShift) throws FilesTooBigForDiffException { myRanges = new LinkedList<Range>(); - Diff.Change ch = Diff.buildChanges(ArrayUtil.toStringArray(upToDate), ArrayUtil.toStringArray(current)); + Diff.Change ch = Diff.buildChanges(ArrayUtil.toStringArray(vcs), ArrayUtil.toStringArray(current)); while (ch != null) { - Range range = Range.createOn(ch, shift, uShift); + Range range = Range.createOn(ch, shift, vcsShift); myRanges.add(range); ch = ch.link; } 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 index 6e08d90a3b5d..1286b6815fcf 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RollbackLineStatusAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RollbackLineStatusAction.java @@ -29,68 +29,60 @@ import com.intellij.openapi.vfs.ReadonlyStatusHandler; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.BitSet; import java.util.List; public class RollbackLineStatusAction extends DumbAwareAction { - public RollbackLineStatusAction() { - super(ActionsBundle.actionText("Vcs.RollbackChangedLines"), - ActionsBundle.actionDescription("Vcs.RollbackChangedLines"), - 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); + Editor editor = e.getData(CommonDataKeys.EDITOR); + if (project == null || editor == null) { + e.getPresentation().setEnabledAndVisible(false); return; } LineStatusTracker tracker = LineStatusTrackerManager.getInstance(project).getLineStatusTracker(editor.getDocument()); if (tracker == null) { - e.getPresentation().setEnabled(false); + e.getPresentation().setEnabledAndVisible(false); return; } - e.getPresentation().setEnabled(true); + e.getPresentation().setEnabledAndVisible(true); } @Override public void actionPerformed(AnActionEvent e) { Project project = e.getProject(); - Editor editor = CommonDataKeys.EDITOR.getData(e.getDataContext()); + Editor editor = e.getRequiredData(CommonDataKeys.EDITOR); LineStatusTracker tracker = LineStatusTrackerManager.getInstance(project).getLineStatusTracker(editor.getDocument()); - if (tracker == null) return; + assert tracker != null; rollback(tracker, editor, null); } protected static void rollback(@NotNull LineStatusTracker tracker, @Nullable Editor editor, @Nullable Range range) { + assert editor != null || range != null; + 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); + BitSet lines = new BitSet(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); + lines.set(line1, line2 + 1); + if (caret.getSelectionEnd() == document.getTextLength()) lines.set(totalLines); } else { - lines.mark(caret.getLogicalPosition().line); - if (caret.getOffset() == document.getTextLength()) lines.mark(totalLines); + lines.set(caret.getLogicalPosition().line); + if (caret.getOffset() == document.getTextLength()) lines.set(totalLines); } } @@ -106,7 +98,7 @@ public class RollbackLineStatusAction extends DumbAwareAction { }); } - private static void doRollback(@NotNull final LineStatusTracker tracker, @NotNull final SegmentTree lines) { + private static void doRollback(@NotNull final LineStatusTracker tracker, @NotNull final BitSet lines) { execute(tracker, new Runnable() { @Override public void run() { @@ -116,7 +108,6 @@ public class RollbackLineStatusAction extends DumbAwareAction { } 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() { 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 f1795b5efc3c..7f71900654b6 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 @@ -30,7 +30,7 @@ public class RollbackLineStatusRangeAction extends RollbackLineStatusAction { @Override public void update(AnActionEvent e) { - e.getPresentation().setEnabled(true); + e.getPresentation().setEnabledAndVisible(true); } public void actionPerformed(final AnActionEvent e) { 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 deleted file mode 100644 index 68307129c13e..000000000000 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/SegmentTree.java +++ /dev/null @@ -1,116 +0,0 @@ -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 38f65446a56f..23ea7e341edb 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 @@ -49,10 +49,10 @@ public class ShowLineStatusRangeDiffAction extends BaseLineStatusRangeAction { return new DiffRequest(myLineStatusTracker.getProject()) { @NotNull public DiffContent[] getContents() { - Range range = expand(myRange, myLineStatusTracker.getDocument(), myLineStatusTracker.getUpToDateDocument()); + Range range = expand(myRange, myLineStatusTracker.getDocument(), myLineStatusTracker.getVcsDocument()); return new DiffContent[]{ - createDiffContent(myLineStatusTracker.getUpToDateDocument(), - myLineStatusTracker.getUpToDateRange(range), + createDiffContent(myLineStatusTracker.getVcsDocument(), + myLineStatusTracker.getVcsRange(range), null), createDiffContent(myLineStatusTracker.getDocument(), myLineStatusTracker.getCurrentTextRange(range), @@ -81,12 +81,12 @@ public class ShowLineStatusRangeDiffAction extends BaseLineStatusRangeAction { 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); + boolean canExpandBefore = range.getLine1() != 0 && range.getVcsLine1() != 0; + boolean canExpandAfter = range.getLine2() < document.getLineCount() && range.getVcsLine2() < uDocument.getLineCount(); + int offset1 = range.getLine1() - (canExpandBefore ? 1 : 0); + int uOffset1 = range.getVcsLine1() - (canExpandBefore ? 1 : 0); + int offset2 = range.getLine2() + (canExpandAfter ? 1 : 0); + int uOffset2 = range.getVcsLine2() + (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/impl/ModuleDefaultVcsRootPolicy.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ModuleDefaultVcsRootPolicy.java index 4b057f49b282..615b0fc35e9e 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 @@ -28,6 +28,7 @@ import com.intellij.openapi.roots.FileIndexFacade; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.changes.DirtBuilder; import com.intellij.openapi.vcs.changes.FilePathUnderVcs; @@ -116,7 +117,7 @@ public class ModuleDefaultVcsRootPolicy extends DefaultVcsRootPolicy { .isValidAncestor(myBaseDir, file)) { return myBaseDir; } - final VirtualFile contentRoot = ProjectRootManager.getInstance(myProject).getFileIndex().getContentRootForFile(file); + final VirtualFile contentRoot = ProjectRootManager.getInstance(myProject).getFileIndex().getContentRootForFile(file, Registry.is("ide.hide.excluded.files")); if (contentRoot != null) { return contentRoot; } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ModuleVcsPathPresenter.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ModuleVcsPathPresenter.java index 5c2adcbccd8d..1e1cbdf55f5f 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ModuleVcsPathPresenter.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/ModuleVcsPathPresenter.java @@ -23,6 +23,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.changes.ContentRevision; import com.intellij.openapi.vcs.changes.patch.RelativePathCalculator; @@ -48,10 +49,10 @@ public class ModuleVcsPathPresenter extends VcsPathPresenter { return ApplicationManager.getApplication().runReadAction(new Computable<String>() { @Override public String compute() { - ProjectFileIndex fileIndex = ProjectRootManager.getInstance(myProject) - .getFileIndex(); - Module module = fileIndex.getModuleForFile(file); - VirtualFile contentRoot = fileIndex.getContentRootForFile(file); + ProjectFileIndex fileIndex = ProjectRootManager.getInstance(myProject).getFileIndex(); + boolean hideExcludedFiles = Registry.is("ide.hide.excluded.files"); + Module module = fileIndex.getModuleForFile(file, hideExcludedFiles); + VirtualFile contentRoot = fileIndex.getContentRootForFile(file, hideExcludedFiles); if (module == null || contentRoot == null) return file.getPresentableUrl(); StringBuffer result = new StringBuffer(); result.append("["); 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 e699383321ae..84ca76e1d89f 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 @@ -661,7 +661,7 @@ public class ProjectLevelVcsManagerImpl extends ProjectLevelVcsManagerEx impleme @Override @NotNull - public VirtualFile[] getRootsUnderVcs(AbstractVcs vcs) { + public VirtualFile[] getRootsUnderVcs(@NotNull AbstractVcs vcs) { return myMappingsToRoots.getRootsUnderVcs(vcs); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/UpToDateLineNumberProviderImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/UpToDateLineNumberProviderImpl.java index 795652f0b773..5d049f3e9c14 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/UpToDateLineNumberProviderImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/UpToDateLineNumberProviderImpl.java @@ -54,15 +54,15 @@ public class UpToDateLineNumberProviderImpl implements UpToDateLineNumberProvide if (lineInRange(range, start) || lineInRange(range, end)) { return true; } - if (range.getOffset1() > start) { - return range.getOffset1() < end; + if (range.getLine1() > start) { + return range.getLine1() < end; } } return false; } private static boolean lineInRange(final Range range, final int currentNumber) { - return range.getOffset1() <= currentNumber && range.getOffset2() >= currentNumber; + return range.getLine1() <= currentNumber && range.getLine2() >= currentNumber; } @Override @@ -72,7 +72,7 @@ public class UpToDateLineNumberProviderImpl implements UpToDateLineNumberProvide return false; } for (Range range : tracker.getRanges()) { - if (range.getOffset1() <= currentNumber && range.getOffset2() >= currentNumber) { + if (range.getLine1() <= currentNumber && range.getLine2() >= currentNumber) { return true; } } @@ -111,8 +111,8 @@ public class UpToDateLineNumberProviderImpl implements UpToDateLineNumberProvide for (final Object range1 : ranges) { Range range = (Range)range1; - int startOffset = range.getOffset1(); - int endOffset = range.getOffset2(); + int startOffset = range.getLine1(); + int endOffset = range.getLine2(); if ((startOffset <= currentNumber) && (endOffset > currentNumber)) { return ABSENT_LINE_NUMBER; diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/projectlevelman/MappingsToRoots.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/projectlevelman/MappingsToRoots.java index 677c26e6d9f1..1be3cbf0cda3 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/projectlevelman/MappingsToRoots.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/projectlevelman/MappingsToRoots.java @@ -42,7 +42,7 @@ public class MappingsToRoots { } @NotNull - public VirtualFile[] getRootsUnderVcs(final AbstractVcs vcs) { + public VirtualFile[] getRootsUnderVcs(@NotNull AbstractVcs vcs) { List<VirtualFile> result = myMappings.getMappingsAsFilesUnderVcs(vcs); final AbstractVcs.RootsConvertor convertor = vcs.getCustomConvertor(); 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 5ff541ebc67d..57754f0190cc 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 @@ -273,7 +273,7 @@ public class NewMappings { return FileUtil.startsWith(systemIndependentPath, mapping.systemIndependentPath()); } - public List<VirtualFile> getMappingsAsFilesUnderVcs(final AbstractVcs vcs) { + public List<VirtualFile> getMappingsAsFilesUnderVcs(@NotNull AbstractVcs vcs) { final List<VirtualFile> result = new ArrayList<VirtualFile>(); final String vcsName = vcs.getName(); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/update/AbstractCommonUpdateAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/update/AbstractCommonUpdateAction.java index d2c7b556b88e..5fa282c06e14 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/update/AbstractCommonUpdateAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/update/AbstractCommonUpdateAction.java @@ -38,7 +38,6 @@ import com.intellij.openapi.vcs.changes.RemoteRevisionsCache; import com.intellij.openapi.vcs.changes.VcsAnnotationRefresher; import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; import com.intellij.openapi.vcs.changes.VcsDirtyScopeManagerImpl; -import com.intellij.openapi.vcs.changes.committed.CommittedChangesAdapter; import com.intellij.openapi.vcs.changes.committed.CommittedChangesCache; import com.intellij.openapi.vcs.ex.ProjectLevelVcsManagerEx; import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier; @@ -46,6 +45,7 @@ import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.util.Consumer; import com.intellij.util.WaitForProgressToShow; import com.intellij.util.ui.OptionsDialog; import com.intellij.vcsUtil.VcsUtil; @@ -550,10 +550,14 @@ public abstract class AbstractCommonUpdateAction extends AbstractVcsAction { } } else if (! myUpdatedFiles.isEmpty()) { - showUpdateTree(continueChainFinal && updateSuccess && noMerged, someSessionWasCancelled); - + final UpdateInfoTree tree = showUpdateTree(continueChainFinal && updateSuccess && noMerged, someSessionWasCancelled); final CommittedChangesCache cache = CommittedChangesCache.getInstance(myProject); - cache.processUpdatedFiles(myUpdatedFiles); + cache.processUpdatedFiles(myUpdatedFiles, new Consumer<List<CommittedChangeList>>() { + @Override + public void consume(List<CommittedChangeList> incomingChangeLists) { + tree.setChangeLists(incomingChangeLists); + } + }); if (someSessionWasCancelled) { VcsBalloonProblemNotifier.showOverChangesView(myProject, "VCS Update Incomplete" + prepareNotificationWithUpdateInfo(), MessageType.WARNING); @@ -592,7 +596,8 @@ public abstract class AbstractCommonUpdateAction extends AbstractVcsAction { } } - private void showUpdateTree(final boolean willBeContinued, final boolean wasCanceled) { + @NotNull + private UpdateInfoTree showUpdateTree(final boolean willBeContinued, final boolean wasCanceled) { RestoreUpdateTree restoreUpdateTree = RestoreUpdateTree.getInstance(myProject); restoreUpdateTree.registerUpdateInformation(myUpdatedFiles, myActionInfo); final String text = getTemplatePresentation().getText() + ((willBeContinued || (myUpdateNumber > 1)) ? ("#" + myUpdateNumber) : ""); @@ -602,13 +607,7 @@ public abstract class AbstractCommonUpdateAction extends AbstractVcsAction { updateInfoTree.setAfter(myAfter); updateInfoTree.setCanGroupByChangeList(canGroupByChangelist(myVcsToVirtualFiles.keySet())); - myProject.getMessageBus().connect(updateInfoTree).subscribe(CommittedChangesCache.COMMITTED_TOPIC, new CommittedChangesAdapter() { - public void incomingChangesUpdated(final List<CommittedChangeList> receivedChanges) { - if (receivedChanges != null) { - updateInfoTree.setChangeLists(receivedChanges); - } - } - }); + return updateInfoTree; } public void onCancel() { diff --git a/platform/vcs-impl/src/com/intellij/vcsUtil/AuthDialog.java b/platform/vcs-impl/src/com/intellij/vcsUtil/AuthDialog.java index d3ed9a84de2c..5d6861e6f39b 100644 --- a/platform/vcs-impl/src/com/intellij/vcsUtil/AuthDialog.java +++ b/platform/vcs-impl/src/com/intellij/vcsUtil/AuthDialog.java @@ -25,7 +25,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; public class AuthDialog extends DialogWrapper { - private AuthenticationPanel authPanel; + private final AuthenticationPanel authPanel; /** * If password if prefilled, it is expected to continue remembering it. @@ -54,6 +54,7 @@ public class AuthDialog extends DialogWrapper { return rememberByDefault; } + @Override protected JComponent createCenterPanel() { return authPanel; } |