| // 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); |
| } |
| } |
| } |