Merge "Start displaying comments and drafts."
This commit is contained in:
@@ -15,6 +15,7 @@
|
|||||||
package com.google.gerrit.client.changes;
|
package com.google.gerrit.client.changes;
|
||||||
|
|
||||||
import com.google.gerrit.client.rpc.NativeMap;
|
import com.google.gerrit.client.rpc.NativeMap;
|
||||||
|
import com.google.gerrit.client.rpc.RestApi;
|
||||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
import com.google.gwt.core.client.JsArray;
|
import com.google.gwt.core.client.JsArray;
|
||||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||||
@@ -23,7 +24,41 @@ public class CommentApi {
|
|||||||
|
|
||||||
public static void comments(PatchSet.Id id,
|
public static void comments(PatchSet.Id id,
|
||||||
AsyncCallback<NativeMap<JsArray<CommentInfo>>> cb) {
|
AsyncCallback<NativeMap<JsArray<CommentInfo>>> cb) {
|
||||||
ChangeApi.revision(id).view("comments").get(cb);
|
revision(id, "comments").get(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void comment(PatchSet.Id id, String commentId,
|
||||||
|
AsyncCallback<NativeMap<JsArray<CommentInfo>>> cb) {
|
||||||
|
revision(id, "comments").id(commentId).get(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void drafts(PatchSet.Id id,
|
||||||
|
AsyncCallback<NativeMap<JsArray<CommentInfo>>> cb) {
|
||||||
|
revision(id, "drafts").get(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void draft(PatchSet.Id id, String draftId,
|
||||||
|
AsyncCallback<NativeMap<JsArray<CommentInfo>>> cb) {
|
||||||
|
revision(id, "drafts").id(draftId).get(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void createDraft(PatchSet.Id id, CommentInfo content,
|
||||||
|
AsyncCallback<NativeMap<JsArray<CommentInfo>>> cb) {
|
||||||
|
revision(id, "drafts").put(content, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updateDraft(PatchSet.Id id, String draftId,
|
||||||
|
CommentInfo content, AsyncCallback<NativeMap<JsArray<CommentInfo>>> cb) {
|
||||||
|
revision(id, "drafts").id(draftId).put(content, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void deleteDraft(PatchSet.Id id, String draftId,
|
||||||
|
AsyncCallback<NativeMap<JsArray<CommentInfo>>> cb) {
|
||||||
|
revision(id, "drafts").id(draftId).delete(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RestApi revision(PatchSet.Id id, String type) {
|
||||||
|
return ChangeApi.revision(id).view(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CommentApi() {
|
private CommentApi() {
|
||||||
|
|||||||
@@ -14,15 +14,22 @@
|
|||||||
|
|
||||||
package com.google.gerrit.client.diff;
|
package com.google.gerrit.client.diff;
|
||||||
|
|
||||||
|
import com.google.gerrit.client.changes.CommentApi;
|
||||||
|
import com.google.gerrit.client.changes.CommentInfo;
|
||||||
import com.google.gerrit.client.diff.DiffInfo.Region;
|
import com.google.gerrit.client.diff.DiffInfo.Region;
|
||||||
import com.google.gerrit.client.diff.DiffInfo.Span;
|
import com.google.gerrit.client.diff.DiffInfo.Span;
|
||||||
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;
|
||||||
|
import com.google.gerrit.client.rpc.NativeMap;
|
||||||
import com.google.gerrit.client.rpc.ScreenLoadCallback;
|
import com.google.gerrit.client.rpc.ScreenLoadCallback;
|
||||||
import com.google.gerrit.client.ui.Screen;
|
import com.google.gerrit.client.ui.Screen;
|
||||||
|
import com.google.gerrit.common.changes.Side;
|
||||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
|
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.JsArrayString;
|
||||||
|
import com.google.gwt.core.client.Scheduler;
|
||||||
|
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
|
||||||
import com.google.gwt.dom.client.Element;
|
import com.google.gwt.dom.client.Element;
|
||||||
import com.google.gwt.dom.client.Style.Unit;
|
import com.google.gwt.dom.client.Style.Unit;
|
||||||
import com.google.gwt.event.logical.shared.ResizeEvent;
|
import com.google.gwt.event.logical.shared.ResizeEvent;
|
||||||
@@ -37,8 +44,13 @@ import net.codemirror.lib.Configuration;
|
|||||||
import net.codemirror.lib.LineCharacter;
|
import net.codemirror.lib.LineCharacter;
|
||||||
import net.codemirror.lib.ModeInjector;
|
import net.codemirror.lib.ModeInjector;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class CodeMirrorDemo extends Screen {
|
public class CodeMirrorDemo extends Screen {
|
||||||
private static final int HEADER_FOOTER = 60 + 15 * 2 + 38;
|
private static final int HEADER_FOOTER = 60 + 15 * 2 + 38;
|
||||||
|
private static final JsArrayString EMPTY =
|
||||||
|
JavaScriptObject.createArray().cast();
|
||||||
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;
|
||||||
@@ -47,6 +59,10 @@ public class CodeMirrorDemo extends Screen {
|
|||||||
private CodeMirror cmA;
|
private CodeMirror cmA;
|
||||||
private CodeMirror cmB;
|
private CodeMirror cmB;
|
||||||
private HandlerRegistration resizeHandler;
|
private HandlerRegistration resizeHandler;
|
||||||
|
private JsArray<CommentInfo> published;
|
||||||
|
private JsArray<CommentInfo> drafts;
|
||||||
|
private List<Runnable> resizeCallbacks;
|
||||||
|
private LineMapper mapper;
|
||||||
|
|
||||||
public CodeMirrorDemo(
|
public CodeMirrorDemo(
|
||||||
PatchSet.Id base,
|
PatchSet.Id base,
|
||||||
@@ -85,7 +101,7 @@ public class CodeMirrorDemo extends Screen {
|
|||||||
new ModeInjector()
|
new ModeInjector()
|
||||||
.add(getContentType(diff.meta_a()))
|
.add(getContentType(diff.meta_a()))
|
||||||
.add(getContentType(diff.meta_b()))
|
.add(getContentType(diff.meta_b()))
|
||||||
.inject(new ScreenLoadCallback<Void>(CodeMirrorDemo.this){
|
.inject(new ScreenLoadCallback<Void>(CodeMirrorDemo.this) {
|
||||||
@Override
|
@Override
|
||||||
protected void preDisplay(Void result) {
|
protected void preDisplay(Void result) {
|
||||||
display(diff);
|
display(diff);
|
||||||
@@ -93,6 +109,16 @@ public class CodeMirrorDemo extends Screen {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
CommentApi.comments(revision,
|
||||||
|
group.add(new GerritCallback<NativeMap<JsArray<CommentInfo>>>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(NativeMap<JsArray<CommentInfo>> m) { published = m.get(path); }
|
||||||
|
}));
|
||||||
|
CommentApi.drafts(revision,
|
||||||
|
group.add(new GerritCallback<NativeMap<JsArray<CommentInfo>>>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(NativeMap<JsArray<CommentInfo>> m) { drafts = m.get(path); }
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -105,6 +131,15 @@ public class CodeMirrorDemo extends Screen {
|
|||||||
cmB.refresh();
|
cmB.refresh();
|
||||||
}
|
}
|
||||||
Window.enableScrolling(false);
|
Window.enableScrolling(false);
|
||||||
|
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
for (Runnable r : resizeCallbacks) {
|
||||||
|
r.run();
|
||||||
|
}
|
||||||
|
resizeCallbacks = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -129,6 +164,14 @@ public class CodeMirrorDemo extends Screen {
|
|||||||
cmA = displaySide(diff.meta_a(), diff.text_a(), diffTable.getCmA());
|
cmA = displaySide(diff.meta_a(), diff.text_a(), diffTable.getCmA());
|
||||||
cmB = displaySide(diff.meta_b(), diff.text_b(), diffTable.getCmB());
|
cmB = displaySide(diff.meta_b(), diff.text_b(), diffTable.getCmB());
|
||||||
render(diff);
|
render(diff);
|
||||||
|
resizeCallbacks = new ArrayList<Runnable>();
|
||||||
|
renderComments(published, false);
|
||||||
|
renderComments(drafts, true);
|
||||||
|
published = null;
|
||||||
|
drafts = null;
|
||||||
|
mapper = null;
|
||||||
|
|
||||||
|
// TODO: Probably need horizontal resize
|
||||||
resizeHandler = Window.addResizeHandler(new ResizeHandler() {
|
resizeHandler = Window.addResizeHandler(new ResizeHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void onResize(ResizeEvent event) {
|
public void onResize(ResizeEvent event) {
|
||||||
@@ -159,40 +202,36 @@ public class CodeMirrorDemo extends Screen {
|
|||||||
.set("styleSelectedText", true)
|
.set("styleSelectedText", true)
|
||||||
.set("value", contents);
|
.set("value", contents);
|
||||||
final CodeMirror cm = CodeMirror.create(ele, cfg);
|
final CodeMirror cm = CodeMirror.create(ele, cfg);
|
||||||
cm.setWidth("100%");
|
|
||||||
cm.setHeight(Window.getClientHeight() - HEADER_FOOTER);
|
cm.setHeight(Window.getClientHeight() - HEADER_FOOTER);
|
||||||
return cm;
|
return cm;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void render(DiffInfo diff) {
|
private void render(DiffInfo diff) {
|
||||||
JsArray<Region> regions = diff.content();
|
JsArray<Region> regions = diff.content();
|
||||||
int lineA = 0, lineB = 0;
|
mapper = new LineMapper();
|
||||||
for (int i = 0; i < regions.length(); i++) {
|
for (int i = 0; i < regions.length(); i++) {
|
||||||
Region current = regions.get(i);
|
Region current = regions.get(i);
|
||||||
if (current.ab() != null) {
|
int origLineA = mapper.getLineA();
|
||||||
lineA += current.ab().length();
|
int origLineB = mapper.getLineB();
|
||||||
lineB += current.ab().length();
|
if (current.ab() != null) { // Common
|
||||||
} else if (current.a() == null && current.b() != null) {
|
// TODO: Handle skips.
|
||||||
int delta = current.b().length();
|
mapper.appendCommon(current.ab().length());
|
||||||
insertEmptyLines(cmA, lineA, delta);
|
} else { // Insert, Delete or Edit
|
||||||
lineB = colorLines(cmB, lineB, delta);
|
JsArrayString currentA = current.a() == null ? EMPTY : current.a();
|
||||||
} else if (current.a() != null && current.b() == null) {
|
JsArrayString currentB = current.b() == null ? EMPTY : current.b();
|
||||||
int delta = current.a().length();
|
|
||||||
insertEmptyLines(cmB, lineB, delta);
|
|
||||||
lineA = colorLines(cmA, lineA, delta);
|
|
||||||
} else {
|
|
||||||
JsArrayString currentA = current.a();
|
|
||||||
JsArrayString currentB = current.b();
|
|
||||||
int aLength = currentA.length();
|
int aLength = currentA.length();
|
||||||
int bLength = currentB.length();
|
int bLength = currentB.length();
|
||||||
int origLineA = lineA;
|
colorLines(cmA, origLineA, aLength);
|
||||||
int origLineB = lineB;
|
colorLines(cmB, origLineB, bLength);
|
||||||
lineA = colorLines(cmA, lineA, aLength);
|
mapper.appendCommon(Math.min(aLength, bLength));
|
||||||
lineB = colorLines(cmB, lineB, bLength);
|
if (aLength < bLength) { // Edit with insertion
|
||||||
if (aLength < bLength) {
|
int insertCnt = bLength - aLength;
|
||||||
insertEmptyLines(cmA, lineA, bLength - aLength);
|
insertEmptyLines(cmA, mapper.getLineA(), insertCnt);
|
||||||
} else if (aLength > bLength) {
|
mapper.appendInsert(insertCnt);
|
||||||
insertEmptyLines(cmB, lineB, aLength - bLength);
|
} else if (aLength > bLength) { // Edit with deletion
|
||||||
|
int deleteCnt = aLength - bLength;
|
||||||
|
insertEmptyLines(cmB, mapper.getLineB(), deleteCnt);
|
||||||
|
mapper.appendDelete(deleteCnt);
|
||||||
}
|
}
|
||||||
markEdit(cmA, currentA, current.edit_a(), origLineA);
|
markEdit(cmA, currentA, current.edit_a(), origLineA);
|
||||||
markEdit(cmB, currentB, current.edit_b(), origLineB);
|
markEdit(cmB, currentB, current.edit_b(), origLineB);
|
||||||
@@ -200,25 +239,44 @@ public class CodeMirrorDemo extends Screen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void insertEmptyLines(CodeMirror cm, int line, int cnt) {
|
private void renderComments(JsArray<CommentInfo> comments, boolean isDraft) {
|
||||||
Element div = DOM.createDiv();
|
Configuration config = Configuration.create().set("coverGutter", true);
|
||||||
div.setClassName(diffTable.style.padding());
|
for (int i = 0; comments != null && i < comments.length(); i++) {
|
||||||
div.getStyle().setHeight(cnt, Unit.EM);
|
CommentInfo info = comments.get(i);
|
||||||
Configuration config = Configuration.create()
|
Side mySide = info.side();
|
||||||
.set("coverGutter", true)
|
CodeMirror cm = mySide == Side.PARENT ? cmA : cmB;
|
||||||
.set("above", line == 0);
|
CodeMirror other = otherCM(cm);
|
||||||
cm.addLineWidget(line == 0 ? 0 : (line - 1), div, config);
|
final CommentBox box = new CommentBox(info.author(), info.updated(),
|
||||||
|
info.message(), isDraft);
|
||||||
|
int line = info.line() - 1; // CommentInfo is 1-based, but CM is 0-based
|
||||||
|
diffTable.add(box);
|
||||||
|
cm.addLineWidget(line, box.getElement(), config);
|
||||||
|
int lineToPad = mapper.lineOnOther(mySide, line);
|
||||||
|
// Estimated height at 21px, fixed by deferring after display
|
||||||
|
final Element paddingOtherside = addPaddingWidget(other,
|
||||||
|
diffTable.style.padding(), lineToPad,
|
||||||
|
21, Unit.PX);
|
||||||
|
Runnable callback = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
paddingOtherside.getStyle().setHeight(
|
||||||
|
box.getOffsetHeight(), Unit.PX);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
resizeCallbacks.add(callback);
|
||||||
|
box.setOpenCloseHandler(callback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int colorLines(CodeMirror cm, int line, int cnt) {
|
private CodeMirror otherCM(CodeMirror me) {
|
||||||
for (int i = 0; i < cnt; i++) {
|
return me == cmA ? cmB : cmA;
|
||||||
cm.addLineClass(line + i, LineClassWhere.WRAP, diffTable.style.diff());
|
|
||||||
}
|
|
||||||
return line + cnt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void markEdit(CodeMirror cm, JsArrayString lines,
|
private void markEdit(CodeMirror cm, JsArrayString lines,
|
||||||
JsArray<Span> edits, int startLine) {
|
JsArray<Span> edits, int startLine) {
|
||||||
|
if (edits == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
EditIterator iter = new EditIterator(lines, startLine);
|
EditIterator iter = new EditIterator(lines, startLine);
|
||||||
Configuration diffOpt = Configuration.create()
|
Configuration diffOpt = Configuration.create()
|
||||||
.set("className", diffTable.style.diff())
|
.set("className", diffTable.style.diff())
|
||||||
@@ -246,8 +304,32 @@ public class CodeMirrorDemo extends Screen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Runnable doScroll(final CodeMirror cm) {
|
private void colorLines(CodeMirror cm, int line, int cnt) {
|
||||||
final CodeMirror other = cm == cmA ? cmB : cmA;
|
for (int i = 0; i < cnt; i++) {
|
||||||
|
cm.addLineClass(line + i, LineClassWhere.WRAP, diffTable.style.diff());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insertEmptyLines(CodeMirror cm, int nextLine, int cnt) {
|
||||||
|
// -1 to compensate for the line we went past when this method is called.
|
||||||
|
addPaddingWidget(cm, diffTable.style.padding(), nextLine - 1,
|
||||||
|
cnt, Unit.EM);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Element addPaddingWidget(CodeMirror cm, String style, int line,
|
||||||
|
int height, Unit unit) {
|
||||||
|
Element div = DOM.createDiv();
|
||||||
|
div.setClassName(style);
|
||||||
|
div.getStyle().setHeight(height, unit);
|
||||||
|
Configuration config = Configuration.create()
|
||||||
|
.set("coverGutter", true)
|
||||||
|
.set("above", line == -1);
|
||||||
|
cm.addLineWidget(line == -1 ? 0 : line, div, config);
|
||||||
|
return div;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Runnable doScroll(final CodeMirror cm) {
|
||||||
|
final CodeMirror other = otherCM(cm);
|
||||||
return new Runnable() {
|
return new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
cm.scrollToY(other.getScrollInfo().getTop());
|
cm.scrollToY(other.getScrollInfo().getTop());
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
//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.AvatarImage;
|
||||||
|
import com.google.gerrit.client.FormatUtil;
|
||||||
|
import com.google.gerrit.client.account.AccountInfo;
|
||||||
|
import com.google.gwt.core.client.GWT;
|
||||||
|
import com.google.gwt.dom.client.Element;
|
||||||
|
import com.google.gwt.event.dom.client.ClickEvent;
|
||||||
|
import com.google.gwt.event.dom.client.ClickHandler;
|
||||||
|
import com.google.gwt.event.shared.HandlerRegistration;
|
||||||
|
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;
|
||||||
|
import com.google.gwt.user.client.ui.HTMLPanel;
|
||||||
|
import com.google.gwt.user.client.ui.Widget;
|
||||||
|
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
|
||||||
|
/** An HtmlPanel holding the DialogBox to display a comment */
|
||||||
|
class CommentBox extends Composite {
|
||||||
|
interface Binder extends UiBinder<HTMLPanel, CommentBox> {}
|
||||||
|
private static Binder uiBinder = GWT.create(Binder.class);
|
||||||
|
|
||||||
|
interface CommentBoxStyle extends CssResource {
|
||||||
|
String open();
|
||||||
|
String close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private HandlerRegistration headerClick;
|
||||||
|
private Runnable clickCallback;
|
||||||
|
|
||||||
|
@UiField
|
||||||
|
Widget header;
|
||||||
|
|
||||||
|
@UiField
|
||||||
|
AvatarImage avatar;
|
||||||
|
|
||||||
|
@UiField
|
||||||
|
Element name;
|
||||||
|
|
||||||
|
@UiField
|
||||||
|
Element summary;
|
||||||
|
|
||||||
|
@UiField
|
||||||
|
Element date;
|
||||||
|
|
||||||
|
@UiField
|
||||||
|
Element contentPanel;
|
||||||
|
|
||||||
|
@UiField
|
||||||
|
Element contentPanelMessage;
|
||||||
|
|
||||||
|
@UiField
|
||||||
|
CommentBoxStyle style;
|
||||||
|
|
||||||
|
CommentBox(AccountInfo author, Timestamp when, String message,
|
||||||
|
boolean isDraft) {
|
||||||
|
initWidget(uiBinder.createAndBindUi(this));
|
||||||
|
// TODO: Format the comment box differently based on whether isDraft
|
||||||
|
// is true.
|
||||||
|
setAuthorNameText(author);
|
||||||
|
date.setInnerText(FormatUtil.shortFormatDayTime(when));
|
||||||
|
setMessageText(message);
|
||||||
|
setOpen(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLoad() {
|
||||||
|
super.onLoad();
|
||||||
|
|
||||||
|
headerClick = header.addDomHandler(new ClickHandler() {
|
||||||
|
@Override
|
||||||
|
public void onClick(ClickEvent event) {
|
||||||
|
setOpen(!isOpen());
|
||||||
|
if (clickCallback != null) {
|
||||||
|
clickCallback.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, ClickEvent.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOpenCloseHandler(final Runnable callback) {
|
||||||
|
clickCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onUnload() {
|
||||||
|
super.onUnload();
|
||||||
|
|
||||||
|
if (headerClick != null) {
|
||||||
|
headerClick.removeHandler();
|
||||||
|
headerClick = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setAuthorNameText(AccountInfo author) {
|
||||||
|
// TODO: Set avatar's display to none if we get a 404.
|
||||||
|
avatar = new AvatarImage(author, 26);
|
||||||
|
name.setInnerText(FormatUtil.name(author));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setMessageText(String message) {
|
||||||
|
if (message == null) {
|
||||||
|
message = "";
|
||||||
|
} else {
|
||||||
|
message = message.trim();
|
||||||
|
}
|
||||||
|
summary.setInnerText(message);
|
||||||
|
// TODO: Change to use setInnerHtml
|
||||||
|
contentPanelMessage.setInnerText(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setOpen(boolean open) {
|
||||||
|
if (open) {
|
||||||
|
removeStyleName(style.close());
|
||||||
|
addStyleName(style.open());
|
||||||
|
} else {
|
||||||
|
removeStyleName(style.open());
|
||||||
|
addStyleName(style.close());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isOpen() {
|
||||||
|
return getStyleName().contains(style.open());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
|
||||||
|
xmlns:g='urn:import:com.google.gwt.user.client.ui'
|
||||||
|
xmlns:c='urn:import:com.google.gerrit.client'>
|
||||||
|
<ui:style type='com.google.gerrit.client.diff.CommentBox.CommentBoxStyle'>
|
||||||
|
.commentBox {
|
||||||
|
background-color: #e5ecf9;
|
||||||
|
border: 1px solid black;
|
||||||
|
}
|
||||||
|
.table {
|
||||||
|
width: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
.summary {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
.summaryText {
|
||||||
|
color: #777;
|
||||||
|
height: 1em;
|
||||||
|
max-width: 80%;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-bottom: 1px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.open .summaryText {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.date {
|
||||||
|
width: 25%;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.close .contentPanel {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.message {
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
</ui:style>
|
||||||
|
<g:HTMLPanel styleName='{style.commentBox}'>
|
||||||
|
<g:HTMLPanel ui:field='header'>
|
||||||
|
<table class='{style.table}'>
|
||||||
|
<tr>
|
||||||
|
<td><c:AvatarImage ui:field='avatar' /></td>
|
||||||
|
<td ui:field='name'></td>
|
||||||
|
<td class='{style.summary}'>
|
||||||
|
<div ui:field='summary' class='{style.summaryText}'></div>
|
||||||
|
</td>
|
||||||
|
<td ui:field='date' class='{style.date}'></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</g:HTMLPanel>
|
||||||
|
<div ui:field='contentPanel' class='{style.contentPanel}'>
|
||||||
|
<div><p ui:field='contentPanelMessage' class='{style.message}'></p></div>
|
||||||
|
<div>
|
||||||
|
<button ui:field='reply'><ui:msg>Reply ...</ui:msg></button>
|
||||||
|
<button ui:field='replyDone'><ui:msg>Reply 'Done'</ui:msg></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</g:HTMLPanel>
|
||||||
|
</ui:UiBinder>
|
||||||
@@ -15,12 +15,13 @@
|
|||||||
package com.google.gerrit.client.diff;
|
package com.google.gerrit.client.diff;
|
||||||
|
|
||||||
import com.google.gwt.core.client.GWT;
|
import com.google.gwt.core.client.GWT;
|
||||||
import com.google.gwt.dom.client.DivElement;
|
import com.google.gwt.dom.client.Element;
|
||||||
import com.google.gwt.resources.client.CssResource;
|
import com.google.gwt.resources.client.CssResource;
|
||||||
import com.google.gwt.uibinder.client.UiBinder;
|
import com.google.gwt.uibinder.client.UiBinder;
|
||||||
import com.google.gwt.uibinder.client.UiField;
|
import com.google.gwt.uibinder.client.UiField;
|
||||||
import com.google.gwt.user.client.ui.Composite;
|
import com.google.gwt.user.client.ui.Composite;
|
||||||
import com.google.gwt.user.client.ui.HTMLPanel;
|
import com.google.gwt.user.client.ui.HTMLPanel;
|
||||||
|
import com.google.gwt.user.client.ui.Widget;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A table with one row and two columns to hold the two CodeMirrors displaying
|
* A table with one row and two columns to hold the two CodeMirrors displaying
|
||||||
@@ -37,10 +38,10 @@ class DiffTable extends Composite {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@UiField
|
@UiField
|
||||||
DivElement cmA;
|
Element cmA;
|
||||||
|
|
||||||
@UiField
|
@UiField
|
||||||
DivElement cmB;
|
Element cmB;
|
||||||
|
|
||||||
@UiField
|
@UiField
|
||||||
LineStyle style;
|
LineStyle style;
|
||||||
@@ -49,12 +50,15 @@ class DiffTable extends Composite {
|
|||||||
initWidget(uiBinder.createAndBindUi(this));
|
initWidget(uiBinder.createAndBindUi(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
DivElement getCmA() {
|
Element getCmA() {
|
||||||
return cmA;
|
return cmA;
|
||||||
}
|
}
|
||||||
|
|
||||||
DivElement getCmB() {
|
Element getCmB() {
|
||||||
return cmB;
|
return cmB;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void add(Widget widget) {
|
||||||
|
((HTMLPanel) getWidget()).add(widget);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,13 @@ limitations under the License.
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
.table {
|
||||||
|
width: 100%;
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
.a, .b {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
.a .diff,
|
.a .diff,
|
||||||
.a .diff .CodeMirror-linenumber {
|
.a .diff .CodeMirror-linenumber {
|
||||||
background-color: #fee;
|
background-color: #fee;
|
||||||
@@ -52,10 +59,10 @@ limitations under the License.
|
|||||||
}
|
}
|
||||||
</ui:style>
|
</ui:style>
|
||||||
<g:HTMLPanel styleName='{style.difftable}'>
|
<g:HTMLPanel styleName='{style.difftable}'>
|
||||||
<table>
|
<table class='{style.table}'>
|
||||||
<tr>
|
<tr>
|
||||||
<td><div ui:field='cmA' class='{style.a}'></div></td>
|
<td ui:field='cmA' class='{style.a}'></td>
|
||||||
<td><div ui:field='cmB' class='{style.b}'></div></td>
|
<td ui:field='cmB' class='{style.b}'></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</g:HTMLPanel>
|
</g:HTMLPanel>
|
||||||
|
|||||||
@@ -0,0 +1,148 @@
|
|||||||
|
// 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.common.changes.Side;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** Helper class to handle calculations involving line gaps. */
|
||||||
|
class LineMapper {
|
||||||
|
private int lineA;
|
||||||
|
private int lineB;
|
||||||
|
private List<LineGap> lineMapAtoB;
|
||||||
|
private List<LineGap> lineMapBtoA;
|
||||||
|
|
||||||
|
LineMapper() {
|
||||||
|
lineMapAtoB = new ArrayList<LineGap>();
|
||||||
|
lineMapBtoA = new ArrayList<LineGap>();
|
||||||
|
}
|
||||||
|
|
||||||
|
int getLineA() {
|
||||||
|
return lineA;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getLineB() {
|
||||||
|
return lineB;
|
||||||
|
}
|
||||||
|
|
||||||
|
void appendCommon(int numLines) {
|
||||||
|
lineA += numLines;
|
||||||
|
lineB += numLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
void appendInsert(int numLines) {
|
||||||
|
int origLineB = lineB;
|
||||||
|
lineB += numLines;
|
||||||
|
int bAheadOfA = lineB - lineA;
|
||||||
|
lineMapAtoB.add(new LineGap(lineA, lineA, bAheadOfA));
|
||||||
|
lineMapBtoA.add(new LineGap(origLineB, lineB - 1, -bAheadOfA));
|
||||||
|
}
|
||||||
|
|
||||||
|
void appendDelete(int numLines) {
|
||||||
|
int origLineA = lineA;
|
||||||
|
lineA += numLines;
|
||||||
|
int aAheadOfB = lineA - lineB;
|
||||||
|
lineMapAtoB.add(new LineGap(origLineA, lineA - 1, -aAheadOfB));
|
||||||
|
lineMapBtoA.add(new LineGap(lineB, lineB, aAheadOfB));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to retrieve the line number on the other side.
|
||||||
|
*
|
||||||
|
* Given a line number on one side, performs a binary search in the lineMap
|
||||||
|
* to find the corresponding LineGap record.
|
||||||
|
*
|
||||||
|
* A LineGap records gap information from the start of an actual gap up to
|
||||||
|
* the start of the next gap. In the following example,
|
||||||
|
* lineMapAtoB will have LineGap: {start: 1, end: 1, delta: 3}
|
||||||
|
* (end doesn't really matter here, as the binary search only looks at start)
|
||||||
|
* lineMapBtoA will have LineGap: {start: 1, end: 3, delta: -3}
|
||||||
|
* These LineGaps control lines between 1 and 5.
|
||||||
|
*
|
||||||
|
* The "delta" is computed as the number to add on our side to get the line
|
||||||
|
* number on the other side given a line after the actual gap, so the result
|
||||||
|
* will be (line + delta). All lines within the actual gap (1 to 3) are
|
||||||
|
* considered corresponding to the last line above the region on the other
|
||||||
|
* side, which is 0 in this case. For these lines, we do (end + delta).
|
||||||
|
*
|
||||||
|
* For example, to get the line number on the left corresponding to 1 on the
|
||||||
|
* right (lineOnOther(REVISION, 1)), the method looks up in lineMapBtoA,
|
||||||
|
* finds the "delta" to be -3, and returns 3 + (-3) = 0 since 1 falls in the
|
||||||
|
* actual gap. On the other hand, the line corresponding to 5 on the right
|
||||||
|
* will be 5 + (-3) = 2, since 5 is in the region after the gap (but still
|
||||||
|
* controlled by the current LineGap).
|
||||||
|
*
|
||||||
|
* PARENT REVISION
|
||||||
|
* 0 | 0
|
||||||
|
* - | 1 \ \
|
||||||
|
* - | 2 | Actual insertion gap |
|
||||||
|
* - | 3 / | Region controlled by one LineGap
|
||||||
|
* 1 | 4 <- delta = 4 - 1 = 3 |
|
||||||
|
* 2 | 5 /
|
||||||
|
* - | 6
|
||||||
|
* ...
|
||||||
|
*/
|
||||||
|
int lineOnOther(Side mySide, int line) {
|
||||||
|
List<LineGap> lineGaps = mySide == Side.PARENT ? lineMapAtoB : lineMapBtoA;
|
||||||
|
// Create a dummy LineGap for the search.
|
||||||
|
int ret = Collections.binarySearch(lineGaps, new LineGap(line));
|
||||||
|
if (ret == -1) {
|
||||||
|
return line;
|
||||||
|
} else {
|
||||||
|
LineGap lookup = lineGaps.get(0 <= ret ? ret : -ret - 2);
|
||||||
|
int end = lookup.end;
|
||||||
|
int delta = lookup.delta;
|
||||||
|
if (lookup.start <= line && line <= end) { // Line falls within gap
|
||||||
|
return end + delta;
|
||||||
|
} else { // Line after gap
|
||||||
|
return line + delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to record line gap info and assist in calculation of line
|
||||||
|
* number on the other side.
|
||||||
|
*
|
||||||
|
* For a mapping from A to B, where A is the side with an insertion:
|
||||||
|
* @field start The start line of the insertion in A.
|
||||||
|
* @field end The exclusive end line of the insertion in A.
|
||||||
|
* @field delta The offset added to A to get the line number in B calculated
|
||||||
|
* from end.
|
||||||
|
*/
|
||||||
|
private static class LineGap implements Comparable<LineGap> {
|
||||||
|
private final int start;
|
||||||
|
private final int end;
|
||||||
|
private final int delta;
|
||||||
|
|
||||||
|
private LineGap(int start, int end, int delta) {
|
||||||
|
this.start = start;
|
||||||
|
this.end = end;
|
||||||
|
this.delta = delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LineGap(int line) {
|
||||||
|
this(line, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(LineGap o) {
|
||||||
|
return start - o.start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -66,9 +66,18 @@ public class CodeMirror extends JavaScriptObject {
|
|||||||
this.addLineClass(line, where, lineClass);
|
this.addLineClass(line, where, lineClass);
|
||||||
}-*/;
|
}-*/;
|
||||||
|
|
||||||
public final native void addLineWidget(int line, Element node,
|
public final native void addWidget(LineCharacter pos, Element node,
|
||||||
|
boolean scrollIntoView) /*-{
|
||||||
|
this.addWidget(pos, node, scrollIntoView);
|
||||||
|
}-*/;
|
||||||
|
|
||||||
|
public final native LineWidget addLineWidget(int line, Element node,
|
||||||
Configuration options) /*-{
|
Configuration options) /*-{
|
||||||
this.addLineWidget(line, node, options);
|
return this.addLineWidget(line, node, options);
|
||||||
|
}-*/;
|
||||||
|
|
||||||
|
public final native int lineAtHeight(int height) /*-{
|
||||||
|
return this.lineAtHeight(height);
|
||||||
}-*/;
|
}-*/;
|
||||||
|
|
||||||
public final native CodeMirrorDoc getDoc() /*-{
|
public final native CodeMirrorDoc getDoc() /*-{
|
||||||
|
|||||||
31
gerrit-gwtui/src/main/java/net/codemirror/lib/LineWidget.java
vendored
Normal file
31
gerrit-gwtui/src/main/java/net/codemirror/lib/LineWidget.java
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
/** LineWidget objects used within CodeMirror. */
|
||||||
|
public class LineWidget extends JavaScriptObject {
|
||||||
|
public static LineWidget create() {
|
||||||
|
return createObject().cast();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final native void clear() /*-{ this.clear(); }-*/;
|
||||||
|
public final native void changed() /*-{ this.changed(); }-*/;
|
||||||
|
public final native JavaScriptObject getLine() /*-{ return this.line; }-*/;
|
||||||
|
|
||||||
|
protected LineWidget() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
// 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 static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import com.google.gerrit.common.changes.Side;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/** Unit tests for LineMapper */
|
||||||
|
public class LineMapperTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAppendCommon() {
|
||||||
|
LineMapper mapper = new LineMapper();
|
||||||
|
mapper.appendCommon(10);
|
||||||
|
assertEquals(10, mapper.getLineA());
|
||||||
|
assertEquals(10, mapper.getLineB());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAppendInsert() {
|
||||||
|
LineMapper mapper = new LineMapper();
|
||||||
|
mapper.appendInsert(10);
|
||||||
|
assertEquals(0, mapper.getLineA());
|
||||||
|
assertEquals(10, mapper.getLineB());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAppendDelete() {
|
||||||
|
LineMapper mapper = new LineMapper();
|
||||||
|
mapper.appendDelete(10);
|
||||||
|
assertEquals(10, mapper.getLineA());
|
||||||
|
assertEquals(0, mapper.getLineB());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindInCommon() {
|
||||||
|
LineMapper mapper = new LineMapper();
|
||||||
|
mapper.appendCommon(10);
|
||||||
|
assertEquals(9, mapper.lineOnOther(Side.PARENT, 9));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindAfterCommon() {
|
||||||
|
LineMapper mapper = new LineMapper();
|
||||||
|
mapper.appendCommon(10);
|
||||||
|
assertEquals(10, mapper.lineOnOther(Side.PARENT, 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindInInsertGap() {
|
||||||
|
LineMapper mapper = new LineMapper();
|
||||||
|
mapper.appendInsert(10);
|
||||||
|
assertEquals(-1, mapper.lineOnOther(Side.REVISION, 9));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindAfterInsertGap() {
|
||||||
|
LineMapper mapper = new LineMapper();
|
||||||
|
mapper.appendInsert(10);
|
||||||
|
assertEquals(0, mapper.lineOnOther(Side.REVISION, 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindInDeleteGap() {
|
||||||
|
LineMapper mapper = new LineMapper();
|
||||||
|
mapper.appendDelete(10);
|
||||||
|
assertEquals(-1, mapper.lineOnOther(Side.PARENT, 9));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindAfterDeleteGap() {
|
||||||
|
LineMapper mapper = new LineMapper();
|
||||||
|
mapper.appendDelete(10);
|
||||||
|
assertEquals(0, mapper.lineOnOther(Side.PARENT, 10));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
VERSION = '3.13'
|
include_defs('//lib/maven.defs')
|
||||||
SHA1 = '7a83ae686d75afd30bb152d7683f2dc27e59ea82'
|
|
||||||
URL = 'http://codemirror.net/codemirror-%s.zip' % VERSION
|
VERSION = 'bb73aeacb8'
|
||||||
|
SHA1 = '3bc9c92b97210135bc83fca7a2bd5f3c4aab496a'
|
||||||
|
URL = GERRIT + 'net/codemirror/codemirror-%s.zip' % VERSION
|
||||||
|
|
||||||
prebuilt_jar(
|
prebuilt_jar(
|
||||||
name = 'codemirror',
|
name = 'codemirror',
|
||||||
|
|||||||
Reference in New Issue
Block a user