blob: a5af7037754ddef01fc0edc0298d5b80d48969af [file] [log] [blame]
// Copyright (C) 2015 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 static com.google.gwt.dom.client.Style.Display.INLINE_BLOCK;
import static com.google.gwt.dom.client.Style.Unit.PX;
import static net.codemirror.lib.CodeMirror.LineClassWhere.WRAP;
import static net.codemirror.lib.CodeMirror.style;
import com.google.gerrit.client.FormatUtil;
import com.google.gerrit.client.RangeInfo;
import com.google.gerrit.client.blame.BlameInfo;
import com.google.gerrit.client.diff.DisplaySide;
import com.google.gerrit.client.rpc.Natives;
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.JsArrayString;
import com.google.gwt.dom.client.Element;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.DOM;
import java.util.Date;
import java.util.Objects;
import net.codemirror.lib.CodeMirror.LineHandle;
/** Additional features added to CodeMirror by Gerrit Code Review. */
public class Extras {
private static final String ANNOTATION_GUTTER_ID = "CodeMirror-lint-markers";
private static final BlameConfig C = GWT.create(BlameConfig.class);
static final native Extras get(CodeMirror c) /*-{ return c.gerritExtras }-*/;
private static native void set(CodeMirror c, Extras e) /*-{ c.gerritExtras = e }-*/;
static void attach(CodeMirror c) {
set(c, new Extras(c));
}
private final CodeMirror cm;
private Element margin;
private DisplaySide side;
private double charWidthPx;
private double lineHeightPx;
private LineHandle activeLine;
private boolean annotated;
private Extras(CodeMirror cm) {
this.cm = cm;
}
public DisplaySide side() {
return side;
}
public void side(DisplaySide s) {
side = s;
}
public double charWidthPx() {
if (charWidthPx <= 1) {
int len = 100;
StringBuilder s = new StringBuilder();
for (int i = 0; i < len; i++) {
s.append('m');
}
Element e = DOM.createSpan();
e.getStyle().setDisplay(INLINE_BLOCK);
e.setInnerText(s.toString());
cm.measure().appendChild(e);
charWidthPx = ((double) e.getOffsetWidth()) / len;
e.removeFromParent();
}
return charWidthPx;
}
public double lineHeightPx() {
if (lineHeightPx <= 1) {
Element p = DOM.createDiv();
int lines = 1;
for (int i = 0; i < lines; i++) {
Element e = DOM.createDiv();
p.appendChild(e);
Element pre = DOM.createElement("pre");
pre.setInnerText("gqyŚŻŹŃ");
e.appendChild(pre);
}
cm.measure().appendChild(p);
lineHeightPx = ((double) p.getOffsetHeight()) / lines;
p.removeFromParent();
}
return lineHeightPx;
}
public void lineLength(int columns) {
if (margin == null) {
margin = DOM.createDiv();
margin.setClassName(style().margin());
cm.mover().appendChild(margin);
}
margin.getStyle().setMarginLeft(columns * charWidthPx(), PX);
}
public void showTabs(boolean show) {
Element e = cm.getWrapperElement();
if (show) {
e.addClassName(style().showTabs());
} else {
e.removeClassName(style().showTabs());
}
}
public final boolean hasActiveLine() {
return activeLine != null;
}
public final LineHandle activeLine() {
return activeLine;
}
public final boolean activeLine(LineHandle line) {
if (Objects.equals(activeLine, line)) {
return false;
}
if (activeLine != null) {
cm.removeLineClass(activeLine, WRAP, style().activeLine());
}
activeLine = line;
cm.addLineClass(activeLine, WRAP, style().activeLine());
return true;
}
public final void clearActiveLine() {
if (activeLine != null) {
cm.removeLineClass(activeLine, WRAP, style().activeLine());
activeLine = null;
}
}
public boolean isAnnotated() {
return annotated;
}
public final void clearAnnotations() {
JsArrayString gutters = ((JsArrayString) JsArrayString.createArray());
cm.setOption("gutters", gutters);
annotated = false;
}
public final void setAnnotations(JsArray<BlameInfo> blameInfos) {
if (blameInfos.length() > 0) {
setBlameInfo(blameInfos);
JsArrayString gutters = ((JsArrayString) JsArrayString.createArray());
gutters.push(ANNOTATION_GUTTER_ID);
cm.setOption("gutters", gutters);
annotated = true;
DateTimeFormat format = DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.DATE_SHORT);
JsArray<LintLine> annotations = JsArray.createArray().cast();
for (BlameInfo blameInfo : Natives.asList(blameInfos)) {
for (RangeInfo range : Natives.asList(blameInfo.ranges())) {
Date commitTime = new Date(blameInfo.time() * 1000L);
String shortId = blameInfo.id().substring(0, 8);
String shortBlame =
C.shortBlameMsg(shortId, format.format(commitTime), blameInfo.author());
String detailedBlame =
C.detailedBlameMsg(
blameInfo.id(),
blameInfo.author(),
FormatUtil.mediumFormat(commitTime),
blameInfo.commitMsg());
annotations.push(
LintLine.create(shortBlame, detailedBlame, shortId, Pos.create(range.start() - 1)));
}
}
cm.setOption("lint", getAnnotation(annotations));
}
}
private native JavaScriptObject getAnnotation(JsArray<LintLine> annotations) /*-{
return {
getAnnotations: function(text, options, cm) { return annotations; }
};
}-*/;
public final native JsArray<BlameInfo> getBlameInfo() /*-{
return this.blameInfos;
}-*/;
public final native void setBlameInfo(JsArray<BlameInfo> blameInfos) /*-{
this['blameInfos'] = blameInfos;
}-*/;
public final void toggleAnnotation() {
toggleAnnotation(getBlameInfo());
}
public final void toggleAnnotation(JsArray<BlameInfo> blameInfos) {
if (isAnnotated()) {
clearAnnotations();
} else {
setAnnotations(blameInfos);
}
}
}