Highlight line-level (aka word) differences in files
We now highlight any changed words within a line replace edit, making the actual changes stand out against the surrounding context that makes up the line. The highlight is computed by constructing a string that covers the entire replaced region and then running the Myers diff algorithm over the individual characters of those two regions. To avoid tiny edits interleaved at every other character in a sentance we combine two neighboring character edits together if there are only 1 or 2 characters between them. There are probably many ways to improve on this algorithm to avoid some nasty corner display cases, but this rule is good enough for now. The highlight data is computed and stored as part of the diff cache, which requires a schema change in this commit. So existing diff cache records will be flushed on the next server start, and they will be recomputed on demand. Bug: issue 169 Change-Id: I69142ebef600e8c3c65821272dad3ee04a497654 Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
@@ -17,5 +17,6 @@
|
||||
<source path='diff' includes='
|
||||
Edit.java
|
||||
Edit_JsonSerializer.java
|
||||
ReplaceEdit.java
|
||||
'/>
|
||||
</module>
|
||||
|
@@ -25,6 +25,8 @@ import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class EditDeserializer implements JsonDeserializer<Edit>,
|
||||
JsonSerializer<Edit> {
|
||||
@@ -34,14 +36,28 @@ public class EditDeserializer implements JsonDeserializer<Edit>,
|
||||
return null;
|
||||
}
|
||||
if (!json.isJsonArray()) {
|
||||
throw new JsonParseException("Expected array of 4for Edit type");
|
||||
throw new JsonParseException("Expected array for Edit type");
|
||||
}
|
||||
|
||||
final JsonArray a = (JsonArray) json;
|
||||
if (a.size() != 4) {
|
||||
final JsonArray o = (JsonArray) json;
|
||||
final int cnt = o.size();
|
||||
if (cnt < 4 || cnt % 4 != 0) {
|
||||
throw new JsonParseException("Expected array of 4 for Edit type");
|
||||
}
|
||||
return new Edit(get(a, 0), get(a, 1), get(a, 2), get(a, 3));
|
||||
|
||||
if (4 == cnt) {
|
||||
return new Edit(get(o, 0), get(o, 1), get(o, 2), get(o, 3));
|
||||
}
|
||||
|
||||
List<Edit> l = new ArrayList<Edit>((cnt / 4) - 1);
|
||||
for (int i = 4; i < cnt;) {
|
||||
int as = get(o, i++);
|
||||
int ae = get(o, i++);
|
||||
int bs = get(o, i++);
|
||||
int be = get(o, i++);
|
||||
l.add(new Edit(as, ae, bs, be));
|
||||
}
|
||||
return new ReplaceEdit(get(o, 0), get(o, 1), get(o, 2), get(o, 3), l);
|
||||
}
|
||||
|
||||
private static int get(final JsonArray a, final int idx)
|
||||
@@ -63,10 +79,19 @@ public class EditDeserializer implements JsonDeserializer<Edit>,
|
||||
return new JsonNull();
|
||||
}
|
||||
final JsonArray a = new JsonArray();
|
||||
add(a, src);
|
||||
if (src instanceof ReplaceEdit) {
|
||||
for (Edit e : ((ReplaceEdit) src).getInternalEdits()) {
|
||||
add(a, e);
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
private void add(final JsonArray a, final Edit src) {
|
||||
a.add(new JsonPrimitive(src.getBeginA()));
|
||||
a.add(new JsonPrimitive(src.getEndA()));
|
||||
a.add(new JsonPrimitive(src.getBeginB()));
|
||||
a.add(new JsonPrimitive(src.getEndB()));
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
@@ -17,6 +17,9 @@ package org.eclipse.jgit.diff;
|
||||
import com.google.gwt.core.client.JavaScriptObject;
|
||||
import com.google.gwtjsonrpc.client.impl.JsonSerializer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Edit_JsonSerializer extends JsonSerializer<Edit> {
|
||||
public static final Edit_JsonSerializer INSTANCE = new Edit_JsonSerializer();
|
||||
|
||||
@@ -25,13 +28,38 @@ public class Edit_JsonSerializer extends JsonSerializer<Edit> {
|
||||
if (jso == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final JavaScriptObject o = (JavaScriptObject) jso;
|
||||
return new Edit(get(o, 0), get(o, 1), get(o, 2), get(o, 3));
|
||||
final int cnt = length(o);
|
||||
if (4 == cnt) {
|
||||
return new Edit(get(o, 0), get(o, 1), get(o, 2), get(o, 3));
|
||||
}
|
||||
|
||||
List<Edit> l = new ArrayList<Edit>((cnt / 4) - 1);
|
||||
for (int i = 4; i < cnt;) {
|
||||
int as = get(o, i++);
|
||||
int ae = get(o, i++);
|
||||
int bs = get(o, i++);
|
||||
int be = get(o, i++);
|
||||
l.add(new Edit(as, ae, bs, be));
|
||||
}
|
||||
return new ReplaceEdit(get(o, 0), get(o, 1), get(o, 2), get(o, 3), l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printJson(final StringBuilder sb, final Edit o) {
|
||||
sb.append('[');
|
||||
append(sb, o);
|
||||
if (o instanceof ReplaceEdit) {
|
||||
for (Edit e : ((ReplaceEdit) o).getInternalEdits()) {
|
||||
sb.append(',');
|
||||
append(sb, e);
|
||||
}
|
||||
}
|
||||
sb.append(']');
|
||||
}
|
||||
|
||||
private void append(final StringBuilder sb, final Edit o) {
|
||||
sb.append(o.getBeginA());
|
||||
sb.append(',');
|
||||
sb.append(o.getEndA());
|
||||
@@ -39,9 +67,11 @@ public class Edit_JsonSerializer extends JsonSerializer<Edit> {
|
||||
sb.append(o.getBeginB());
|
||||
sb.append(',');
|
||||
sb.append(o.getEndB());
|
||||
sb.append(']');
|
||||
}
|
||||
|
||||
private static native int length(JavaScriptObject jso)
|
||||
/*-{ return jso.length; }-*/;
|
||||
|
||||
private static native int get(JavaScriptObject jso, int idx)
|
||||
/*-{ return jso[idx]; }-*/;
|
||||
}
|
||||
|
@@ -0,0 +1,35 @@
|
||||
// Copyright (C) 2010 The Android Open Source Project
|
||||
//
|
||||
// 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 org.eclipse.jgit.diff;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ReplaceEdit extends Edit {
|
||||
private List<Edit> internalEdit;
|
||||
|
||||
public ReplaceEdit(int as, int ae, int bs, int be, List<Edit> internal) {
|
||||
super(as, ae, bs, be);
|
||||
internalEdit = internal;
|
||||
}
|
||||
|
||||
public ReplaceEdit(Edit orig, List<Edit> internal) {
|
||||
super(orig.getBeginA(), orig.getEndA(), orig.getBeginB(), orig.getEndB());
|
||||
internalEdit = internal;
|
||||
}
|
||||
|
||||
public List<Edit> getInternalEdits() {
|
||||
return internalEdit;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user