quick SE comments for quick SE people


Tired of typing the same thing over and over again? Now, with QuickComment, you can type ";;linkonly" (for example) and have it expand to a detailed comment to a user explaining why link-only answers are bad. Inspired by AutoReviewComments.


Current version is 1.0.2.

How to use

Install as a normal userscript (for Chrome, drag and drop into chrome://extensions, or just see here for a detailed guide for all browsers). Then go to any SE site, open a comment box, and type ;;welcome and press the space bar for a short introduction.


some of this list is copied from AutoReviewComments - Pro-forma comments for SE

  1. Read your comment before you post it!

    Inserting a QuickComment only inserts the text; it doesn't submit the comment. This is so that you can check the comment before posting!

  2. Insert as many templates as you would like

    Multiple commands in a single comment are supported; for example: ;;welcome ;;linkonly ;;thanks. Simply type them one after the other!

  3. Full customization

    You can change the prefix (;; by default) and the commands, which also support regexp matching for site hostnames (ex. stackapps.com) to customize your QuickComments to specific sites.

  4. Easy import/export and simple editing via a JSON editor

    The format for customizing your QuickComments is JSON, which is easy to copy/paste wherever you want, including into an IDE to edit as you please.

  5. Sublime-esque "match-anywhere" searcher

    The script doesn't just search for substrings of the commands: it also searches for subsequences. For example, if you want to access the ;;welcome command, you can just type ;;wlc. Exact matches (ex. ;;welc) will still show up first in the list of suggestions.

  6. Access to data about the post being commented on

    QuickComment will automatically replace $STIENAME with the site's name (ex. Stack Apps), $SITEURL with its URL (ex. stackapps.com), and $USERNAME with the name of the user whose post you are commenting on. added in 1.0.1

  7. Easy access on all sites / computers by storing information in profile

    For example, just add this at the end of your profile:

    prefix: ";;", commands: [
      {"sites": ".", "cmd": "welcome", "text": "Welcome to $SITENAME!"}

    Now just click the "(auto)" link (next to the "qc" link) on any site or any computer, and you'll have access to an easy ;;welcome command. added in 1.0.2


Here is the full code of stackexchange-quickcomment.user.js. Warning: it may be outdated; see the Github repo (in the Download section above) for the newest version.

// ==UserScript==
// @name stackexchange-quickcomment
// @namespace http://keyboardfire.com/
// @license MIT
// @description Quick SE comments for quick SE people
// @version 1.0.2
// @match *://*.stackexchange.com/*
// @match *://*.stackoverflow.com/*
// @match *://*.superuser.com/*
// @match *://*.serverfault.com/*
// @match *://*.askububtu.com/*
// @match *://*.stackapps.com/*
// @match *://*.mathoverflow.net/*
// ==/UserScript==

var userscript = function($) {

if (!localStorage.__stackexchange_quickcomment_data) {
    localStorage.__stackexchange_quickcomment_data = JSON.stringify({
        prefix: ';;',
        commands: [
            {sites: '.', cmd: 'welcome', text: 'Welcome to stackexchange-quickcomment! ' +
                'To use, simply click the "qc" link to the right of this comment box to edit your quickcomments. The format is JSON. ' +
                'The `sites` field is a regexp that tests against the hostname (ex. "$SITEURL") so as to only pertain to a specific site or sites. ' +
                'The `cmd` field is the command that you can type in after the prefix to access the quickcomment. ' +
                'Finally, the text field is the text that will be inserted when you use the quickcomment. Enjoy!'}
var data = JSON.parse(localStorage.__stackexchange_quickcomment_data);

var commentModes = {NORMAL: 0, COMMAND: 1}, commentMode, focusedComment, commentPopup, commentPopupSelectionIndex;
$(document).on('focus', 'textarea[name="comment"]', function() {
    var currentComment = $(this).closest('form').attr('id');
    if (focusedComment != currentComment) {
        focusedComment = currentComment;
}).on('keydown keyup', 'textarea[name="comment"]', function(e) { // 'keydown keyup' to prevent weird timing issues with typing really fast
    switch (commentMode) {
    case commentModes.NORMAL:
        if (new RegExp(data.prefix + '$').test(this.value)) {
            commentMode = commentModes.COMMAND;
    case commentModes.COMMAND:
        if (e.which == 27) { // esc
        } else if (e.which == 32) { // space
            this.value = this.value.replace(new RegExp('^(.*)' + data.prefix + '.*$'), function(m, g1) { return g1; })
                + parseText(data.commands[parseInt(commentPopup.children().eq(commentPopupSelectionIndex).attr('data-cmdindex'))].text, this);
        } else if (e.which == 37 || e.which == 39) { // left / right respectively
            if (e.type == 'keydown') {
                var delta = e.which - 38; // this just happened to work out perfectly
                commentPopup.children().eq(commentPopupSelectionIndex).css('font-weight', 'normal');
                commentPopupSelectionIndex = Math.max(0, Math.min(commentPopup.children().length-1, commentPopupSelectionIndex + delta));
                commentPopup.children().eq(commentPopupSelectionIndex).css('font-weight', 'bold');
        } else {
            if (e.which == 8 && !new RegExp(data.prefix).test(this.value)) { // backspace
            } else {
                updatePopup(this.value.match(new RegExp('.*' + data.prefix + '(.*)$'))[1]);

$(document).on('click', '.comments-link', function() {
    var td = $(this).parent();
    var intr = setInterval(function() {
        var helpLink = $('.comment-help-link', td);
        if (helpLink.length) {
            if ($('.stackexchange-quickcomment-link', td).length === 0) {
                helpLink.parent().append($('<span>').text(' | ').addClass('lsep'))
                    .append($('<a>').text('qc').addClass('stackexchange-quickcomment-link').click(function() {
                        var tr;
                        $(this).closest('tr').parent().append(tr = $('<tr>').append(
                            $('<td>').append($('<textarea>').attr({cols: 68, rows: 3, id: 'stackexchange-quickcomment-editor'}).css('height', '20em').val(prettyJSON(data)))
                            $('<td>').append($('<input>').attr({type: 'submit'}).val('Save').click(function(e){
                                data = JSON.parse('{' + $('textarea', tr).val() + '}');
                                localStorage.__stackexchange_quickcomment_data = JSON.stringify(data);
                    })).append($('<span>').text(' | ').addClass('lsep'))
                    .append($('<a>').text('(auto)').click(function() {
                            url: (window.location.origin + '/users/edit/' + $('.topbar a.profile-me').attr('href').match(/\d+/)[0]),
                            success: function(resp) {
                                var profileData = unHTMLEncode(resp.match(/&lt;!--stackexchange-quickcomment([\s\S]+?)--&gt;/)[1]);
                                data = JSON.parse('{' + profileData + '}');
                                localStorage.__stackexchange_quickcomment_data = JSON.stringify(data);
                                alert('Data successfully pulled from profile');
    }, 5);

function createPopup(el) {
    commentPopup = $('<div>');

function updatePopup(txt) {
    commentPopupSelectionIndex = 0;
    for (var i = 0; i < data.commands.length; i++) {
        if (new RegExp(data.commands[i].sites).test(window.location.hostname) && extMatch(txt, data.commands[i].cmd)) {
            commentPopup[data.commands[i].cmd.indexOf(txt) > -1 ? 'prepend' : 'append']
                ($('<span>').text(data.commands[i].cmd).attr('data-cmdindex', '' + i).css({
                    padding: '2px'
    commentPopup.children().eq(0).css('font-weight', 'bold');

function destroyPopup() {
    commentMode = commentModes.NORMAL;
    if (commentPopup) commentPopup.remove();

function parseText(text, commentBox) {
    return text
        .replace(/\$SITENAME/g, $('.site-icon.favicon').attr('title'))
        .replace(/\$SITEURL/g, window.location.hostname)
        .replace(/\$USERNAME/g, $(commentBox).closest('div[class="question"],div[class="answer"]').find('.post-signature:last .user-details a').text())

function prettyJSON(obj) {
    return JSON.stringify(obj)
        .replace(/^\{|\}$/g, '')
        .replace(/"\},/g, '"},\n  ') // this will break if string contains the substring '"},', but that's incredibly unlikely 
        .replace(':[', ': [\n  ')
        .replace(']', '\n]')
        .replace(/:"/g, ': "')
        .replace(/,"/g, ', "')

function unHTMLEncode(text) {
    // todo: make this work for all texts (this is the lazy version)
    return text
        .replace(/&quot;/g, '"')
        .replace(/&#39;/g, '\'')
        .replace(/&amp;/g, '&') // this must come last

function extMatch(needle, haystack) {
    return new RegExp(needle.split('').join('.*')).test(haystack);


var el = document.createElement('script');
el.type = 'text/javascript';
el.text = '(' + userscript + ')(jQuery);';

(v1.0.1): metadata referring to the post being commented on

For example,

  • $SITENAME: the name of the current site (ex. Stack Apps)
  • $SITEURL: the URL of the current site (ex. stackapps.com)
  • $USERNAME: the owner of the post being commented on

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .