問題描述

我想出了一個使用者指令碼的想法,以便在編寫 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 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。