問題描述

我想出了一個用户腳本的想法,以便在編寫 a previous answer 時簡化在 Stack Exchange 上的寫作答案。

此用户腳本在每個代碼段的頂部添加 「評論」 鏈接。點擊後,它會在每個代碼行前添加複選框。您選擇要註釋的行的複選框,再次單擊該鏈接 – 現在已更改為 「添加到應答」,並且代碼將被複制到您的答案。

我相信這個用户腳本可以大大減少上下滾動,您始終從問題中複製一些代碼,將其粘貼到答案中,再次向上滾動,複製代碼,向下滾動,粘貼,向上滾動等等… 現在你可以從上到下瀏覽代碼,並選擇要註釋的行。

我已經使用 Google Chrome + Tampermonkey 測試了這個用户名,但我相信它也可以使用 Firefox + Greasemonkey 以及支持用户腳本的其他瀏覽器/擴展組合。

該代碼也可在 GitHub 上獲得。

如果您能夠從鏈接中添加用户腳本 use this link

// ==UserScript==
// @name          Auto-Review
// @author        Simon Forsberg
// @namespace     zomis
// @homepage      https://www.github.com/Zomis/Auto-Review
// @description   Adds checkboxes for copying code in a post to an answer.
// @include       http://stackoverflow.com/*
// @include       http://meta.stackoverflow.com/*
// @include       http://superuser.com/*
// @include       http://serverfault.com/*
// @include       http://meta.superuser.com/*
// @include       http://meta.serverfault.com/*
// @include       http://stackapps.com/*
// @include       http://askubuntu.com/*
// @include       http://*.stackexchange.com/*
// @exclude       http://chat.stackexchange.com/*
// ==/UserScript==

function embedFunction(name, theFunction) {
    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.textContent = theFunction.toString().replace(/function ?/, 'function ' + name);
    document.getElementsByTagName('head')[0].appendChild(script);
}

embedFunction('showAutoreviewButtons', function(clickedObject) {

    var i;
    if ($(clickedObject).data('review')) {
        var answer = $("#wmd-input");
        var answer_text = answer.val();
        var added_lines = 0;
        var added_blocks = 0;

        // loop through checkboxes and prepare answer
        var checkboxes = $("input.autoreview");
        var block = [];
        for (i = 0; i < checkboxes.length; i++) {
            if (!$(checkboxes[i]).prop('checked')) {
                continue;
            }

            var checkbox = $(checkboxes[i]);
            var line_data = (checkbox).data('line');
            block.push(line_data);
            if ((i === checkboxes.length - 1) || !$(checkboxes[i + 1]).prop('checked')) {
                // add block
                var block_line;
                var cut_count = 1000;
                for (block_line = 0; block_line < block.length; block_line++) {
                    var cut_this = block[block_line].indexOf(block[block_line].trim());
                    if (cut_count > cut_this) {
                        cut_count = cut_this;
                    }
                }
                for (block_line = 0; block_line < block.length; block_line++) {
                    answer_text = answer_text + "n    " + block[block_line].substr(cut_count);
                }
                answer_text += "nn---n";
                added_lines += block.length;
                added_blocks++;
                block = [];
            }
        }

        answer.val(answer_text);
        alert(added_lines + " lines in " + added_blocks + " blocks added to answer.");

        return;
    }
    $(clickedObject).data('review', true);
    $(clickedObject).text("Add to answer");

    var spans = $("code span", $(clickedObject).next());
    console.log(spans.length);

    var count = spans.length;
    var line = "";
    var first = null;
    for (i = 0; i < count; i++) {
        var element = $(spans[i]);

        if (first === null) {
            first = element;
        }
        if (element.text().indexOf("n") !== -1) {
            console.log(i + " line: " + line);

            var lines = element.text().split("n");
            element.text("");
            for (var line_index = 1; line_index < lines.length; line_index++) {
                var current_line = lines[line_index];
                var prev_line = lines[line_index - 1];

                var span;
                // Add the last part of the previous line
                if (line_index == 1) {
                    line += prev_line;
                    span = $('<span class="pln zomis before">' + prev_line + 'n</span>');
                    element.after(span);
                    element = span;
                }

                // Add the checkbox for the previous line
                if (line.length > 0) {
                    var dataProperty = 'data-line="' + line + '" ';
                    var checkbox = $('<input type="checkbox" ' + dataProperty + ' class="autoreview"></input>');
                    first.before(checkbox);
                    first = null;
                }

                // Add the beginning <span> element for the current line
                if (line_index < lines.length - 1) {
                    current_line += "n";
                }
                span = $('<span class="pln zomis after">' + current_line + '</span>');
                element.after(span);
                first = span;
                element = span;
                line = current_line;
            }
        }
        else {
            line += element.text().replace(/\/g, '\\').replace(/"/g, '\"');
        }
    }
    if (line.length > 0) {
        dataProperty = 'data-line="' + line + '" ';
        checkbox = $('<input type="checkbox" ' + dataProperty + ' class="autoreview"></input>');
        first.before(checkbox);
    }
});

$('pre code').parent().before('<a href="" onclick="showAutoreviewButtons($(this))">(Review)</a>');

主要問題:

  • 我遵循 JavaScript 約定嗎?我已經看到 people.coding( "like this" ); 在每個參數附近有額外的空間,我也看到人們在方法的開頭聲明所有變量,這是一些官方的慣例嗎?我在這裏打官司嗎我覺得我在 JavaScript 代碼中使用 Java 約定。

  • 可以清理代碼,還要使用實用方法和東西,同時保持瀏覽器的兼容性?例如,在代碼中找出一個塊的縮進,我在想我將如何在 Java 中使用 block.stream().mapToInt(str -> str.indexOf(str.trim())).min(),在這裏可以做類似的事情?

  • 歡迎任何其他意見!

我敢打賭有很多事情可以改進。事實上,這是一個用户腳本讓我覺得有點限制我可以做什麼,但也許這只是因為我不知道如何做這些用户腳本。

我計劃將這個用户名發貼到 http://www.stackapps.com,當我很高興的時候,任何 feature-requests /UI 建議都可以添加為 github 的問題,或者你可以在 The 2nd Monitor 中 ping 我。

任何人在編寫他們的答案時使用腳本的人都是 Auto-upvote!

最佳解決方案

Naming

我認識到命名可能很難,但是’Auto-Review’ 是一個與 StackExchange 上退出的 UserScript 衝突的名稱叫做 AutoReviewComments

Protocols

有許多 UserScript 約定您沒有遵守。

首先,對於 Firefox,您需要將標題部分包含到”preserved” 註釋塊中:

/** @preserve
// ==UserScript==
.....
// ==/UserScript==
*/

在處理 JavaScript 之後,保留需要保留註釋塊。這允許’compiled’ 版本保留引用,FireFox 可以知道它是什麼。

其他瀏覽器可能沒有相同的要求。

此外,您還需要指定 @grant 權限,以使 GreaseMonkey 快樂。在你的情況下,none 是適當的:

// @grant   none

一旦我做了這些修改,用户腳本在我的 FireFox 中很好地加載。

Usability

我建議有四種 user-experience 增強功能:

  1. 沒有 POPUPS – 彈出窗口是一個可怕的分心

  2. Scroll-to-inserted 內容 – 插入代碼塊後,滾動到編輯點並使其在屏幕上可見

  3. 在答案輸入框中觸發 changed-event – 這將更新答案的’preview’ 。目前,您必須在答案框中手動更改某些內容以進行預覽更新。

  4. 處理後,您應該清除複選框。如果您需要稍後複製不同的塊,un-check 每個盒子都是一個 PITA 。

Review

  • double-array-dereferencing 是不必要的 micro-optimization 。您有以下代碼 (在此使用您的 userscript 複製):

    for (i = 0; i < checkboxes.length; i++) {
    if (!$(checkboxes[i]).prop('checked')) {
    continue;
    }

    var checkbox = $(checkboxes[i]);
    var line_data = (checkbox).data('line');

    那代碼 double-dereferences $(checkboxes[i])。我認為這是因為你不想攜帶變量的開銷,如果沒有檢查。這是一個 early-optimization 。代碼將更簡單:

    for (i = 0; i < checkboxes.length; i++) {
        var checkbox = $(checkboxes[i]);
        if (!checkbox.prop('checked')) {
            continue;
        }
    
        var line_data = (checkbox).data('line');
    
  • var 聲明。 JavaScript ‘hoists’ 所有變量聲明到包含它的函數的頂部。與其他語言不同,JavaScript 不應使用’block-local’ 聲明進行編碼。最好的做法是將所有變量聲明移動到函數中的第一個項目中。 This Stack Overflow answer 比我更好地解釋它。

次佳解決方案

編碼風格

我不是一個 JavaScript 大師,但我相當肯定編碼風格指南比 Java 更加嚴格。在運算符周圍使用間距的方式,像 Java 一樣的大括號的放置似乎很常見,我從來沒有聽説有人反對。還有一些其他的風格,像你提到的,但它們是罕見的。有些人也喜歡使用 2 個空格而不是 4 個縮進,但這似乎在很大程度上是一個味道的問題。

作為參考,PyCharm 是 JetBrains(與 IntelliJ 相同的品種) 的 Python + Web 開發的夢幻 IDE,它對 JavaScript 有很好的支持,並且它不反對您的代碼的風格。我會把它作為一個好兆頭。對於像 people.coding( "like this" ); 這樣的代碼,auto-format 函數會刪除括號內的空格。

壞習慣

首先,將代碼粘貼到 http://jshint.com/中,看看你自己:

  • 一個重複的變量定義:checkbox

  • 使用範圍以外的幾個變量

另外,我覺得奇怪的是,你不能在 embedFunction 中充分利用 jQuery 。由於您已經在腳本中的其他位置使用了 jQuery,而不是:

   document.getElementsByTagName('head')[0].appendChild(script); 

你可以簡化為:

$('head').append(script);

更新:看來 (從你的評論),這個建議不適用於你”as-is” 。我會試着弄清楚為什麼。在此期間,我的反對意見仍然存在:在同一個腳本中混合使用經典的 JavaScript 和 jQuery 是有點奇怪,就好像它是由不同的人或同一個人在不同的時間完成的。我認為更好地保持一致,並且編寫經典的 JavaScript 而不依賴於 jQuery,或者完全擁抱 jQuery 。

緩存 $(...)查詢的結果

DOM 查找不是很便宜。所以每當你重複查找時,考慮緩存一個變量,例如:

if (!$(checkboxes[i]).prop('checked')) {     continue; } var checkbox = $(checkboxes[i]); 

如果您首先分配給 checkbox,然後執行 if,以上更好。它也減少了 checkboxes[i]的重複。

後來我在代碼中看到 $(clickedObject)多次。當然也可以緩存。

奇怪的元素

在這一行:

var line_data = (checkbox).data('line'); 

checkbox 周圍的括號是… 好奇… 你不需要它們,可能會讓讀者誤以為 jQuery 被錯誤地省略了 $(它不是) 。

增強作業

這個賦值可以用+=來代替擴充賦值:

answer_text = answer_text + "n    " + block[block_line].substr(cut_count); 

參考文獻

注:本文內容整合自 Google/Baidu/Bing 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。