blob: 2d41d5ab8eb7ccd8cf930be529f0015cc66bc19e [file] [log] [blame]
/*
* Copyright 2000-2013 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.diff.actions;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diff.DiffBundle;
import com.intellij.openapi.diff.impl.DiffPanelImpl;
import com.intellij.openapi.diff.impl.fragments.Fragment;
import com.intellij.openapi.diff.impl.fragments.FragmentList;
import com.intellij.openapi.diff.impl.highlighting.FragmentSide;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.ReadonlyStatusHandler;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
public class MergeOperations {
private final DiffPanelImpl myDiffPanel;
private final FragmentSide mySide;
private static final List<Operation> NO_OPERATIONS = ContainerUtil.emptyList();
private static final Condition<Fragment> NOT_EQUAL_FRAGMENT = new Condition<Fragment>() {
public boolean value(Fragment fragment) {
return fragment.getType() != null;
}
};
public MergeOperations(DiffPanelImpl diffPanel, FragmentSide side) {
myDiffPanel = diffPanel;
mySide = side;
}
@NotNull
public List<Operation> getOperations() {
Fragment fragment = getCurrentFragment();
if (fragment == null) return NO_OPERATIONS;
ArrayList<Operation> operations = new ArrayList<Operation>(3);
TextRange range = fragment.getRange(mySide);
if (range.getLength() > 0) {
if (isWritable(mySide)) operations.add(removeOperation(range, getDocument()));
TextRange otherRange = fragment.getRange(mySide.otherSide());
boolean otherIsWritable = isWritable(mySide.otherSide());
if (otherIsWritable) operations.add(insertOperation(range, otherRange.getEndOffset(), getDocument(), getOtherDocument()));
if (otherRange.getLength() > 0 && otherIsWritable) operations.add(replaceOperation(range, otherRange, getDocument(), getOtherDocument()));
}
return operations;
}
private boolean isWritable(FragmentSide side) {
Editor editor = myDiffPanel.getEditor(side);
return !editor.isViewer() && canMakeWritable(editor.getDocument());
}
private static boolean canMakeWritable(final Document document) {
if (document.isWritable()) {
return true;
}
final VirtualFile file = FileDocumentManager.getInstance().getFile(document);
if (file != null && file.isInLocalFileSystem()) {
return true;
}
return false;
}
public void selectSuggestion() {
Fragment fragment = getCurrentFragment();
if (fragment == null) return;
setSelection(fragment, mySide);
setSelection(fragment, mySide.otherSide());
}
private void setSelection(Fragment fragment, FragmentSide side) {
TextRange range = fragment.getRange(side);
if (range.getLength() > 0)
myDiffPanel.getEditor(side).getSelectionModel().setSelection(range.getStartOffset(), range.getEndOffset());
}
private static Operation replaceOperation(TextRange range, TextRange otherRange, Document document, Document otherDocument) {
return new Operation(DiffBundle.message("merge.editor.replace.operation.name"),
AllIcons.Diff.Arrow,
otherDocument,
replaceModification(range, document, otherRange, otherDocument));
}
@Nullable
public static Operation mostSensible(Document document, Document otherDocument, TextRange range, TextRange otherRange) {
if (!canMakeWritable(document) && !canMakeWritable(otherDocument)) return null;
if (range.getLength() != 0) {
if (canMakeWritable(otherDocument))
return otherRange.getLength() != 0 ?
replaceOperation(range, otherRange, document, otherDocument) :
insertOperation(range, otherRange.getEndOffset(), document, otherDocument);
else return otherRange.getLength() == 0 ? removeOperation(range, document) : null;
}
return null;
}
private static Runnable replaceModification(TextRange range, Document document,
final TextRange otherRange, final Document otherDocument) {
final String replacement = getSubstring(document, range);
return new Runnable() {
public void run() {
otherDocument.replaceString(otherRange.getStartOffset(), otherRange.getEndOffset(), replacement);
}
};
}
private static Operation insertOperation(TextRange range, int offset, Document document, Document otherDocument) {
return new Operation(DiffBundle.message("merge.editor.insert.operation.name"),
AllIcons.Diff.Arrow,
otherDocument,
insertModification(range, document, offset, otherDocument));
}
private static Runnable insertModification(TextRange range, Document document,
final int offset, final Document otherDocument) {
final String insertion = getSubstring(document, range);
return new Runnable(){
public void run() {
otherDocument.insertString(offset, insertion);
}
};
}
private static String getSubstring(Document document, TextRange range) {
return document.getText(range);
}
private Document getOtherDocument() {
return myDiffPanel.getEditor(mySide.otherSide()).getDocument();
}
private static Operation removeOperation(TextRange range, Document document) {
return new Operation(DiffBundle.message("merge.editor.remove.operation.name"),
AllIcons.Diff.Remove,
document,
removeModification(range, document));
}
private static Runnable removeModification(final TextRange range, final Document document) {
return new Runnable(){
public void run() {
document.deleteString(range.getStartOffset(), range.getEndOffset());
}
};
}
private Document getDocument() {
return myDiffPanel.getEditor(mySide).getDocument();
}
public Fragment getCurrentFragment() {
FragmentList fragments = myDiffPanel.getFragments();
int caretPosition = myDiffPanel.getEditor(mySide).getCaretModel().getOffset();
return fragments.getFragmentAt(caretPosition, mySide, NOT_EQUAL_FRAGMENT);
}
public static class Operation {
private final String myName;
private final Document myDocument;
private final Runnable myModification;
private final Icon myGutterIcon;
private boolean myPerformed = false;
public Operation(String name, Icon icon, final Document document, Runnable modification) {
myName = name;
myGutterIcon = icon;
myDocument = document;
myModification = modification;
}
public Icon getGutterIcon() {
return myGutterIcon;
}
public String getName() {
return myName;
}
public void perform(final Project project) {
if (myPerformed) return;
myPerformed = true;
if (!myDocument.isWritable()) {
final VirtualFile file = FileDocumentManager.getInstance().getFile(myDocument);
final ReadonlyStatusHandler.OperationStatus status = ReadonlyStatusHandler.getInstance(project).ensureFilesWritable(file);
if (status.hasReadonlyFiles()) return;
}
ApplicationManager.getApplication().runWriteAction(new Runnable(){
public void run() {
CommandProcessor.getInstance().executeCommand(project, myModification, getName(), null);
}
});
}
}
}