問題描述
我想出了一個用户腳本的想法,以便在編寫 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 增強功能:
-
沒有 POPUPS – 彈出窗口是一個可怕的分心
-
Scroll-to-inserted 內容 – 插入代碼塊後,滾動到編輯點並使其在屏幕上可見
-
在答案輸入框中觸發 changed-event – 這將更新答案的’preview’ 。目前,您必須在答案框中手動更改某些內容以進行預覽更新。
-
處理後,您應該清除複選框。如果您需要稍後複製不同的塊,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 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。