Merge changes from topic 'tags-api' * changes: ListTags: Support filtering by substring and regex ProjectApi: Refactor to reduce duplicate code in tags and branches ListTags: Add support for pagination with --start and --limit ListBranches: Split filtering and pagination out to a utility class Make BranchInfo and TagInfo share a common base class Implement tags API ListTags: Create RevWalk in try-with-resource RefNames: Add support for refs/tags/ in shortName()
diff --git a/Documentation/doc.css.in b/Documentation/doc.css.in index 6be89f6..429e81c 100644 --- a/Documentation/doc.css.in +++ b/Documentation/doc.css.in
@@ -17,6 +17,10 @@ border-bottom: 2px solid silver; } +h1 { + margin-top: 1.5em; +} + p { margin: 0.5em 0 0.5em 0; }
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt index d753d90..7497a64 100644 --- a/Documentation/rest-api-accounts.txt +++ b/Documentation/rest-api-accounts.txt
@@ -1339,11 +1339,15 @@ )]}' { "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 @@ { "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 @@ 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 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]]
diff --git a/Documentation/rest-api-groups.txt b/Documentation/rest-api-groups.txt index afdf36d..e0df4ca 100644 --- a/Documentation/rest-api-groups.txt +++ b/Documentation/rest-api-groups.txt
@@ -172,6 +172,46 @@ GET /groups/?n=25&S=50 HTTP/1.0 ---- +[[suggest-group]] +==== Suggest Group +The `suggest` option indicates a user-entered string that +should be auto-completed to group names. +If this option is set and `n` is not set, then `n` defaults to 10. + +When using this option, +the `project` or `p` option can be used to name the current project, +to allow context-dependent suggestions. + +Not compatible with `visible-to-all`, `owned`, `user`, `match`, `q`, +or `S`. +(Attempts to use one of those options combined with `suggest` will +error out.) + +.Request +---- + GET /groups/?suggest=ad&p=All-Projects HTTP/1.0 +---- + +.Response +---- + HTTP/1.1 200 OK + Content-Disposition: attachment + Content-Type: application/json; charset=UTF-8 + + )]}' + { + "Administrators": { + "url": "#/admin/groups/uuid-59b92f35489e62c80d1ab1bf0c2d17843038df8b", + "options": {}, + "description": "Gerrit Site Administrators", + "group_id": 1, + "owner": "Administrators", + "owner_id": "59b92f35489e62c80d1ab1bf0c2d17843038df8b", + "id": "59b92f35489e62c80d1ab1bf0c2d17843038df8b" + } + } +---- + [[get-group]] === Get Group --
diff --git a/Documentation/user-inline-edit.txt b/Documentation/user-inline-edit.txt index 2da0c25..fff2501 100644 --- a/Documentation/user-inline-edit.txt +++ b/Documentation/user-inline-edit.txt
@@ -176,9 +176,6 @@ ** "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
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/GroupsIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/GroupsIT.java index 5b8b87f..c72edd7 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/GroupsIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/GroupsIT.java
@@ -402,6 +402,13 @@ } @Test + public void testSuggestGroup() throws Exception { + Map<String, GroupInfo> groups = gApi.groups().list().withSuggest("adm").getAsMap(); + assertThat(groups).containsKey("Administrators"); + assertThat(groups).hasSize(1); + } + + @Test public void testAllGroupInfoFieldsSetCorrectly() throws Exception { AccountGroup adminGroup = getFromCache("Administrators"); Map<String, GroupInfo> groups =
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/EditPreferencesIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/EditPreferencesIT.java index de6e3aa..8770c3c 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/EditPreferencesIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/EditPreferencesIT.java
@@ -19,6 +19,7 @@ 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 @@ 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 @@ 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); } }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/Groups.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/Groups.java index ab09e5f..b909f31 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/Groups.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/Groups.java
@@ -63,6 +63,7 @@ private int limit; private int start; private String substring; + private String suggest; public List<GroupInfo> get() throws RestApiException { Map<String, GroupInfo> map = getAsMap(); @@ -128,6 +129,11 @@ return this; } + public ListRequest withSuggest(String suggest) { + this.suggest = suggest; + return this; + } + public EnumSet<ListGroupsOption> getOptions() { return options; } @@ -163,5 +169,9 @@ public String getSubstring() { return substring; } + + public String getSuggest() { + return suggest; + } } }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java index e80d3d66..3e45523 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/EditPreferencesInfo.java
@@ -18,23 +18,31 @@ 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; } }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/KeyMapType.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/KeyMapType.java new file mode 100644 index 0000000..261168d --- /dev/null +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/KeyMapType.java
@@ -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 +} \ No newline at end of file
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/EditPreferences.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/EditPreferences.java index d8ae4a8..8ee573e 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/EditPreferences.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/EditPreferences.java
@@ -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 @@ 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 @@ } 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 @@ } 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() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.java index 28b2aa8..f900c94 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.java
@@ -20,6 +20,7 @@ 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 @@ @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 @@ this.view = view; initWidget(uiBinder.createAndBindUi(this)); initTheme(); + initKeyMapType(); } void set(EditPreferences prefs) { @@ -75,12 +81,16 @@ 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 @@ } } + @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 @@ 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 @@ }); } + @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 @@ 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()); + } }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.ui.xml index 84a70b4..ccf620c 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.ui.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditPreferencesBox.ui.xml
@@ -166,6 +166,10 @@ <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' visibleLength='4' @@ -178,6 +182,12 @@ 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'> <g:upFace><ui:msg>Hide</ui:msg></g:upFace> @@ -213,6 +223,20 @@ </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> <g:Button ui:field='apply' styleName='{style.apply}'>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java index 60da8fb..3c91d25 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/editor/EditScreen.java
@@ -44,6 +44,7 @@ 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 @@ @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 @@ 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)); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupsImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupsImpl.java index 97ba5d3..3d2c960 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupsImpl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupsImpl.java
@@ -138,6 +138,7 @@ list.setLimit(req.getLimit()); list.setStart(req.getStart()); list.setMatchSubstring(req.getSubstring()); + list.setSuggest(req.getSuggest()); try { return list.apply(tlr); } catch (OrmException e) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/FileContentUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/FileContentUtil.java index 86ce61e..fef09a2 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/FileContentUtil.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/FileContentUtil.java
@@ -60,6 +60,8 @@ private static final int MAX_SIZE = 5 << 20; private static final String ZIP_TYPE = "application/zip"; private static final Random rng = new Random(); + private static final CharMatcher LOWERCASE_OR_DIGITS = + CharMatcher.inRange('a', 'z').or(CharMatcher.inRange('0', '9')); private final GitRepositoryManager repoManager; private final FileTypeRegistry registry; @@ -128,8 +130,8 @@ public BinaryResult downloadContent(ProjectState project, ObjectId revstr, String path, @Nullable String suffix) throws ResourceNotFoundException, IOException { - suffix = Strings.emptyToNull(CharMatcher.inRange('a', 'z') - .retainFrom(Strings.nullToEmpty(suffix))); + suffix = Strings.emptyToNull( + LOWERCASE_OR_DIGITS.retainFrom(Strings.nullToEmpty(suffix))); try (Repository repo = openRepository(project); RevWalk rw = new RevWalk(repo)) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java index bf5193f..023e3d4 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java
@@ -16,14 +16,18 @@ import com.google.common.base.MoreObjects; import com.google.common.base.Strings; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; +import com.google.gerrit.common.Nullable; +import com.google.gerrit.common.data.GroupDescription; import com.google.gerrit.common.data.GroupDescriptions; import com.google.gerrit.common.data.GroupReference; import com.google.gerrit.common.errors.NoSuchGroupException; import com.google.gerrit.extensions.client.ListGroupsOption; import com.google.gerrit.extensions.common.GroupInfo; +import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.RestReadView; import com.google.gerrit.extensions.restapi.TopLevelResource; import com.google.gerrit.extensions.restapi.Url; @@ -32,6 +36,7 @@ import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.account.AccountResource; import com.google.gerrit.server.account.GetGroups; +import com.google.gerrit.server.account.GroupBackend; import com.google.gerrit.server.account.GroupCache; import com.google.gerrit.server.account.GroupComparator; import com.google.gerrit.server.account.GroupControl; @@ -64,6 +69,7 @@ private final IdentifiedUser.GenericFactory userFactory; private final Provider<GetGroups> accountGetGroups; private final GroupJson json; + private final GroupBackend groupBackend; private EnumSet<ListGroupsOption> options = EnumSet.noneOf(ListGroupsOption.class); @@ -73,6 +79,7 @@ private int limit; private int start; private String matchSubstring; + private String suggest; @Option(name = "--project", aliases = {"-p"}, usage = "projects for which the groups should be listed") @@ -121,6 +128,11 @@ this.matchSubstring = matchSubstring; } + @Option(name = "--suggest", usage = "to get a suggestion of groups") + public void setSuggest(String suggest) { + this.suggest = suggest; + } + @Option(name = "-o", usage = "Output options per group") void addOption(ListGroupsOption o) { options.add(o); @@ -137,7 +149,8 @@ final GroupControl.GenericFactory genericGroupControlFactory, final Provider<IdentifiedUser> identifiedUser, final IdentifiedUser.GenericFactory userFactory, - final Provider<GetGroups> accountGetGroups, GroupJson json) { + final Provider<GetGroups> accountGetGroups, GroupJson json, + GroupBackend groupBackend) { this.groupCache = groupCache; this.groupControlFactory = groupControlFactory; this.genericGroupControlFactory = genericGroupControlFactory; @@ -145,6 +158,7 @@ this.userFactory = userFactory; this.accountGetGroups = accountGetGroups; this.json = json; + this.groupBackend = groupBackend; } public void setOptions(EnumSet<ListGroupsOption> options) { @@ -161,7 +175,7 @@ @Override public SortedMap<String, GroupInfo> apply(TopLevelResource resource) - throws OrmException { + throws OrmException, BadRequestException { SortedMap<String, GroupInfo> output = Maps.newTreeMap(); for (GroupInfo info : get()) { output.put(MoreObjects.firstNonNull( @@ -172,53 +186,107 @@ return output; } - public List<GroupInfo> get() throws OrmException { - List<GroupInfo> groupInfos; + public List<GroupInfo> get() throws OrmException, BadRequestException { + if (!Strings.isNullOrEmpty(suggest)) { + return suggestGroups(); + } + + if (owned) { + return getGroupsOwnedBy( + user != null ? userFactory.create(user) : identifiedUser.get()); + } + if (user != null) { - if (owned) { - groupInfos = getGroupsOwnedBy(userFactory.create(user)); - } else { - groupInfos = accountGetGroups.get().apply( - new AccountResource(userFactory.create(user))); + return accountGetGroups.get().apply( + new AccountResource(userFactory.create(user))); + } + + return getAllGroups(); + } + + private List<GroupInfo> getAllGroups() throws OrmException { + List<GroupInfo> groupInfos; + List<AccountGroup> groupList; + if (!projects.isEmpty()) { + Map<AccountGroup.UUID, AccountGroup> groups = Maps.newHashMap(); + for (final ProjectControl projectControl : projects) { + final Set<GroupReference> groupsRefs = projectControl.getAllGroups(); + for (final GroupReference groupRef : groupsRefs) { + final AccountGroup group = groupCache.get(groupRef.getUUID()); + if (group != null) { + groups.put(group.getGroupUUID(), group); + } + } } + groupList = filterGroups(groups.values()); } else { - if (owned) { - groupInfos = getGroupsOwnedBy(identifiedUser.get()); - } else { - List<AccountGroup> groupList; - if (!projects.isEmpty()) { - Map<AccountGroup.UUID, AccountGroup> groups = Maps.newHashMap(); - for (final ProjectControl projectControl : projects) { - final Set<GroupReference> groupsRefs = projectControl.getAllGroups(); - for (final GroupReference groupRef : groupsRefs) { - final AccountGroup group = groupCache.get(groupRef.getUUID()); - if (group != null) { - groups.put(group.getGroupUUID(), group); - } - } - } - groupList = filterGroups(groups.values()); - } else { - groupList = filterGroups(groupCache.all()); - } - groupInfos = Lists.newArrayListWithCapacity(groupList.size()); - int found = 0; - int foundIndex = 0; - for (AccountGroup group : groupList) { - if (foundIndex++ < start) { - continue; - } - if (limit > 0 && ++found > limit) { - break; - } - groupInfos.add(json.addOptions(options).format( - GroupDescriptions.forAccountGroup(group))); - } + groupList = filterGroups(groupCache.all()); + } + groupInfos = Lists.newArrayListWithCapacity(groupList.size()); + int found = 0; + int foundIndex = 0; + for (AccountGroup group : groupList) { + if (foundIndex++ < start) { + continue; + } + if (limit > 0 && ++found > limit) { + break; + } + groupInfos.add(json.addOptions(options).format( + GroupDescriptions.forAccountGroup(group))); + } + return groupInfos; + } + + private List<GroupInfo> suggestGroups() throws OrmException, BadRequestException { + if (conflictingSuggestParameters()) { + throw new BadRequestException( + "You should only have no more than one --project and -n with --suggest"); + } + + List<GroupReference> groupRefs = Lists.newArrayList(Iterables.limit( + groupBackend.suggest( + suggest, Iterables.getFirst(projects, null)), + limit <= 0 ? 10 : Math.min(limit, 10))); + + List<GroupInfo> groupInfos = Lists.newArrayListWithCapacity(groupRefs.size()); + for (final GroupReference ref : groupRefs) { + GroupDescription.Basic desc = groupBackend.get(ref.getUUID()); + if (desc != null) { + groupInfos.add(json.addOptions(options).format(desc)); } } return groupInfos; } + private boolean conflictingSuggestParameters() { + if (Strings.isNullOrEmpty(suggest)) { + return false; + } + if (projects.size() > 1) { + return true; + } + if (visibleToAll) { + return true; + } + if (user != null) { + return true; + } + if (owned) { + return true; + } + if (start != 0) { + return true; + } + if (!groupsToInspect.isEmpty()) { + return true; + } + if (!Strings.isNullOrEmpty(matchSubstring)) { + return true; + } + return false; + } + private List<GroupInfo> getGroupsOwnedBy(IdentifiedUser user) throws OrmException { List<GroupInfo> groups = Lists.newArrayList();
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java index ad72b13..75072e8 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java
@@ -19,10 +19,12 @@ import com.google.common.base.MoreObjects; import com.google.common.base.Strings; import com.google.gerrit.extensions.common.GroupInfo; +import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.Url; import com.google.gerrit.reviewdb.client.AccountGroup; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.account.GetGroups; +import com.google.gerrit.server.account.GroupBackend; import com.google.gerrit.server.account.GroupCache; import com.google.gerrit.server.account.GroupControl; import com.google.gerrit.server.group.GroupJson; @@ -71,12 +73,13 @@ final Provider<IdentifiedUser> identifiedUser, final IdentifiedUser.GenericFactory userFactory, final Provider<GetGroups> accountGetGroups, - final GroupJson json) { + final GroupJson json, + GroupBackend groupBackend) { super(groupCache, groupControlFactory, genericGroupControlFactory, - identifiedUser, userFactory, accountGetGroups, json); + identifiedUser, userFactory, accountGetGroups, json, groupBackend); } - void display(final PrintWriter out) throws OrmException { + void display(final PrintWriter out) throws OrmException, BadRequestException { final ColumnFormatter formatter = new ColumnFormatter(out, '\t'); for (final GroupInfo info : get()) { formatter.addColumn(MoreObjects.firstNonNull(info.name, "n/a"));
diff --git a/lib/codemirror/cm.defs b/lib/codemirror/cm.defs index 8259252..ca1c9ae 100644 --- a/lib/codemirror/cm.defs +++ b/lib/codemirror/cm.defs
@@ -9,10 +9,13 @@ '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',