您很可能会看到我的更多问题结构与该问题相同。如果您想自己使用它,请随时使用。
相关的先前存在的工具:
自动校正CR和SO代码
用于在代码审阅中选择代码片段的书签
代码下载
为了方便起见,可以在GitHub上找到此代码(非常感谢@amon教会了我很多关于如何使用git的知识
类摘要(10079字节,在342行中,共4个文件)
写入的字节数
ReviewPrepareFrame:JFrame,用于让用户选择应进行审查的文件
ReviewPreparer:最重要的类,负责大部分工作。
TextAreaOutputStream:OutputStream用于输出到
JTextArea
。Code
CountingStream :(大约679个字节)在27行中)
/**
* An output stream that keeps track of how many bytes that has been written to it.
*/
public class CountingStream extends FilterOutputStream {
private final AtomicInteger bytesWritten;
public CountingStream(OutputStream out) {
super(out);
this.bytesWritten = new AtomicInteger();
}
@Override
public void write(int b) throws IOException {
bytesWritten.incrementAndGet();
super.write(b);
}
public int getBytesWritten() {
return bytesWritten.get();
}
}
ReviewPrepareFrame:(在109行中约为3178字节)
public class ReviewPrepareFrame extends JFrame {
private static final long serialVersionUID = 2050188992596669693L;
private JPanel contentPane;
private final JTextArea result = new JTextArea();
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
new ReviewPrepareFrame().setVisible(true);
}
catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public ReviewPrepareFrame() {
setTitle("Prepare code for Code Review");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
JPanel panel = new JPanel();
contentPane.add(panel, BorderLayout.NORTH);
final DefaultListModel<File> model = new DefaultListModel<>();
final JList<File> list = new JList<File>();
panel.add(list);
list.setModel(model);
JButton btnAddFiles = new JButton("Add files");
btnAddFiles.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JFileChooser dialog = new JFileChooser();
dialog.setMultiSelectionEnabled(true);
if (dialog.showOpenDialog(ReviewPrepareFrame.this) == JFileChooser.APPROVE_OPTION) {
for (File file : dialog.getSelectedFiles()) {
model.addElement(file);
}
}
}
});
panel.add(btnAddFiles);
JButton btnRemoveFiles = new JButton("Remove files");
btnRemoveFiles.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (File file : new ArrayList<>(list.getSelectedValuesList())) {
model.removeElement(file);
}
}
});
panel.add(btnRemoveFiles);
JButton performButton = new JButton("Create Question stub with code included");
performButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
result.setText("");
ReviewPreparer preparer = new ReviewPreparer(filesToList(model));
TextAreaOutputStream outputStream = new TextAreaOutputStream(result);
preparer.createFormattedQuestion(outputStream);
}
});
contentPane.add(performButton, BorderLayout.SOUTH);
contentPane.add(result, BorderLayout.CENTER);
}
public List<File> filesToList(DefaultListModel<File> model) {
List<File> files = new ArrayList<>();
for (int i = 0; i < model.getSize(); i++) {
files.add(model.get(i));
}
return files;
}
}
ReviewPreparer:(165行中约5416字节)
public class ReviewPreparer {
private final List<File> files;
public ReviewPreparer(List<File> files) {
this.files = new ArrayList<>();
for (File file : files) {
if (file.getName().lastIndexOf('.') == -1)
continue;
if (file.length() < 10)
continue;
this.files.add(file);
}
}
public int createFormattedQuestion(OutputStream out) {
CountingStream counter = new CountingStream(out);
PrintStream ps = new PrintStream(counter);
outputHeader(ps);
outputFileNames(ps);
outputFileContents(ps);
outputDependencies(ps);
outputFooter(ps);
ps.print("Question Length: ");
ps.println(counter.getBytesWritten());
return counter.getBytesWritten();
}
private void outputFooter(PrintStream ps) {
ps.println("#Usage / Test");
ps.println();
ps.println();
ps.println("#Questions");
ps.println();
ps.println();
ps.println();
}
private void outputDependencies(PrintStream ps) {
List<String> dependencies = new ArrayList<>();
for (File file : files) {
try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file)))) {
String line;
while ((line = in.readLine()) != null) {
if (!line.startsWith("import ")) continue;
if (line.startsWith("import java.")) continue;
if (line.startsWith("import javax.")) continue;
String importStatement = line.substring("import ".length());
importStatement = importStatement.substring(0, importStatement.length() - 1); // cut the semicolon
dependencies.add(importStatement);
}
}
catch (IOException e) {
ps.println("Could not read " + file.getAbsolutePath());
ps.println();
// this will be handled by another function
}
}
if (!dependencies.isEmpty()) {
ps.println("#Dependencies");
ps.println();
for (String str : dependencies)
ps.println("- " + str + ": ");
}
ps.println();
}
private int countLines(File file) throws IOException {
return Files.readAllLines(file.toPath(), StandardCharsets.UTF_8).size();
}
private void outputFileContents(PrintStream ps) {
ps.println();
for (File file : files) {
try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file)))) {
ps.printf("**%s:** (approximately %d bytes in %d lines)", className(file), file.length(), countLines(file));
ps.println();
ps.println();
String line;
int importStatementsFinished = 0;
while ((line = in.readLine()) != null) {
// skip package and import declarations
if (line.startsWith("package ")) continue;
if (line.startsWith("import ")) {
importStatementsFinished = 1;
continue;
}
if (importStatementsFinished >= 0) importStatementsFinished = -1;
if (importStatementsFinished == -1 && line.trim().isEmpty()) continue;
importStatementsFinished = -2;
line = line.replaceAll(" ", "\t"); // replace four spaces with tabs, since that takes less space
ps.print(" "); // format as code for StackExchange, this needs to be spaces.
ps.println(line);
}
}
catch (IOException e) {
ps.print("> ");
e.printStackTrace(ps);
}
ps.println();
}
}
private void outputFileNames(PrintStream ps) {
int totalLength = 0;
int totalLines = 0;
for (File file : files) {
totalLength += file.length();
try {
totalLines += countLines(file);
}
catch (IOException e) {
ps.println("Unable to determine line count for " + file.getAbsolutePath());
}
}
ps.printf("###Class Summary (%d bytes in %d lines in %d files)", totalLength, totalLines, files.size());
ps.println();
ps.println();
for (File file : files) {
ps.println("- " + className(file) + ": ");
}
}
private String className(File file) {
String str = file.getName();
return str.substring(0, str.lastIndexOf('.'));
}
private void outputHeader(PrintStream ps) {
ps.println("#Description");
ps.println();
ps.println("- Add some [description for what the code does](http://meta.codereview.stackexchange.com/questions/1226/code-should-include-a-description-of-what-the-code-does)");
ps.println("- Is this a follow-up question? Answer [What has changed, Which question was the previous one, and why you are looking for another review](http://meta.codereview.stackexchange.com/questions/1065/how-to-post-a-follow-up-question)");
ps.println();
ps.println("#Code download");
ps.println();
ps.println("For convenience, this code can be downloaded from [somewhere](http://github.com repository perhaps?)");
ps.println();
}
public static void main(String[] args) throws IOException {
// List<File> fileList = Arrays.asList(new File("C:/_zomisnet/_reviewtest").listFiles());
List<File> fileList = Arrays.asList(new File("./src/net/zomis/reviewprep").listFiles());
new ReviewPreparer(fileList).createFormattedQuestion(System.out);
}
}
TextAreaOutputStream:(41行中约806字节)
public class TextAreaOutputStream extends OutputStream {
private final JTextArea textArea;
private final StringBuilder sb = new StringBuilder();
public TextAreaOutputStream(final JTextArea textArea) {
this.textArea = textArea;
}
@Override
public void flush() {
}
@Override
public void close() {
}
@Override
public void write(int b) throws IOException {
if (b == '\n') {
final String text = sb.toString() + "\n";
SwingUtilities.invokeLater(new Runnable() {
public void run() {
textArea.append(text);
}
});
sb.setLength(0);
return;
}
sb.append((char) b);
}
}
用法/测试
输入:运行JFrame,选择应检查的文件,单击按钮。
输出:我当前正在编写此问题的存根:)
问题
寻找关于我的代码的一般注释。如果您要继续我已经开始的工作,该代码是开源的。我目前只专门处理Java,因为这是我最常编写的代码,但是我相信以后可以添加对其他语言的更好支持(我不是在问怎么做,或者不是在问你这样做,我只是说经过一些修改,我相信是有可能的。)
#1 楼
我鼓励您产生更明确的输出,尤其是文件名。如果我想反向进行此过程并将代码刮到机器上的文件中,请使用如下所示的Python脚本… import json
from lxml import html
import re
import requests
FILENAME_HINT_XPATH = "../preceding-sibling::p[1]/strong/text()"
def code_for_post(site, post):
r = requests.get('https://api.stackexchange.com/2.1/posts/{1}?site={0}&filter=withbody'.format(site, post))
j = json.loads(r.text)
body = j['items'][0]['body']
tree = html.fromstring(body)
code_elements = tree.xpath("//pre/code[%s]" % (FILENAME_HINT_XPATH))
return dict((c.xpath(FILENAME_HINT_XPATH)[0], c.findtext(".")) for c in code_elements)
def write_files(code):
extension = '.java' # <-- Yuck, due to @Simon.
for filename_hint, content in code.iteritems():
filename = re.sub(r'[^A-Za-z0-9]', '', filename_hint) + extension
with open(filename, 'w') as f:
print >>f, content
write_files(code_for_post('codereview', 41198))
...然后我必须对文件扩展名进行假设。
调用方法可以改进。我建议不要硬编码特定目录以查找源文件,而是建议...
如果将文件作为命令行参数显式传递给程序,请使用这些文件。
如果指定了目录,则使用其中包含的所有文件,不包括具有明显非ASCII内容的文件。
如果不使用命令行参数,则对当前目录进行操作。
能够说出
java ReviewPreparer *.java |
pbcopy
很高兴。评论
\ $ \ begingroup \ $
你的意思是我应该打印出它是.java文件?
\ $ \ endgroup \ $
–西蒙·福斯伯格
2014年2月8日在9:20
\ $ \ begingroup \ $
是的,那会让我删除#Yuck行。
\ $ \ endgroup \ $
– 200_success
2014年2月8日在9:25
#2 楼
忠实地传达代码比通过用制表符替换空格来节省一些输出字节要重要得多:line = line.replaceAll(" ", "\t"); // replace four spaces with tabs, since that takes less space
这个问题本身,我怀疑您的工具负责在接下来的一行中引入该错误:两个。
评论
\ $ \ begingroup \ $
哎呀!确实正确。我也在考虑是否用空格代替制表符会更好呢? (唯一的问题是我想要查看的代码往往很大:))
\ $ \ endgroup \ $
–西蒙·福斯伯格
14年2月8日在9:58
\ $ \ begingroup \ $
我认为,忠实地传达代码的必要性超出了内容的处理范围。
\ $ \ endgroup \ $
– 200_success
2014年2月8日在10:13
\ $ \ begingroup \ $
那里很重要。最好只在每行的开头添加四个空格,然后将其留在那。
\ $ \ endgroup \ $
–西蒙·福斯伯格
2014年2月8日在10:19
\ $ \ begingroup \ $
@SimonAndréForsberg为错误发现+1到200_success,为“不要乱扔...”而另外+1。 (不过上限为+1)。另外,除非是Python,否则应该只在行首/末尾加上空格,在这种情况下,唯一安全的修改是在前面添加4个空格。
\ $ \ endgroup \ $
–rolfl
2014年2月8日在12:47
评论
现在,此问题进行了跟进:codereview.stackexchange.com/questions/41225/…