InlineEdit: Support Emacs and Vim key maps

Change-Id: I980afc3848f267811ab5890f9447fba16beed5d4
This commit is contained in:
David Ostrovsky 2015-05-14 22:33:16 +02:00
parent 8018fbbc1e
commit c4e01806ff
10 changed files with 98 additions and 7 deletions

View File

@ -1339,6 +1339,7 @@ link:#edit-preferences-info[EditPreferencesInfo] entity.
)]}'
{
"theme": "ECLIPSE",
"key_map_type": "VIM",
"tab_size": 4,
"line_length": 80,
"hide_top_menu": true,
@ -1365,6 +1366,7 @@ link:#edit-preferences-info[EditPreferencesInfo] entity.
{
"theme": "ECLIPSE",
"key_map_type": "VIM",
"tab_size": 4,
"line_length": 80,
"hide_top_menu": true,
@ -1757,6 +1759,9 @@ 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` ||

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;
@ -42,6 +43,7 @@ public class EditPreferencesIT extends AbstractDaemonTest {
assertThat(out.syntaxHighlighting).isTrue();
assertThat(out.hideLineNumbers).isNull();
assertThat(out.theme).isEqualTo(Theme.DEFAULT);
assertThat(out.keyMapType).isEqualTo(KeyMapType.DEFAULT);
// change some default values
out.lineLength = 80;
@ -52,6 +54,7 @@ public class EditPreferencesIT extends AbstractDaemonTest {
out.syntaxHighlighting = false;
out.hideLineNumbers = true;
out.theme = Theme.TWILIGHT;
out.keyMapType = KeyMapType.EMACS;
r = adminSession.put(endPoint, out);
assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT);
@ -78,5 +81,6 @@ public class EditPreferencesIT extends AbstractDaemonTest {
assertThat(out.syntaxHighlighting).isNull();
assertThat(out.hideLineNumbers).isEqualTo(in.hideLineNumbers);
assertThat(out.theme).isEqualTo(in.theme);
assertThat(out.keyMapType).isEqualTo(in.keyMapType);
}
}

View File

@ -24,6 +24,7 @@ public class EditPreferencesInfo {
public Boolean syntaxHighlighting;
public Boolean hideLineNumbers;
public Theme theme;
public KeyMapType keyMapType;
public static EditPreferencesInfo defaults() {
EditPreferencesInfo i = new EditPreferencesInfo();
@ -35,6 +36,7 @@ public class EditPreferencesInfo {
i.syntaxHighlighting = true;
i.hideLineNumbers = 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;
@ -29,6 +30,7 @@ public class EditPreferences extends JavaScriptObject {
p.syntaxHighlighting(in.syntaxHighlighting);
p.hideLineNumbers(in.hideLineNumbers);
p.theme(in.theme);
p.keyMapType(in.keyMapType);
return p;
}
@ -41,6 +43,7 @@ public class EditPreferences extends JavaScriptObject {
p.syntaxHighlighting = syntaxHighlighting();
p.hideLineNumbers = hideLineNumbers();
p.theme = theme();
p.keyMapType = keyMapType();
}
public final void theme(Theme i) {
@ -48,6 +51,11 @@ 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 hideTopMenu(boolean s) /*-{ this.hide_top_menu = s }-*/;
@ -62,6 +70,12 @@ 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 native boolean hideTopMenu() /*-{ return this.hide_top_menu || false }-*/;

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;
@ -61,6 +62,7 @@ class EditPreferencesBox extends Composite {
@UiField ToggleButton whitespaceErrors;
@UiField ToggleButton lineNumbers;
@UiField ListBox theme;
@UiField ListBox keyMap;
@UiField Button apply;
@UiField Button save;
@ -68,6 +70,7 @@ class EditPreferencesBox extends Composite {
this.view = view;
initWidget(uiBinder.createAndBindUi(this));
initTheme();
initKeyMapType();
}
void set(EditPreferences prefs) {
@ -81,6 +84,7 @@ class EditPreferencesBox extends Composite {
whitespaceErrors.setValue(prefs.showWhitespaceErrors());
lineNumbers.setValue(prefs.hideLineNumbers());
setTheme(prefs.theme());
setKeyMapType(prefs.keyMapType());
}
@UiHandler("tabWidth")
@ -150,6 +154,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 +222,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'

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() {
@ -441,7 +449,7 @@ public class EditScreen extends Screen {
.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,6 +9,7 @@ CM_JS = [
'lib/codemirror.js',
'mode/meta.js',
'keymap/vim.js',
'keymap/emacs.js',
]
CM_ADDONS = [