Add diff preferences panel to SideBySide2
The diff settings are accessible by the "," key. They are shown on a PopupPanel that is similar to KeyHelpPopup and is placed on top of the CodeMirror on the left. Almost all settings are applied immediately after the control widget has been updated with its new value. Because CodeMirror3 only renders partial content, response time is nearly instant. Intraline differences can be hidden and shown by a trivial CSS class being added or removed, making it very efficient for the user to toggle this on or off. If the intraline data has not been loaded from the server it is first loaded in the background, and then the view is rendered again by updating the relevant markers and widgets. Most other properties (tab size, syntax highlighting, whitespace errors) are easily set on the fly by updating CodeMirror with the new option. Lines of context can now be directly entered by the user, or cleared to view the entire file with no skip bars. The panel respects most of the diff preferences. Changing column width is unlikely to be supported, and skipping of uncommented and deleted files will need a rewrite of PatchValidator. Bug: issue 2165 Inspired-by: Doug Kelly <doug.kelly@garmin.com> Change-Id: I06e2305d723bd95b5f5eafcfe3bd1b827c8d3d9f
This commit is contained in:
@@ -284,7 +284,6 @@ Missing features
|
||||
Several features have not been implemented yet:
|
||||
|
||||
* Allow to see if a reviewer can't vote on a label
|
||||
* Change diff view preferences
|
||||
|
||||
GERRIT
|
||||
------
|
||||
|
||||
@@ -33,6 +33,11 @@ public class AccountApi {
|
||||
return new RestApi("/accounts/").view("self");
|
||||
}
|
||||
|
||||
public static void putDiffPreferences(DiffPreferences in,
|
||||
AsyncCallback<DiffPreferences> cb) {
|
||||
self().view("preferences.diff").put(in, cb);
|
||||
}
|
||||
|
||||
/** Retrieve the username */
|
||||
public static void getUsername(String account, AsyncCallback<NativeString> cb) {
|
||||
new RestApi("/accounts/").id(account).view("username").get(cb);
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
// 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.account;
|
||||
|
||||
import com.google.gerrit.reviewdb.client.AccountDiffPreference;
|
||||
import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
|
||||
import com.google.gwt.core.client.JavaScriptObject;
|
||||
|
||||
public class DiffPreferences extends JavaScriptObject {
|
||||
public static DiffPreferences create(AccountDiffPreference in) {
|
||||
DiffPreferences p = createObject().cast();
|
||||
if (in == null) {
|
||||
in = AccountDiffPreference.createDefault(null);
|
||||
}
|
||||
p.ignoreWhitespace(in.getIgnoreWhitespace());
|
||||
p.tabSize(in.getTabSize());
|
||||
p.context(in.getContext());
|
||||
p.intralineDifference(in.isIntralineDifference());
|
||||
p.showLineEndings(in.isShowLineEndings());
|
||||
p.showTabs(in.isShowTabs());
|
||||
p.showWhitespaceErrors(in.isShowWhitespaceErrors());
|
||||
p.syntaxHighlighting(in.isSyntaxHighlighting());
|
||||
p.expandAllComments(in.isExpandAllComments());
|
||||
return p;
|
||||
}
|
||||
|
||||
public final void copyTo(AccountDiffPreference p) {
|
||||
p.setIgnoreWhitespace(ignoreWhitespace());
|
||||
p.setTabSize(tabSize());
|
||||
p.setContext((short)context());
|
||||
p.setIntralineDifference(intralineDifference());
|
||||
p.setShowLineEndings(showLineEndings());
|
||||
p.setShowTabs(showTabs());
|
||||
p.setShowWhitespaceErrors(showWhitespaceErrors());
|
||||
p.setSyntaxHighlighting(syntaxHighlighting());
|
||||
p.setExpandAllComments(expandAllComments());
|
||||
}
|
||||
|
||||
public final void ignoreWhitespace(Whitespace i) {
|
||||
setIgnoreWhitespaceRaw(i.toString());
|
||||
}
|
||||
private final native void setIgnoreWhitespaceRaw(String i) /*-{ this.ignore_whitespace = i }-*/;
|
||||
|
||||
public final native void tabSize(int t) /*-{ this.tab_size = t }-*/;
|
||||
public final native void context(int c) /*-{ this.context = c }-*/;
|
||||
public final native void intralineDifference(boolean i) /*-{ this.intraline_difference = i }-*/;
|
||||
public final native void showLineEndings(boolean s) /*-{ this.show_line_endings = s }-*/;
|
||||
public final native void showTabs(boolean s) /*-{ this.show_tabs = s }-*/;
|
||||
public final native void showWhitespaceErrors(boolean s) /*-{ this.show_whitespace_errors = s }-*/;
|
||||
public final native void syntaxHighlighting(boolean s) /*-{ this.syntax_highlighting = s }-*/;
|
||||
public final native void expandAllComments(boolean e) /*-{ this.expand_all_comments = e }-*/;
|
||||
|
||||
public final Whitespace ignoreWhitespace() {
|
||||
String s = ignoreWhitespaceRaw();
|
||||
return s != null ? Whitespace.valueOf(s) : Whitespace.IGNORE_NONE;
|
||||
}
|
||||
private final native String ignoreWhitespaceRaw() /*-{ return this.ignore_whitespace }-*/;
|
||||
|
||||
public final native int tabSize() /*-{ return this.tab_size }-*/;
|
||||
public final native int context() /*-{ return this.context }-*/;
|
||||
public final native boolean intralineDifference() /*-{ return this.intraline_difference }-*/;
|
||||
public final native boolean showLineEndings() /*-{ return this.show_line_endings }-*/;
|
||||
public final native boolean showTabs() /*-{ return this.show_tabs }-*/;
|
||||
public final native boolean showWhitespaceErrors() /*-{ return this.show_whitespace_errors }-*/;
|
||||
public final native boolean syntaxHighlighting() /*-{ return this.syntax_highlighting }-*/;
|
||||
public final native boolean expandAllComments() /*-{ return this.expand_all_comments }-*/;
|
||||
|
||||
protected DiffPreferences() {
|
||||
}
|
||||
}
|
||||
@@ -40,10 +40,11 @@ class DiffTable extends Composite {
|
||||
String fullscreen();
|
||||
String intralineBg();
|
||||
String diff();
|
||||
String noIntraline();
|
||||
String activeLine();
|
||||
String range();
|
||||
String rangeHighlight();
|
||||
String showtabs();
|
||||
String showTabs();
|
||||
}
|
||||
|
||||
@UiField
|
||||
|
||||
@@ -65,6 +65,8 @@ limitations under the License.
|
||||
.b .diff { background-color: #9f9; }
|
||||
.a .intralineBg { background-color: #fee; }
|
||||
.b .intralineBg { background-color: #dfd; }
|
||||
.noIntraline .a .intralineBg { background-color: #faa; }
|
||||
.noIntraline .b .intralineBg { background-color: #9f9; }
|
||||
|
||||
.fileCommentRow {
|
||||
background-color: #f7f7f7;
|
||||
@@ -103,7 +105,7 @@ limitations under the License.
|
||||
text-decoration: underline;
|
||||
z-index: 2;
|
||||
}
|
||||
.showtabs .cm-tab:before {
|
||||
.showTabs .cm-tab:before {
|
||||
content: "\00bb";
|
||||
color: #f00;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.CheckBox;
|
||||
import com.google.gwt.user.client.ui.Composite;
|
||||
import com.google.gwt.user.client.ui.HTMLPanel;
|
||||
import com.google.gwt.user.client.ui.UIObject;
|
||||
import com.google.gwtexpui.globalkey.client.KeyCommand;
|
||||
import com.google.gwtexpui.globalkey.client.KeyCommandSet;
|
||||
import com.google.gwtexpui.safehtml.client.SafeHtml;
|
||||
@@ -218,7 +219,7 @@ class Header extends Composite {
|
||||
return nextPath;
|
||||
}
|
||||
|
||||
void removeNoDiff() {
|
||||
noDiff.removeFromParent();
|
||||
void setNoDiff(boolean visible) {
|
||||
UIObject.setVisible(noDiff, visible);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// 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.account.DiffPreferences;
|
||||
import com.google.gwt.event.logical.shared.CloseEvent;
|
||||
import com.google.gwt.event.logical.shared.CloseHandler;
|
||||
import com.google.gwt.user.client.ui.PopupPanel;
|
||||
import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
|
||||
|
||||
class PreferencesAction{
|
||||
private final SideBySide2 view;
|
||||
private final DiffPreferences prefs;
|
||||
private PopupPanel popup;
|
||||
private PreferencesBox current;
|
||||
|
||||
PreferencesAction(SideBySide2 view, DiffPreferences prefs) {
|
||||
this.view = view;
|
||||
this.prefs = prefs;
|
||||
}
|
||||
|
||||
void show() {
|
||||
if (popup != null) {
|
||||
// Already open? Close the dialog.
|
||||
hide();
|
||||
return;
|
||||
}
|
||||
|
||||
current = new PreferencesBox(view);
|
||||
current.set(prefs);
|
||||
|
||||
popup = new PopupPanel(true, false);
|
||||
popup.setStyleName(current.style.dialog());
|
||||
popup.add(current);
|
||||
popup.addCloseHandler(new CloseHandler<PopupPanel>() {
|
||||
@Override
|
||||
public void onClose(CloseEvent<PopupPanel> event) {
|
||||
view.getCmB().focus();
|
||||
popup = null;
|
||||
current = null;
|
||||
}
|
||||
});
|
||||
popup.setPopupPositionAndShow(new PositionCallback() {
|
||||
@Override
|
||||
public void setPosition(int offsetWidth, int offsetHeight) {
|
||||
popup.setPopupPosition(390, 120);
|
||||
}
|
||||
});
|
||||
current.setFocus(true);
|
||||
}
|
||||
|
||||
void hide() {
|
||||
if (popup != null) {
|
||||
popup.hide();
|
||||
popup = null;
|
||||
current = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
// 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 com.google.gerrit.reviewdb.client.AccountDiffPreference.WHOLE_FILE_CONTEXT;
|
||||
import static com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace.IGNORE_ALL_SPACE;
|
||||
import static com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace.IGNORE_NONE;
|
||||
import static com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace.IGNORE_SPACE_AT_EOL;
|
||||
import static com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace.IGNORE_SPACE_CHANGE;
|
||||
import static com.google.gwt.event.dom.client.KeyCodes.KEY_ESCAPE;
|
||||
|
||||
import com.google.gerrit.client.Gerrit;
|
||||
import com.google.gerrit.client.account.AccountApi;
|
||||
import com.google.gerrit.client.account.DiffPreferences;
|
||||
import com.google.gerrit.client.patches.PatchUtil;
|
||||
import com.google.gerrit.client.rpc.GerritCallback;
|
||||
import com.google.gerrit.client.ui.NpIntTextBox;
|
||||
import com.google.gerrit.reviewdb.client.AccountDiffPreference;
|
||||
import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.event.dom.client.ChangeEvent;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.KeyDownEvent;
|
||||
import com.google.gwt.event.dom.client.KeyDownHandler;
|
||||
import com.google.gwt.event.logical.shared.ValueChangeEvent;
|
||||
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.uibinder.client.UiHandler;
|
||||
import com.google.gwt.user.client.ui.Anchor;
|
||||
import com.google.gwt.user.client.ui.Button;
|
||||
import com.google.gwt.user.client.ui.Composite;
|
||||
import com.google.gwt.user.client.ui.HTMLPanel;
|
||||
import com.google.gwt.user.client.ui.ListBox;
|
||||
import com.google.gwt.user.client.ui.PopupPanel;
|
||||
import com.google.gwt.user.client.ui.ToggleButton;
|
||||
|
||||
import net.codemirror.lib.CodeMirror;
|
||||
|
||||
/** Displays current diff preferences. */
|
||||
class PreferencesBox extends Composite {
|
||||
interface Binder extends UiBinder<HTMLPanel, PreferencesBox> {}
|
||||
private static final Binder uiBinder = GWT.create(Binder.class);
|
||||
|
||||
interface Style extends CssResource {
|
||||
String dialog();
|
||||
}
|
||||
|
||||
private final SideBySide2 view;
|
||||
private final CodeMirror cmA;
|
||||
private final CodeMirror cmB;
|
||||
private DiffPreferences prefs;
|
||||
|
||||
@UiField Style style;
|
||||
@UiField Anchor close;
|
||||
@UiField ListBox ignoreWhitespace;
|
||||
@UiField NpIntTextBox tabWidth;
|
||||
@UiField NpIntTextBox context;
|
||||
@UiField ToggleButton intralineDifference;
|
||||
@UiField ToggleButton syntaxHighlighting;
|
||||
@UiField ToggleButton whitespaceErrors;
|
||||
@UiField ToggleButton showTabs;
|
||||
@UiField ToggleButton expandAllComments;
|
||||
@UiField Button apply;
|
||||
@UiField Button save;
|
||||
|
||||
PreferencesBox(SideBySide2 view) {
|
||||
this.view = view;
|
||||
this.cmA = view.getCmA();
|
||||
this.cmB = view.getCmB();
|
||||
|
||||
initWidget(uiBinder.createAndBindUi(this));
|
||||
initIgnoreWhitespace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
super.onLoad();
|
||||
|
||||
save.setVisible(Gerrit.isSignedIn());
|
||||
addDomHandler(
|
||||
new KeyDownHandler() {
|
||||
@Override
|
||||
public void onKeyDown(KeyDownEvent event) {
|
||||
if (event.getNativeKeyCode() == KEY_ESCAPE
|
||||
|| event.getNativeKeyCode() == ',') {
|
||||
close();
|
||||
}
|
||||
}
|
||||
},
|
||||
KeyDownEvent.getType());
|
||||
}
|
||||
|
||||
void set(DiffPreferences prefs) {
|
||||
this.prefs = prefs;
|
||||
|
||||
setIgnoreWhitespace(prefs.ignoreWhitespace());
|
||||
tabWidth.setIntValue(prefs.tabSize());
|
||||
syntaxHighlighting.setValue(prefs.syntaxHighlighting());
|
||||
whitespaceErrors.setValue(prefs.showWhitespaceErrors());
|
||||
showTabs.setValue(prefs.showTabs());
|
||||
expandAllComments.setValue(prefs.expandAllComments());
|
||||
|
||||
switch (view.getIntraLineStatus()) {
|
||||
case OFF:
|
||||
case OK:
|
||||
intralineDifference.setValue(prefs.intralineDifference());
|
||||
break;
|
||||
|
||||
case TIMEOUT:
|
||||
case FAILURE:
|
||||
intralineDifference.setValue(false);
|
||||
intralineDifference.setEnabled(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if (prefs.context() == WHOLE_FILE_CONTEXT) {
|
||||
context.setText("");
|
||||
} else {
|
||||
context.setIntValue(prefs.context());
|
||||
}
|
||||
}
|
||||
|
||||
@UiHandler("ignoreWhitespace")
|
||||
void onIgnoreWhitespace(ChangeEvent e) {
|
||||
prefs.ignoreWhitespace(Whitespace.valueOf(
|
||||
ignoreWhitespace.getValue(ignoreWhitespace.getSelectedIndex())));
|
||||
view.reloadDiffInfo();
|
||||
}
|
||||
|
||||
@UiHandler("intralineDifference")
|
||||
void onIntralineDifference(ValueChangeEvent<Boolean> e) {
|
||||
prefs.intralineDifference(e.getValue());
|
||||
view.setShowIntraline(prefs.intralineDifference());
|
||||
}
|
||||
|
||||
@UiHandler("context")
|
||||
void onContext(ValueChangeEvent<String> e) {
|
||||
String v = e.getValue();
|
||||
int c;
|
||||
if (v != null && v.length() > 0) {
|
||||
c = Math.min(Math.max(0, Integer.parseInt(v)), 32767);
|
||||
} else if (v == null || v.isEmpty()) {
|
||||
c = WHOLE_FILE_CONTEXT;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
prefs.context(c);
|
||||
view.setContext(prefs.context());
|
||||
}
|
||||
|
||||
@UiHandler("tabWidth")
|
||||
void onTabWidth(ValueChangeEvent<String> e) {
|
||||
String v = e.getValue();
|
||||
if (v != null && v.length() > 0) {
|
||||
prefs.tabSize(Math.max(1, Integer.parseInt(v)));
|
||||
view.operation(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
cmA.setOption("tabSize", prefs.tabSize());
|
||||
cmB.setOption("tabSize", prefs.tabSize());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@UiHandler("expandAllComments")
|
||||
void onExpandAllComments(ValueChangeEvent<Boolean> e) {
|
||||
prefs.expandAllComments(e.getValue());
|
||||
view.setExpandAllComments(prefs.expandAllComments());
|
||||
}
|
||||
|
||||
@UiHandler("showTabs")
|
||||
void onShowTabs(ValueChangeEvent<Boolean> e) {
|
||||
prefs.showTabs(e.getValue());
|
||||
view.setShowTabs(prefs.showTabs());
|
||||
}
|
||||
|
||||
@UiHandler("syntaxHighlighting")
|
||||
void onSyntaxHighlighting(ValueChangeEvent<Boolean> e) {
|
||||
prefs.syntaxHighlighting(e.getValue());
|
||||
view.setSyntaxHighlighting(prefs.syntaxHighlighting());
|
||||
}
|
||||
|
||||
@UiHandler("whitespaceErrors")
|
||||
void onWhitespaceErrors(ValueChangeEvent<Boolean> e) {
|
||||
prefs.showWhitespaceErrors(e.getValue());
|
||||
view.operation(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
cmA.setOption("showTrailingSpace", prefs.showWhitespaceErrors());
|
||||
cmB.setOption("showTrailingSpace", prefs.showWhitespaceErrors());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@UiHandler("apply")
|
||||
void onApply(ClickEvent e) {
|
||||
close();
|
||||
}
|
||||
|
||||
@UiHandler("save")
|
||||
void onSave(ClickEvent e) {
|
||||
AccountApi.putDiffPreferences(prefs, new GerritCallback<DiffPreferences>() {
|
||||
@Override
|
||||
public void onSuccess(DiffPreferences result) {
|
||||
AccountDiffPreference p = Gerrit.getAccountDiffPreference();
|
||||
if (p == null) {
|
||||
p = AccountDiffPreference.createDefault(Gerrit.getUserAccount().getId());
|
||||
}
|
||||
result.copyTo(p);
|
||||
Gerrit.setAccountDiffPreference(p);
|
||||
}
|
||||
});
|
||||
close();
|
||||
}
|
||||
|
||||
@UiHandler("close")
|
||||
void onClose(ClickEvent e) {
|
||||
e.preventDefault();
|
||||
close();
|
||||
}
|
||||
|
||||
void setFocus(boolean focus) {
|
||||
ignoreWhitespace.setFocus(focus);
|
||||
}
|
||||
|
||||
private void close() {
|
||||
((PopupPanel) getParent()).hide();
|
||||
}
|
||||
|
||||
private void setIgnoreWhitespace(Whitespace v) {
|
||||
String name = v != null ? v.name() : IGNORE_NONE.name();
|
||||
for (int i = 0; i < ignoreWhitespace.getItemCount(); i++) {
|
||||
if (ignoreWhitespace.getValue(i).equals(name)) {
|
||||
ignoreWhitespace.setSelectedIndex(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ignoreWhitespace.setSelectedIndex(0);
|
||||
}
|
||||
|
||||
private void initIgnoreWhitespace() {
|
||||
ignoreWhitespace.addItem(
|
||||
PatchUtil.C.whitespaceIGNORE_NONE(),
|
||||
IGNORE_NONE.name());
|
||||
ignoreWhitespace.addItem(
|
||||
PatchUtil.C.whitespaceIGNORE_SPACE_AT_EOL(),
|
||||
IGNORE_SPACE_AT_EOL.name());
|
||||
ignoreWhitespace.addItem(
|
||||
PatchUtil.C.whitespaceIGNORE_SPACE_CHANGE(),
|
||||
IGNORE_SPACE_CHANGE.name());
|
||||
ignoreWhitespace.addItem(
|
||||
PatchUtil.C.whitespaceIGNORE_ALL_SPACE(),
|
||||
IGNORE_ALL_SPACE.name());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
<?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:x='urn:import:com.google.gerrit.client.ui'>
|
||||
<ui:style type='com.google.gerrit.client.diff.PreferencesBox.Style'>
|
||||
@external .gwt-TextBox;
|
||||
@external .gwt-ToggleButton .html-face;
|
||||
@external .gwt-ToggleButton-up;
|
||||
@external .gwt-ToggleButton-up-hovering;
|
||||
@external .gwt-ToggleButton-up-disabled;
|
||||
@external .gwt-ToggleButton-down;
|
||||
@external .gwt-ToggleButton-down-hovering;
|
||||
@external .gwt-ToggleButton-down-disabled;
|
||||
|
||||
.dialog {
|
||||
background: #000000 none repeat scroll 0 50%;
|
||||
color: #ffffff;
|
||||
font-family: arial,sans-serif;
|
||||
font-weight: bold;
|
||||
overflow: hidden;
|
||||
text-align: left;
|
||||
text-shadow: 1px 1px 7px #000000;
|
||||
min-width: 300px;
|
||||
opacity: 0.90;
|
||||
z-index: 200;
|
||||
-webkit-border-radius: 10px;
|
||||
-moz-border-radius: 10px;
|
||||
}
|
||||
|
||||
.dialog .box { margin: 10px; }
|
||||
.box .gwt-TextBox { padding: 0; }
|
||||
.table tr { min-height: 23px; }
|
||||
.table th,
|
||||
.table td {
|
||||
white-space: nowrap;
|
||||
color: #ffffff;
|
||||
}
|
||||
.table th {
|
||||
padding-right: 8px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.box a,
|
||||
.box a:visited,
|
||||
.box a:hover {
|
||||
color: #dddd00;
|
||||
}
|
||||
|
||||
.box .gwt-ToggleButton {
|
||||
position: relative;
|
||||
height: 19px;
|
||||
width: 140px;
|
||||
background: #fff;
|
||||
color: #000;
|
||||
text-shadow: none;
|
||||
}
|
||||
.box .gwt-ToggleButton .html-face {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 68px;
|
||||
height: 17px;
|
||||
line-height: 17px;
|
||||
text-align: center;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
.box .gwt-ToggleButton-up,
|
||||
.box .gwt-ToggleButton-up-hovering,
|
||||
.box .gwt-ToggleButton-up-disabled,
|
||||
.box .gwt-ToggleButton-down,
|
||||
.box .gwt-ToggleButton-down-hovering,
|
||||
.box .gwt-ToggleButton-down-disabled {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
.box .gwt-ToggleButton-up .html-face,
|
||||
.box .gwt-ToggleButton-up-hovering .html-face {
|
||||
left: 0;
|
||||
background: #cacaca;
|
||||
border-style: outset;
|
||||
}
|
||||
.box .gwt-ToggleButton-down .html-face,
|
||||
.box .gwt-ToggleButton-down-hovering .html-face {
|
||||
right: 0;
|
||||
background: #bcf;
|
||||
border-style: inset;
|
||||
}
|
||||
|
||||
.box button {
|
||||
margin: 6px 3px 0 0;
|
||||
border-color: rgba(0, 0, 0, 0.1);
|
||||
text-align: center;
|
||||
font-size: 8pt;
|
||||
font-weight: bold;
|
||||
border: 1px solid;
|
||||
cursor: pointer;
|
||||
color: #444;
|
||||
background-color: #f5f5f5;
|
||||
background-image: -webkit-linear-gradient(top, #f5f5f5, #f1f1f1);
|
||||
-webkit-border-radius: 2px;
|
||||
-webkit-box-sizing: content-box;
|
||||
}
|
||||
.box button div {
|
||||
color: #444;
|
||||
height: 10px;
|
||||
min-width: 54px;
|
||||
line-height: 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
button.apply {
|
||||
background-color: #4d90fe;
|
||||
background-image: -webkit-linear-gradient(top, #4d90fe, #4d90fe);
|
||||
}
|
||||
button.apply div { color: #fff; }
|
||||
|
||||
button.save {
|
||||
margin-left: 10px;
|
||||
color: #d14836;
|
||||
background-color: #d14836;
|
||||
background-image: -webkit-linear-gradient(top, #d14836, #d14836);
|
||||
}
|
||||
button.save div { color: #fff; }
|
||||
</ui:style>
|
||||
|
||||
<g:HTMLPanel styleName='{style.box}'>
|
||||
<table style='width: 100%'>
|
||||
<tr>
|
||||
<td><ui:msg>Diff Preferences</ui:msg></td>
|
||||
<td style='text-align: right'>
|
||||
<g:Anchor ui:field='close' href='javascript:;'><ui:msg>Close</ui:msg></g:Anchor>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr/>
|
||||
<table class='{style.table}'>
|
||||
<tr>
|
||||
<th><ui:msg>Ignore Whitespace</ui:msg></th>
|
||||
<td><g:ListBox ui:field='ignoreWhitespace'/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><ui:msg>Tab Width</ui:msg></th>
|
||||
<td><x:NpIntTextBox ui:field='tabWidth'
|
||||
visibleLength='4'
|
||||
alignment='RIGHT'/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><ui:msg>Lines of Context</ui:msg></th>
|
||||
<td><x:NpIntTextBox ui:field='context'
|
||||
visibleLength='4'
|
||||
alignment='RIGHT'/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><ui:msg>Intraline Difference</ui:msg></th>
|
||||
<td><g:ToggleButton ui:field='intralineDifference'>
|
||||
<g:upFace><ui:msg>Hide</ui:msg></g:upFace>
|
||||
<g:downFace><ui:msg>Show</ui:msg></g:downFace>
|
||||
</g:ToggleButton></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><ui:msg>Syntax Highlighting</ui:msg></th>
|
||||
<td><g:ToggleButton ui:field='syntaxHighlighting'>
|
||||
<g:upFace><ui:msg>Hide</ui:msg></g:upFace>
|
||||
<g:downFace><ui:msg>Show</ui:msg></g:downFace>
|
||||
</g:ToggleButton></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><ui:msg>Whitespace Errors</ui:msg></th>
|
||||
<td><g:ToggleButton ui:field='whitespaceErrors'>
|
||||
<g:upFace><ui:msg>Hide</ui:msg></g:upFace>
|
||||
<g:downFace><ui:msg>Show</ui:msg></g:downFace>
|
||||
</g:ToggleButton></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><ui:msg>Show Tabs</ui:msg></th>
|
||||
<td><g:ToggleButton ui:field='showTabs'>
|
||||
<g:upFace><ui:msg>Hide</ui:msg></g:upFace>
|
||||
<g:downFace><ui:msg>Show</ui:msg></g:downFace>
|
||||
</g:ToggleButton></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><ui:msg>Expand All Comments</ui:msg></th>
|
||||
<td><g:ToggleButton ui:field='expandAllComments'>
|
||||
<g:upFace><ui:msg>Collapse</ui:msg></g:upFace>
|
||||
<g:downFace><ui:msg>Expand</ui:msg></g:downFace>
|
||||
</g:ToggleButton></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<g:Button ui:field='apply' styleName='{style.apply}'>
|
||||
<div><ui:msg>Apply</ui:msg></div>
|
||||
</g:Button>
|
||||
<g:Button ui:field='save' styleName='{style.save}'>
|
||||
<div><ui:msg>Save</ui:msg></div>
|
||||
</g:Button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</g:HTMLPanel>
|
||||
</ui:UiBinder>
|
||||
@@ -15,6 +15,7 @@
|
||||
package com.google.gerrit.client.diff;
|
||||
|
||||
import com.google.gerrit.client.Gerrit;
|
||||
import com.google.gerrit.client.account.DiffPreferences;
|
||||
import com.google.gerrit.client.change.ChangeScreen2;
|
||||
import com.google.gerrit.client.changes.ChangeApi;
|
||||
import com.google.gerrit.client.changes.ChangeInfo;
|
||||
@@ -80,6 +81,7 @@ import net.codemirror.lib.KeyMap;
|
||||
import net.codemirror.lib.LineCharacter;
|
||||
import net.codemirror.lib.LineWidget;
|
||||
import net.codemirror.lib.ModeInjector;
|
||||
import net.codemirror.lib.TextMarker;
|
||||
import net.codemirror.lib.TextMarker.FromTo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -109,7 +111,7 @@ public class SideBySide2 extends Screen {
|
||||
private final PatchSet.Id base;
|
||||
private final PatchSet.Id revision;
|
||||
private final String path;
|
||||
private AccountDiffPreference pref;
|
||||
private DiffPreferences prefs;
|
||||
|
||||
private CodeMirror cmA;
|
||||
private CodeMirror cmB;
|
||||
@@ -121,6 +123,8 @@ public class SideBySide2 extends Screen {
|
||||
private JsArray<CommentInfo> draftsRevision;
|
||||
private DiffInfo diff;
|
||||
private LineMapper mapper;
|
||||
private List<TextMarker> markers;
|
||||
private List<Runnable> undoLineClass;
|
||||
private CommentLinkProcessor commentLinkProcessor;
|
||||
private Map<String, PublishedBox> publishedMap;
|
||||
private Map<LineHandle, CommentBox> lineActiveBoxMap;
|
||||
@@ -128,15 +132,16 @@ public class SideBySide2 extends Screen {
|
||||
private Map<LineHandle, PaddingManager> linePaddingManagerMap;
|
||||
private Map<LineHandle, LinePaddingWidgetWrapper> linePaddingOnOtherSideMap;
|
||||
private List<DiffChunkInfo> diffChunks;
|
||||
private List<SkippedLine> skips;
|
||||
private Set<SkipBar> skipBars;
|
||||
private Set<DraftBox> unsaved;
|
||||
private int context;
|
||||
|
||||
private KeyCommandSet keysNavigation;
|
||||
private KeyCommandSet keysAction;
|
||||
private KeyCommandSet keysComment;
|
||||
private List<HandlerRegistration> handlers;
|
||||
private List<Runnable> deferred;
|
||||
private PreferencesAction prefsAction;
|
||||
private int reloadVersionId;
|
||||
|
||||
public SideBySide2(
|
||||
PatchSet.Id base,
|
||||
@@ -147,12 +152,7 @@ public class SideBySide2 extends Screen {
|
||||
this.changeId = revision.getParentKey();
|
||||
this.path = path;
|
||||
|
||||
pref = Gerrit.getAccountDiffPreference();
|
||||
if (pref == null) {
|
||||
pref = AccountDiffPreference.createDefault(null);
|
||||
}
|
||||
context = pref.getContext();
|
||||
|
||||
prefs = DiffPreferences.create(Gerrit.getAccountDiffPreference());
|
||||
unsaved = new HashSet<DraftBox>();
|
||||
handlers = new ArrayList<HandlerRegistration>(6);
|
||||
// TODO: Re-implement necessary GlobalKey bindings.
|
||||
@@ -182,8 +182,8 @@ public class SideBySide2 extends Screen {
|
||||
DiffApi.diff(revision, path)
|
||||
.base(base)
|
||||
.wholeFile()
|
||||
.intraline(pref.isIntralineDifference())
|
||||
.ignoreWhitespace(pref.getIgnoreWhitespace())
|
||||
.intraline(prefs.intralineDifference())
|
||||
.ignoreWhitespace(prefs.ignoreWhitespace())
|
||||
.get(cmGroup.addFinal(new GerritCallback<DiffInfo>() {
|
||||
@Override
|
||||
public void onSuccess(DiffInfo diffInfo) {
|
||||
@@ -227,9 +227,7 @@ public class SideBySide2 extends Screen {
|
||||
commentLinkProcessor = result.getCommentLinkProcessor();
|
||||
setTheme(result.getTheme());
|
||||
|
||||
DiffInfo diffInfo = diff;
|
||||
diff = null;
|
||||
display(diffInfo);
|
||||
display(diff);
|
||||
}
|
||||
}));
|
||||
}
|
||||
@@ -265,6 +263,9 @@ public class SideBySide2 extends Screen {
|
||||
if (cmB != null) {
|
||||
cmB.getWrapperElement().removeFromParent();
|
||||
}
|
||||
if (prefsAction != null) {
|
||||
prefsAction.hide();
|
||||
}
|
||||
|
||||
Window.enableScrolling(true);
|
||||
Gerrit.setHeaderVisible(true);
|
||||
@@ -314,6 +315,12 @@ public class SideBySide2 extends Screen {
|
||||
(header.hasNext() ? header.next : header.up).go();
|
||||
}
|
||||
})
|
||||
.on("','", new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
prefsAction.show();
|
||||
}
|
||||
})
|
||||
.on("Shift-/", new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -375,6 +382,12 @@ public class SideBySide2 extends Screen {
|
||||
upToChange(true).run();
|
||||
}
|
||||
});
|
||||
keysAction.add(new KeyCommand(0, ',', PatchUtil.C.showPreferences()) {
|
||||
@Override
|
||||
public void onKeyPress(KeyPressEvent event) {
|
||||
prefsAction.show();
|
||||
}
|
||||
});
|
||||
|
||||
if (Gerrit.isSignedIn()) {
|
||||
keysAction.add(new NoOpKeyCommand(0, 'c', PatchUtil.C.commentInsert()));
|
||||
@@ -420,9 +433,6 @@ public class SideBySide2 extends Screen {
|
||||
}
|
||||
|
||||
private void display(final DiffInfo diffInfo) {
|
||||
skips = new ArrayList<SkippedLine>();
|
||||
linePaddingOnOtherSideMap = new HashMap<LineHandle, LinePaddingWidgetWrapper>();
|
||||
diffChunks = new ArrayList<DiffChunkInfo>();
|
||||
lineActiveBoxMap = new HashMap<LineHandle, CommentBox>();
|
||||
linePublishedBoxesMap = new HashMap<LineHandle, List<PublishedBox>>();
|
||||
linePaddingManagerMap = new HashMap<LineHandle, PaddingManager>();
|
||||
@@ -430,18 +440,13 @@ public class SideBySide2 extends Screen {
|
||||
publishedMap = new HashMap<String, PublishedBox>();
|
||||
}
|
||||
|
||||
if (pref.isShowTabs()) {
|
||||
diffTable.addStyleName(DiffTable.style.showtabs());
|
||||
}
|
||||
setShowTabs(prefs.showTabs());
|
||||
setShowIntraline(prefs.intralineDifference());
|
||||
|
||||
cmA = createCodeMirror(diffInfo.meta_a(), diffInfo.text_a(), diffTable.cmA);
|
||||
cmB = createCodeMirror(diffInfo.meta_b(), diffInfo.text_b(), diffTable.cmB);
|
||||
|
||||
cmA.operation(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
cmB.operation(new Runnable() {
|
||||
@Override
|
||||
operation(new Runnable() {
|
||||
public void run() {
|
||||
// Estimate initial CM3 height, fixed up in onShowView.
|
||||
int height = Window.getClientHeight()
|
||||
@@ -462,15 +467,14 @@ public class SideBySide2 extends Screen {
|
||||
if (draftsRevision != null) {
|
||||
renderDrafts(draftsRevision);
|
||||
}
|
||||
renderSkips();
|
||||
}
|
||||
});
|
||||
renderSkips(prefs.context());
|
||||
}
|
||||
});
|
||||
|
||||
registerCmEvents(cmA);
|
||||
registerCmEvents(cmB);
|
||||
|
||||
prefsAction = new PreferencesAction(this, prefs);
|
||||
scrollingGlue = GWT.create(ScrollSynchronizer.class);
|
||||
scrollingGlue.init(diffTable, cmA, cmB, mapper);
|
||||
resizeHandler = Window.addResizeHandler(new ResizeHandler() {
|
||||
@@ -490,43 +494,85 @@ public class SideBySide2 extends Screen {
|
||||
.set("cursorBlinkRate", 0)
|
||||
.set("cursorHeight", 0.85)
|
||||
.set("lineNumbers", true)
|
||||
.set("tabSize", pref.getTabSize())
|
||||
.set("tabSize", prefs.tabSize())
|
||||
.set("mode", getContentType(meta))
|
||||
.set("lineWrapping", false)
|
||||
.set("styleSelectedText", true)
|
||||
.set("showTrailingSpace", pref.isShowWhitespaceErrors())
|
||||
.set("showTrailingSpace", prefs.showWhitespaceErrors())
|
||||
.set("keyMap", "vim_ro")
|
||||
.set("value", meta != null ? contents : "");
|
||||
return CodeMirror.create(parent, cfg);
|
||||
}
|
||||
|
||||
DiffInfo.IntraLineStatus getIntraLineStatus() {
|
||||
return diff.intraline_status();
|
||||
}
|
||||
|
||||
void setShowTabs(boolean b) {
|
||||
if (b) {
|
||||
diffTable.addStyleName(DiffTable.style.showTabs());
|
||||
} else {
|
||||
diffTable.removeStyleName(DiffTable.style.showTabs());
|
||||
}
|
||||
}
|
||||
|
||||
void setShowIntraline(boolean b) {
|
||||
if (b && getIntraLineStatus() == DiffInfo.IntraLineStatus.OFF) {
|
||||
reloadDiffInfo();
|
||||
} else if (b) {
|
||||
diffTable.removeStyleName(DiffTable.style.noIntraline());
|
||||
} else {
|
||||
diffTable.addStyleName(DiffTable.style.noIntraline());
|
||||
}
|
||||
}
|
||||
|
||||
void setSyntaxHighlighting(final boolean b) {
|
||||
operation(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
cmA.setOption("mode", b ? getContentType(diff.meta_a()) : null);
|
||||
cmB.setOption("mode", b ? getContentType(diff.meta_b()) : null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void setContext(final int context) {
|
||||
operation(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
renderSkips(context);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void setExpandAllComments(boolean b) {
|
||||
for (PublishedBox box : publishedMap.values()) {
|
||||
box.setOpen(b);
|
||||
}
|
||||
}
|
||||
|
||||
private void render(DiffInfo diff) {
|
||||
JsArray<Region> regions = diff.content();
|
||||
if (!(regions.length() == 0 ||
|
||||
regions.length() == 1 && regions.get(0).ab() != null)) {
|
||||
header.removeNoDiff();
|
||||
}
|
||||
|
||||
header.setNoDiff(regions.length() == 0
|
||||
|| (regions.length() == 1 && regions.get(0).ab() != null));
|
||||
|
||||
mapper = new LineMapper();
|
||||
markers = new ArrayList<TextMarker>();
|
||||
undoLineClass = new ArrayList<Runnable>();
|
||||
linePaddingOnOtherSideMap = new HashMap<LineHandle, LinePaddingWidgetWrapper>();
|
||||
diffChunks = new ArrayList<DiffChunkInfo>();
|
||||
|
||||
String diffColor = diff.meta_a() == null || diff.meta_b() == null
|
||||
? DiffTable.style.intralineBg()
|
||||
: DiffTable.style.diff();
|
||||
mapper = new LineMapper();
|
||||
|
||||
for (int i = 0; i < regions.length(); i++) {
|
||||
Region current = regions.get(i);
|
||||
int origLineA = mapper.getLineA();
|
||||
int origLineB = mapper.getLineB();
|
||||
if (current.ab() != null) { // Common
|
||||
int length = current.ab().length();
|
||||
mapper.appendCommon(length);
|
||||
if (i == 0 && length > context + 1) {
|
||||
skips.add(new SkippedLine(0, 0, length - context));
|
||||
} else if (i == regions.length() - 1 && length > context + 1) {
|
||||
skips.add(new SkippedLine(origLineA + context, origLineB + context,
|
||||
length - context));
|
||||
} else if (length > 2 * context + 1) {
|
||||
skips.add(new SkippedLine(origLineA + context, origLineB + context,
|
||||
length - 2 * context));
|
||||
}
|
||||
mapper.appendCommon(current.ab().length());
|
||||
} else { // Insert, Delete or Edit
|
||||
JsArrayString currentA = current.a() == null ? EMPTY : current.a();
|
||||
JsArrayString currentB = current.b() == null ? EMPTY : current.b();
|
||||
@@ -732,6 +778,9 @@ public class SideBySide2 extends Screen {
|
||||
CodeMirror cm = getCmFromSide(side);
|
||||
PublishedBox box = new PublishedBox(this, cm, side, commentLinkProcessor,
|
||||
getPatchSetIdFromSide(side), info);
|
||||
if (prefs.expandAllComments()) {
|
||||
box.setOpen(true);
|
||||
}
|
||||
publishedMap.put(info.id(), box);
|
||||
if (!info.has_line()) {
|
||||
diffTable.addFileCommentBox(box);
|
||||
@@ -766,6 +815,9 @@ public class SideBySide2 extends Screen {
|
||||
DraftBox box = new DraftBox(
|
||||
this, getCmFromSide(side), side, commentLinkProcessor,
|
||||
getPatchSetIdFromSide(side), info);
|
||||
if (prefs.expandAllComments()) {
|
||||
box.setOpen(true);
|
||||
}
|
||||
if (publishedBase != null || publishedRevision != null) {
|
||||
PublishedBox replyToBox = publishedMap.get(info.in_reply_to());
|
||||
if (replyToBox != null) {
|
||||
@@ -782,15 +834,40 @@ public class SideBySide2 extends Screen {
|
||||
}
|
||||
}
|
||||
|
||||
private void renderSkips() {
|
||||
private void renderSkips(int context) {
|
||||
clearSkipBars();
|
||||
|
||||
if (context == AccountDiffPreference.WHOLE_FILE_CONTEXT) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: This is not optimal, but shouldn't bee too costly in most cases.
|
||||
* Maybe rewrite after done keeping track of diff chunk positions.
|
||||
*/
|
||||
JsArray<Region> regions = diff.content();
|
||||
List<SkippedLine> skips = new ArrayList<SkippedLine>();
|
||||
int lineA = 0, lineB = 0;
|
||||
for (int i = 0; i < regions.length(); i++) {
|
||||
Region current = regions.get(i);
|
||||
if (current.ab() != null) {
|
||||
int len = current.ab().length();
|
||||
if (i == 0 && len > context + 1) {
|
||||
skips.add(new SkippedLine(0, 0, len - context));
|
||||
} else if (i == regions.length() - 1 && len > context + 1) {
|
||||
skips.add(new SkippedLine(lineA + context, lineB + context,
|
||||
len - context));
|
||||
} else if (len > 2 * context + 1) {
|
||||
skips.add(new SkippedLine(lineA + context, lineB + context,
|
||||
len - 2 * context));
|
||||
}
|
||||
lineA += len;
|
||||
lineB += len;
|
||||
} else {
|
||||
lineA += current.a() != null ? current.a().length() : 0;
|
||||
lineB += current.b() != null ? current.b().length() : 0;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This is not optimal, but shouldn't be too costly in most cases.
|
||||
// Maybe rewrite after done keeping track of diff chunk positions.
|
||||
skipBars = new HashSet<SkipBar>();
|
||||
for (CommentBox box : lineActiveBoxMap.values()) {
|
||||
List<SkippedLine> temp = new ArrayList<SkippedLine>();
|
||||
for (SkippedLine skip : skips) {
|
||||
@@ -827,9 +904,45 @@ public class SideBySide2 extends Screen {
|
||||
SkipBar barA = renderSkipHelper(cmA, skip);
|
||||
SkipBar barB = renderSkipHelper(cmB, skip);
|
||||
SkipBar.link(barA, barB);
|
||||
skipBars.add(barA);
|
||||
skipBars.add(barB);
|
||||
}
|
||||
}
|
||||
|
||||
private void clearSkipBars() {
|
||||
if (skipBars != null) {
|
||||
for (SkipBar bar : skipBars) {
|
||||
bar.expandAll();
|
||||
}
|
||||
skipBars = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void clearMarkers() {
|
||||
if (markers != null) {
|
||||
for (TextMarker m : markers) {
|
||||
m.clear();
|
||||
}
|
||||
markers = null;
|
||||
}
|
||||
if (undoLineClass != null) {
|
||||
for (Runnable r : undoLineClass) {
|
||||
r.run();
|
||||
}
|
||||
undoLineClass = null;
|
||||
}
|
||||
if (linePaddingOnOtherSideMap != null) {
|
||||
for (LinePaddingWidgetWrapper x : linePaddingOnOtherSideMap.values()) {
|
||||
x.getWidget().clear();
|
||||
}
|
||||
linePaddingOnOtherSideMap = null;
|
||||
}
|
||||
}
|
||||
|
||||
void remove(SkipBar bar) {
|
||||
skipBars.remove(bar);
|
||||
}
|
||||
|
||||
private void checkAndAddSkip(List<SkippedLine> list, SkippedLine toAdd) {
|
||||
if (toAdd.getSize() > 1) {
|
||||
list.add(toAdd);
|
||||
@@ -840,7 +953,7 @@ public class SideBySide2 extends Screen {
|
||||
int size = skip.getSize();
|
||||
int markStart = cm == cmA ? skip.getStartA() : skip.getStartB();
|
||||
int markEnd = markStart + size - 1;
|
||||
SkipBar bar = new SkipBar(cm);
|
||||
SkipBar bar = new SkipBar(this, cm);
|
||||
diffTable.add(bar);
|
||||
Configuration markerConfig = Configuration.create()
|
||||
.set("collapsed", true)
|
||||
@@ -900,22 +1013,36 @@ public class SideBySide2 extends Screen {
|
||||
LineCharacter to = iter.advance(span.mark());
|
||||
int fromLine = from.getLine();
|
||||
if (last.getLine() == fromLine) {
|
||||
cm.markText(last, from, intralineBgOpt);
|
||||
markers.add(cm.markText(last, from, intralineBgOpt));
|
||||
} else {
|
||||
cm.markText(CodeMirror.pos(fromLine, 0), from, intralineBgOpt);
|
||||
markers.add(cm.markText(CodeMirror.pos(fromLine, 0), from, intralineBgOpt));
|
||||
}
|
||||
cm.markText(from, to, diffOpt);
|
||||
markers.add(cm.markText(from, to, diffOpt));
|
||||
last = to;
|
||||
for (int line = fromLine; line < to.getLine(); line++) {
|
||||
cm.addLineClass(line, LineClassWhere.BACKGROUND,
|
||||
DiffTable.style.diff());
|
||||
}
|
||||
colorLines(cm, LineClassWhere.BACKGROUND,
|
||||
DiffTable.style.diff(),
|
||||
fromLine, to.getLine());
|
||||
}
|
||||
}
|
||||
|
||||
private void colorLines(CodeMirror cm, String color, int line, int cnt) {
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
cm.addLineClass(line + i, LineClassWhere.WRAP, color);
|
||||
colorLines(cm, LineClassWhere.WRAP, color, line, line + cnt);
|
||||
}
|
||||
|
||||
private void colorLines(final CodeMirror cm, final LineClassWhere where,
|
||||
final String className, final int start, final int end) {
|
||||
if (start < end) {
|
||||
for (int line = start; line < end; line++) {
|
||||
cm.addLineClass(line, where, className);
|
||||
}
|
||||
undoLineClass.add(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (int line = start; line < end; line++) {
|
||||
cm.removeLineClass(line, where, className);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1259,11 +1386,7 @@ public class SideBySide2 extends Screen {
|
||||
@Override
|
||||
public void execute() {
|
||||
deferred = null;
|
||||
cmA.operation(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
cmB.operation(new Runnable() {
|
||||
@Override
|
||||
operation(new Runnable() {
|
||||
public void run() {
|
||||
for (Runnable thunk : list) {
|
||||
thunk.run();
|
||||
@@ -1273,8 +1396,6 @@ public class SideBySide2 extends Screen {
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
deferred.add(thunk);
|
||||
}
|
||||
|
||||
@@ -1364,7 +1485,7 @@ public class SideBySide2 extends Screen {
|
||||
}
|
||||
|
||||
private String getContentType(DiffInfo.FileMeta meta) {
|
||||
return pref.isSyntaxHighlighting()
|
||||
return prefs.syntaxHighlighting()
|
||||
&& meta != null
|
||||
&& meta.content_type() != null
|
||||
? ModeInjector.getContentType(meta.content_type())
|
||||
@@ -1379,14 +1500,28 @@ public class SideBySide2 extends Screen {
|
||||
return cmB;
|
||||
}
|
||||
|
||||
void operation(final Runnable apply) {
|
||||
cmA.operation(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
cmB.operation(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
apply.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void prefetchNextFile() {
|
||||
String nextPath = header.getNextPath();
|
||||
if (nextPath != null) {
|
||||
DiffApi.diff(revision, nextPath)
|
||||
.base(base)
|
||||
.wholeFile()
|
||||
.intraline(pref.isIntralineDifference())
|
||||
.ignoreWhitespace(pref.getIgnoreWhitespace())
|
||||
.intraline(prefs.intralineDifference())
|
||||
.ignoreWhitespace(prefs.ignoreWhitespace())
|
||||
.get(new AsyncCallback<DiffInfo>() {
|
||||
@Override
|
||||
public void onSuccess(DiffInfo info) {
|
||||
@@ -1402,4 +1537,32 @@ public class SideBySide2 extends Screen {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void reloadDiffInfo() {
|
||||
final int id = ++reloadVersionId;
|
||||
DiffApi.diff(revision, path)
|
||||
.base(base)
|
||||
.wholeFile()
|
||||
.intraline(prefs.intralineDifference())
|
||||
.ignoreWhitespace(prefs.ignoreWhitespace())
|
||||
.get(new GerritCallback<DiffInfo>() {
|
||||
@Override
|
||||
public void onSuccess(DiffInfo diffInfo) {
|
||||
if (id == reloadVersionId && isAttached()) {
|
||||
diff = diffInfo;
|
||||
operation(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
clearSkipBars();
|
||||
clearMarkers();
|
||||
diffTable.sidePanel.clearDiffGutters();
|
||||
setShowIntraline(prefs.intralineDifference());
|
||||
render(diff);
|
||||
renderSkips(prefs.context());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import net.codemirror.lib.CodeMirror;
|
||||
import net.codemirror.lib.LineCharacter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/** The Widget that handles the scrollbar gutters */
|
||||
@@ -135,12 +136,27 @@ class SidePanel extends Composite {
|
||||
gutters.remove(wrapper);
|
||||
}
|
||||
|
||||
@SuppressWarnings("incomplete-switch")
|
||||
void clearDiffGutters() {
|
||||
for (Iterator<GutterWrapper> i = gutters.iterator(); i.hasNext();) {
|
||||
GutterWrapper wrapper = i.next();
|
||||
switch (wrapper.type) {
|
||||
case INSERT:
|
||||
case DELETE:
|
||||
case EDIT:
|
||||
wrapper.gutter.removeFromParent();
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class GutterWrapper {
|
||||
private SidePanel host;
|
||||
private Label gutter;
|
||||
private CodeMirror cm;
|
||||
private int line;
|
||||
private HandlerRegistration regClick;
|
||||
private GutterType type;
|
||||
|
||||
GutterWrapper(SidePanel host, Label anchor, CodeMirror cm, int line,
|
||||
GutterType type) {
|
||||
@@ -148,6 +164,7 @@ class SidePanel extends Composite {
|
||||
this.gutter = anchor;
|
||||
this.cm = cm;
|
||||
this.line = line;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
private void replaceClickHandler(ClickHandler newHandler) {
|
||||
|
||||
@@ -46,8 +46,6 @@ class SkipBar extends Composite {
|
||||
.set("inclusiveLeft", true)
|
||||
.set("inclusiveRight", true);
|
||||
|
||||
private LineWidget widget;
|
||||
|
||||
interface SkipBarStyle extends CssResource {
|
||||
String noExpand();
|
||||
}
|
||||
@@ -64,13 +62,16 @@ class SkipBar extends Composite {
|
||||
@UiField
|
||||
SkipBarStyle style;
|
||||
|
||||
private final SideBySide2 parent;
|
||||
private final CodeMirror cm;
|
||||
private LineWidget widget;
|
||||
private TextMarker marker;
|
||||
private SkipBar otherBar;
|
||||
private CodeMirror cm;
|
||||
private int numSkipLines;
|
||||
|
||||
SkipBar(CodeMirror cmInstance) {
|
||||
cm = cmInstance;
|
||||
SkipBar(SideBySide2 parent, final CodeMirror cm) {
|
||||
this.parent = parent;
|
||||
this.cm = cm;
|
||||
skipNum = new Anchor(true);
|
||||
upArrow = new Anchor(true);
|
||||
downArrow = new Anchor(true);
|
||||
@@ -129,10 +130,9 @@ class SkipBar extends Composite {
|
||||
widget.clear();
|
||||
}
|
||||
|
||||
private void expandAll() {
|
||||
void expandAll() {
|
||||
clearMarkerAndWidget();
|
||||
removeFromParent();
|
||||
cm.focus();
|
||||
}
|
||||
|
||||
private void expandBefore() {
|
||||
@@ -148,7 +148,6 @@ class SkipBar extends Composite {
|
||||
.set("noHScroll", true);
|
||||
setWidget(cm.addLineWidget(newStart - 1, getElement(), config));
|
||||
updateSkipNum();
|
||||
cm.focus();
|
||||
}
|
||||
|
||||
private void expandAfter() {
|
||||
@@ -168,24 +167,27 @@ class SkipBar extends Composite {
|
||||
setWidget(cm.addLineWidget(newEnd + 1, getElement(), config));
|
||||
}
|
||||
updateSkipNum();
|
||||
cm.focus();
|
||||
}
|
||||
|
||||
@UiHandler("skipNum")
|
||||
void onExpandAll(ClickEvent e) {
|
||||
parent.remove(this);
|
||||
otherBar.expandAll();
|
||||
expandAll();
|
||||
cm.focus();
|
||||
}
|
||||
|
||||
@UiHandler("upArrow")
|
||||
void onExpandBefore(ClickEvent e) {
|
||||
otherBar.expandBefore();
|
||||
expandBefore();
|
||||
cm.focus();
|
||||
}
|
||||
|
||||
@UiHandler("downArrow")
|
||||
void onExpandAfter(ClickEvent e) {
|
||||
otherBar.expandAfter();
|
||||
expandAfter();
|
||||
cm.focus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ public interface PatchConstants extends Constants {
|
||||
String fileList();
|
||||
String expandComment();
|
||||
String expandAllCommentsOnCurrentLine();
|
||||
String showPreferences();
|
||||
|
||||
String toggleReviewed();
|
||||
String markAsReviewedAndGoToNext();
|
||||
|
||||
@@ -34,6 +34,7 @@ commentNext = Next comment
|
||||
fileList = Browse files in patch set
|
||||
expandComment = Expand or collapse comment
|
||||
expandAllCommentsOnCurrentLine = Expand or collapse all comments on current line
|
||||
showPreferences = Show diff preferences
|
||||
|
||||
toggleReviewed = Toggle the reviewed flag
|
||||
markAsReviewedAndGoToNext = Mark patch as reviewed and go to next unreviewed patch
|
||||
|
||||
@@ -44,6 +44,10 @@ public class CodeMirror extends JavaScriptObject {
|
||||
this.setOption(option, value);
|
||||
}-*/;
|
||||
|
||||
public final native void setOption(String option, String value) /*-{
|
||||
this.setOption(option, value);
|
||||
}-*/;
|
||||
|
||||
public final native void setOption(String option, JavaScriptObject val) /*-{
|
||||
this.setOption(option, val);
|
||||
}-*/;
|
||||
|
||||
Reference in New Issue
Block a user