/*
* Copyright 2000-2016 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.fileEditor.impl;
import com.intellij.diff.DiffContentFactory;
import com.intellij.diff.DiffManager;
import com.intellij.diff.DiffRequestPanel;
import com.intellij.diff.contents.DocumentContent;
import com.intellij.diff.requests.DiffRequest;
import com.intellij.diff.requests.SimpleDiffRequest;
import com.intellij.diff.util.DiffUserDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.ProjectLocator;
import com.intellij.openapi.project.ex.ProjectEx;
import com.intellij.openapi.ui.DialogBuilder;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileEvent;
import com.intellij.ui.UIBundle;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* @author peter
*/
public class MemoryDiskConflictResolver {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.fileEditor.impl.MemoryDiskConflictResolver");
private final Set<VirtualFile> myConflicts = new LinkedHashSet<>();
void beforeContentChange(@NotNull VirtualFileEvent event) {
if (event.isFromSave()) return;
VirtualFile file = event.getFile();
if (!file.isValid() || hasConflict(file)) return;
Document document = FileDocumentManager.getInstance().getCachedDocument(file);
if (document == null || !FileDocumentManager.getInstance().isDocumentUnsaved(document)) return;
long documentStamp = document.getModificationStamp();
long oldFileStamp = event.getOldModificationStamp();
if (documentStamp != oldFileStamp) {
LOG.info("reload " + file.getName() + " from disk?");
LOG.info(" documentStamp:" + documentStamp);
LOG.info(" oldFileStamp:" + oldFileStamp);
if (myConflicts.isEmpty()) {
ApplicationManager.getApplication().invokeLater(this::processConflicts);
}
myConflicts.add(file);
}
}
boolean hasConflict(VirtualFile file) {
return myConflicts.contains(file);
}
private void processConflicts() {
ArrayList<VirtualFile> conflicts = new ArrayList<>(myConflicts);
myConflicts.clear();
for (VirtualFile file : conflicts) {
Document document = FileDocumentManager.getInstance().getCachedDocument(file);
if (document != null && askReloadFromDisk(file, document)) {
FileDocumentManager.getInstance().reloadFromDisk(document);
}
}
}
public boolean askReloadFromDisk(VirtualFile file, Document document) {
String message = UIBundle.message("file.cache.conflict.message.text", file.getPresentableUrl());
final DialogBuilder builder = new DialogBuilder();
builder.setCenterPanel(new JLabel(message, Messages.getQuestionIcon(), SwingConstants.CENTER));
builder.addOkAction().setText(UIBundle.message("file.cache.conflict.load.fs.changes.button"));
builder.addCancelAction().setText(UIBundle.message("file.cache.conflict.keep.memory.changes.button"));
builder.addAction(new AbstractAction(UIBundle.message("file.cache.conflict.show.difference.button")) {
@Override
public void actionPerformed(ActionEvent e) {
final ProjectEx project = (ProjectEx)ProjectLocator.getInstance().guessProjectForFile(file);
FileType fileType = file.getFileType();
String fsContent = LoadTextUtil.loadText(file).toString();
DocumentContent content1 = DiffContentFactory.getInstance().create(project, fsContent, fileType);
DocumentContent content2 = DiffContentFactory.getInstance().create(project, document, file);
String title = UIBundle.message("file.cache.conflict.for.file.dialog.title", file.getPresentableUrl());
String title1 = UIBundle.message("file.cache.conflict.diff.content.file.system.content");
String title2 = UIBundle.message("file.cache.conflict.diff.content.memory.content");
DiffRequest request = new SimpleDiffRequest(title, content1, content2, title1, title2);
request.putUserData(DiffUserDataKeys.GO_TO_SOURCE_DISABLE, true);
DialogBuilder diffBuilder = new DialogBuilder(project);
DiffRequestPanel diffPanel = DiffManager.getInstance().createRequestPanel(project, diffBuilder, diffBuilder.getWindow());
diffPanel.setRequest(request);
diffBuilder.setCenterPanel(diffPanel.getComponent());
diffBuilder.setDimensionServiceKey("FileDocumentManager.FileCacheConflict");
diffBuilder.addOkAction().setText(UIBundle.message("file.cache.conflict.save.changes.button"));
diffBuilder.addCancelAction();
diffBuilder.setTitle(title);
if (diffBuilder.show() == DialogWrapper.OK_EXIT_CODE) {
builder.getDialogWrapper().close(DialogWrapper.CANCEL_EXIT_CODE);
}
}
});
builder.setTitle(UIBundle.message("file.cache.conflict.dialog.title"));
builder.setButtonsAlignment(SwingConstants.CENTER);
builder.setHelpId("reference.dialogs.fileCacheConflict");
return builder.show() == 0;
}
}