summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKushal Pandya <kushalspandya@gmail.com>2019-06-25 10:19:30 +0000
committerKushal Pandya <kushalspandya@gmail.com>2019-06-25 10:19:30 +0000
commitb1038a3a8db9960f39812d7c6e3c888e35c4d679 (patch)
tree9527aca4e2d130fe30948f137791f12b9ed32343
parente3fa9d122b0a593b0c6441a35cbad8ab6c8e70b9 (diff)
parentde7abc06511fef0342af256075987903f68567bd (diff)
downloadgitlab-ce-b1038a3a8db9960f39812d7c6e3c888e35c4d679.tar.gz
Merge branch 'mh/colon-autocomplete' into 'master'
Allow autocompleting labels using colons and spaces Closes #63343 and #56510 See merge request gitlab-org/gitlab-ce!29749
-rw-r--r--app/assets/javascripts/commons/polyfills.js1
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js26
-rw-r--r--changelogs/unreleased/mh-colon-autocomplete.yml5
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb70
4 files changed, 97 insertions, 5 deletions
diff --git a/app/assets/javascripts/commons/polyfills.js b/app/assets/javascripts/commons/polyfills.js
index d0cc4897aeb..a4394ab7e92 100644
--- a/app/assets/javascripts/commons/polyfills.js
+++ b/app/assets/javascripts/commons/polyfills.js
@@ -12,6 +12,7 @@ import 'core-js/es/promise/finally';
import 'core-js/es/string/code-point-at';
import 'core-js/es/string/from-code-point';
import 'core-js/es/string/includes';
+import 'core-js/es/string/starts-with';
import 'core-js/es/symbol';
import 'core-js/es/map';
import 'core-js/es/weak-map';
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index 3b73dd83c9f..b308cd9c236 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -318,6 +318,7 @@ class GfmAutoComplete {
}
setupLabels($input) {
+ const instance = this;
const fetchData = this.fetchData.bind(this);
const LABEL_COMMAND = { LABEL: '/label', UNLABEL: '/unlabel', RELABEL: '/relabel' };
let command = '';
@@ -348,7 +349,6 @@ class GfmAutoComplete {
}));
},
matcher(flag, subtext) {
- const match = GfmAutoComplete.defaultMatcher(flag, subtext, this.app.controllers);
const subtextNodes = subtext
.split(/\n+/g)
.pop()
@@ -366,6 +366,27 @@ class GfmAutoComplete {
return null;
});
+ // If any label matches the inserted text after the last `~`, suggest those labels,
+ // even if any spaces or funky characters were typed.
+ // This allows matching labels like "Accepting merge requests".
+ const labels = instance.cachedData[flag];
+ if (labels) {
+ if (!subtext.includes(flag)) {
+ // Do not match if there is no `~` before the cursor
+ return null;
+ }
+ const lastCandidate = subtext.split(flag).pop();
+ if (labels.find(label => label.title.startsWith(lastCandidate))) {
+ return lastCandidate;
+ }
+ } else {
+ // Load all labels into the autocompleter.
+ // This needs to happen if e.g. editing a label in an existing comment, because normally
+ // label data would only be loaded only once you type `~`.
+ fetchData(this.$inputor, this.at);
+ }
+
+ const match = GfmAutoComplete.defaultMatcher(flag, subtext, this.app.controllers);
return match && match.length ? match[1] : null;
},
filter(query, data, searchKey) {
@@ -563,8 +584,9 @@ class GfmAutoComplete {
const accentAChar = decodeURI('%C3%80');
const accentYChar = decodeURI('%C3%BF');
+ // Holy regex, batman!
const regexp = new RegExp(
- `^(?:\\B|[^a-zA-Z0-9_\`${atSymbolsWithoutBar}]|\\s)${resultantFlag}(?!${atSymbolsWithBar})((?:[A-Za-z${accentAChar}-${accentYChar}0-9_'.+-]|[^\\x00-\\x7a])*)$`,
+ `^(?:\\B|[^a-zA-Z0-9_\`${atSymbolsWithoutBar}]|\\s)${resultantFlag}(?!${atSymbolsWithBar})((?:[A-Za-z${accentAChar}-${accentYChar}0-9_'.+-:]|[^\\x00-\\x7a])*)$`,
'gi',
);
diff --git a/changelogs/unreleased/mh-colon-autocomplete.yml b/changelogs/unreleased/mh-colon-autocomplete.yml
new file mode 100644
index 00000000000..8b169c22588
--- /dev/null
+++ b/changelogs/unreleased/mh-colon-autocomplete.yml
@@ -0,0 +1,5 @@
+---
+title: Allow auto-completing scoped labels
+merge_request: 29749
+author:
+type: added
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index 8eb413bdd8d..40845ec48f9 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -3,14 +3,14 @@ require 'rails_helper'
describe 'GFM autocomplete', :js do
let(:issue_xss_title) { 'This will execute alert<img src=x onerror=alert(2)&lt;img src=x onerror=alert(1)&gt;' }
let(:user_xss_title) { 'eve <img src=x onerror=alert(2)&lt;img src=x onerror=alert(1)&gt;' }
- let(:label_xss_title) { 'alert label &lt;img src=x onerror="alert(\'Hello xss\');" a'}
+ let(:label_xss_title) { 'alert label &lt;img src=x onerror="alert(\'Hello xss\');" a' }
let(:milestone_xss_title) { 'alert milestone &lt;img src=x onerror="alert(\'Hello xss\');" a' }
let(:user_xss) { create(:user, name: user_xss_title, username: 'xss.user') }
- let(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') }
+ let(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') }
let(:project) { create(:project) }
let(:label) { create(:label, project: project, title: 'special+') }
- let(:issue) { create(:issue, project: project) }
+ let(:issue) { create(:issue, project: project) }
before do
project.add_maintainer(user)
@@ -293,6 +293,70 @@ describe 'GFM autocomplete', :js do
expect(find('.atwho-view-ul').text).to have_content('alert label')
end
end
+
+ it 'allows colons when autocompleting scoped labels' do
+ create(:label, project: project, title: 'scoped:label')
+
+ note = find('#note-body')
+ type(note, '~scoped:')
+
+ wait_for_requests
+
+ page.within '.atwho-container #at-view-labels' do
+ expect(find('.atwho-view-ul').text).to have_content('scoped:label')
+ end
+ end
+
+ it 'allows colons when autocompleting scoped labels with double colons' do
+ create(:label, project: project, title: 'scoped::label')
+
+ note = find('#note-body')
+ type(note, '~scoped::')
+
+ wait_for_requests
+
+ page.within '.atwho-container #at-view-labels' do
+ expect(find('.atwho-view-ul').text).to have_content('scoped::label')
+ end
+ end
+
+ it 'allows spaces when autocompleting multi-word labels' do
+ create(:label, project: project, title: 'Accepting merge requests')
+
+ note = find('#note-body')
+ type(note, '~Accepting merge')
+
+ wait_for_requests
+
+ page.within '.atwho-container #at-view-labels' do
+ expect(find('.atwho-view-ul').text).to have_content('Accepting merge requests')
+ end
+ end
+
+ it 'only autocompletes the latest label' do
+ create(:label, project: project, title: 'Accepting merge requests')
+ create(:label, project: project, title: 'Accepting job applicants')
+
+ note = find('#note-body')
+ type(note, '~Accepting merge requests foo bar ~Accepting job')
+
+ wait_for_requests
+
+ page.within '.atwho-container #at-view-labels' do
+ expect(find('.atwho-view-ul').text).to have_content('Accepting job applicants')
+ end
+ end
+
+ it 'does not autocomplete labels if no tilde is typed' do
+ create(:label, project: project, title: 'Accepting merge requests')
+
+ note = find('#note-body')
+ type(note, 'Accepting merge')
+
+ wait_for_requests
+
+ expect(page).not_to have_css('.atwho-container #at-view-labels')
+ end
end
shared_examples 'autocomplete suggestions' do