Starting to implement side-by-side diff. Including the mark-selection addon from CodeMirror. Adding more API wrappers to CodeMirror to support line formatting. Adding line coloring and padding for inserts and deletes to CodeMirrorDemo. Change-Id: I0a96506166de8e9dc32241b21d99cce9a3890fad
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CodeMirrorDemo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CodeMirrorDemo.java index 66abe88..70b9ff9 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CodeMirrorDemo.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CodeMirrorDemo.java
@@ -14,19 +14,24 @@ package com.google.gerrit.client.diff; +import com.google.gerrit.client.diff.DiffInfo.Region; import com.google.gerrit.client.rpc.CallbackGroup; import com.google.gerrit.client.rpc.GerritCallback; import com.google.gerrit.client.rpc.ScreenLoadCallback; import com.google.gerrit.client.ui.Screen; import com.google.gerrit.reviewdb.client.PatchSet; +import com.google.gwt.core.client.JsArray; import com.google.gwt.dom.client.Element; import com.google.gwt.event.logical.shared.ResizeEvent; import com.google.gwt.event.logical.shared.ResizeHandler; import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Window; import net.codemirror.lib.CodeMirror; +import net.codemirror.lib.CodeMirror.LineClassWhere; import net.codemirror.lib.Configuration; +import net.codemirror.lib.LineCharacter; import net.codemirror.lib.ModeInjector; public class CodeMirrorDemo extends Screen { @@ -116,6 +121,7 @@ private void display(DiffInfo diff) { cmA = displaySide(diff.meta_a(), diff.text_a(), diffTable.getCmA()); cmB = displaySide(diff.meta_b(), diff.text_b(), diffTable.getCmB()); + render(diff); resizeHandler = Window.addResizeHandler(new ResizeHandler() { @Override public void onResize(ResizeEvent event) { @@ -141,13 +147,65 @@ .set("lineNumbers", true) .set("tabSize", 2) .set("mode", getContentType(meta)) - .set("value", contents); + .set("value", contents) + .setInfinity("viewportMargin"); final CodeMirror cm = CodeMirror.create(ele, cfg); cm.setWidth("100%"); cm.setHeight(Window.getClientHeight() - HEADER_FOOTER); return cm; } + private void addPadding(CodeMirror cm, int line) { + Element div = DOM.createDiv(); + div.setClassName(diffTable.style.padding()); + cm.addLineWidget(line, div, null); + } + + private void render(DiffInfo diff) { + JsArray<Region> regions = diff.content(); + Configuration insertOpt = Configuration.create() + .set("className", diffTable.style.insert()) + .set("readOnly", true); + Configuration deleteOpt = Configuration.create() + .set("className", diffTable.style.delete()) + .set("readOnly", true); + int lineA = 0, lineB = 0; + for (int i = 0; i < regions.length(); i++) { + Region current = regions.get(i); + if (current.ab() != null) { + lineA += current.ab().length(); + lineB += current.ab().length(); + } else if (current.a() == null && current.b() != null) { + int delta = current.b().length(); + for (int j = 0; j < delta; j++) { + addPadding(cmA, lineA - 1); + } + for (int j = 0; j < delta; j++) { + cmB.addLineClass(lineB, LineClassWhere.WRAP, + diffTable.style.insert()); + LineCharacter from = LineCharacter.create(lineB, 0); + cmB.markText(from, from, insertOpt); + lineB++; + } + } else if (current.a() != null && current.b() == null) { + int delta = current.a().length(); + for (int j = 0; j < delta; j++) { + addPadding(cmB, lineB - 1); + } + for (int j = 0; j < delta; j++) { + cmA.addLineClass(lineA, LineClassWhere.WRAP, + diffTable.style.delete()); + LineCharacter from = LineCharacter.create(lineA, 0); + cmA.markText(from, from, deleteOpt); + lineA++; + } + } else { // TODO: Handle intraline edit. + lineA += current.a().length(); + lineB += current.a().length(); + } + } + } + private static String getContentType(DiffInfo.FileMeta meta) { return meta != null && meta.content_type() != null ? ModeInjector.getContentType(meta.content_type())
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java index bb223e1..e681630 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java
@@ -16,6 +16,7 @@ import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.DivElement; +import com.google.gwt.resources.client.CssResource; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.ui.Composite; @@ -29,12 +30,22 @@ interface Binder extends UiBinder<HTMLPanel, DiffTable> {} private static Binder uiBinder = GWT.create(Binder.class); + interface LineStyle extends CssResource { + String insert(); + String delete(); + String intraline(); + String padding(); + } + @UiField DivElement cmA; @UiField DivElement cmB; + @UiField + LineStyle style; + DiffTable() { initWidget(uiBinder.createAndBindUi(this)); }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml index 735a0e6..5e4cf0d 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml
@@ -16,7 +16,49 @@ --> <ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui'> - <g:HTMLPanel> + <ui:style field='css'> + @external .CodeMirror, .CodeMirror-selectedtext, .CodeMirror-scroll; + + .difftable .CodeMirror { + border: 1px solid #eee; + height: auto; + } + .difftable .CodeMirror pre { + padding: 0; + overflow: hidden; + } + .difftable .CodeMirror-selectedtext.intraline, + .difftable .CodeMirror-selectedtext.insert { + opacity: 0.5; + } + .difftable .CodeMirror-scroll { + overflow-x: hidden; + overflow-y: hidden; + } + <!--.difftable .CodeMirror-vscrollbar { + display: none !important; + }--> + </ui:style> + <ui:style type='com.google.gerrit.client.diff.DiffTable.LineStyle'> + @external .CodeMirror-linenumber; + + .insert, + .insert .CodeMirror-linenumber { + background-color: #dfd; + } + .delete, + .delete .CodeMirror-linenumber { + background-color: #fee; + } + .intraline { + background-color: #9f9; + } + .padding { + background-color: #eee; + height: 1em; + } + </ui:style> + <g:HTMLPanel styleName='{css.difftable}'> <table> <tr> <td><div ui:field='cmA'></div></td>
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/CodeMirror.gwt.xml b/gerrit-gwtui/src/main/java/net/codemirror/CodeMirror.gwt.xml index 64ac16d..a3641a553 100644 --- a/gerrit-gwtui/src/main/java/net/codemirror/CodeMirror.gwt.xml +++ b/gerrit-gwtui/src/main/java/net/codemirror/CodeMirror.gwt.xml
@@ -16,6 +16,7 @@ <module> <inherits name='com.google.gwt.logging.Logging'/> <inherits name='com.google.gwt.resources.Resources'/> + <source path='addon'/> <source path='lib'/> <source path='mode'/> </module>
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/addon/Addons.java b/gerrit-gwtui/src/main/java/net/codemirror/addon/Addons.java new file mode 100644 index 0000000..1cddc7f --- /dev/null +++ b/gerrit-gwtui/src/main/java/net/codemirror/addon/Addons.java
@@ -0,0 +1,28 @@ +// 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 net.codemirror.addon; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.resources.client.ClientBundle; +import com.google.gwt.resources.client.DataResource; +import com.google.gwt.resources.client.DataResource.DoNotEmbed; + +public interface Addons extends ClientBundle { + public static final Addons I = GWT.create(Addons.class); + + @Source("selection/mark-selection.js") + @DoNotEmbed + DataResource mark_selection(); +}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java index 11d10df..42664b7 100644 --- a/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java +++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java
@@ -28,7 +28,8 @@ Loader.initLibrary(cb); } - public static native CodeMirror create(Element parent, Configuration cfg) /*-{ + public static native CodeMirror create(Element parent, + Configuration cfg) /*-{ return $wnd.CodeMirror(parent, cfg); }-*/; @@ -42,6 +43,30 @@ public final native void refresh() /*-{ this.refresh(); }-*/; public final native Element getWrapperElement() /*-{ return this.getWrapperElement(); }-*/; + public final native void markText(LineCharacter from, LineCharacter to, + Configuration options) /*-{ + this.markText(from, to, options); + }-*/; + + public enum LineClassWhere { + TEXT, BACKGROUND, WRAP; + } + + public final void addLineClass(int line, LineClassWhere where, + String className) { + addLineClassNative(line, where.name().toLowerCase(), className); + } + + private final native void addLineClassNative(int line, String where, + String lineClass) /*-{ + this.addLineClass(line, where, lineClass); + }-*/; + + public final native void addLineWidget(int line, Element node, + Configuration options) /*-{ + this.addLineWidget(line, node, options); + }-*/; + protected CodeMirror() { } }
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/Configuration.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/Configuration.java index a121697..57fcc2b 100644 --- a/gerrit-gwtui/src/main/java/net/codemirror/lib/Configuration.java +++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/Configuration.java
@@ -36,6 +36,9 @@ public final native Configuration set(String name, boolean val) /*-{ this[name] = val; return this; }-*/; + public final native Configuration setInfinity(String name) + /*-{ this[name] = Infinity; return this; }-*/; + protected Configuration() { } }
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/LineCharacter.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/LineCharacter.java new file mode 100644 index 0000000..8a8e9d5 --- /dev/null +++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/LineCharacter.java
@@ -0,0 +1,39 @@ +// 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 net.codemirror.lib; + +import com.google.gwt.core.client.JavaScriptObject; + +/** {line, ch} objects used within CodeMirror. */ +public class LineCharacter extends JavaScriptObject { + public static LineCharacter create(int line, int ch) { + LineCharacter lineCh = createObject().cast(); + return lineCh.setLine(line).setCh(ch); + } + + private final native LineCharacter setLine(int line) /*-{ + this.line = line; return this; + }-*/; + + private final native LineCharacter setCh(int ch) /*-{ + this.ch = ch; return this; + }-*/; + + public final native int getLine() /*-{ return this.line; }-*/; + public final native int getCh() /*-{ return this.ch; }-*/; + + protected LineCharacter() { + } +}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/Loader.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/Loader.java index 0637a1b..6373906d6 100644 --- a/gerrit-gwtui/src/main/java/net/codemirror/lib/Loader.java +++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/Loader.java
@@ -14,6 +14,7 @@ package net.codemirror.lib; +import com.google.gerrit.client.rpc.CallbackGroup; import com.google.gwt.core.client.Callback; import com.google.gwt.core.client.ScriptInjector; import com.google.gwt.dom.client.ScriptElement; @@ -25,6 +26,8 @@ import com.google.gwt.safehtml.shared.SafeUri; import com.google.gwt.user.client.rpc.AsyncCallback; +import net.codemirror.addon.Addons; + import java.util.logging.Level; import java.util.logging.Logger; @@ -36,8 +39,14 @@ if (isLibLoaded()) { cb.onSuccess(null); } else { + CallbackGroup group = new CallbackGroup(); injectCss(Lib.I.css()); - injectScript(Lib.I.js().getSafeUri(), cb); + injectScript( + Lib.I.js().getSafeUri(), group.add(new AsyncCallback<Void>() { + public void onFailure(Throwable caught) {} + public void onSuccess(Void result) {} + })); + injectScript(Addons.I.mark_selection().getSafeUri(), group.add(cb)); } }