SideBySide2: Refactor coloring logic to new top level class
Break out the code used to color modified regions, moving it all to a new top level class in the package. This class unfortunately also has to keep track of the LineMapper and the padding widgets. Change-Id: Id72d36cbf1d3bd3fe9df220521901ec50b4c5ddb
This commit is contained in:
@@ -0,0 +1,344 @@
|
|||||||
|
// Copyright (C) 2013 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 com.google.gerrit.client.diff;
|
||||||
|
|
||||||
|
import com.google.gerrit.client.diff.DiffInfo.Region;
|
||||||
|
import com.google.gerrit.client.diff.DiffInfo.Span;
|
||||||
|
import com.google.gerrit.client.diff.PaddingManager.LinePaddingWidgetWrapper;
|
||||||
|
import com.google.gerrit.client.rpc.Natives;
|
||||||
|
import com.google.gwt.core.client.JsArray;
|
||||||
|
import com.google.gwt.core.client.JsArrayString;
|
||||||
|
import com.google.gwt.dom.client.Element;
|
||||||
|
import com.google.gwt.dom.client.Style.Unit;
|
||||||
|
|
||||||
|
import net.codemirror.lib.CodeMirror;
|
||||||
|
import net.codemirror.lib.CodeMirror.LineClassWhere;
|
||||||
|
import net.codemirror.lib.CodeMirror.LineHandle;
|
||||||
|
import net.codemirror.lib.Configuration;
|
||||||
|
import net.codemirror.lib.LineCharacter;
|
||||||
|
import net.codemirror.lib.TextMarker;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/** Colors modified regions for {@link SideBySide2}. */
|
||||||
|
class ChunkManager {
|
||||||
|
private final SideBySide2 host;
|
||||||
|
private final CodeMirror cmA;
|
||||||
|
private final CodeMirror cmB;
|
||||||
|
private final SidePanel sidePanel;
|
||||||
|
private final LineMapper mapper;
|
||||||
|
|
||||||
|
private List<DiffChunkInfo> chunks;
|
||||||
|
private List<TextMarker> markers;
|
||||||
|
private List<Runnable> undo;
|
||||||
|
private Map<LineHandle, LinePaddingWidgetWrapper> paddingOnOtherSide;
|
||||||
|
|
||||||
|
ChunkManager(SideBySide2 host,
|
||||||
|
CodeMirror cmA,
|
||||||
|
CodeMirror cmB,
|
||||||
|
SidePanel sidePanel) {
|
||||||
|
this.host = host;
|
||||||
|
this.cmA = cmA;
|
||||||
|
this.cmB = cmB;
|
||||||
|
this.sidePanel = sidePanel;
|
||||||
|
this.mapper = new LineMapper();
|
||||||
|
}
|
||||||
|
|
||||||
|
LineMapper getLineMapper() {
|
||||||
|
return mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
DiffChunkInfo getFirst() {
|
||||||
|
return chunks.isEmpty() ? null : chunks.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
mapper.reset();
|
||||||
|
for (TextMarker m : markers) {
|
||||||
|
m.clear();
|
||||||
|
}
|
||||||
|
for (Runnable r : undo) {
|
||||||
|
r.run();
|
||||||
|
}
|
||||||
|
for (LinePaddingWidgetWrapper x : paddingOnOtherSide.values()) {
|
||||||
|
x.getWidget().clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void render(DiffInfo diff) {
|
||||||
|
chunks = new ArrayList<DiffChunkInfo>();
|
||||||
|
markers = new ArrayList<TextMarker>();
|
||||||
|
undo = new ArrayList<Runnable>();
|
||||||
|
paddingOnOtherSide = new HashMap<LineHandle, LinePaddingWidgetWrapper>();
|
||||||
|
|
||||||
|
String diffColor = diff.meta_a() == null || diff.meta_b() == null
|
||||||
|
? DiffTable.style.intralineBg()
|
||||||
|
: DiffTable.style.diff();
|
||||||
|
|
||||||
|
for (Region current : Natives.asList(diff.content())) {
|
||||||
|
if (current.ab() != null) {
|
||||||
|
mapper.appendCommon(current.ab().length());
|
||||||
|
} else {
|
||||||
|
render(current, diffColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void render(Region region, String diffColor) {
|
||||||
|
int startA = mapper.getLineA();
|
||||||
|
int startB = mapper.getLineB();
|
||||||
|
|
||||||
|
JsArrayString a = region.a();
|
||||||
|
JsArrayString b = region.b();
|
||||||
|
int aLen = a != null ? a.length() : 0;
|
||||||
|
int bLen = b != null ? b.length() : 0;
|
||||||
|
|
||||||
|
String color = a == null || b == null
|
||||||
|
? diffColor
|
||||||
|
: DiffTable.style.intralineBg();
|
||||||
|
|
||||||
|
colorLines(cmA, color, startA, aLen);
|
||||||
|
colorLines(cmB, color, startB, bLen);
|
||||||
|
markEdit(cmA, startA, a, region.edit_a());
|
||||||
|
markEdit(cmB, startB, b, region.edit_b());
|
||||||
|
addGutterTag(region, startA, startB);
|
||||||
|
mapper.appendReplace(aLen, bLen);
|
||||||
|
|
||||||
|
int endA = mapper.getLineA() - 1;
|
||||||
|
int endB = mapper.getLineB() - 1;
|
||||||
|
if (aLen > 0) {
|
||||||
|
addDiffChunkAndPadding(cmB, endB, endA, aLen, bLen > 0);
|
||||||
|
}
|
||||||
|
if (bLen > 0) {
|
||||||
|
addDiffChunkAndPadding(cmA, endA, endB, bLen, aLen > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addGutterTag(Region region, int startA, int startB) {
|
||||||
|
if (region.a() == null) {
|
||||||
|
sidePanel.addGutter(cmB, startB, SidePanel.GutterType.INSERT);
|
||||||
|
} else if (region.b() == null) {
|
||||||
|
sidePanel.addGutter(cmA, startA, SidePanel.GutterType.DELETE);
|
||||||
|
} else {
|
||||||
|
sidePanel.addGutter(cmB, startB, SidePanel.GutterType.EDIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markEdit(CodeMirror cm, int startLine,
|
||||||
|
JsArrayString lines, JsArray<Span> edits) {
|
||||||
|
if (lines == null || edits == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditIterator iter = new EditIterator(lines, startLine);
|
||||||
|
Configuration bg = Configuration.create()
|
||||||
|
.set("className", DiffTable.style.intralineBg())
|
||||||
|
.set("readOnly", true);
|
||||||
|
|
||||||
|
Configuration diff = Configuration.create()
|
||||||
|
.set("className", DiffTable.style.diff())
|
||||||
|
.set("readOnly", true);
|
||||||
|
|
||||||
|
LineCharacter last = CodeMirror.pos(0, 0);
|
||||||
|
for (Span span : Natives.asList(edits)) {
|
||||||
|
LineCharacter from = iter.advance(span.skip());
|
||||||
|
LineCharacter to = iter.advance(span.mark());
|
||||||
|
if (from.getLine() == last.getLine()) {
|
||||||
|
markers.add(cm.markText(last, from, bg));
|
||||||
|
} else {
|
||||||
|
markers.add(cm.markText(CodeMirror.pos(from.getLine(), 0), from, bg));
|
||||||
|
}
|
||||||
|
markers.add(cm.markText(from, to, diff));
|
||||||
|
last = to;
|
||||||
|
colorLines(cm, LineClassWhere.BACKGROUND,
|
||||||
|
DiffTable.style.diff(),
|
||||||
|
from.getLine(), to.getLine());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void colorLines(CodeMirror cm, String color, int line, int cnt) {
|
||||||
|
colorLines(cm, LineClassWhere.WRAP, color, line, line + cnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void colorLines(final CodeMirror cm, final LineClassWhere where,
|
||||||
|
final String className, final int start, final int end) {
|
||||||
|
if (start < end) {
|
||||||
|
for (int line = start; line < end; line++) {
|
||||||
|
cm.addLineClass(line, where, className);
|
||||||
|
}
|
||||||
|
undo.add(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
for (int line = start; line < end; line++) {
|
||||||
|
cm.removeLineClass(line, where, className);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addDiffChunkAndPadding(CodeMirror cmToPad, int lineToPad,
|
||||||
|
int lineOnOther, int chunkSize, boolean edit) {
|
||||||
|
CodeMirror otherCm = host.otherCm(cmToPad);
|
||||||
|
paddingOnOtherSide.put(otherCm.getLineHandle(lineOnOther),
|
||||||
|
new LinePaddingWidgetWrapper(host.addPaddingWidget(cmToPad,
|
||||||
|
lineToPad, 0, Unit.EM, null), lineToPad, chunkSize));
|
||||||
|
chunks.add(new DiffChunkInfo(otherCm.side(),
|
||||||
|
lineOnOther - chunkSize + 1, lineOnOther, edit));
|
||||||
|
}
|
||||||
|
|
||||||
|
Runnable diffChunkNav(final CodeMirror cm, final Direction dir) {
|
||||||
|
return new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
int line = cm.hasActiveLine() ? cm.getLineNumber(cm.getActiveLine()) : 0;
|
||||||
|
int res = Collections.binarySearch(
|
||||||
|
chunks,
|
||||||
|
new DiffChunkInfo(cm.side(), line, 0, false),
|
||||||
|
getDiffChunkComparator());
|
||||||
|
if (res < 0) {
|
||||||
|
res = -res - (dir == Direction.PREV ? 1 : 2);
|
||||||
|
}
|
||||||
|
res = res + (dir == Direction.PREV ? -1 : 1);
|
||||||
|
if (res < 0 || chunks.size() <= res) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DiffChunkInfo lookUp = chunks.get(res);
|
||||||
|
// If edit, skip the deletion chunk and set focus on the insertion one.
|
||||||
|
if (lookUp.isEdit() && lookUp.getSide() == DisplaySide.A) {
|
||||||
|
res = res + (dir == Direction.PREV ? -1 : 1);
|
||||||
|
if (res < 0 || chunks.size() <= res) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DiffChunkInfo target = chunks.get(res);
|
||||||
|
CodeMirror targetCm = host.getCmFromSide(target.getSide());
|
||||||
|
targetCm.setCursor(LineCharacter.create(target.getStart()));
|
||||||
|
targetCm.focus();
|
||||||
|
targetCm.scrollToY(
|
||||||
|
targetCm.heightAtLine(target.getStart(), "local") -
|
||||||
|
0.5 * cmB.getScrollbarV().getClientHeight());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Comparator<DiffChunkInfo> getDiffChunkComparator() {
|
||||||
|
// Chunks are ordered by their starting line. If it's a deletion,
|
||||||
|
// use its corresponding line on the revision side for comparison.
|
||||||
|
// In the edit case, put the deletion chunk right before the
|
||||||
|
// insertion chunk. This placement guarantees well-ordering.
|
||||||
|
return new Comparator<DiffChunkInfo>() {
|
||||||
|
@Override
|
||||||
|
public int compare(DiffChunkInfo a, DiffChunkInfo b) {
|
||||||
|
if (a.getSide() == b.getSide()) {
|
||||||
|
return a.getStart() - b.getStart();
|
||||||
|
} else if (a.getSide() == DisplaySide.A) {
|
||||||
|
int comp = mapper.lineOnOther(a.getSide(), a.getStart())
|
||||||
|
.getLine() - b.getStart();
|
||||||
|
return comp == 0 ? -1 : comp;
|
||||||
|
} else {
|
||||||
|
int comp = a.getStart() -
|
||||||
|
mapper.lineOnOther(b.getSide(), b.getStart()).getLine();
|
||||||
|
return comp == 0 ? 1 : comp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
DiffChunkInfo getDiffChunk(DisplaySide side, int line) {
|
||||||
|
int res = Collections.binarySearch(
|
||||||
|
chunks,
|
||||||
|
new DiffChunkInfo(side, line, 0, false), // Dummy DiffChunkInfo
|
||||||
|
getDiffChunkComparator());
|
||||||
|
if (res >= 0) {
|
||||||
|
return chunks.get(res);
|
||||||
|
} else { // The line might be within a DiffChunk
|
||||||
|
res = -res - 1;
|
||||||
|
if (res > 0) {
|
||||||
|
DiffChunkInfo info = chunks.get(res - 1);
|
||||||
|
if (info.getSide() == side && info.getStart() <= line &&
|
||||||
|
line <= info.getEnd()) {
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void resizePadding(final CodeMirror cm,
|
||||||
|
final LineHandle line,
|
||||||
|
final DisplaySide side) {
|
||||||
|
if (paddingOnOtherSide.containsKey(line)) {
|
||||||
|
host.defer(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
resizePaddingOnOtherSide(side, cm.getLineNumber(line));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void resizePaddingOnOtherSide(DisplaySide mySide, int line) {
|
||||||
|
CodeMirror cm = host.getCmFromSide(mySide);
|
||||||
|
LineHandle handle = cm.getLineHandle(line);
|
||||||
|
final LinePaddingWidgetWrapper otherWrapper = paddingOnOtherSide.get(handle);
|
||||||
|
double myChunkHeight = cm.heightAtLine(line + 1) -
|
||||||
|
cm.heightAtLine(line - otherWrapper.getChunkLength() + 1);
|
||||||
|
Element otherPadding = otherWrapper.getElement();
|
||||||
|
int otherPaddingHeight = otherPadding.getOffsetHeight();
|
||||||
|
CodeMirror otherCm = host.otherCm(cm);
|
||||||
|
int otherLine = otherWrapper.getOtherLine();
|
||||||
|
LineHandle other = otherCm.getLineHandle(otherLine);
|
||||||
|
if (paddingOnOtherSide.containsKey(other)) {
|
||||||
|
LinePaddingWidgetWrapper myWrapper = paddingOnOtherSide.get(other);
|
||||||
|
Element myPadding = paddingOnOtherSide.get(other).getElement();
|
||||||
|
int myPaddingHeight = myPadding.getOffsetHeight();
|
||||||
|
myChunkHeight -= myPaddingHeight;
|
||||||
|
double otherChunkHeight = otherCm.heightAtLine(otherLine + 1) -
|
||||||
|
otherCm.heightAtLine(otherLine - myWrapper.getChunkLength() + 1) -
|
||||||
|
otherPaddingHeight;
|
||||||
|
double delta = myChunkHeight - otherChunkHeight;
|
||||||
|
if (delta > 0) {
|
||||||
|
if (myPaddingHeight != 0) {
|
||||||
|
myPadding.getStyle().setHeight((double) 0, Unit.PX);
|
||||||
|
myWrapper.getWidget().changed();
|
||||||
|
}
|
||||||
|
if (otherPaddingHeight != delta) {
|
||||||
|
otherPadding.getStyle().setHeight(delta, Unit.PX);
|
||||||
|
otherWrapper.getWidget().changed();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (myPaddingHeight != -delta) {
|
||||||
|
myPadding.getStyle().setHeight(-delta, Unit.PX);
|
||||||
|
myWrapper.getWidget().changed();
|
||||||
|
}
|
||||||
|
if (otherPaddingHeight != 0) {
|
||||||
|
otherPadding.getStyle().setHeight((double) 0, Unit.PX);
|
||||||
|
otherWrapper.getWidget().changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (otherPaddingHeight != myChunkHeight) {
|
||||||
|
otherPadding.getStyle().setHeight(myChunkHeight, Unit.PX);
|
||||||
|
otherWrapper.getWidget().changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -85,9 +85,8 @@ abstract class CommentBox extends Composite {
|
|||||||
assert selfWidgetWrapper != null;
|
assert selfWidgetWrapper != null;
|
||||||
selfWidgetWrapper.getWidget().changed();
|
selfWidgetWrapper.getWidget().changed();
|
||||||
if (diffChunkInfo != null) {
|
if (diffChunkInfo != null) {
|
||||||
commentManager.getSideBySide2().resizePaddingOnOtherSide(
|
commentManager.getSideBySide2().getChunkManager()
|
||||||
cm.side(),
|
.resizePaddingOnOtherSide(cm.side(), diffChunkInfo.getEnd());
|
||||||
diffChunkInfo.getEnd());
|
|
||||||
} else {
|
} else {
|
||||||
assert widgetManager != null;
|
assert widgetManager != null;
|
||||||
widgetManager.resizePaddingWidget();
|
widgetManager.resizePaddingWidget();
|
||||||
|
|||||||
@@ -337,8 +337,9 @@ class CommentManager {
|
|||||||
|
|
||||||
int lineToPad = host.lineOnOther(cm.side(), line).getLine();
|
int lineToPad = host.lineOnOther(cm.side(), line).getLine();
|
||||||
LineHandle otherHandle = other.getLineHandle(lineToPad);
|
LineHandle otherHandle = other.getLineHandle(lineToPad);
|
||||||
DiffChunkInfo myChunk = host.getDiffChunk(cm.side(), line);
|
ChunkManager chunkMgr = host.getChunkManager();
|
||||||
DiffChunkInfo otherChunk = host.getDiffChunk(other.side(), lineToPad);
|
DiffChunkInfo myChunk = chunkMgr.getDiffChunk(cm.side(), line);
|
||||||
|
DiffChunkInfo otherChunk = chunkMgr.getDiffChunk(other.side(), lineToPad);
|
||||||
PaddingManager otherManager;
|
PaddingManager otherManager;
|
||||||
if (linePaddingManager.containsKey(otherHandle)) {
|
if (linePaddingManager.containsKey(otherHandle)) {
|
||||||
otherManager = linePaddingManager.get(otherHandle);
|
otherManager = linePaddingManager.get(otherHandle);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import com.google.gerrit.client.changes.ChangeInfo;
|
|||||||
import com.google.gerrit.client.changes.ChangeInfo.RevisionInfo;
|
import com.google.gerrit.client.changes.ChangeInfo.RevisionInfo;
|
||||||
import com.google.gerrit.client.changes.ReviewInfo;
|
import com.google.gerrit.client.changes.ReviewInfo;
|
||||||
import com.google.gerrit.client.changes.Util;
|
import com.google.gerrit.client.changes.Util;
|
||||||
|
import com.google.gerrit.client.diff.DiffInfo.Region;
|
||||||
import com.google.gerrit.client.patches.PatchUtil;
|
import com.google.gerrit.client.patches.PatchUtil;
|
||||||
import com.google.gerrit.client.rpc.CallbackGroup;
|
import com.google.gerrit.client.rpc.CallbackGroup;
|
||||||
import com.google.gerrit.client.rpc.GerritCallback;
|
import com.google.gerrit.client.rpc.GerritCallback;
|
||||||
@@ -313,7 +314,10 @@ class Header extends Composite {
|
|||||||
return nextPath;
|
return nextPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setNoDiff(boolean visible) {
|
void setNoDiff(DiffInfo diff) {
|
||||||
UIObject.setVisible(noDiff, visible);
|
JsArray<Region> regions = diff.content();
|
||||||
|
boolean b = regions.length() == 0
|
||||||
|
|| (regions.length() == 1 && regions.get(0).ab() != null);
|
||||||
|
UIObject.setVisible(noDiff, b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,12 @@ class LineMapper {
|
|||||||
private List<LineGap> lineMapBtoA;
|
private List<LineGap> lineMapBtoA;
|
||||||
|
|
||||||
LineMapper() {
|
LineMapper() {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
lineA = 0;
|
||||||
|
lineB = 0;
|
||||||
lineMapAtoB = new ArrayList<LineGap>();
|
lineMapAtoB = new ArrayList<LineGap>();
|
||||||
lineMapBtoA = new ArrayList<LineGap>();
|
lineMapBtoA = new ArrayList<LineGap>();
|
||||||
}
|
}
|
||||||
@@ -43,6 +49,17 @@ class LineMapper {
|
|||||||
lineB += numLines;
|
lineB += numLines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void appendReplace(int aLen, int bLen) {
|
||||||
|
appendCommon(Math.min(aLen, bLen));
|
||||||
|
if (aLen < bLen) { // Edit with insertion
|
||||||
|
int insertCnt = bLen - aLen;
|
||||||
|
appendInsert(insertCnt);
|
||||||
|
} else if (aLen > bLen) { // Edit with deletion
|
||||||
|
int deleteCnt = aLen - bLen;
|
||||||
|
appendDelete(deleteCnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void appendInsert(int numLines) {
|
void appendInsert(int numLines) {
|
||||||
int origLineB = lineB;
|
int origLineB = lineB;
|
||||||
lineB += numLines;
|
lineB += numLines;
|
||||||
|
|||||||
@@ -21,10 +21,7 @@ import com.google.gerrit.client.changes.ChangeApi;
|
|||||||
import com.google.gerrit.client.changes.ChangeInfo;
|
import com.google.gerrit.client.changes.ChangeInfo;
|
||||||
import com.google.gerrit.client.changes.ChangeInfo.RevisionInfo;
|
import com.google.gerrit.client.changes.ChangeInfo.RevisionInfo;
|
||||||
import com.google.gerrit.client.changes.ChangeList;
|
import com.google.gerrit.client.changes.ChangeList;
|
||||||
import com.google.gerrit.client.diff.DiffInfo.Region;
|
|
||||||
import com.google.gerrit.client.diff.DiffInfo.Span;
|
|
||||||
import com.google.gerrit.client.diff.LineMapper.LineOnOtherInfo;
|
import com.google.gerrit.client.diff.LineMapper.LineOnOtherInfo;
|
||||||
import com.google.gerrit.client.diff.PaddingManager.LinePaddingWidgetWrapper;
|
|
||||||
import com.google.gerrit.client.diff.PaddingManager.PaddingWidgetWrapper;
|
import com.google.gerrit.client.diff.PaddingManager.PaddingWidgetWrapper;
|
||||||
import com.google.gerrit.client.patches.PatchUtil;
|
import com.google.gerrit.client.patches.PatchUtil;
|
||||||
import com.google.gerrit.client.projects.ConfigInfoCache;
|
import com.google.gerrit.client.projects.ConfigInfoCache;
|
||||||
@@ -38,9 +35,7 @@ import com.google.gerrit.common.changes.ListChangesOption;
|
|||||||
import com.google.gerrit.reviewdb.client.Change;
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
import com.google.gwt.core.client.GWT;
|
import com.google.gwt.core.client.GWT;
|
||||||
import com.google.gwt.core.client.JavaScriptObject;
|
|
||||||
import com.google.gwt.core.client.JsArray;
|
import com.google.gwt.core.client.JsArray;
|
||||||
import com.google.gwt.core.client.JsArrayString;
|
|
||||||
import com.google.gwt.core.client.Scheduler;
|
import com.google.gwt.core.client.Scheduler;
|
||||||
import com.google.gwt.core.client.Scheduler.RepeatingCommand;
|
import com.google.gwt.core.client.Scheduler.RepeatingCommand;
|
||||||
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
|
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
|
||||||
@@ -81,23 +76,15 @@ import net.codemirror.lib.LineCharacter;
|
|||||||
import net.codemirror.lib.LineWidget;
|
import net.codemirror.lib.LineWidget;
|
||||||
import net.codemirror.lib.ModeInjector;
|
import net.codemirror.lib.ModeInjector;
|
||||||
import net.codemirror.lib.Rect;
|
import net.codemirror.lib.Rect;
|
||||||
import net.codemirror.lib.TextMarker;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class SideBySide2 extends Screen {
|
public class SideBySide2 extends Screen {
|
||||||
interface Binder extends UiBinder<FlowPanel, SideBySide2> {}
|
interface Binder extends UiBinder<FlowPanel, SideBySide2> {}
|
||||||
private static final Binder uiBinder = GWT.create(Binder.class);
|
private static final Binder uiBinder = GWT.create(Binder.class);
|
||||||
|
|
||||||
private static final JsArrayString EMPTY =
|
|
||||||
JavaScriptObject.createArray().cast();
|
|
||||||
|
|
||||||
@UiField(provided = true)
|
@UiField(provided = true)
|
||||||
Header header;
|
Header header;
|
||||||
|
|
||||||
@@ -108,8 +95,8 @@ public class SideBySide2 extends Screen {
|
|||||||
private final PatchSet.Id base;
|
private final PatchSet.Id base;
|
||||||
private final PatchSet.Id revision;
|
private final PatchSet.Id revision;
|
||||||
private final String path;
|
private final String path;
|
||||||
private final DisplaySide startSide;
|
private DisplaySide startSide;
|
||||||
private final int startLine;
|
private int startLine;
|
||||||
private DiffPreferences prefs;
|
private DiffPreferences prefs;
|
||||||
|
|
||||||
private CodeMirror cmA;
|
private CodeMirror cmA;
|
||||||
@@ -117,13 +104,9 @@ public class SideBySide2 extends Screen {
|
|||||||
private HandlerRegistration resizeHandler;
|
private HandlerRegistration resizeHandler;
|
||||||
private DiffInfo diff;
|
private DiffInfo diff;
|
||||||
private boolean largeFile;
|
private boolean largeFile;
|
||||||
private LineMapper mapper;
|
private ChunkManager chunkManager;
|
||||||
private List<TextMarker> markers;
|
|
||||||
private List<Runnable> undoLineClass;
|
|
||||||
private CommentManager commentManager;
|
private CommentManager commentManager;
|
||||||
private SkipManager skipManager;
|
private SkipManager skipManager;
|
||||||
private Map<LineHandle, LinePaddingWidgetWrapper> linePaddingOnOtherSideMap;
|
|
||||||
private List<DiffChunkInfo> diffChunks;
|
|
||||||
|
|
||||||
private KeyCommandSet keysNavigation;
|
private KeyCommandSet keysNavigation;
|
||||||
private KeyCommandSet keysAction;
|
private KeyCommandSet keysAction;
|
||||||
@@ -250,6 +233,13 @@ public class SideBySide2 extends Screen {
|
|||||||
});
|
});
|
||||||
diffTable.sidePanel.adjustGutters(cmB);
|
diffTable.sidePanel.adjustGutters(cmB);
|
||||||
|
|
||||||
|
if (startSide == null && diff.meta_b() != null) {
|
||||||
|
DiffChunkInfo d = chunkManager.getFirst();
|
||||||
|
if (d != null) {
|
||||||
|
startSide = d.getSide();
|
||||||
|
startLine = d.getStart() + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (startSide != null && startLine > 0) {
|
if (startSide != null && startLine > 0) {
|
||||||
int line = startLine - 1;
|
int line = startLine - 1;
|
||||||
CodeMirror cm = getCmFromSide(startSide);
|
CodeMirror cm = getCmFromSide(startSide);
|
||||||
@@ -258,18 +248,6 @@ public class SideBySide2 extends Screen {
|
|||||||
}
|
}
|
||||||
cm.setCursor(LineCharacter.create(line));
|
cm.setCursor(LineCharacter.create(line));
|
||||||
cm.focus();
|
cm.focus();
|
||||||
} else if (diff.meta_b() != null) {
|
|
||||||
int line = 0;
|
|
||||||
if (!diffChunks.isEmpty()) {
|
|
||||||
DiffChunkInfo d = diffChunks.get(0);
|
|
||||||
CodeMirror cm = getCmFromSide(d.getSide());
|
|
||||||
line = d.getStart();
|
|
||||||
if (cm.lineAtHeight(height - 20) < line) {
|
|
||||||
cm.scrollToY(cm.heightAtLine(line, "local") - 0.5 * height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cmB.setCursor(LineCharacter.create(line));
|
|
||||||
cmB.focus();
|
|
||||||
} else {
|
} else {
|
||||||
cmA.setCursor(LineCharacter.create(0));
|
cmA.setCursor(LineCharacter.create(0));
|
||||||
cmA.focus();
|
cmA.focus();
|
||||||
@@ -335,7 +313,7 @@ public class SideBySide2 extends Screen {
|
|||||||
.on("Enter", commentManager.toggleOpenBox(cm))
|
.on("Enter", commentManager.toggleOpenBox(cm))
|
||||||
.on("'c'", commentManager.insertNewDraft(cm))
|
.on("'c'", commentManager.insertNewDraft(cm))
|
||||||
.on("N", maybeNextVimSearch(cm))
|
.on("N", maybeNextVimSearch(cm))
|
||||||
.on("P", diffChunkNav(cm, Direction.PREV))
|
.on("P", chunkManager.diffChunkNav(cm, Direction.PREV))
|
||||||
.on("Shift-O", commentManager.openClosePublished(cm))
|
.on("Shift-O", commentManager.openClosePublished(cm))
|
||||||
.on("Shift-Left", moveCursorToSide(cm, DisplaySide.A))
|
.on("Shift-Left", moveCursorToSide(cm, DisplaySide.A))
|
||||||
.on("Shift-Right", moveCursorToSide(cm, DisplaySide.B))
|
.on("Shift-Right", moveCursorToSide(cm, DisplaySide.B))
|
||||||
@@ -491,6 +469,7 @@ public class SideBySide2 extends Screen {
|
|||||||
|
|
||||||
cmA = newCM(diff.meta_a(), diff.text_a(), DisplaySide.A, diffTable.cmA);
|
cmA = newCM(diff.meta_a(), diff.text_a(), DisplaySide.A, diffTable.cmA);
|
||||||
cmB = newCM(diff.meta_b(), diff.text_b(), DisplaySide.B, diffTable.cmB);
|
cmB = newCM(diff.meta_b(), diff.text_b(), DisplaySide.B, diffTable.cmB);
|
||||||
|
chunkManager = new ChunkManager(this, cmA, cmB, diffTable.sidePanel);
|
||||||
skipManager = new SkipManager(this, commentManager);
|
skipManager = new SkipManager(this, commentManager);
|
||||||
|
|
||||||
operation(new Runnable() {
|
operation(new Runnable() {
|
||||||
@@ -512,7 +491,7 @@ public class SideBySide2 extends Screen {
|
|||||||
|
|
||||||
registerCmEvents(cmA);
|
registerCmEvents(cmA);
|
||||||
registerCmEvents(cmB);
|
registerCmEvents(cmB);
|
||||||
new ScrollSynchronizer(diffTable, cmA, cmB, mapper);
|
new ScrollSynchronizer(diffTable, cmA, cmB, chunkManager.getLineMapper());
|
||||||
|
|
||||||
prefsAction = new PreferencesAction(this, prefs);
|
prefsAction = new PreferencesAction(this, prefs);
|
||||||
header.init(prefsAction);
|
header.init(prefsAction);
|
||||||
@@ -535,7 +514,7 @@ public class SideBySide2 extends Screen {
|
|||||||
String contents,
|
String contents,
|
||||||
DisplaySide side,
|
DisplaySide side,
|
||||||
Element parent) {
|
Element parent) {
|
||||||
Configuration cfg = Configuration.create()
|
return CodeMirror.create(side, parent, Configuration.create()
|
||||||
.set("readOnly", true)
|
.set("readOnly", true)
|
||||||
.set("cursorBlinkRate", 0)
|
.set("cursorBlinkRate", 0)
|
||||||
.set("cursorHeight", 0.85)
|
.set("cursorHeight", 0.85)
|
||||||
@@ -546,8 +525,7 @@ public class SideBySide2 extends Screen {
|
|||||||
.set("styleSelectedText", true)
|
.set("styleSelectedText", true)
|
||||||
.set("showTrailingSpace", prefs.showWhitespaceErrors())
|
.set("showTrailingSpace", prefs.showWhitespaceErrors())
|
||||||
.set("keyMap", "vim_ro")
|
.set("keyMap", "vim_ro")
|
||||||
.set("value", meta != null ? contents : "");
|
.set("value", meta != null ? contents : ""));
|
||||||
return CodeMirror.create(side, parent, cfg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DiffInfo.IntraLineStatus getIntraLineStatus() {
|
DiffInfo.IntraLineStatus getIntraLineStatus() {
|
||||||
@@ -621,86 +599,8 @@ public class SideBySide2 extends Screen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void render(DiffInfo diff) {
|
private void render(DiffInfo diff) {
|
||||||
JsArray<Region> regions = diff.content();
|
header.setNoDiff(diff);
|
||||||
|
chunkManager.render(diff);
|
||||||
header.setNoDiff(regions.length() == 0
|
|
||||||
|| (regions.length() == 1 && regions.get(0).ab() != null));
|
|
||||||
|
|
||||||
mapper = new LineMapper();
|
|
||||||
markers = new ArrayList<TextMarker>();
|
|
||||||
undoLineClass = new ArrayList<Runnable>();
|
|
||||||
linePaddingOnOtherSideMap = new HashMap<LineHandle, LinePaddingWidgetWrapper>();
|
|
||||||
diffChunks = new ArrayList<DiffChunkInfo>();
|
|
||||||
|
|
||||||
String diffColor = diff.meta_a() == null || diff.meta_b() == null
|
|
||||||
? DiffTable.style.intralineBg()
|
|
||||||
: DiffTable.style.diff();
|
|
||||||
|
|
||||||
for (int i = 0; i < regions.length(); i++) {
|
|
||||||
Region current = regions.get(i);
|
|
||||||
int origLineA = mapper.getLineA();
|
|
||||||
int origLineB = mapper.getLineB();
|
|
||||||
if (current.ab() != null) { // Common
|
|
||||||
mapper.appendCommon(current.ab().length());
|
|
||||||
} else { // Insert, Delete or Edit
|
|
||||||
JsArrayString currentA = current.a() == null ? EMPTY : current.a();
|
|
||||||
JsArrayString currentB = current.b() == null ? EMPTY : current.b();
|
|
||||||
int aLength = currentA.length();
|
|
||||||
int bLength = currentB.length();
|
|
||||||
String color = currentA == EMPTY || currentB == EMPTY
|
|
||||||
? diffColor
|
|
||||||
: DiffTable.style.intralineBg();
|
|
||||||
colorLines(cmA, color, origLineA, aLength);
|
|
||||||
colorLines(cmB, color, origLineB, bLength);
|
|
||||||
int commonCnt = Math.min(aLength, bLength);
|
|
||||||
mapper.appendCommon(commonCnt);
|
|
||||||
if (aLength < bLength) { // Edit with insertion
|
|
||||||
int insertCnt = bLength - aLength;
|
|
||||||
mapper.appendInsert(insertCnt);
|
|
||||||
} else if (aLength > bLength) { // Edit with deletion
|
|
||||||
int deleteCnt = aLength - bLength;
|
|
||||||
mapper.appendDelete(deleteCnt);
|
|
||||||
}
|
|
||||||
int chunkEndA = mapper.getLineA() - 1;
|
|
||||||
int chunkEndB = mapper.getLineB() - 1;
|
|
||||||
if (aLength > 0) {
|
|
||||||
addDiffChunkAndPadding(cmB, chunkEndB, chunkEndA, aLength, bLength > 0);
|
|
||||||
}
|
|
||||||
if (bLength > 0) {
|
|
||||||
addDiffChunkAndPadding(cmA, chunkEndA, chunkEndB, bLength, aLength > 0);
|
|
||||||
}
|
|
||||||
markEdit(cmA, currentA, current.edit_a(), origLineA);
|
|
||||||
markEdit(cmB, currentB, current.edit_b(), origLineB);
|
|
||||||
if (aLength == 0) {
|
|
||||||
diffTable.sidePanel.addGutter(cmB, origLineB, SidePanel.GutterType.INSERT);
|
|
||||||
} else if (bLength == 0) {
|
|
||||||
diffTable.sidePanel.addGutter(cmA, origLineA, SidePanel.GutterType.DELETE);
|
|
||||||
} else {
|
|
||||||
diffTable.sidePanel.addGutter(cmB, origLineB, SidePanel.GutterType.EDIT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void clearMarkers() {
|
|
||||||
if (markers != null) {
|
|
||||||
for (TextMarker m : markers) {
|
|
||||||
m.clear();
|
|
||||||
}
|
|
||||||
markers = null;
|
|
||||||
}
|
|
||||||
if (undoLineClass != null) {
|
|
||||||
for (Runnable r : undoLineClass) {
|
|
||||||
r.run();
|
|
||||||
}
|
|
||||||
undoLineClass = null;
|
|
||||||
}
|
|
||||||
if (linePaddingOnOtherSideMap != null) {
|
|
||||||
for (LinePaddingWidgetWrapper x : linePaddingOnOtherSideMap.values()) {
|
|
||||||
x.getWidget().clear();
|
|
||||||
}
|
|
||||||
linePaddingOnOtherSideMap = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeMirror otherCm(CodeMirror me) {
|
CodeMirror otherCm(CodeMirror me) {
|
||||||
@@ -712,69 +612,7 @@ public class SideBySide2 extends Screen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LineOnOtherInfo lineOnOther(DisplaySide side, int line) {
|
LineOnOtherInfo lineOnOther(DisplaySide side, int line) {
|
||||||
return mapper.lineOnOther(side, line);
|
return chunkManager.getLineMapper().lineOnOther(side, line);
|
||||||
}
|
|
||||||
|
|
||||||
private void markEdit(CodeMirror cm, JsArrayString lines,
|
|
||||||
JsArray<Span> edits, int startLine) {
|
|
||||||
if (edits == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
EditIterator iter = new EditIterator(lines, startLine);
|
|
||||||
Configuration intralineBgOpt = Configuration.create()
|
|
||||||
.set("className", DiffTable.style.intralineBg())
|
|
||||||
.set("readOnly", true);
|
|
||||||
Configuration diffOpt = Configuration.create()
|
|
||||||
.set("className", DiffTable.style.diff())
|
|
||||||
.set("readOnly", true);
|
|
||||||
LineCharacter last = CodeMirror.pos(0, 0);
|
|
||||||
for (int i = 0; i < edits.length(); i++) {
|
|
||||||
Span span = edits.get(i);
|
|
||||||
LineCharacter from = iter.advance(span.skip());
|
|
||||||
LineCharacter to = iter.advance(span.mark());
|
|
||||||
int fromLine = from.getLine();
|
|
||||||
if (last.getLine() == fromLine) {
|
|
||||||
markers.add(cm.markText(last, from, intralineBgOpt));
|
|
||||||
} else {
|
|
||||||
markers.add(cm.markText(CodeMirror.pos(fromLine, 0), from, intralineBgOpt));
|
|
||||||
}
|
|
||||||
markers.add(cm.markText(from, to, diffOpt));
|
|
||||||
last = to;
|
|
||||||
colorLines(cm, LineClassWhere.BACKGROUND,
|
|
||||||
DiffTable.style.diff(),
|
|
||||||
fromLine, to.getLine());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void colorLines(CodeMirror cm, String color, int line, int cnt) {
|
|
||||||
colorLines(cm, LineClassWhere.WRAP, color, line, line + cnt);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void colorLines(final CodeMirror cm, final LineClassWhere where,
|
|
||||||
final String className, final int start, final int end) {
|
|
||||||
if (start < end) {
|
|
||||||
for (int line = start; line < end; line++) {
|
|
||||||
cm.addLineClass(line, where, className);
|
|
||||||
}
|
|
||||||
undoLineClass.add(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
for (int line = start; line < end; line++) {
|
|
||||||
cm.removeLineClass(line, where, className);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addDiffChunkAndPadding(CodeMirror cmToPad, int lineToPad,
|
|
||||||
int lineOnOther, int chunkSize, boolean edit) {
|
|
||||||
CodeMirror otherCm = otherCm(cmToPad);
|
|
||||||
linePaddingOnOtherSideMap.put(otherCm.getLineHandle(lineOnOther),
|
|
||||||
new LinePaddingWidgetWrapper(addPaddingWidget(cmToPad,
|
|
||||||
lineToPad, 0, Unit.EM, null), lineToPad, chunkSize));
|
|
||||||
diffChunks.add(new DiffChunkInfo(otherCm.side(),
|
|
||||||
lineOnOther - chunkSize + 1, lineOnOther, edit));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PaddingWidgetWrapper addPaddingWidget(CodeMirror cm,
|
PaddingWidgetWrapper addPaddingWidget(CodeMirror cm,
|
||||||
@@ -862,7 +700,7 @@ public class SideBySide2 extends Screen {
|
|||||||
cm.addLineClass(
|
cm.addLineClass(
|
||||||
handle, LineClassWhere.WRAP, DiffTable.style.activeLine());
|
handle, LineClassWhere.WRAP, DiffTable.style.activeLine());
|
||||||
LineOnOtherInfo info =
|
LineOnOtherInfo info =
|
||||||
mapper.lineOnOther(cm.side(), cm.getLineNumber(handle));
|
lineOnOther(cm.side(), cm.getLineNumber(handle));
|
||||||
if (info.isAligned()) {
|
if (info.isAligned()) {
|
||||||
LineHandle oLineHandle = other.getLineHandle(info.getLine());
|
LineHandle oLineHandle = other.getLineHandle(info.getLine());
|
||||||
other.setActiveLine(oLineHandle);
|
other.setActiveLine(oLineHandle);
|
||||||
@@ -933,7 +771,7 @@ public class SideBySide2 extends Screen {
|
|||||||
return new Runnable() {
|
return new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
if (cmSrc.hasActiveLine()) {
|
if (cmSrc.hasActiveLine()) {
|
||||||
cmDst.setCursor(LineCharacter.create(mapper.lineOnOther(
|
cmDst.setCursor(LineCharacter.create(lineOnOther(
|
||||||
sideSrc,
|
sideSrc,
|
||||||
cmSrc.getLineNumber(cmSrc.getActiveLine())).getLine()));
|
cmSrc.getLineNumber(cmSrc.getActiveLine())).getLine()));
|
||||||
}
|
}
|
||||||
@@ -949,93 +787,12 @@ public class SideBySide2 extends Screen {
|
|||||||
if (cm.hasVimSearchHighlight()) {
|
if (cm.hasVimSearchHighlight()) {
|
||||||
CodeMirror.handleVimKey(cm, "n");
|
CodeMirror.handleVimKey(cm, "n");
|
||||||
} else {
|
} else {
|
||||||
diffChunkNav(cm, Direction.NEXT).run();
|
chunkManager.diffChunkNav(cm, Direction.NEXT).run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private Runnable diffChunkNav(final CodeMirror cm, final Direction dir) {
|
|
||||||
return new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
int line = cm.hasActiveLine() ? cm.getLineNumber(cm.getActiveLine()) : 0;
|
|
||||||
int res = Collections.binarySearch(
|
|
||||||
diffChunks,
|
|
||||||
new DiffChunkInfo(cm.side(), line, 0, false),
|
|
||||||
getDiffChunkComparator());
|
|
||||||
if (res < 0) {
|
|
||||||
res = -res - (dir == Direction.PREV ? 1 : 2);
|
|
||||||
}
|
|
||||||
res = res + (dir == Direction.PREV ? -1 : 1);
|
|
||||||
if (res < 0 || diffChunks.size() <= res) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DiffChunkInfo lookUp = diffChunks.get(res);
|
|
||||||
// If edit, skip the deletion chunk and set focus on the insertion one.
|
|
||||||
if (lookUp.isEdit() && lookUp.getSide() == DisplaySide.A) {
|
|
||||||
res = res + (dir == Direction.PREV ? -1 : 1);
|
|
||||||
if (res < 0 || diffChunks.size() <= res) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DiffChunkInfo target = diffChunks.get(res);
|
|
||||||
CodeMirror targetCm = getCmFromSide(target.getSide());
|
|
||||||
targetCm.setCursor(LineCharacter.create(target.getStart()));
|
|
||||||
targetCm.focus();
|
|
||||||
targetCm.scrollToY(
|
|
||||||
targetCm.heightAtLine(target.getStart(), "local") -
|
|
||||||
0.5 * cmB.getScrollbarV().getClientHeight());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Diff chunks are ordered by their starting lines. If it's a deletion,
|
|
||||||
* use its corresponding line on the revision side for comparison. In
|
|
||||||
* the edit case, put the deletion chunk right before the insertion chunk.
|
|
||||||
* This placement guarantees well-ordering.
|
|
||||||
*/
|
|
||||||
private Comparator<DiffChunkInfo> getDiffChunkComparator() {
|
|
||||||
return new Comparator<DiffChunkInfo>() {
|
|
||||||
@Override
|
|
||||||
public int compare(DiffChunkInfo o1, DiffChunkInfo o2) {
|
|
||||||
if (o1.getSide() == o2.getSide()) {
|
|
||||||
return o1.getStart() - o2.getStart();
|
|
||||||
} else if (o1.getSide() == DisplaySide.A) {
|
|
||||||
int comp = mapper.lineOnOther(o1.getSide(), o1.getStart())
|
|
||||||
.getLine() - o2.getStart();
|
|
||||||
return comp == 0 ? -1 : comp;
|
|
||||||
} else {
|
|
||||||
int comp = o1.getStart() -
|
|
||||||
mapper.lineOnOther(o2.getSide(), o2.getStart()).getLine();
|
|
||||||
return comp == 0 ? 1 : comp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
DiffChunkInfo getDiffChunk(DisplaySide side, int line) {
|
|
||||||
int res = Collections.binarySearch(
|
|
||||||
diffChunks,
|
|
||||||
new DiffChunkInfo(side, line, 0, false), // Dummy DiffChunkInfo
|
|
||||||
getDiffChunkComparator());
|
|
||||||
if (res >= 0) {
|
|
||||||
return diffChunks.get(res);
|
|
||||||
} else { // The line might be within a DiffChunk
|
|
||||||
res = -res - 1;
|
|
||||||
if (res > 0) {
|
|
||||||
DiffChunkInfo info = diffChunks.get(res - 1);
|
|
||||||
if (info.getSide() == side && info.getStart() <= line &&
|
|
||||||
line <= info.getEnd()) {
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
void defer(Runnable thunk) {
|
void defer(Runnable thunk) {
|
||||||
if (deferred == null) {
|
if (deferred == null) {
|
||||||
@@ -1058,66 +815,13 @@ public class SideBySide2 extends Screen {
|
|||||||
deferred.add(thunk);
|
deferred.add(thunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
void resizePaddingOnOtherSide(DisplaySide mySide, int line) {
|
|
||||||
CodeMirror cm = getCmFromSide(mySide);
|
|
||||||
LineHandle handle = cm.getLineHandle(line);
|
|
||||||
final LinePaddingWidgetWrapper otherWrapper = linePaddingOnOtherSideMap.get(handle);
|
|
||||||
double myChunkHeight = cm.heightAtLine(line + 1) -
|
|
||||||
cm.heightAtLine(line - otherWrapper.getChunkLength() + 1);
|
|
||||||
Element otherPadding = otherWrapper.getElement();
|
|
||||||
int otherPaddingHeight = otherPadding.getOffsetHeight();
|
|
||||||
CodeMirror otherCm = otherCm(cm);
|
|
||||||
int otherLine = otherWrapper.getOtherLine();
|
|
||||||
LineHandle other = otherCm.getLineHandle(otherLine);
|
|
||||||
if (linePaddingOnOtherSideMap.containsKey(other)) {
|
|
||||||
LinePaddingWidgetWrapper myWrapper = linePaddingOnOtherSideMap.get(other);
|
|
||||||
Element myPadding = linePaddingOnOtherSideMap.get(other).getElement();
|
|
||||||
int myPaddingHeight = myPadding.getOffsetHeight();
|
|
||||||
myChunkHeight -= myPaddingHeight;
|
|
||||||
double otherChunkHeight = otherCm.heightAtLine(otherLine + 1) -
|
|
||||||
otherCm.heightAtLine(otherLine - myWrapper.getChunkLength() + 1) -
|
|
||||||
otherPaddingHeight;
|
|
||||||
double delta = myChunkHeight - otherChunkHeight;
|
|
||||||
if (delta > 0) {
|
|
||||||
if (myPaddingHeight != 0) {
|
|
||||||
myPadding.getStyle().setHeight((double) 0, Unit.PX);
|
|
||||||
myWrapper.getWidget().changed();
|
|
||||||
}
|
|
||||||
if (otherPaddingHeight != delta) {
|
|
||||||
otherPadding.getStyle().setHeight(delta, Unit.PX);
|
|
||||||
otherWrapper.getWidget().changed();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (myPaddingHeight != -delta) {
|
|
||||||
myPadding.getStyle().setHeight(-delta, Unit.PX);
|
|
||||||
myWrapper.getWidget().changed();
|
|
||||||
}
|
|
||||||
if (otherPaddingHeight != 0) {
|
|
||||||
otherPadding.getStyle().setHeight((double) 0, Unit.PX);
|
|
||||||
otherWrapper.getWidget().changed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (otherPaddingHeight != myChunkHeight) {
|
|
||||||
otherPadding.getStyle().setHeight(myChunkHeight, Unit.PX);
|
|
||||||
otherWrapper.getWidget().changed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Maybe integrate this with PaddingManager.
|
// TODO: Maybe integrate this with PaddingManager.
|
||||||
private RenderLineHandler resizeLinePadding(final DisplaySide side) {
|
private RenderLineHandler resizeLinePadding(final DisplaySide side) {
|
||||||
return new RenderLineHandler() {
|
return new RenderLineHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handle(final CodeMirror instance, final LineHandle handle,
|
public void handle(CodeMirror cm, LineHandle lh, Element e) {
|
||||||
Element element) {
|
commentManager.resizePadding(lh);
|
||||||
commentManager.resizePadding(handle);
|
chunkManager.resizePadding(cm, lh, side);
|
||||||
if (linePaddingOnOtherSideMap.containsKey(handle)) {
|
|
||||||
defer(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
resizePaddingOnOtherSide(side, instance.getLineNumber(handle));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1156,6 +860,10 @@ public class SideBySide2 extends Screen {
|
|||||||
return prefs;
|
return prefs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChunkManager getChunkManager() {
|
||||||
|
return chunkManager;
|
||||||
|
}
|
||||||
|
|
||||||
CommentManager getCommentManager() {
|
CommentManager getCommentManager() {
|
||||||
return commentManager;
|
return commentManager;
|
||||||
}
|
}
|
||||||
@@ -1214,7 +922,7 @@ public class SideBySide2 extends Screen {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
skipManager.removeAll();
|
skipManager.removeAll();
|
||||||
clearMarkers();
|
chunkManager.reset();
|
||||||
diffTable.sidePanel.clearDiffGutters();
|
diffTable.sidePanel.clearDiffGutters();
|
||||||
setShowIntraline(prefs.intralineDifference());
|
setShowIntraline(prefs.intralineDifference());
|
||||||
render(diff);
|
render(diff);
|
||||||
|
|||||||
Reference in New Issue
Block a user