/**
* Copyright (c) 2005-2012 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package org.python.pydev.changed_lines;
import java.util.ArrayList;
import org.eclipse.compare.rangedifferencer.IRangeComparator;
import org.eclipse.compare.rangedifferencer.RangeDifference;
import org.eclipse.compare.rangedifferencer.RangeDifferencer;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.core.filebuffers.ITextFileBufferManager;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.python.pydev.core.log.Log;
/**
* Based on org.eclipse.jdt.internal.ui.javaeditor.EditorUtility.calculateChangedLineRegions
*/
public class ChangedLinesComputer {
/**
* Return the lines which have changed in the given buffer since the last save occurred.
*
* @param buffer the buffer to compare contents from
* @param monitor to report progress to
* @return the regions of the changed lines or null if something went wrong.
*/
public static int[] calculateChangedLines(final ITextFileBuffer buffer, final IProgressMonitor monitor)
throws CoreException {
int[] result = null;
try {
monitor.beginTask("Calculating changed lines", 20);
IFileStore fileStore = buffer.getFileStore();
ITextFileBufferManager fileBufferManager = FileBuffers.createTextFileBufferManager();
fileBufferManager.connectFileStore(fileStore, getSubProgressMonitor(monitor, 15));
try {
IDocument currentDocument = buffer.getDocument();
IDocument oldDocument = ((ITextFileBuffer) fileBufferManager.getFileStoreFileBuffer(fileStore))
.getDocument();
result = getChangedLines(oldDocument, currentDocument);
} finally {
fileBufferManager.disconnectFileStore(fileStore, getSubProgressMonitor(monitor, 5));
monitor.done();
}
} catch (Exception e) {
Log.log(e);
return null;
}
return result;
}
/**
* Return all the changed lines.
*
* @param oldDocument a document containing the old content
* @param currentDocument a document containing the current content
* @return the changed regions
* @throws BadLocationException if fetching the line information fails
*/
public static int[] getChangedLines(IDocument oldDocument, IDocument currentDocument) throws BadLocationException {
/*
* Do not change the type of those local variables. We use Object
* here in order to prevent loading of the Compare plug-in at load
* time of this class.
*/
Object leftSide = new LineComparator(oldDocument);
Object rightSide = new LineComparator(currentDocument);
RangeDifference[] differences = RangeDifferencer.findDifferences((IRangeComparator) leftSide,
(IRangeComparator) rightSide);
//It holds that:
//1. Ranges are sorted:
// forAll r1,r2 element differences: indexOf(r1)<indexOf(r2) -> r1.rightStart()<r2.rightStart();
//2. Successive changed lines are merged into on RangeDifference
// forAll r1,r2 element differences: r1.rightStart()<r2.rightStart() -> r1.rightEnd()<r2.rightStart
ArrayList<Integer> regions = new ArrayList<Integer>();
for (int i = 0; i < differences.length; i++) {
RangeDifference curr = differences[i];
if (curr.kind() == RangeDifference.CHANGE && curr.rightLength() > 0) {
int startLine = curr.rightStart();
int endLine = curr.rightEnd() - 1;
if (startLine == endLine) {
regions.add(startLine);
} else {
for (int iLine = startLine; iLine <= endLine; iLine++) {
regions.add(iLine);
}
}
}
}
int size = regions.size();
int[] ret = new int[size];
for (int i = 0; i < size; i++) {
ret[i] = regions.get(i);
}
return ret;
}
/**
* Creates and returns a new sub-progress monitor for the
* given parent monitor.
*
* @param monitor the parent progress monitor
* @param ticks the number of work ticks allocated from the parent monitor
* @return the new sub-progress monitor
* @since 3.4
*/
private static IProgressMonitor getSubProgressMonitor(IProgressMonitor monitor, int ticks) {
if (monitor != null) {
return new SubProgressMonitor(monitor, ticks, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
}
return new NullProgressMonitor();
}
}