com.google.gerrit.client.diff.Unified.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gerrit.client.diff.Unified.java

Source

// 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 java.lang.Double.POSITIVE_INFINITY;

import com.google.gerrit.client.DiffObject;
import com.google.gerrit.client.Dispatcher;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.diff.UnifiedChunkManager.LineRegionInfo;
import com.google.gerrit.client.patches.PatchUtil;
import com.google.gerrit.client.projects.ConfigInfoCache;
import com.google.gerrit.client.rpc.ScreenLoadCallback;
import com.google.gerrit.client.ui.InlineHyperlink;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo.DiffView;
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.ImageResourceRenderer;
import com.google.gwt.user.client.ui.InlineHTML;
import com.google.gwtexpui.globalkey.client.GlobalKey;
import com.google.gwtexpui.safehtml.client.SafeHtml;
import java.util.Collections;
import java.util.List;
import net.codemirror.lib.CodeMirror;
import net.codemirror.lib.CodeMirror.LineHandle;
import net.codemirror.lib.Configuration;
import net.codemirror.lib.Pos;
import net.codemirror.lib.ScrollInfo;

public class Unified extends DiffScreen {
    interface Binder extends UiBinder<FlowPanel, Unified> {
    }

    private static final Binder uiBinder = GWT.create(Binder.class);

    @UiField(provided = true)
    UnifiedTable diffTable;

    private CodeMirror cm;

    private UnifiedChunkManager chunkManager;
    private UnifiedCommentManager commentManager;

    private boolean autoHideDiffTableHeader;

    public Unified(DiffObject base, DiffObject revision, String path, DisplaySide startSide, int startLine) {
        super(base, revision, path, startSide, startLine, DiffView.UNIFIED_DIFF);

        diffTable = new UnifiedTable(this, base, revision, path);
        add(uiBinder.createAndBindUi(this));
        addDomHandler(GlobalKey.STOP_PROPAGATION, KeyPressEvent.getType());
    }

    @Override
    ScreenLoadCallback<ConfigInfoCache.Entry> getScreenLoadCallback(final CommentsCollections comments) {
        return new ScreenLoadCallback<ConfigInfoCache.Entry>(Unified.this) {
            @Override
            protected void preDisplay(ConfigInfoCache.Entry result) {
                commentManager = new UnifiedCommentManager(Unified.this, base, revision, path,
                        result.getCommentLinkProcessor(), getChangeStatus().isOpen());
                setTheme(result.getTheme());
                display(comments);
                header.setupPrevNextFiles(comments);
            }
        };
    }

    @Override
    public void onShowView() {
        super.onShowView();

        operation(() -> {
            resizeCodeMirror();
            cm.refresh();
        });
        setLineLength(Patch.COMMIT_MSG.equals(path) ? 72 : prefs.lineLength());
        diffTable.refresh();

        if (getStartLine() == 0) {
            DiffChunkInfo d = chunkManager.getFirst();
            if (d != null) {
                if (d.isEdit() && d.getSide() == DisplaySide.A) {
                    setStartSide(DisplaySide.B);
                } else {
                    setStartSide(d.getSide());
                }
                setStartLine(chunkManager.getCmLine(d.getStart(), d.getSide()) + 1);
            }
        }
        if (getStartSide() != null && getStartLine() > 0) {
            cm.scrollToLine(chunkManager.getCmLine(getStartLine() - 1, getStartSide()));
            cm.focus();
        } else {
            cm.setCursor(Pos.create(0));
            cm.focus();
        }
        if (Gerrit.isSignedIn() && prefs.autoReview()) {
            header.autoReview();
        }
        prefetchNextFile();
    }

    @Override
    void registerCmEvents(CodeMirror cm) {
        super.registerCmEvents(cm);

        cm.on("scroll", () -> {
            ScrollInfo si = cm.getScrollInfo();
            if (autoHideDiffTableHeader) {
                updateDiffTableHeader(si);
            }
        });
        maybeRegisterRenderEntireFileKeyMap(cm);
    }

    @Override
    public void registerKeys() {
        super.registerKeys();

        registerHandlers();
    }

    @Override
    FocusHandler getFocusHandler() {
        return new FocusHandler() {
            @Override
            public void onFocus(FocusEvent event) {
                cm.focus();
            }
        };
    }

    private void display(CommentsCollections comments) {
        DiffInfo diff = getDiff();
        setThemeStyles(prefs.theme().isDark());
        setShowIntraline(prefs.intralineDifference());
        if (prefs.showLineNumbers()) {
            diffTable.addStyleName(Resources.I.diffTableStyle().showLineNumbers());
        }

        cm = newCm(diff.metaA() == null ? diff.metaB() : diff.metaA(), diff.textUnified(), diffTable.cm);
        setShowTabs(prefs.showTabs());

        chunkManager = new UnifiedChunkManager(this, cm, diffTable.scrollbar);

        operation(() -> {
            // Estimate initial CodeMirror height, fixed up in onShowView.
            int height = Window.getClientHeight() - (Gerrit.getHeaderFooterHeight() + 18);
            cm.setHeight(height);

            render(diff);
            commentManager.render(comments, prefs.expandAllComments());
            skipManager.render(prefs.context(), diff);
        });

        registerCmEvents(cm);

        setPrefsAction(new PreferencesAction(this, prefs));
        header.init(getPrefsAction(), getSideBySideDiffLink(), diff.unifiedWebLinks());
        setAutoHideDiffHeader(prefs.autoHideDiffTableHeader());

        setupSyntaxHighlighting();
    }

    private List<InlineHyperlink> getSideBySideDiffLink() {
        InlineHyperlink toSideBySideDiffLink = new InlineHyperlink();
        toSideBySideDiffLink.setHTML(new ImageResourceRenderer().render(Gerrit.RESOURCES.sideBySideDiff()));
        toSideBySideDiffLink.setTargetHistoryToken(Dispatcher.toSideBySide(base, revision, path));
        toSideBySideDiffLink.setTitle(PatchUtil.C.sideBySideDiff());
        return Collections.singletonList(toSideBySideDiffLink);
    }

    @Override
    CodeMirror newCm(DiffInfo.FileMeta meta, String contents, Element parent) {
        JsArrayString gutters = JavaScriptObject.createArray().cast();
        gutters.push(UnifiedTable.style.lineNumbersLeft());
        gutters.push(UnifiedTable.style.lineNumbersRight());

        return CodeMirror.create(parent,
                Configuration.create().set("cursorBlinkRate", prefs.cursorBlinkRate()).set("cursorHeight", 0.85)
                        .set("gutters", gutters).set("inputStyle", "textarea").set("keyMap", "vim_ro")
                        .set("lineNumbers", false).set("lineWrapping", prefs.lineWrapping())
                        .set("matchBrackets", prefs.matchBrackets())
                        .set("mode", getFileSize() == FileSize.SMALL ? getContentType(meta) : null)
                        .set("readOnly", true).set("scrollbarStyle", "overlay").set("styleSelectedText", true)
                        .set("showTrailingSpace", prefs.showWhitespaceErrors()).set("tabSize", prefs.tabSize())
                        .set("theme", prefs.theme().name().toLowerCase()).set("value", meta != null ? contents : "")
                        .set("viewportMargin", renderEntireFile() ? POSITIVE_INFINITY : 10));
    }

    @Override
    void setShowLineNumbers(boolean b) {
        super.setShowLineNumbers(b);

        cm.refresh();
    }

    private void setLineNumber(DisplaySide side, int cmLine, Integer line, String styleName) {
        SafeHtml html = SafeHtml.asis(line != null ? line.toString() : "&nbsp;");
        InlineHTML gutter = new InlineHTML(html);
        diffTable.add(gutter);
        gutter.setStyleName(styleName);
        cm.setGutterMarker(cmLine, side == DisplaySide.A ? UnifiedTable.style.lineNumbersLeft()
                : UnifiedTable.style.lineNumbersRight(), gutter.getElement());
    }

    void setLineNumber(DisplaySide side, int cmLine, int line) {
        setLineNumber(side, cmLine, line, UnifiedTable.style.unifiedLineNumber());
    }

    void setLineNumberEmpty(DisplaySide side, int cmLine) {
        setLineNumber(side, cmLine, null, UnifiedTable.style.unifiedLineNumberEmpty());
    }

    @Override
    void setSyntaxHighlighting(boolean b) {
        final DiffInfo diff = getDiff();
        if (b) {
            injectMode(diff, new AsyncCallback<Void>() {
                @Override
                public void onSuccess(Void result) {
                    if (prefs.syntaxHighlighting()) {
                        cm.setOption("mode", getContentType(diff.metaA() == null ? diff.metaB() : diff.metaA()));
                    }
                }

                @Override
                public void onFailure(Throwable caught) {
                    prefs.syntaxHighlighting(false);
                }
            });
        } else {
            cm.setOption("mode", (String) null);
        }
    }

    @Override
    void setAutoHideDiffHeader(boolean autoHide) {
        if (autoHide) {
            updateDiffTableHeader(cm.getScrollInfo());
        } else {
            diffTable.setHeaderVisible(true);
        }
        autoHideDiffTableHeader = autoHide;
    }

    private void updateDiffTableHeader(ScrollInfo si) {
        if (si.top() == 0) {
            diffTable.setHeaderVisible(true);
        } else if (si.top() > 0.5 * si.clientHeight()) {
            diffTable.setHeaderVisible(false);
        }
    }

    @Override
    Runnable updateActiveLine(CodeMirror cm) {
        return () -> {
            // The rendering of active lines has to be deferred. Reflow
            // caused by adding and removing styles chokes Firefox when arrow
            // key (or j/k) is held down. Performance on Chrome is fine
            // without the deferral.
            //
            Scheduler.get().scheduleDeferred(() -> {
                LineHandle handle = cm.getLineHandleVisualStart(cm.getCursor("end").line());
                cm.extras().activeLine(handle);
            });
        };
    }

    @Override
    CodeMirror getCmFromSide(DisplaySide side) {
        return cm;
    }

    @Override
    int getCmLine(int line, DisplaySide side) {
        return chunkManager.getCmLine(line, side);
    }

    LineRegionInfo getLineRegionInfoFromCmLine(int cmLine) {
        return chunkManager.getLineRegionInfoFromCmLine(cmLine);
    }

    @Override
    void operation(Runnable apply) {
        cm.operation(apply::run);
    }

    @Override
    CodeMirror[] getCms() {
        return new CodeMirror[] { cm };
    }

    @Override
    UnifiedTable getDiffTable() {
        return diffTable;
    }

    @Override
    UnifiedChunkManager getChunkManager() {
        return chunkManager;
    }

    @Override
    UnifiedCommentManager getCommentManager() {
        return commentManager;
    }

    @Override
    boolean isSideBySide() {
        return false;
    }

    @Override
    String getLineNumberClassName() {
        return UnifiedTable.style.unifiedLineNumber();
    }
}