Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/gfm_auto_complete.js')
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js108
1 files changed, 93 insertions, 15 deletions
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index 409733c73b9..62948f74aaa 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -52,7 +52,6 @@ export const defaultAutocompleteConfig = {
milestones: true,
labels: true,
snippets: true,
- vulnerabilities: true,
};
class GfmAutoComplete {
@@ -179,6 +178,9 @@ class GfmAutoComplete {
}
setupEmoji($input) {
+ const self = this;
+ const { filter, ...defaults } = this.getDefaultCallbacks();
+
// Emoji
$input.atwho({
at: ':',
@@ -189,18 +191,47 @@ class GfmAutoComplete {
}
return tmpl;
},
- // eslint-disable-next-line no-template-curly-in-string
- insertTpl: ':${name}:',
+ insertTpl: GfmAutoComplete.Emoji.insertTemplateFunction,
skipSpecialCharacterTest: true,
data: GfmAutoComplete.defaultLoadingData,
callbacks: {
- ...this.getDefaultCallbacks(),
+ ...defaults,
matcher(flag, subtext) {
const regexp = new RegExp(`(?:[^${glRegexp.unicodeLetters}0-9:]|\n|^):([^:]*)$`, 'gi');
const match = regexp.exec(subtext);
return match && match.length ? match[1] : null;
},
+ filter(query, items, searchKey) {
+ const filtered = filter.call(this, query, items, searchKey);
+ if (query.length === 0 || GfmAutoComplete.isLoading(items)) {
+ return filtered;
+ }
+
+ // map from value to "<value> is <field> of <emoji>", arranged by emoji
+ const emojis = {};
+ filtered.forEach(({ name: value }) => {
+ self.emojiLookup[value].forEach(({ emoji: { name }, kind }) => {
+ let entry = emojis[name];
+ if (!entry) {
+ entry = {};
+ emojis[name] = entry;
+ }
+ if (!(kind in entry) || value.localeCompare(entry[kind]) < 0) {
+ entry[kind] = value;
+ }
+ });
+ });
+
+ // collate results to list, prefering name > unicode > alias > description
+ const results = [];
+ Object.values(emojis).forEach(({ name, unicode, alias, description }) => {
+ results.push(name || unicode || alias || description);
+ });
+
+ // return to the form atwho wants
+ return results.map(name => ({ name }));
+ },
},
});
}
@@ -593,12 +624,7 @@ class GfmAutoComplete {
if (this.cachedData[at]) {
this.loadData($input, at, this.cachedData[at]);
} else if (GfmAutoComplete.atTypeMap[at] === 'emojis') {
- Emoji.initEmojiMap()
- .then(() => {
- this.loadData($input, at, Emoji.getValidEmojiNames());
- GfmAutoComplete.glEmojiTag = Emoji.glEmojiTag;
- })
- .catch(() => {});
+ this.loadEmojiData($input, at).catch(() => {});
} else if (dataSource) {
AjaxCache.retrieve(dataSource, true)
.then(data => {
@@ -621,6 +647,39 @@ class GfmAutoComplete {
return $input.trigger('keyup');
}
+ async loadEmojiData($input, at) {
+ await Emoji.initEmojiMap();
+
+ // All the emoji
+ const emojis = Emoji.getAllEmoji();
+
+ // Add all of the fields to atwho's database
+ this.loadData($input, at, [
+ ...Object.keys(emojis), // Names
+ ...Object.values(emojis).flatMap(({ aliases }) => aliases), // Aliases
+ ...Object.values(emojis).map(({ e }) => e), // Unicode values
+ ...Object.values(emojis).map(({ d }) => d), // Descriptions
+ ]);
+
+ // Construct a lookup that can correlate a value to "<value> is the <field> of <emoji>"
+ const lookup = {};
+ const add = (key, kind, emoji) => {
+ if (!(key in lookup)) {
+ lookup[key] = [];
+ }
+ lookup[key].push({ kind, emoji });
+ };
+ Object.values(emojis).forEach(emoji => {
+ add(emoji.name, 'name', emoji);
+ add(emoji.d, 'description', emoji);
+ add(emoji.e, 'unicode', emoji);
+ emoji.aliases.forEach(a => add(a, 'alias', emoji));
+ });
+ this.emojiLookup = lookup;
+
+ GfmAutoComplete.glEmojiTag = Emoji.glEmojiTag;
+ }
+
clearCache() {
this.cachedData = {};
}
@@ -648,8 +707,7 @@ class GfmAutoComplete {
// https://github.com/ichord/At.js
const atSymbolsWithBar = Object.keys(controllers)
.join('|')
- .replace(/[$]/, '\\$&')
- .replace(/[+]/, '\\+');
+ .replace(/[$]/, '\\$&');
const atSymbolsWithoutBar = Object.keys(controllers).join('');
const targetSubtext = subtext.split(GfmAutoComplete.regexSubtext).pop();
const resultantFlag = flag.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
@@ -680,19 +738,39 @@ GfmAutoComplete.atTypeMap = {
'~': 'labels',
'%': 'milestones',
'/': 'commands',
- '+': 'vulnerabilities',
$: 'snippets',
};
+function findEmoji(name) {
+ return Emoji.searchEmoji(name, { match: 'contains', raw: true }).sort((a, b) => {
+ if (a.index !== b.index) {
+ return a.index - b.index;
+ }
+ return a.field.localeCompare(b.field);
+ });
+}
+
// Emoji
GfmAutoComplete.glEmojiTag = null;
GfmAutoComplete.Emoji = {
+ insertTemplateFunction(value) {
+ const results = findEmoji(value.name);
+ if (results.length) {
+ return `:${results[0].emoji.name}:`;
+ }
+ return `:${value.name}:`;
+ },
templateFunction(name) {
// glEmojiTag helper is loaded on-demand in fetchData()
- if (GfmAutoComplete.glEmojiTag) {
+ if (!GfmAutoComplete.glEmojiTag) return `<li>${name}</li>`;
+
+ const results = findEmoji(name);
+ if (!results.length) {
return `<li>${name} ${GfmAutoComplete.glEmojiTag(name)}</li>`;
}
- return `<li>${name}</li>`;
+
+ const { field, emoji } = results[0];
+ return `<li>${field} ${GfmAutoComplete.glEmojiTag(emoji.name)}</li>`;
},
};
// Team Members