2 * Copyright 2000-2016 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.openapi.vcs.impl;
18 import com.intellij.execution.filters.TextConsoleBuilderFactory;
19 import com.intellij.execution.ui.ConsoleView;
20 import com.intellij.execution.ui.ConsoleViewContentType;
21 import com.intellij.ide.AppLifecycleListener;
22 import com.intellij.openapi.Disposable;
23 import com.intellij.openapi.actionSystem.ActionManager;
24 import com.intellij.openapi.actionSystem.ActionPlaces;
25 import com.intellij.openapi.actionSystem.ActionToolbar;
26 import com.intellij.openapi.actionSystem.DefaultActionGroup;
27 import com.intellij.openapi.application.ApplicationManager;
28 import com.intellij.openapi.application.ModalityState;
29 import com.intellij.openapi.components.*;
30 import com.intellij.openapi.diagnostic.Logger;
31 import com.intellij.openapi.editor.markup.TextAttributes;
32 import com.intellij.openapi.extensions.Extensions;
33 import com.intellij.openapi.progress.ProcessCanceledException;
34 import com.intellij.openapi.progress.ProgressManager;
35 import com.intellij.openapi.project.Project;
36 import com.intellij.openapi.project.ProjectManager;
37 import com.intellij.openapi.project.ProjectManagerAdapter;
38 import com.intellij.openapi.roots.FileIndexFacade;
39 import com.intellij.openapi.util.*;
40 import com.intellij.openapi.util.io.FileUtil;
41 import com.intellij.openapi.util.registry.Registry;
42 import com.intellij.openapi.util.text.StringUtil;
43 import com.intellij.openapi.vcs.*;
44 import com.intellij.openapi.vcs.changes.ChangesUtil;
45 import com.intellij.openapi.vcs.changes.VcsAnnotationLocalChangesListener;
46 import com.intellij.openapi.vcs.changes.VcsAnnotationLocalChangesListenerImpl;
47 import com.intellij.openapi.vcs.checkout.CompositeCheckoutListener;
48 import com.intellij.openapi.vcs.ex.ProjectLevelVcsManagerEx;
49 import com.intellij.openapi.vcs.history.VcsHistoryCache;
50 import com.intellij.openapi.vcs.impl.projectlevelman.*;
51 import com.intellij.openapi.vcs.roots.VcsRootScanner;
52 import com.intellij.openapi.vcs.update.ActionInfo;
53 import com.intellij.openapi.vcs.update.UpdateInfoTree;
54 import com.intellij.openapi.vcs.update.UpdatedFiles;
55 import com.intellij.openapi.vcs.update.UpdatedFilesListener;
56 import com.intellij.openapi.vfs.LocalFileSystem;
57 import com.intellij.openapi.vfs.VfsUtilCore;
58 import com.intellij.openapi.vfs.VirtualFile;
59 import com.intellij.openapi.wm.ToolWindow;
60 import com.intellij.openapi.wm.ToolWindowId;
61 import com.intellij.openapi.wm.ToolWindowManager;
62 import com.intellij.project.ProjectKt;
63 import com.intellij.ui.content.Content;
64 import com.intellij.ui.content.ContentFactory;
65 import com.intellij.ui.content.ContentManager;
66 import com.intellij.util.ContentUtilEx;
67 import com.intellij.util.Processor;
68 import com.intellij.util.containers.ContainerUtil;
69 import com.intellij.util.messages.MessageBusConnection;
70 import com.intellij.util.text.DateFormatUtil;
71 import org.jdom.Attribute;
72 import org.jdom.DataConversionException;
73 import org.jdom.Element;
74 import org.jetbrains.annotations.CalledInAwt;
75 import org.jetbrains.annotations.*;
80 import java.util.List;
82 @State(name = "ProjectLevelVcsManager", storages = @Storage(StoragePathMacros.WORKSPACE_FILE))
83 public class ProjectLevelVcsManagerImpl extends ProjectLevelVcsManagerEx implements ProjectComponent, PersistentStateComponent<Element> {
84 private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl");
85 @NonNls private static final String SETTINGS_EDITED_MANUALLY = "settingsEditedManually";
87 private final ProjectLevelVcsManagerSerialization mySerialization;
88 private final OptionsAndConfirmations myOptionsAndConfirmations;
90 private final NewMappings myMappings;
91 private final Project myProject;
92 private final MappingsToRoots myMappingsToRoots;
94 private ContentManager myContentManager;
95 private ConsoleView myConsole;
96 private final Disposable myConsoleDisposer = new Disposable() {
98 public void dispose() {
99 if (myConsole != null) {
100 Disposer.dispose(myConsole);
106 private final VcsInitialization myInitialization;
108 @NonNls private static final String ELEMENT_MAPPING = "mapping";
109 @NonNls private static final String ATTRIBUTE_DIRECTORY = "directory";
110 @NonNls private static final String ATTRIBUTE_VCS = "vcs";
111 @NonNls private static final String ATTRIBUTE_DEFAULT_PROJECT = "defaultProject";
112 @NonNls private static final String ELEMENT_ROOT_SETTINGS = "rootSettings";
113 @NonNls private static final String ATTRIBUTE_CLASS = "class";
115 private boolean myMappingsLoaded;
116 private boolean myHaveLegacyVcsConfiguration;
117 private final DefaultVcsRootPolicy myDefaultVcsRootPolicy;
119 private volatile int myBackgroundOperationCounter;
121 private final Set<ActionKey> myBackgroundRunningTasks = ContainerUtil.newHashSet();
123 private final List<Pair<String, ConsoleViewContentType>> myPendingOutput = ContainerUtil.newArrayList();
125 private final VcsHistoryCache myVcsHistoryCache;
126 private final ContentRevisionCache myContentRevisionCache;
127 private final FileIndexFacade myExcludedIndex;
128 private final VcsFileListenerContextHelper myVcsFileListenerContextHelper;
129 private final VcsAnnotationLocalChangesListenerImpl myAnnotationLocalChangesListener;
131 public ProjectLevelVcsManagerImpl(Project project,
132 final FileStatusManager manager,
133 final FileIndexFacade excludedFileIndex,
134 ProjectManager projectManager,
135 DefaultVcsRootPolicy defaultVcsRootPolicy,
136 VcsFileListenerContextHelper vcsFileListenerContextHelper) {
138 mySerialization = new ProjectLevelVcsManagerSerialization();
139 myOptionsAndConfirmations = new OptionsAndConfirmations();
141 myDefaultVcsRootPolicy = defaultVcsRootPolicy;
143 myInitialization = new VcsInitialization(myProject);
144 Disposer.register(project, myInitialization); // wait for the thread spawned in VcsInitialization to terminate
145 projectManager.addProjectManagerListener(project, new ProjectManagerAdapter() {
147 public void projectClosing(Project project) {
148 Disposer.dispose(myInitialization);
151 if (project.isDefault()) {
152 // default project is disposed in write action, so treat it differently
153 MessageBusConnection connection = ApplicationManager.getApplication().getMessageBus().connect();
154 connection.subscribe(AppLifecycleListener.TOPIC, new AppLifecycleListener.Adapter() {
156 public void appClosing() {
157 Disposer.dispose(myInitialization);
161 myMappings = new NewMappings(myProject, this, manager);
162 myMappingsToRoots = new MappingsToRoots(myMappings, myProject);
164 myVcsHistoryCache = new VcsHistoryCache();
165 myContentRevisionCache = new ContentRevisionCache();
166 myVcsFileListenerContextHelper = vcsFileListenerContextHelper;
167 VcsListener vcsListener = () -> {
168 myVcsHistoryCache.clear();
169 myVcsFileListenerContextHelper.possiblySwitchActivation(hasActiveVcss());
171 myExcludedIndex = excludedFileIndex;
172 MessageBusConnection connection = myProject.getMessageBus().connect();
173 connection.subscribe(ProjectLevelVcsManager.VCS_CONFIGURATION_CHANGED, vcsListener);
174 connection.subscribe(ProjectLevelVcsManager.VCS_CONFIGURATION_CHANGED_IN_PLUGIN, vcsListener);
175 connection.subscribe(UpdatedFilesListener.UPDATED_FILES, myContentRevisionCache::clearCurrent);
176 myAnnotationLocalChangesListener = new VcsAnnotationLocalChangesListenerImpl(myProject, this);
180 public void initComponent() {
181 myOptionsAndConfirmations.init(mySerialization::getInitOptionValue);
184 public void registerVcs(AbstractVcs vcs) {
185 AllVcses.getInstance(myProject).registerManually(vcs);
190 public AbstractVcs findVcsByName(String name) {
191 if (name == null) return null;
192 AbstractVcs result = myProject.isDisposed() ? null : AllVcses.getInstance(myProject).getByName(name);
193 ProgressManager.checkCanceled();
199 public VcsDescriptor getDescriptor(final String name) {
200 if (name == null) return null;
201 if (myProject.isDisposed()) return null;
202 return AllVcses.getInstance(myProject).getDescriptor(name);
206 public void iterateVfUnderVcsRoot(VirtualFile file, Processor<VirtualFile> processor) {
207 VcsRootIterator.iterateVfUnderVcsRoot(myProject, file, processor);
211 public VcsDescriptor[] getAllVcss() {
212 return AllVcses.getInstance(myProject).getAll();
215 public boolean haveVcses() {
216 return !AllVcses.getInstance(myProject).isEmpty();
220 public void disposeComponent() {
222 myMappings.disposeMe();
223 Disposer.dispose(myAnnotationLocalChangesListener);
224 myContentManager = null;
226 ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject);
227 if (toolWindowManager != null && toolWindowManager.getToolWindow(ToolWindowId.VCS) != null) {
228 toolWindowManager.unregisterToolWindow(ToolWindowId.VCS);
234 public VcsAnnotationLocalChangesListener getAnnotationLocalChangesListener() {
235 return myAnnotationLocalChangesListener;
239 public void projectOpened() {
240 addInitializationRequest(VcsInitObject.AFTER_COMMON, () -> {
241 if (!ApplicationManager.getApplication().isUnitTestMode()) {
242 VcsRootChecker[] checkers = Extensions.getExtensions(VcsRootChecker.EXTENSION_POINT_NAME);
243 if (checkers.length != 0) {
244 VcsRootScanner.start(myProject, checkers);
251 public void projectClosed() {
257 public String getComponentName() {
258 return "ProjectLevelVcsManager";
262 public boolean checkAllFilesAreUnder(AbstractVcs abstractVcs, VirtualFile[] files) {
263 if (files == null) return false;
264 for (VirtualFile file : files) {
265 if (getVcsFor(file) != abstractVcs) {
274 public AbstractVcs getVcsFor(@NotNull VirtualFile file) {
275 final String vcsName = myMappings.getVcsFor(file);
276 if (vcsName == null || vcsName.isEmpty()) {
279 return AllVcses.getInstance(myProject).getByName(vcsName);
284 public AbstractVcs getVcsFor(final FilePath file) {
285 final VirtualFile vFile = ChangesUtil.findValidParentAccurately(file);
286 return ApplicationManager.getApplication().runReadAction((Computable<AbstractVcs>)() -> {
287 if (!ApplicationManager.getApplication().isUnitTestMode() && !myProject.isInitialized()) return null;
288 if (myProject.isDisposed()) throw new ProcessCanceledException();
290 return getVcsFor(vFile);
298 public VirtualFile getVcsRootFor(@Nullable final VirtualFile file) {
299 final VcsDirectoryMapping mapping = myMappings.getMappingFor(file);
300 if (mapping == null) {
303 final String directory = mapping.getDirectory();
304 if (directory.isEmpty()) {
305 return myDefaultVcsRootPolicy.getVcsRootFor(file);
307 return LocalFileSystem.getInstance().findFileByPath(directory);
312 public VcsRoot getVcsRootObjectFor(final VirtualFile file) {
313 final VcsDirectoryMapping mapping = myMappings.getMappingFor(file);
314 if (mapping == null) {
317 final String directory = mapping.getDirectory();
318 final AbstractVcs vcs = findVcsByName(mapping.getVcs());
319 if (directory.isEmpty()) {
320 return new VcsRoot(vcs, myDefaultVcsRootPolicy.getVcsRootFor(file));
322 return new VcsRoot(vcs, LocalFileSystem.getInstance().findFileByPath(directory));
327 public VirtualFile getVcsRootFor(final FilePath file) {
328 if (myProject.isDisposed()) return null;
329 VirtualFile vFile = ChangesUtil.findValidParentAccurately(file);
331 return getVcsRootFor(vFile);
337 public VcsRoot getVcsRootObjectFor(final FilePath file) {
338 if (myProject.isDisposed()) {
341 VirtualFile vFile = ChangesUtil.findValidParentAccurately(file);
343 return getVcsRootObjectFor(vFile);
348 public void unregisterVcs(@NotNull AbstractVcs vcs) {
349 if (!ApplicationManager.getApplication().isUnitTestMode() && myMappings.haveActiveVcs(vcs.getName())) {
351 LOG.warn("Active vcs '" + vcs.getName() + "' is being unregistered. Remove from mappings first.");
353 myMappings.beingUnregistered(vcs.getName());
354 AllVcses.getInstance(myProject).unregisterManually(vcs);
358 public ContentManager getContentManager() {
359 if (myContentManager == null) {
360 ToolWindow changes = ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.VCS);
361 myContentManager = changes == null ? null : changes.getContentManager();
363 return myContentManager;
367 public boolean checkVcsIsActive(AbstractVcs vcs) {
368 return checkVcsIsActive(vcs.getName());
372 public boolean checkVcsIsActive(final String vcsName) {
373 return myMappings.haveActiveVcs(vcsName);
377 public AbstractVcs[] getAllActiveVcss() {
378 return myMappings.getActiveVcses();
382 public boolean hasActiveVcss() {
383 return myMappings.hasActiveVcss();
387 public boolean hasAnyMappings() {
388 return !myMappings.isEmpty();
393 public void addMessageToConsoleWindow(final String message, final TextAttributes attributes) {
394 addMessageToConsoleWindow(message, new ConsoleViewContentType("", attributes));
398 public void addMessageToConsoleWindow(@Nullable final String message, @NotNull final ConsoleViewContentType contentType) {
399 if (!Registry.is("vcs.showConsole")) {
402 if (StringUtil.isEmptyOrSpaces(message)) {
406 ApplicationManager.getApplication().invokeLater(() -> {
407 // for default and disposed projects the ContentManager is not available.
408 if (myProject.isDisposed() || myProject.isDefault()) return;
409 final ContentManager contentManager = getContentManager();
410 if (contentManager == null) {
411 myPendingOutput.add(Pair.create(message, contentType));
414 getOrCreateConsoleContent(contentManager);
415 printToConsole(message, contentType);
417 }, ModalityState.defaultModalityState());
420 private Content getOrCreateConsoleContent(final ContentManager contentManager) {
421 final String displayName = VcsBundle.message("vcs.console.toolwindow.display.name");
422 Content content = contentManager.findContent(displayName);
423 if (content == null) {
426 myConsole = TextConsoleBuilderFactory.getInstance().createBuilder(myProject).getConsole();
428 JPanel panel = new JPanel(new BorderLayout());
429 panel.add(myConsole.getComponent(), BorderLayout.CENTER);
431 ActionToolbar toolbar = ActionManager.getInstance()
432 .createActionToolbar(ActionPlaces.UNKNOWN, new DefaultActionGroup(myConsole.createConsoleActions()), false);
433 panel.add(toolbar.getComponent(), BorderLayout.WEST);
435 content = ContentFactory.SERVICE.getInstance().createContent(panel, displayName, true);
436 content.setDisposer(myConsoleDisposer);
437 contentManager.addContent(content);
439 for (Pair<String, ConsoleViewContentType> pair : myPendingOutput) {
440 printToConsole(pair.first, pair.second);
442 myPendingOutput.clear();
447 private void printToConsole(@NotNull String message, @NotNull ConsoleViewContentType contentType) {
448 myConsole.print(message + "\n", contentType);
451 private void releaseConsole() {
452 Disposer.dispose(myConsoleDisposer);
457 public VcsShowSettingOption getOptions(VcsConfiguration.StandardOption option) {
458 return myOptionsAndConfirmations.getOptions(option);
462 public List<VcsShowOptionsSettingImpl> getAllOptions() {
463 return myOptionsAndConfirmations.getAllOptions();
468 public VcsShowSettingOption getStandardOption(@NotNull VcsConfiguration.StandardOption option, @NotNull AbstractVcs vcs) {
469 final VcsShowOptionsSettingImpl options = (VcsShowOptionsSettingImpl)getOptions(option);
470 options.addApplicableVcs(vcs);
476 public VcsShowSettingOption getOrCreateCustomOption(@NotNull String vcsActionName, @NotNull AbstractVcs vcs) {
477 return myOptionsAndConfirmations.getOrCreateCustomOption(vcsActionName, vcs);
481 public void showProjectOperationInfo(final UpdatedFiles updatedFiles, String displayActionName) {
482 showUpdateProjectInfo(updatedFiles, displayActionName, ActionInfo.STATUS, false);
486 public UpdateInfoTree showUpdateProjectInfo(UpdatedFiles updatedFiles, String displayActionName, ActionInfo actionInfo, boolean canceled) {
487 if (!myProject.isOpen() || myProject.isDisposed()) return null;
488 ContentManager contentManager = getContentManager();
489 if (contentManager == null) {
490 return null; // content manager is made null during dispose; flag is set later
492 final UpdateInfoTree updateInfoTree = new UpdateInfoTree(contentManager, myProject, updatedFiles, displayActionName, actionInfo);
493 ContentUtilEx.addTabbedContent(contentManager, updateInfoTree, "Update Info", DateFormatUtil.formatDateTime(System.currentTimeMillis()), true, updateInfoTree);
494 ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.VCS).activate(null);
495 updateInfoTree.expandRootChildren();
496 return updateInfoTree;
499 public void cleanupMappings() {
500 myMappings.cleanupMappings();
504 public List<VcsDirectoryMapping> getDirectoryMappings() {
505 return myMappings.getDirectoryMappings();
509 public List<VcsDirectoryMapping> getDirectoryMappings(final AbstractVcs vcs) {
510 return myMappings.getDirectoryMappings(vcs.getName());
515 public VcsDirectoryMapping getDirectoryMappingFor(final FilePath path) {
516 VirtualFile vFile = ChangesUtil.findValidParentAccurately(path);
518 return myMappings.getMappingFor(vFile);
523 private boolean hasExplicitMapping(final VirtualFile vFile) {
524 final VcsDirectoryMapping mapping = myMappings.getMappingFor(vFile);
525 return mapping != null && !mapping.isDefaultMapping();
529 public void setDirectoryMapping(final String path, final String activeVcsName) {
530 if (myMappingsLoaded) return; // ignore per-module VCS settings if the mapping table was loaded from .ipr
531 myHaveLegacyVcsConfiguration = true;
532 myMappings.setMapping(FileUtil.toSystemIndependentName(path), activeVcsName);
535 public void setAutoDirectoryMapping(String path, String activeVcsName) {
536 final List<VirtualFile> defaultRoots = myMappings.getDefaultRoots();
537 if (defaultRoots.size() == 1 && StringUtil.isEmpty(myMappings.haveDefaultMapping())) {
538 myMappings.removeDirectoryMapping(new VcsDirectoryMapping("", ""));
540 myMappings.setMapping(path, activeVcsName);
543 public void removeDirectoryMapping(VcsDirectoryMapping mapping) {
544 myMappings.removeDirectoryMapping(mapping);
548 public void setDirectoryMappings(final List<VcsDirectoryMapping> items) {
549 myHaveLegacyVcsConfiguration = true;
550 myMappings.setDirectoryMappings(items);
554 public void iterateVcsRoot(final VirtualFile root, final Processor<FilePath> iterator) {
555 VcsRootIterator.iterateVcsRoot(myProject, root, iterator);
559 public void iterateVcsRoot(VirtualFile root,
560 Processor<FilePath> iterator,
561 @Nullable VirtualFileFilter directoryFilter) {
562 VcsRootIterator.iterateVcsRoot(myProject, root, iterator, directoryFilter);
567 public Element getState() {
568 Element element = new Element("state");
569 mySerialization.writeExternalUtil(element, myOptionsAndConfirmations);
570 if (myHaveLegacyVcsConfiguration) {
571 element.setAttribute(SETTINGS_EDITED_MANUALLY, "true");
577 public void loadState(Element state) {
578 mySerialization.readExternalUtil(state, myOptionsAndConfirmations);
579 final Attribute attribute = state.getAttribute(SETTINGS_EDITED_MANUALLY);
580 if (attribute != null) {
582 myHaveLegacyVcsConfiguration = attribute.getBooleanValue();
584 catch (DataConversionException ignored) {
591 public VcsShowConfirmationOption getStandardConfirmation(@NotNull VcsConfiguration.StandardConfirmation option,
593 final VcsShowConfirmationOptionImpl result = getConfirmation(option);
595 result.addApplicableVcs(vcs);
601 public List<VcsShowConfirmationOptionImpl> getAllConfirmations() {
602 return myOptionsAndConfirmations.getAllConfirmations();
607 public VcsShowConfirmationOptionImpl getConfirmation(VcsConfiguration.StandardConfirmation option) {
608 return myOptionsAndConfirmations.getConfirmation(option);
611 private final Map<VcsListener, MessageBusConnection> myAdapters = new HashMap<>();
614 public void addVcsListener(VcsListener listener) {
615 MessageBusConnection connection = myProject.getMessageBus().connect();
616 connection.subscribe(VCS_CONFIGURATION_CHANGED, listener);
617 myAdapters.put(listener, connection);
621 public void removeVcsListener(VcsListener listener) {
622 final MessageBusConnection connection = myAdapters.remove(listener);
623 if (connection != null) {
624 connection.disconnect();
629 public void startBackgroundVcsOperation() {
630 myBackgroundOperationCounter++;
634 public void stopBackgroundVcsOperation() {
635 // in fact, the condition is "should not be called under ApplicationManager.invokeLater() and similar"
636 assert !ApplicationManager.getApplication().isDispatchThread() || ApplicationManager.getApplication().isUnitTestMode();
637 LOG.assertTrue(myBackgroundOperationCounter > 0, "myBackgroundOperationCounter > 0");
638 myBackgroundOperationCounter--;
642 public boolean isBackgroundVcsOperationRunning() {
643 return myBackgroundOperationCounter > 0;
647 public List<VirtualFile> getRootsUnderVcsWithoutFiltering(final AbstractVcs vcs) {
648 return myMappings.getMappingsAsFilesUnderVcs(vcs);
653 public VirtualFile[] getRootsUnderVcs(@NotNull AbstractVcs vcs) {
654 return myMappingsToRoots.getRootsUnderVcs(vcs);
658 public List<VirtualFile> getDetailedVcsMappings(final AbstractVcs vcs) {
659 return myMappingsToRoots.getDetailedVcsMappings(vcs);
663 public VirtualFile[] getAllVersionedRoots() {
664 List<VirtualFile> vFiles = new ArrayList<>();
665 final AbstractVcs[] vcses = myMappings.getActiveVcses();
666 for (AbstractVcs vcs : vcses) {
667 Collections.addAll(vFiles, getRootsUnderVcs(vcs));
669 return VfsUtilCore.toVirtualFileArray(vFiles);
674 public VcsRoot[] getAllVcsRoots() {
675 List<VcsRoot> vcsRoots = new ArrayList<>();
676 final AbstractVcs[] vcses = myMappings.getActiveVcses();
677 for (AbstractVcs vcs : vcses) {
678 final VirtualFile[] roots = getRootsUnderVcs(vcs);
679 for (VirtualFile root : roots) {
680 vcsRoots.add(new VcsRoot(vcs, root));
683 return vcsRoots.toArray(new VcsRoot[vcsRoots.size()]);
687 public void updateActiveVcss() {
692 public void notifyDirectoryMappingChanged() {
693 myProject.getMessageBus().syncPublisher(VCS_CONFIGURATION_CHANGED).directoryMappingChanged();
696 void readDirectoryMappings(final Element element) {
699 final List<VcsDirectoryMapping> mappingsList = new ArrayList<>();
700 boolean haveNonEmptyMappings = false;
701 for (Element child : element.getChildren(ELEMENT_MAPPING)) {
702 final String vcs = child.getAttributeValue(ATTRIBUTE_VCS);
703 if (vcs != null && !vcs.isEmpty()) {
704 haveNonEmptyMappings = true;
706 VcsDirectoryMapping mapping = new VcsDirectoryMapping(child.getAttributeValue(ATTRIBUTE_DIRECTORY), vcs);
707 mappingsList.add(mapping);
709 Element rootSettingsElement = child.getChild(ELEMENT_ROOT_SETTINGS);
710 if (rootSettingsElement != null) {
711 String className = rootSettingsElement.getAttributeValue(ATTRIBUTE_CLASS);
712 AbstractVcs vcsInstance = findVcsByName(mapping.getVcs());
713 if (vcsInstance != null && className != null) {
714 final VcsRootSettings rootSettings = vcsInstance.createEmptyVcsRootSettings();
715 if (rootSettings != null) {
717 rootSettings.readExternal(rootSettingsElement);
718 mapping.setRootSettings(rootSettings);
720 catch (InvalidDataException e) {
721 LOG.error("Failed to load VCS root settings class " + className + " for VCS " + vcsInstance.getClass().getName(), e);
727 boolean defaultProject = Boolean.TRUE.toString().equals(element.getAttributeValue(ATTRIBUTE_DEFAULT_PROJECT));
728 // run autodetection if there's no VCS in default project and
729 if (haveNonEmptyMappings || !defaultProject) {
730 myMappingsLoaded = true;
732 myMappings.setDirectoryMappings(mappingsList);
735 void writeDirectoryMappings(@NotNull Element element) {
736 if (myProject.isDefault()) {
737 element.setAttribute(ATTRIBUTE_DEFAULT_PROJECT, Boolean.TRUE.toString());
739 for (VcsDirectoryMapping mapping : getDirectoryMappings()) {
740 VcsRootSettings rootSettings = mapping.getRootSettings();
741 if (rootSettings == null && StringUtil.isEmpty(mapping.getDirectory()) && StringUtil.isEmpty(mapping.getVcs())) {
745 Element child = new Element(ELEMENT_MAPPING);
746 child.setAttribute(ATTRIBUTE_DIRECTORY, mapping.getDirectory());
747 child.setAttribute(ATTRIBUTE_VCS, mapping.getVcs());
748 if (rootSettings != null) {
749 Element rootSettingsElement = new Element(ELEMENT_ROOT_SETTINGS);
750 rootSettingsElement.setAttribute(ATTRIBUTE_CLASS, rootSettings.getClass().getName());
752 rootSettings.writeExternal(rootSettingsElement);
753 child.addContent(rootSettingsElement);
755 catch (WriteExternalException e) {
759 element.addContent(child);
763 public boolean needAutodetectMappings() {
764 return !myHaveLegacyVcsConfiguration && !myMappingsLoaded;
768 * Used to guess VCS for automatic mapping through a look into a working copy
772 public AbstractVcs findVersioningVcs(VirtualFile file) {
773 final VcsDescriptor[] vcsDescriptors = getAllVcss();
774 VcsDescriptor probableVcs = null;
775 for (VcsDescriptor vcsDescriptor : vcsDescriptors) {
776 if (vcsDescriptor.probablyUnderVcs(file)) {
777 if (probableVcs != null) {
780 probableVcs = vcsDescriptor;
783 return probableVcs == null ? null : findVcsByName(probableVcs.getName());
787 public CheckoutProvider.Listener getCompositeCheckoutListener() {
788 return new CompositeCheckoutListener(myProject);
792 public void fireDirectoryMappingsChanged() {
793 if (myProject.isOpen() && !myProject.isDisposed()) {
794 myMappings.mappingsChanged();
799 public String haveDefaultMapping() {
800 return myMappings.haveDefaultMapping();
804 * @deprecated {@link BackgroundableActionLock}
807 public BackgroundableActionEnabledHandler getBackgroundableActionHandler(final VcsBackgroundableActions action) {
808 ApplicationManager.getApplication().assertIsDispatchThread();
809 return new BackgroundableActionEnabledHandler(myProject, action);
813 boolean isBackgroundTaskRunning(@NotNull Object... keys) {
814 ApplicationManager.getApplication().assertIsDispatchThread();
815 return myBackgroundRunningTasks.contains(new ActionKey(keys));
819 void startBackgroundTask(@NotNull Object... keys) {
820 ApplicationManager.getApplication().assertIsDispatchThread();
821 LOG.assertTrue(myBackgroundRunningTasks.add(new ActionKey(keys)));
825 void stopBackgroundTask(@NotNull Object... keys) {
826 ApplicationManager.getApplication().assertIsDispatchThread();
827 LOG.assertTrue(myBackgroundRunningTasks.remove(new ActionKey(keys)));
830 public void addInitializationRequest(final VcsInitObject vcsInitObject, final Runnable runnable) {
831 ApplicationManager.getApplication().runReadAction(() -> myInitialization.add(vcsInitObject, runnable));
835 public boolean isFileInContent(@Nullable final VirtualFile vf) {
836 return ApplicationManager.getApplication().runReadAction((Computable<Boolean>)() ->
837 vf != null && (myExcludedIndex.isInContent(vf) || isFileInBaseDir(vf) || vf.equals(myProject.getBaseDir()) ||
838 hasExplicitMapping(vf) || isInDirectoryBasedRoot(vf)
839 || !Registry.is("ide.hide.excluded.files") && myExcludedIndex.isExcludedFile(vf))
844 public boolean isIgnored(VirtualFile vf) {
845 if (Registry.is("ide.hide.excluded.files")) {
846 return myExcludedIndex.isExcludedFile(vf);
849 return myExcludedIndex.isUnderIgnored(vf);
853 private boolean isInDirectoryBasedRoot(@Nullable VirtualFile file) {
854 if (file != null && ProjectKt.isDirectoryBased(myProject)) {
855 return ProjectKt.getStateStore(myProject).isProjectFile(file);
860 private boolean isFileInBaseDir(final VirtualFile file) {
861 VirtualFile parent = file.getParent();
862 return !file.isDirectory() && parent != null && parent.equals(myProject.getBaseDir());
866 public VcsHistoryCache getVcsHistoryCache() {
867 return myVcsHistoryCache;
871 public ContentRevisionCache getContentRevisionCache() {
872 return myContentRevisionCache;
876 public void waitForInitialized() {
877 myInitialization.waitForCompletion();
880 private static class ActionKey {
881 private final Object[] myObjects;
883 ActionKey(@NotNull Object... objects) {
888 public final boolean equals(Object o) {
889 if (o == null || getClass() != o.getClass()) return false;
890 return Arrays.equals(myObjects, ((ActionKey)o).myObjects);
894 public final int hashCode() {
895 return Arrays.hashCode(myObjects);
899 public String toString() {
900 return getClass() + " - " + Arrays.toString(myObjects);