In progress: [Issue 89] Proto buffer editor needs to have options to
remove whitespace at end of lines
Added code to figure out edited lines of an editor.
diff --git a/com.google.eclipse.protobuf.ui/META-INF/MANIFEST.MF b/com.google.eclipse.protobuf.ui/META-INF/MANIFEST.MF
index e2473fb..3ba9271 100644
--- a/com.google.eclipse.protobuf.ui/META-INF/MANIFEST.MF
+++ b/com.google.eclipse.protobuf.ui/META-INF/MANIFEST.MF
@@ -17,7 +17,8 @@
com.ibm.icu,
org.eclipse.emf.databinding,
org.eclipse.core.resources,
- org.eclipse.core.filesystem;bundle-version="1.3.100"
+ org.eclipse.core.filesystem;bundle-version="1.3.100",
+ org.eclipse.compare.core;bundle-version="3.5.200"
Import-Package: org.apache.log4j,
org.apache.commons.logging
Bundle-RequiredExecutionEnvironment: J2SE-1.5
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/Editors.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/Editors.java
new file mode 100644
index 0000000..c9f70de
--- /dev/null
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/Editors.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2011 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.protobuf.ui.util;
+
+import static com.google.eclipse.protobuf.ui.ProtobufUiModule.PLUGIN_ID;
+import static org.eclipse.compare.rangedifferencer.RangeDifferencer.findDifferences;
+import static org.eclipse.core.runtime.IStatus.ERROR;
+import static org.eclipse.core.runtime.Status.OK_STATUS;
+import static org.eclipse.core.runtime.SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK;
+
+import com.google.inject.Singleton;
+
+import org.apache.log4j.Logger;
+import org.eclipse.compare.rangedifferencer.*;
+import org.eclipse.core.filebuffers.*;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.runtime.*;
+import org.eclipse.jface.text.*;
+
+import java.util.*;
+
+/**
+ * Utility methods related to editors. Adapted from CDT's {@code org.eclipse.cdt.internal.ui.util.EditorUtility}.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+@Singleton
+public class Editors {
+
+ private static Logger logger = Logger.getLogger(Editors.class);
+
+ public static IRegion[] calculateChangedLineRegions(final ITextFileBuffer buffer, final IProgressMonitor monitor)
+ throws CoreException {
+ final IRegion[][] result = new IRegion[1][];
+ final IStatus[] errorStatus = new IStatus[] { OK_STATUS };
+ try {
+ SafeRunner.run(new ISafeRunnable() {
+ public void handleException(Throwable exception) {
+ logger.error(exception.getMessage(), exception);
+ String msg = "An error occurred while calculating the changed regions. See error log for details.";
+ errorStatus[0] = new Status(ERROR, PLUGIN_ID, 0, msg, exception);
+ result[0] = null;
+ }
+
+ public void run() throws Exception {
+ monitor.beginTask("Calculating changed regions", 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[0] = getChangedLineRegions(oldDocument, currentDocument);
+ } finally {
+ fileBufferManager.disconnectFileStore(fileStore, getSubProgressMonitor(monitor, 5));
+ monitor.done();
+ }
+ }
+
+ /*
+ * Returns regions of all lines which differ comparing {@code oldDocument}s content with
+ * {@code currentDocument}s content. Successive lines are merged into one region.
+ */
+ private IRegion[] getChangedLineRegions(IDocument oldDocument, IDocument currentDocument) {
+ RangeDifference[] differences =
+ findDifferences(new LineComparator(oldDocument), new LineComparator(currentDocument));
+ List<IRegion> regions = new ArrayList<IRegion>();
+ final int numberOfLines = currentDocument.getNumberOfLines();
+ for (RangeDifference current : differences) {
+ if (current.kind() == RangeDifference.CHANGE) {
+ int startLine = Math.min(current.rightStart(), numberOfLines - 1);
+ int endLine = current.rightEnd() - 1;
+ IRegion startLineRegion;
+ try {
+ startLineRegion = currentDocument.getLineInformation(startLine);
+ if (startLine >= endLine) {
+ // startLine > endLine indicates a deletion of one or more lines.
+ // Deletions are ignored except at the end of the document.
+ if (startLine == endLine
+ || startLineRegion.getOffset() + startLineRegion.getLength() == currentDocument.getLength()) {
+ regions.add(startLineRegion);
+ }
+ continue;
+ }
+ IRegion endLineRegion = currentDocument.getLineInformation(endLine);
+ int startOffset = startLineRegion.getOffset();
+ int endOffset = endLineRegion.getOffset() + endLineRegion.getLength();
+ regions.add(new Region(startOffset, endOffset - startOffset));
+ } catch (BadLocationException e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+ }
+ return regions.toArray(new IRegion[regions.size()]);
+ }
+ });
+ } finally {
+ if (!errorStatus[0].isOK()) throw new CoreException(errorStatus[0]);
+ }
+ return result[0];
+ }
+
+ private static IProgressMonitor getSubProgressMonitor(IProgressMonitor monitor, int ticks) {
+ if (monitor != null) return new SubProgressMonitor(monitor, ticks, PREPEND_MAIN_LABEL_TO_SUBTASK);
+ return new NullProgressMonitor();
+ }
+}
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/LineComparator.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/LineComparator.java
new file mode 100644
index 0000000..0515724
--- /dev/null
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/LineComparator.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2011 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.protobuf.ui.util;
+
+import org.apache.log4j.Logger;
+import org.eclipse.compare.rangedifferencer.IRangeComparator;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+
+/**
+ * Adapted from CDT's {@code org.eclipse.cdt.internal.ui.text.LineSeparator}.
+ *
+ * This implementation of <code>IRangeComparator</code> compares lines of a document. The lines are compared using a DJB
+ * hash function.
+ */
+class LineComparator implements IRangeComparator {
+
+ private static final long UNKNOWN_HASH = Long.MIN_VALUE;
+
+ private static Logger logger = Logger.getLogger(LineComparator.class);
+
+ private final IDocument document;
+ private final long[] hashes;
+
+ public LineComparator(IDocument document) {
+ this.document = document;
+ hashes = new long[document.getNumberOfLines()];
+ for (int i = 0; i < hashes.length; i++) {
+ hashes[i] = UNKNOWN_HASH;
+ }
+ }
+
+ public int getRangeCount() {
+ return document.getNumberOfLines();
+ }
+
+ public boolean rangesEqual(int thisIndex, IRangeComparator other, int otherIndex) {
+ try {
+ return getHash(thisIndex) == ((LineComparator) other).getHash(otherIndex);
+ } catch (BadLocationException e) {
+ logger.error(e.getMessage(), e);
+ return false;
+ }
+ }
+
+ public boolean skipRangeComparison(int length, int maxLength, IRangeComparator other) {
+ return false;
+ }
+
+ /**
+ * Returns the hash of the given line.
+ * @param line the number of the line in the document to get the hash for.
+ * @return the hash of the line.
+ * @throws BadLocationException if the line number is invalid.
+ */
+ private int getHash(int line) throws BadLocationException {
+ long hash = hashes[line];
+ if (hash == UNKNOWN_HASH) {
+ IRegion lineRegion = document.getLineInformation(line);
+ String lineContents = document.get(lineRegion.getOffset(), lineRegion.getLength());
+ hash = computeDJBHash(lineContents);
+ hashes[line] = hash;
+ }
+ return (int) hash;
+ }
+
+ /**
+ * Compute a hash using the DJB hash algorithm.
+ * @param s the string for which to compute a hash.
+ * @return the DJB hash value of the {@code String}.
+ */
+ private int computeDJBHash(String s) {
+ int hash = 5381;
+ int length = s.length();
+ for (int i = 0; i < length; i++) {
+ char ch = s.charAt(i);
+ hash = (hash << 5) + hash + ch;
+ }
+ return hash;
+ }
+}