问题描述
我想出了一个用户脚本的想法,以便在编写 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="javascript:void(0);" 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 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。