| /* |
| * 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); |
| } |
| }); |
| } |
| } |
| } |