Merge changes I844d49c7,I8d107a70,Ie54063f3,I980afc38

* changes:
  InlineEdit: Allow to activate auto-close brackets Codemirror addon
  InlineEdit: Allow to activate match brackets Codemirror addon
  InlineEdit: Make cursor blink rate customizable
  InlineEdit: Support Emacs and Vim key maps
This commit is contained in:
David Pursehouse 2015-09-17 00:25:12 +00:00 committed by Gerrit Code Review
commit 39a05b6f00
10 changed files with 194 additions and 10 deletions

View File

@ -1339,11 +1339,15 @@ link:#edit-preferences-info[EditPreferencesInfo] entity.
)]}'
{
"theme": "ECLIPSE",
"key_map_type": "VIM",
"tab_size": 4,
"line_length": 80,
"cursor_blink_rate": 530,
"hide_top_menu": true,
"show_whitespace_errors": true,
"hide_line_numbers": true
"hide_line_numbers": true,
"match_brackets": true,
"auto_close_brackets": true
}
----
@ -1365,13 +1369,17 @@ link:#edit-preferences-info[EditPreferencesInfo] entity.
{
"theme": "ECLIPSE",
"key_map_type": "VIM",
"tab_size": 4,
"line_length": 80,
"cursor_blink_rate": 530,
"hide_top_menu": true,
"show_tabs": true,
"show_whitespace_errors": true,
"syntax_highlighting": true,
"hide_line_numbers": true
"hide_line_numbers": true,
"match_brackets": true,
"auto_close_brackets": true
}
----
@ -1757,10 +1765,16 @@ preferences of a user.
The CodeMirror theme. Currently only a subset of light and dark
CodeMirror themes are supported. Light themes `DEFAULT`, `ECLIPSE`,
`ELEGANT`, `NEAT`. Dark themes `MIDNIGHT`, `NIGHT`, `TWILIGHT`.
|`key_map_type` ||
The CodeMirror key map. Currently only a subset of key maps are
supported: `DEFAULT`, `EMACS`, `VIM`.
|`tab_size` ||
Number of spaces that should be used to display one tab.
|`line_length` ||
Number of characters that should be displayed per line.
|`cursor_blink_rate` ||
Half-period in milliseconds used for cursor blinking.
Setting it to 0 disables cursor blinking.
|`hide_top_menu` |not set if `false`|
If true the top menu header and site header is hidden.
|`show_tabs` |not set if `false`|
@ -1771,6 +1785,10 @@ Whether whitespace errors should be shown.
Whether syntax highlighting should be enabled.
|`hide_line_numbers` |not set if `false`|
Whether line numbers should be hidden.
|`match_brackets` |not set if `false`|
Whether matching brackets should be highlighted.
|`auto_close_brackets` |not set if `false`|
Whether brackets and quotes should be auto-closed during typing.
|===========================================
[[email-info]]

View File

@ -176,9 +176,6 @@ with thefollowing logic on click:
** "save-when-file-was-changed" or
** "close-when-no-changes"
* Allow to activate different key maps, supported by CM: Emacs, Sublime, Vim. Load key
maps dynamically. Currently default mode is used.
* Implement conflict resolution during rebase of change edit using inline edit
feature by creating new edit on top of current patch set with auto merge content

View File

@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.extensions.client.EditPreferencesInfo;
import com.google.gerrit.extensions.client.KeyMapType;
import com.google.gerrit.extensions.client.Theme;
import org.apache.http.HttpStatus;
@ -36,22 +37,30 @@ public class EditPreferencesIT extends AbstractDaemonTest {
assertThat(out.lineLength).isEqualTo(100);
assertThat(out.tabSize).isEqualTo(8);
assertThat(out.cursorBlinkRate).isEqualTo(0);
assertThat(out.hideTopMenu).isNull();
assertThat(out.showTabs).isTrue();
assertThat(out.showWhitespaceErrors).isNull();
assertThat(out.syntaxHighlighting).isTrue();
assertThat(out.hideLineNumbers).isNull();
assertThat(out.matchBrackets).isTrue();
assertThat(out.autoCloseBrackets).isNull();
assertThat(out.theme).isEqualTo(Theme.DEFAULT);
assertThat(out.keyMapType).isEqualTo(KeyMapType.DEFAULT);
// change some default values
out.lineLength = 80;
out.tabSize = 4;
out.cursorBlinkRate = 500;
out.hideTopMenu = true;
out.showTabs = false;
out.showWhitespaceErrors = true;
out.syntaxHighlighting = false;
out.hideLineNumbers = true;
out.matchBrackets = false;
out.autoCloseBrackets = true;
out.theme = Theme.TWILIGHT;
out.keyMapType = KeyMapType.EMACS;
r = adminSession.put(endPoint, out);
assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT);
@ -72,11 +81,15 @@ public class EditPreferencesIT extends AbstractDaemonTest {
EditPreferencesInfo in) {
assertThat(out.lineLength).isEqualTo(in.lineLength);
assertThat(out.tabSize).isEqualTo(in.tabSize);
assertThat(out.cursorBlinkRate).isEqualTo(in.cursorBlinkRate);
assertThat(out.hideTopMenu).isEqualTo(in.hideTopMenu);
assertThat(out.showTabs).isNull();
assertThat(out.showWhitespaceErrors).isEqualTo(in.showWhitespaceErrors);
assertThat(out.syntaxHighlighting).isNull();
assertThat(out.hideLineNumbers).isEqualTo(in.hideLineNumbers);
assertThat(out.matchBrackets).isNull();
assertThat(out.autoCloseBrackets).isEqualTo(in.autoCloseBrackets);
assertThat(out.theme).isEqualTo(in.theme);
assertThat(out.keyMapType).isEqualTo(in.keyMapType);
}
}

View File

@ -18,23 +18,31 @@ package com.google.gerrit.extensions.client;
public class EditPreferencesInfo {
public Integer tabSize;
public Integer lineLength;
public Integer cursorBlinkRate;
public Boolean hideTopMenu;
public Boolean showTabs;
public Boolean showWhitespaceErrors;
public Boolean syntaxHighlighting;
public Boolean hideLineNumbers;
public Boolean matchBrackets;
public Boolean autoCloseBrackets;
public Theme theme;
public KeyMapType keyMapType;
public static EditPreferencesInfo defaults() {
EditPreferencesInfo i = new EditPreferencesInfo();
i.tabSize = 8;
i.lineLength = 100;
i.cursorBlinkRate = 0;
i.hideTopMenu = false;
i.showTabs = true;
i.showWhitespaceErrors = false;
i.syntaxHighlighting = true;
i.hideLineNumbers = false;
i.matchBrackets = true;
i.autoCloseBrackets = false;
i.theme = Theme.DEFAULT;
i.keyMapType = KeyMapType.DEFAULT;
return i;
}
}

View File

@ -0,0 +1,21 @@
// 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 com.google.gerrit.extensions.client;
public enum KeyMapType {
DEFAULT,
EMACS,
VIM
}

View File

@ -15,6 +15,7 @@
package com.google.gerrit.client.account;
import com.google.gerrit.extensions.client.EditPreferencesInfo;
import com.google.gerrit.extensions.client.KeyMapType;
import com.google.gerrit.extensions.client.Theme;
import com.google.gwt.core.client.JavaScriptObject;
@ -23,24 +24,32 @@ public class EditPreferences extends JavaScriptObject {
EditPreferences p = createObject().cast();
p.tabSize(in.tabSize);
p.lineLength(in.lineLength);
p.cursorBlinkRate(in.cursorBlinkRate);
p.hideTopMenu(in.hideTopMenu);
p.showTabs(in.showTabs);
p.showWhitespaceErrors(in.showWhitespaceErrors);
p.syntaxHighlighting(in.syntaxHighlighting);
p.hideLineNumbers(in.hideLineNumbers);
p.matchBrackets(in.matchBrackets);
p.autoCloseBrackets(in.autoCloseBrackets);
p.theme(in.theme);
p.keyMapType(in.keyMapType);
return p;
}
public final void copyTo(EditPreferencesInfo p) {
p.tabSize = tabSize();
p.lineLength = lineLength();
p.cursorBlinkRate = cursorBlinkRate();
p.hideTopMenu = hideTopMenu();
p.showTabs = showTabs();
p.showWhitespaceErrors = showWhitespaceErrors();
p.syntaxHighlighting = syntaxHighlighting();
p.hideLineNumbers = hideLineNumbers();
p.matchBrackets = matchBrackets();
p.autoCloseBrackets = autoCloseBrackets();
p.theme = theme();
p.keyMapType = keyMapType();
}
public final void theme(Theme i) {
@ -48,13 +57,21 @@ public class EditPreferences extends JavaScriptObject {
}
private final native void setThemeRaw(String i) /*-{ this.theme = i }-*/;
public final void keyMapType(KeyMapType i) {
setkeyMapTypeRaw(i != null ? i.toString() : KeyMapType.DEFAULT.toString());
}
private final native void setkeyMapTypeRaw(String i) /*-{ this.key_map_type = i }-*/;
public final native void tabSize(int t) /*-{ this.tab_size = t }-*/;
public final native void lineLength(int c) /*-{ this.line_length = c }-*/;
public final native void cursorBlinkRate(int r) /*-{ this.cursor_blink_rate = r }-*/;
public final native void hideTopMenu(boolean s) /*-{ this.hide_top_menu = 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 hideLineNumbers(boolean s) /*-{ this.hide_line_numbers = s }-*/;
public final native void matchBrackets(boolean m) /*-{ this.match_brackets = m }-*/;
public final native void autoCloseBrackets(boolean c) /*-{ this.auto_close_brackets = c }-*/;
public final Theme theme() {
String s = themeRaw();
@ -62,13 +79,22 @@ public class EditPreferences extends JavaScriptObject {
}
private final native String themeRaw() /*-{ return this.theme }-*/;
public final KeyMapType keyMapType() {
String s = keyMapTypeRaw();
return s != null ? KeyMapType.valueOf(s) : KeyMapType.DEFAULT;
}
private final native String keyMapTypeRaw() /*-{ return this.key_map_type }-*/;
public final int tabSize() {return get("tab_size", 8); }
public final int lineLength() {return get("line_length", 100); }
public final int cursorBlinkRate() {return get("cursor_blink_rate", 0); }
public final native boolean hideTopMenu() /*-{ return this.hide_top_menu || false }-*/;
public final native boolean showTabs() /*-{ return this.show_tabs || false }-*/;
public final native boolean showWhitespaceErrors() /*-{ return this.show_whitespace_errors || false }-*/;
public final native boolean syntaxHighlighting() /*-{ return this.syntax_highlighting || false }-*/;
public final native boolean hideLineNumbers() /*-{ return this.hide_line_numbers || false }-*/;
public final native boolean matchBrackets() /*-{ return this.match_brackets || false }-*/;
public final native boolean autoCloseBrackets() /*-{ return this.auto_close_brackets || false }-*/;
private final native int get(String n, int d) /*-{ return this.hasOwnProperty(n) ? this[n] : d }-*/;
protected EditPreferences() {

View File

@ -20,6 +20,7 @@ import com.google.gerrit.client.account.AccountApi;
import com.google.gerrit.client.account.EditPreferences;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.ui.NpIntTextBox;
import com.google.gerrit.extensions.client.KeyMapType;
import com.google.gerrit.extensions.client.Theme;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ChangeEvent;
@ -55,12 +56,16 @@ class EditPreferencesBox extends Composite {
@UiField Anchor close;
@UiField NpIntTextBox tabWidth;
@UiField NpIntTextBox lineLength;
@UiField NpIntTextBox cursorBlinkRate;
@UiField ToggleButton topMenu;
@UiField ToggleButton syntaxHighlighting;
@UiField ToggleButton showTabs;
@UiField ToggleButton whitespaceErrors;
@UiField ToggleButton lineNumbers;
@UiField ToggleButton matchBrackets;
@UiField ToggleButton autoCloseBrackets;
@UiField ListBox theme;
@UiField ListBox keyMap;
@UiField Button apply;
@UiField Button save;
@ -68,6 +73,7 @@ class EditPreferencesBox extends Composite {
this.view = view;
initWidget(uiBinder.createAndBindUi(this));
initTheme();
initKeyMapType();
}
void set(EditPreferences prefs) {
@ -75,12 +81,16 @@ class EditPreferencesBox extends Composite {
tabWidth.setIntValue(prefs.tabSize());
lineLength.setIntValue(prefs.lineLength());
cursorBlinkRate.setIntValue(prefs.cursorBlinkRate());
topMenu.setValue(!prefs.hideTopMenu());
syntaxHighlighting.setValue(prefs.syntaxHighlighting());
showTabs.setValue(prefs.showTabs());
whitespaceErrors.setValue(prefs.showWhitespaceErrors());
lineNumbers.setValue(prefs.hideLineNumbers());
matchBrackets.setValue(prefs.matchBrackets());
autoCloseBrackets.setValue(prefs.autoCloseBrackets());
setTheme(prefs.theme());
setKeyMapType(prefs.keyMapType());
}
@UiHandler("tabWidth")
@ -101,6 +111,17 @@ class EditPreferencesBox extends Composite {
}
}
@UiHandler("cursorBlinkRate")
void onCursoBlinkRate(ValueChangeEvent<String> e) {
String v = e.getValue();
if (v != null && v.length() > 0) {
// A negative value hides the cursor entirely:
// don't let user shoot himself in the foot.
prefs.cursorBlinkRate(Math.max(0, Integer.parseInt(v)));
view.getEditor().setOption("cursorBlinkRate", prefs.cursorBlinkRate());
}
}
@UiHandler("topMenu")
void onTopMenu(ValueChangeEvent<Boolean> e) {
prefs.hideTopMenu(!e.getValue());
@ -132,6 +153,18 @@ class EditPreferencesBox extends Composite {
view.setSyntaxHighlighting(prefs.syntaxHighlighting());
}
@UiHandler("matchBrackets")
void onMatchBrackets(ValueChangeEvent<Boolean> e) {
prefs.matchBrackets(e.getValue());
view.getEditor().setOption("matchBrackets", prefs.matchBrackets());
}
@UiHandler("autoCloseBrackets")
void onCloseBrackets(ValueChangeEvent<Boolean> e) {
prefs.autoCloseBrackets(e.getValue());
view.getEditor().setOption("autoCloseBrackets", prefs.autoCloseBrackets());
}
@UiHandler("theme")
void onTheme(@SuppressWarnings("unused") ChangeEvent e) {
final Theme newTheme = Theme.valueOf(theme.getValue(theme.getSelectedIndex()));
@ -150,6 +183,14 @@ class EditPreferencesBox extends Composite {
});
}
@UiHandler("keyMap")
void onKeyMap(@SuppressWarnings("unused") ChangeEvent e) {
KeyMapType keyMapType = KeyMapType.valueOf(
keyMap.getValue(keyMap.getSelectedIndex()));
prefs.keyMapType(keyMapType);
view.getEditor().setOption("keyMap", keyMapType.name().toLowerCase());
}
@UiHandler("apply")
void onApply(@SuppressWarnings("unused") ClickEvent e) {
close();
@ -210,4 +251,27 @@ class EditPreferencesBox extends Composite {
Theme.TWILIGHT.name().toLowerCase(),
Theme.TWILIGHT.name());
}
private void setKeyMapType(KeyMapType v) {
String name = v != null ? v.name() : KeyMapType.DEFAULT.name();
for (int i = 0; i < keyMap.getItemCount(); i++) {
if (keyMap.getValue(i).equals(name)) {
keyMap.setSelectedIndex(i);
return;
}
}
keyMap.setSelectedIndex(0);
}
private void initKeyMapType() {
keyMap.addItem(
KeyMapType.DEFAULT.name().toLowerCase(),
KeyMapType.DEFAULT.name());
keyMap.addItem(
KeyMapType.EMACS.name().toLowerCase(),
KeyMapType.EMACS.name());
keyMap.addItem(
KeyMapType.VIM.name().toLowerCase(),
KeyMapType.VIM.name());
}
}

View File

@ -165,6 +165,10 @@ limitations under the License.
<th><ui:msg>Theme</ui:msg></th>
<td><g:ListBox ui:field='theme'/></td>
</tr>
<tr>
<th><ui:msg>Key Map</ui:msg></th>
<td><g:ListBox ui:field='keyMap'/></td>
</tr>
<tr>
<th><ui:msg>Tab Width</ui:msg></th>
<td><x:NpIntTextBox ui:field='tabWidth'
@ -177,6 +181,12 @@ limitations under the License.
visibleLength='4'
alignment='RIGHT'/></td>
</tr>
<tr>
<th><ui:msg>Cursor Blink Rate</ui:msg></th>
<td><x:NpIntTextBox ui:field='cursorBlinkRate'
visibleLength='4'
alignment='RIGHT'/></td>
</tr>
<tr>
<th><ui:msg>Top Menu</ui:msg></th>
<td><g:ToggleButton ui:field='topMenu'>
@ -212,6 +222,20 @@ limitations under the License.
<g:downFace><ui:msg>Show</ui:msg></g:downFace>
</g:ToggleButton></td>
</tr>
<tr>
<th><ui:msg>Match Brackets</ui:msg></th>
<td><g:ToggleButton ui:field='matchBrackets'>
<g:upFace><ui:msg>Off</ui:msg></g:upFace>
<g:downFace><ui:msg>On</ui:msg></g:downFace>
</g:ToggleButton></td>
</tr>
<tr>
<th><ui:msg>Auto Close Brackets</ui:msg></th>
<td><g:ToggleButton ui:field='autoCloseBrackets'>
<g:upFace><ui:msg>Off</ui:msg></g:upFace>
<g:downFace><ui:msg>On</ui:msg></g:downFace>
</g:ToggleButton></td>
</tr>
<tr>
<td></td>
<td>

View File

@ -44,6 +44,7 @@ import com.google.gerrit.client.ui.InlineHyperlink;
import com.google.gerrit.client.ui.Screen;
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.extensions.client.EditPreferencesInfo;
import com.google.gerrit.extensions.client.KeyMapType;
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gwt.core.client.GWT;
@ -257,11 +258,18 @@ public class EditScreen extends Screen {
@Override
public void registerKeys() {
super.registerKeys();
cm.addKeyMap(KeyMap.create()
KeyMap localKeyMap = KeyMap.create();
localKeyMap
.on("Ctrl-L", gotoLine())
.on("Cmd-L", gotoLine())
.on("Cmd-S", save())
.on("Ctrl-S", save()));
.on("Cmd-S", save());
// TODO(davido): Find a better way to prevent key maps collisions
if (prefs.keyMapType() != KeyMapType.EMACS) {
localKeyMap.on("Ctrl-S", save());
}
cm.addKeyMap(localKeyMap);
}
private Runnable gotoLine() {
@ -433,15 +441,17 @@ public class EditScreen extends Screen {
cm = CodeMirror.create(editor, Configuration.create()
.set("value", content)
.set("readOnly", false)
.set("cursorBlinkRate", 0)
.set("cursorBlinkRate", prefs.cursorBlinkRate())
.set("cursorHeight", 0.85)
.set("lineNumbers", prefs.hideLineNumbers())
.set("tabSize", prefs.tabSize())
.set("lineWrapping", false)
.set("matchBrackets", prefs.matchBrackets())
.set("autoCloseBrackets", prefs.autoCloseBrackets())
.set("scrollbarStyle", "overlay")
.set("styleSelectedText", true)
.set("showTrailingSpace", prefs.showWhitespaceErrors())
.set("keyMap", "default")
.set("keyMap", prefs.keyMapType().name().toLowerCase())
.set("theme", prefs.theme().name().toLowerCase())
.set("mode", mode != null ? mode.mode() : null));
}

View File

@ -9,10 +9,13 @@ CM_JS = [
'lib/codemirror.js',
'mode/meta.js',
'keymap/vim.js',
'keymap/emacs.js',
]
CM_ADDONS = [
'dialog/dialog.js',
'edit/closebrackets.js',
'edit/matchbrackets.js',
'edit/trailingspace.js',
'scroll/annotatescrollbar.js',
'scroll/simplescrollbars.js',