diff --git a/rhodecode/public/js/src/rhodecode/codemirror.js b/rhodecode/public/js/src/rhodecode/codemirror.js
--- a/rhodecode/public/js/src/rhodecode/codemirror.js
+++ b/rhodecode/public/js/src/rhodecode/codemirror.js
@@ -29,6 +29,178 @@ cmLog.setLevel(Logger.OFF);
//global cache for inline forms
var userHintsCache = {};
+// global timer, used to cancel async loading
+var CodeMirrorLoadUserHintTimer;
+
+var escapeRegExChars = function(value) {
+ return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
+};
+
+/**
+ * Load hints from external source returns an array of objects in a format
+ * that hinting lib requires
+ * @returns {Array}
+ */
+var CodeMirrorLoadUserHints = function(query, triggerHints) {
+ cmLog.debug('Loading mentions users via AJAX');
+ var _users = [];
+ $.ajax({
+ type: 'GET',
+ data: {query: query},
+ url: pyroutes.url('user_autocomplete_data'),
+ headers: {'X-PARTIAL-XHR': true},
+ async: true
+ })
+ .done(function(data) {
+ var tmpl = '{1}';
+ $.each(data.suggestions, function(i) {
+ var userObj = data.suggestions[i];
+
+ if (userObj.username !== "default") {
+ _users.push({
+ text: userObj.username + " ",
+ org_text: userObj.username,
+ displayText: userObj.value_display, // search that field
+ // internal caches
+ _icon_link: userObj.icon_link,
+ _text: userObj.value_display,
+
+ render: function(elt, data, completion) {
+ var el = document.createElement('div');
+ el.className = "CodeMirror-hint-entry";
+ el.innerHTML = tmpl.format(
+ completion._icon_link, completion._text);
+ elt.appendChild(el);
+ }
+ });
+ }
+ });
+ cmLog.debug('Mention users loaded');
+ // set to global cache
+ userHintsCache[query] = _users;
+ triggerHints(userHintsCache[query]);
+ })
+ .fail(function(data, textStatus, xhr) {
+ alert("error processing request: " + textStatus);
+ });
+};
+
+/**
+ * filters the results based on the current context
+ * @param users
+ * @param context
+ * @returns {Array}
+ */
+var CodeMirrorFilterUsers = function(users, context) {
+ var MAX_LIMIT = 10;
+ var filtered_users = [];
+ var curWord = context.string;
+
+ cmLog.debug('Filtering users based on query:', curWord);
+ $.each(users, function(i) {
+ var match = users[i];
+ var searchText = match.displayText;
+
+ if (!curWord ||
+ searchText.toLowerCase().lastIndexOf(curWord) !== -1) {
+ // reset state
+ match._text = match.displayText;
+ if (curWord) {
+ // do highlighting
+ var pattern = '(' + escapeRegExChars(curWord) + ')';
+ match._text = searchText.replace(
+ new RegExp(pattern, 'gi'), '$1<\/strong>');
+ }
+
+ filtered_users.push(match);
+ }
+ // to not return to many results, use limit of filtered results
+ if (filtered_users.length > MAX_LIMIT) {
+ return false;
+ }
+ });
+
+ return filtered_users;
+};
+
+var CodeMirrorMentionHint = function(editor, callback, options) {
+ var cur = editor.getCursor();
+ var curLine = editor.getLine(cur.line).slice(0, cur.ch);
+
+ // match on @ +1char
+ var tokenMatch = new RegExp(
+ '(^@| @)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]*)$').exec(curLine);
+
+ var tokenStr = '';
+ if (tokenMatch !== null && tokenMatch.length > 0){
+ tokenStr = tokenMatch[0].strip();
+ } else {
+ // skip if we didn't match our token
+ return;
+ }
+
+ var context = {
+ start: (cur.ch - tokenStr.length) + 1,
+ end: cur.ch,
+ string: tokenStr.slice(1),
+ type: null
+ };
+
+ // case when we put the @sign in fron of a string,
+ // eg <@ we put it here>sometext then we need to prepend to text
+ if (context.end > cur.ch) {
+ context.start = context.start + 1; // we add to the @ sign
+ context.end = cur.ch; // don't eat front part just append
+ context.string = context.string.slice(1, cur.ch - context.start);
+ }
+
+ cmLog.debug('Mention context', context);
+
+ var triggerHints = function(userHints){
+ return callback({
+ list: CodeMirrorFilterUsers(userHints, context),
+ from: CodeMirror.Pos(cur.line, context.start),
+ to: CodeMirror.Pos(cur.line, context.end)
+ });
+ };
+
+ var queryBasedHintsCache = undefined;
+ // if we have something in the cache, try to fetch the query based cache
+ if (userHintsCache !== {}){
+ queryBasedHintsCache = userHintsCache[context.string];
+ }
+
+ if (queryBasedHintsCache !== undefined) {
+ cmLog.debug('Users loaded from cache');
+ triggerHints(queryBasedHintsCache);
+ } else {
+ // this takes care for async loading, and then displaying results
+ // and also propagates the userHintsCache
+ window.clearTimeout(CodeMirrorLoadUserHintTimer);
+ CodeMirrorLoadUserHintTimer = setTimeout(function() {
+ CodeMirrorLoadUserHints(context.string, triggerHints);
+ }, 300);
+ }
+};
+
+var CodeMirrorCompleteAfter = function(cm, pred) {
+ var options = {
+ completeSingle: false,
+ async: true,
+ closeOnUnfocus: true
+ };
+ var cur = cm.getCursor();
+ setTimeout(function() {
+ if (!cm.state.completionActive) {
+ cmLog.debug('Trigger mentions hinting');
+ CodeMirror.showHint(cm, CodeMirror.hint.mentions, options);
+ }
+ }, 100);
+
+ // tell CodeMirror we didn't handle the key
+ // trick to trigger on a char but still complete it
+ return CodeMirror.Pass;
+};
var initCodeMirror = function(textAreadId, resetUrl, focus, options) {
var ta = $('#' + textAreadId).get(0);
@@ -61,9 +233,6 @@ var initCodeMirror = function(textAreadI
var initCommentBoxCodeMirror = function(textAreaId, triggerActions){
var initialHeight = 100;
- // global timer, used to cancel async loading
- var loadUserHintTimer;
-
if (typeof userHintsCache === "undefined") {
userHintsCache = {};
cmLog.debug('Init empty cache for mentions');
@@ -72,96 +241,6 @@ var initCommentBoxCodeMirror = function(
cmLog.debug('Element for textarea not found', textAreaId);
return;
}
- var escapeRegExChars = function(value) {
- return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
- };
- /**
- * Load hints from external source returns an array of objects in a format
- * that hinting lib requires
- * @returns {Array}
- */
- var loadUserHints = function(query, triggerHints) {
- cmLog.debug('Loading mentions users via AJAX');
- var _users = [];
- $.ajax({
- type: 'GET',
- data: {query: query},
- url: pyroutes.url('user_autocomplete_data'),
- headers: {'X-PARTIAL-XHR': true},
- async: true
- })
- .done(function(data) {
- var tmpl = '{1}';
- $.each(data.suggestions, function(i) {
- var userObj = data.suggestions[i];
-
- if (userObj.username !== "default") {
- _users.push({
- text: userObj.username + " ",
- org_text: userObj.username,
- displayText: userObj.value_display, // search that field
- // internal caches
- _icon_link: userObj.icon_link,
- _text: userObj.value_display,
-
- render: function(elt, data, completion) {
- var el = document.createElement('div');
- el.className = "CodeMirror-hint-entry";
- el.innerHTML = tmpl.format(
- completion._icon_link, completion._text);
- elt.appendChild(el);
- }
- });
- }
- });
- cmLog.debug('Mention users loaded');
- // set to global cache
- userHintsCache[query] = _users;
- triggerHints(userHintsCache[query]);
- })
- .fail(function(data, textStatus, xhr) {
- alert("error processing request: " + textStatus);
- });
- };
-
- /**
- * filters the results based on the current context
- * @param users
- * @param context
- * @returns {Array}
- */
- var filterUsers = function(users, context) {
- var MAX_LIMIT = 10;
- var filtered_users = [];
- var curWord = context.string;
-
- cmLog.debug('Filtering users based on query:', curWord);
- $.each(users, function(i) {
- var match = users[i];
- var searchText = match.displayText;
-
- if (!curWord ||
- searchText.toLowerCase().lastIndexOf(curWord) !== -1) {
- // reset state
- match._text = match.displayText;
- if (curWord) {
- // do highlighting
- var pattern = '(' + escapeRegExChars(curWord) + ')';
- match._text = searchText.replace(
- new RegExp(pattern, 'gi'), '$1<\/strong>');
- }
-
- filtered_users.push(match);
- }
- // to not return to many results, use limit of filtered results
- if (filtered_users.length > MAX_LIMIT) {
- return false;
- }
- });
-
- return filtered_users;
- };
-
/**
* Filter action based on typed in text
* @param actions
@@ -200,25 +279,6 @@ var initCommentBoxCodeMirror = function(
return filtered_actions;
};
- var completeAfter = function(cm, pred) {
- var options = {
- completeSingle: false,
- async: true,
- closeOnUnfocus: true
- };
- var cur = cm.getCursor();
- setTimeout(function() {
- if (!cm.state.completionActive) {
- cmLog.debug('Trigger mentions hinting');
- CodeMirror.showHint(cm, CodeMirror.hint.mentions, options);
- }
- }, 100);
-
- // tell CodeMirror we didn't handle the key
- // trick to trigger on a char but still complete it
- return CodeMirror.Pass;
- };
-
var submitForm = function(cm, pred) {
$(cm.display.input.textarea.form).submit();
return CodeMirror.Pass;
@@ -238,7 +298,7 @@ var initCommentBoxCodeMirror = function(
};
var extraKeys = {
- "'@'": completeAfter,
+ "'@'": CodeMirrorCompleteAfter,
Tab: function(cm) {
// space indent instead of TABS
var spaces = new Array(cm.getOption("indentUnit") + 1).join(" ");
@@ -285,66 +345,6 @@ var initCommentBoxCodeMirror = function(
self.setSize(null, height);
});
- var mentionHint = function(editor, callback, options) {
- var cur = editor.getCursor();
- var curLine = editor.getLine(cur.line).slice(0, cur.ch);
-
- // match on @ +1char
- var tokenMatch = new RegExp(
- '(^@| @)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]*)$').exec(curLine);
-
- var tokenStr = '';
- if (tokenMatch !== null && tokenMatch.length > 0){
- tokenStr = tokenMatch[0].strip();
- } else {
- // skip if we didn't match our token
- return;
- }
-
- var context = {
- start: (cur.ch - tokenStr.length) + 1,
- end: cur.ch,
- string: tokenStr.slice(1),
- type: null
- };
-
- // case when we put the @sign in fron of a string,
- // eg <@ we put it here>sometext then we need to prepend to text
- if (context.end > cur.ch) {
- context.start = context.start + 1; // we add to the @ sign
- context.end = cur.ch; // don't eat front part just append
- context.string = context.string.slice(1, cur.ch - context.start);
- }
-
- cmLog.debug('Mention context', context);
-
- var triggerHints = function(userHints){
- return callback({
- list: filterUsers(userHints, context),
- from: CodeMirror.Pos(cur.line, context.start),
- to: CodeMirror.Pos(cur.line, context.end)
- });
- };
-
- var queryBasedHintsCache = undefined;
- // if we have something in the cache, try to fetch the query based cache
- if (userHintsCache !== {}){
- queryBasedHintsCache = userHintsCache[context.string];
- }
-
- if (queryBasedHintsCache !== undefined) {
- cmLog.debug('Users loaded from cache');
- triggerHints(queryBasedHintsCache);
- } else {
- // this takes care for async loading, and then displaying results
- // and also propagates the userHintsCache
- window.clearTimeout(loadUserHintTimer);
- loadUserHintTimer = setTimeout(function() {
- loadUserHints(context.string, triggerHints);
- }, 300);
- }
- };
-
var actionHint = function(editor, options) {
var cur = editor.getCursor();
var curLine = editor.getLine(cur.line).slice(0, cur.ch);
@@ -408,7 +408,7 @@ var initCommentBoxCodeMirror = function(
to: CodeMirror.Pos(cur.line, context.end)
};
};
- CodeMirror.registerHelper("hint", "mentions", mentionHint);
+ CodeMirror.registerHelper("hint", "mentions", CodeMirrorMentionHint);
CodeMirror.registerHelper("hint", "actions", actionHint);
return cm;
};