diff --git a/.hgignore b/.hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -32,6 +32,8 @@ syntax: regexp
 ^kallithea/public/css/style\.css\.map$
 ^kallithea/public/js/bootstrap\.js$
 ^kallithea/public/js/dataTables\.bootstrap\.js$
+^kallithea/public/js/jquery\.atwho\.min\.js$
+^kallithea/public/js/jquery\.caret\.min\.js$
 ^kallithea/public/js/jquery\.dataTables\.js$
 ^kallithea/public/js/jquery\.flot\.js$
 ^kallithea/public/js/jquery\.flot\.selection\.js$
diff --git a/LICENSE.md b/LICENSE.md
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -84,6 +84,38 @@ using the ''kallithea-cli front-end-buil
 
 
 
+At.js
+-----
+
+Kallithea uses the Javascript system called
+[At.js](http://ichord.github.com/At.js),
+which can be found together with its Corresponding Source in
+https://github.com/ichord/At.js at tag v1.5.4.
+
+It is Copyright 2013 chord.luo@gmail.com and is under an
+[MIT-permissive license](MIT-Permissive-License.txt).
+
+It is not distributed with Kallithea, but will be downloaded
+using the ''kallithea-cli front-end-build'' command.
+
+
+
+Caret.js
+--------
+
+Kallithea uses the Javascript system called
+[Caret.js](http://ichord.github.com/Caret.js/),
+which can be found together with its Corresponding Source in
+https://github.com/ichord/Caret.js at tag v0.3.1.
+
+It is Copyright 2013 chord.luo@gmail.com and is under an
+[MIT-permissive license](MIT-Permissive-License.txt).
+
+It is not distributed with Kallithea, but will be downloaded
+using the ''kallithea-cli front-end-build'' command.
+
+
+
 DataTables
 ----------
 
diff --git a/kallithea/bin/kallithea_cli_front_end.py b/kallithea/bin/kallithea_cli_front_end.py
--- a/kallithea/bin/kallithea_cli_front_end.py
+++ b/kallithea/bin/kallithea_cli_front_end.py
@@ -66,11 +66,13 @@ def front_end_build(install_deps, genera
         click.echo("Preparing Bootstrap JS")
         shutil.copy(os.path.join(front_end_dir, 'node_modules', 'bootstrap', 'dist', 'js', 'bootstrap.js'), os.path.join(public_dir, 'js', 'bootstrap.js'))
 
-        click.echo("Preparing jQuery JS with Flot")
+        click.echo("Preparing jQuery JS with Flot, Caret and Atwho")
         shutil.copy(os.path.join(front_end_dir, 'node_modules', 'jquery', 'dist', 'jquery.min.js'), os.path.join(public_dir, 'js', 'jquery.min.js'))
         shutil.copy(os.path.join(front_end_dir, 'node_modules', 'jquery.flot', 'jquery.flot.js'), os.path.join(public_dir, 'js', 'jquery.flot.js'))
         shutil.copy(os.path.join(front_end_dir, 'node_modules', 'jquery.flot', 'jquery.flot.selection.js'), os.path.join(public_dir, 'js', 'jquery.flot.selection.js'))
         shutil.copy(os.path.join(front_end_dir, 'node_modules', 'jquery.flot', 'jquery.flot.time.js'), os.path.join(public_dir, 'js', 'jquery.flot.time.js'))
+        shutil.copy(os.path.join(front_end_dir, 'node_modules', 'jquery.caret', 'dist', 'jquery.caret.min.js'), os.path.join(public_dir, 'js', 'jquery.caret.min.js'))
+        shutil.copy(os.path.join(front_end_dir, 'node_modules', 'at.js', 'dist', 'js', 'jquery.atwho.min.js'), os.path.join(public_dir, 'js', 'jquery.atwho.min.js'))
 
         click.echo("Preparing DataTables JS")
         shutil.copy(os.path.join(front_end_dir, 'node_modules', 'datatables.net', 'js', 'jquery.dataTables.js'), os.path.join(public_dir, 'js', 'jquery.dataTables.js'))
diff --git a/kallithea/front-end/package.json b/kallithea/front-end/package.json
--- a/kallithea/front-end/package.json
+++ b/kallithea/front-end/package.json
@@ -2,11 +2,13 @@
   "name": "kallithea",
   "private": true,
   "dependencies": {
+    "at.js": "1.5.4",
     "bootstrap": "3.3.7",
     "codemirror": "4.7",
     "datatables.net": "1.10.13",
     "datatables.net-bs": "1.10.13",
     "jquery": "1.12.3",
+    "jquery.caret": "0.3.1",
     "jquery.flot": "0.8.3",
     "select2": "3.5.1",
     "select2-bootstrap-css": "1.2.4"
diff --git a/kallithea/public/js/base.js b/kallithea/public/js/base.js
--- a/kallithea/public/js/base.js
+++ b/kallithea/public/js/base.js
@@ -751,7 +751,7 @@ function _comment_div_append_form($comme
 
     tooltip_activate();
     if ($textarea.length > 0) {
-        MentionsAutoComplete($textarea, _USERS_AC_DATA);
+        MentionsAutoComplete($textarea);
     }
     if (f_path) {
         $textarea.focus();
@@ -1155,65 +1155,30 @@ var MembersAutoComplete = function ($inp
     });
 }
 
-var MentionsAutoComplete = function ($inputElement, users_list) {
-    var $container = $('<div/>').insertAfter($inputElement);
-
-    var matchUsers = function (sQuery) {
-            // use the search string from $inputElement instead of sQuery
-            if(!$container.data('search')){
-                // return empty list so the input list isn't shown
-                return []
-            }
-            return autocompleteMatchUsers($container.data('search'), users_list);
-    }
-
-    var datasource = new YAHOO.util.FunctionDataSource(matchUsers);
-    var mentionsAC = new YAHOO.widget.AutoComplete($inputElement[0], $container[0], datasource);
-    mentionsAC.useShadow = false;
-    mentionsAC.resultTypeList = false;
-    mentionsAC.animVert = false;
-    mentionsAC.animHoriz = false;
-    mentionsAC.animSpeed = 0.1;
-    mentionsAC.suppressInputUpdate = true;
-    mentionsAC.formatResult = function (oResultData, sQuery, sResultMatch) {
-        // use the search string from $inputElement instead of sQuery
-        return autocompleteFormatter(oResultData, $container.data('search'), sResultMatch);
-    }
-
-    // Handler for selection of an entry
-    if(mentionsAC.itemSelectEvent){
-        mentionsAC.itemSelectEvent.subscribe(function (sType, aArgs) {
-            var myAC = aArgs[0]; // reference back to the AC instance
-            var elLI = aArgs[1]; // reference to the selected LI element
-            var oData = aArgs[2]; // object literal of selected item's result data
-            myAC.getInputEl().value = $container.data('before') + oData.nname + ' ' + $container.data('after');
-            _setCaretPosition($(myAC.getInputEl()), myAC.dataSource.before.length + oData.nname.length + 1);
-        });
-    }
-
-    // Must match utils2.py MENTIONS_REGEX.
-    // Operates on a string from char before @ up to cursor.
-    // Check that the char before @ doesn't look like an email address, and match to end of string.
-    var mentionRe = new RegExp('(?:^|[^a-zA-Z0-9])@([a-zA-Z0-9][-_.a-zA-Z0-9]*[a-zA-Z0-9])$');
-
-    $inputElement.keyup(function(e){
-            var currentMessage = $inputElement.val();
-            var currentCaretPosition = $inputElement[0].selectionStart;
-
-            $container.data('search', '');
-            var messageBeforeCaret = currentMessage.substr(0, currentCaretPosition);
-            var lastAtPos = messageBeforeCaret.lastIndexOf('@');
-            if(lastAtPos >= 0){
-                // Search from one char before last @ ... if possible
-                var m = mentionRe.exec(messageBeforeCaret.substr(Math.max(0, lastAtPos - 1)));
-                if(m){
-                    $container.data('before', currentMessage.substr(0, lastAtPos + 1));
-                    $container.data('search', currentMessage.substr(lastAtPos + 1, currentCaretPosition - lastAtPos - 1));
-                    $container.data('after', currentMessage.substr(currentCaretPosition));
-                }
-            }
-        });
-}
+var MentionsAutoComplete = function ($inputElement) {
+  $inputElement.atwho({
+    at: "@",
+    callbacks: {
+      remoteFilter: function(query, callback) {
+        $.getJSON(
+          pyroutes.url('users_and_groups_data'),
+          {
+            query: query,
+            types: 'users'
+          },
+          function(data) {
+            callback(data.results)
+          }
+        );
+      },
+      sorter: function(query, items, searchKey) {
+        return items;
+      }
+    },
+    displayTpl: "<li>" + autocompleteGravatar('${fname} ${lname} (${nname})', '${gravatar_lnk}', 16) + "</li>",
+    insertTpl: "${atwho-at}${nname}"
+  });
+};
 
 
 // Set caret at the given position in the input element
diff --git a/kallithea/public/less/main.less b/kallithea/public/less/main.less
--- a/kallithea/public/less/main.less
+++ b/kallithea/public/less/main.less
@@ -11,6 +11,7 @@
 /* 3rd party styles */
 @import "node_modules/bootstrap/less/bootstrap.less";
 @import (inline) "node_modules/datatables.net-bs/css/dataTables.bootstrap.css";
+@import (inline) "node_modules/at.js/dist/css/jquery.atwho.css";
 @import (less) "node_modules/select2/select2.css";
 @import (less) "node_modules/select2-bootstrap-css/select2-bootstrap.css";
 @import (less) "tmp/pygments.css";
diff --git a/kallithea/public/less/style.less b/kallithea/public/less/style.less
--- a/kallithea/public/less/style.less
+++ b/kallithea/public/less/style.less
@@ -929,3 +929,9 @@ nav.navbar #quick > li > a,
 #context-pages > ul > li > a {
   height: @navbar-height;
 }
+
+/* at.js */
+.atwho-view strong {
+  /* the blue color doesn't look good, use normal color */
+  color: inherit;
+}
diff --git a/kallithea/templates/base/root.html b/kallithea/templates/base/root.html
--- a/kallithea/templates/base/root.html
+++ b/kallithea/templates/base/root.html
@@ -74,6 +74,8 @@
         <script type="text/javascript" src="${h.url('/js/bootstrap.js', ver=c.kallithea_version)}"></script>
         <script type="text/javascript" src="${h.url('/js/select2.js', ver=c.kallithea_version)}"></script>
         <script type="text/javascript" src="${h.url('/js/native.history.js', ver=c.kallithea_version)}"></script>
+        <script type="text/javascript" src="${h.url('/js/jquery.caret.min.js', ver=c.kallithea_version)}"></script>
+        <script type="text/javascript" src="${h.url('/js/jquery.atwho.min.js', ver=c.kallithea_version)}"></script>
         <script type="text/javascript" src="${h.url('/js/base.js', ver=c.kallithea_version)}"></script>
         ## EXTRA FOR JS
         <%block name="js_extra"/>