diff --git a/wpiformat/test/test_bracenewline.py b/wpiformat/test/test_bracenewline.py new file mode 100644 index 00000000..52f83367 --- /dev/null +++ b/wpiformat/test/test_bracenewline.py @@ -0,0 +1,107 @@ +import os + +from wpiformat.config import Config +from wpiformat.bracenewline import BraceNewline + + +def test_bracenewline(): + task = BraceNewline() + + inputs = [] + outputs = [] + + # Brackets on same line + inputs.append(("./Test.cpp", + "void func1() {}" + os.linesep + \ + "void func2() {}" + os.linesep)) + outputs.append(( + "void func1() {}" + os.linesep + \ + os.linesep + \ + "void func2() {}" + os.linesep, True, True)) + + # Brackets on next line + inputs.append(("./Test.cpp", + "void func1() {" + os.linesep + \ + "}" + os.linesep + \ + "void func2() {" + os.linesep + \ + "}" + os.linesep)) + outputs.append(( + "void func1() {" + os.linesep + \ + "}" + os.linesep + \ + os.linesep + \ + "void func2() {" + os.linesep + \ + "}" + os.linesep, True, True)) + + # Comments after brackets + inputs.append(("./Test.cpp", + "void func1() {" + os.linesep + \ + "} // This is a comment" + os.linesep + \ + "void func2() {" + os.linesep + \ + "} /* This is a comment */" + os.linesep + \ + "void func3() {" + os.linesep + \ + "}" + os.linesep)) + outputs.append(( + "void func1() {" + os.linesep + \ + "} // This is a comment" + os.linesep + \ + os.linesep + \ + "void func2() {" + os.linesep + \ + "} /* This is a comment */" + os.linesep + \ + os.linesep + \ + "void func3() {" + os.linesep + \ + "}" + os.linesep, True, True)) + + # Don't add line separators to uncondensed if statements (after last brace + # is OK) + inputs.append(("./Test.cpp", + "void func1() {" + os.linesep + \ + " if (1) {" + os.linesep + \ + " }" + os.linesep + \ + " else {" + os.linesep + \ + " }" + os.linesep + \ + "}" + os.linesep)) + outputs.append((inputs[len(inputs) - 1][1], False, True)) + + # Don't add line separators to condensed if statements (after last brace + # is OK) + inputs.append(("./Test.cpp", + "void func1() {" + os.linesep + \ + " if (1) {" + os.linesep + \ + " } else if () {" + os.linesep + \ + " } else {" + os.linesep + \ + " // comment" + os.linesep + \ + " }" + os.linesep + \ + "}" + os.linesep)) + outputs.append((inputs[len(inputs) - 1][1], False, True)) + + # Don't add line separators before #endif statements + inputs.append(("./Main.cpp", + "using decay_t = typename decay::type;" + os.linesep + \ + "} // namespace std" + os.linesep + \ + "#endif" + os.linesep)) + outputs.append((inputs[len(inputs) - 1][1], False, True)) + + # Don't insert line separators within multiline comments + inputs.append(("./Main.java", + "/* to fine tune the pid loop." + os.linesep + \ + " *" + os.linesep + \ + " * @return the {@link PIDController} used by this {@link PIDSubsystem}" + os.linesep + \ + " */" + os.linesep + \ + "public PIDController getPIDController() {" + os.linesep)) + outputs.append((inputs[len(inputs) - 1][1], False, True)) + + # Don't insert line separators within single line comments + inputs.append(("./Main.java", + "// @return the {@link PIDController} used by this {@link PIDSubsystem}" + os.linesep + \ + "public PIDController getPIDController() {" + os.linesep)) + outputs.append((inputs[len(inputs) - 1][1], False, True)) + + assert len(inputs) == len(outputs) + + config_file = Config(os.path.abspath(os.getcwd()), ".styleguide") + for i in range(len(inputs)): + print("Testing {}...".format(i)) + output, file_changed, success = task.run_pipeline( + config_file, inputs[i][0], inputs[i][1]) + assert output == outputs[i][0] + assert file_changed == outputs[i][1] + assert success == outputs[i][2] diff --git a/wpiformat/test/test_newline.py b/wpiformat/test/test_eofnewline.py similarity index 93% rename from wpiformat/test/test_newline.py rename to wpiformat/test/test_eofnewline.py index 84cb6e2b..ee28b06c 100644 --- a/wpiformat/test/test_newline.py +++ b/wpiformat/test/test_eofnewline.py @@ -1,11 +1,11 @@ import os from wpiformat.config import Config -from wpiformat.newline import Newline +from wpiformat.eofnewline import EofNewline -def test_newline(): - task = Newline() +def test_eofnewline(): + task = EofNewline() inputs = [] outputs = [] diff --git a/wpiformat/wpiformat/__init__.py b/wpiformat/wpiformat/__init__.py index b064820c..40a65c78 100644 --- a/wpiformat/wpiformat/__init__.py +++ b/wpiformat/wpiformat/__init__.py @@ -9,13 +9,14 @@ import sys from wpiformat.bracecomment import BraceComment +from wpiformat.bracenewline import BraceNewline from wpiformat.clangformat import ClangFormat from wpiformat.config import Config +from wpiformat.eofnewline import EofNewline from wpiformat.includeguard import IncludeGuard from wpiformat.includeorder import IncludeOrder from wpiformat.licenseupdate import LicenseUpdate from wpiformat.lint import Lint -from wpiformat.newline import Newline from wpiformat.pyformat import PyFormat from wpiformat.stdlib import Stdlib from wpiformat.task import Task @@ -328,9 +329,10 @@ def main(): # so it can clean up their formatting. task_pipeline = [ BraceComment(), + BraceNewline(), + EofNewline(), IncludeGuard(), LicenseUpdate(str(args.year)), - Newline(), Stdlib(), IncludeOrder(), UsingDeclaration(), diff --git a/wpiformat/wpiformat/bracenewline.py b/wpiformat/wpiformat/bracenewline.py new file mode 100644 index 00000000..d8828997 --- /dev/null +++ b/wpiformat/wpiformat/bracenewline.py @@ -0,0 +1,66 @@ +"""This task ensures braces are followed by two line separators.""" + +import re + +from wpiformat.task import Task + + +class BraceNewline(Task): + + def should_process_file(self, config_file, name): + return config_file.is_c_file(name) or config_file.is_cpp_file( + name) or name.endswith("java") + + def run_pipeline(self, config_file, name, lines): + linesep = Task.get_linesep(lines) + + comment_regex = re.compile("//|/\*|\*/") + + lines_list = lines.split(linesep) + + in_multiline_comment = False + for i in range(len(lines_list)): + match = comment_regex.search(lines_list[i]) + if not match: + line = lines_list[i].rstrip() + else: + # While in a multiline comment, we only care about "*/" + if in_multiline_comment: + if match.group() == "*/": + line = lines_list[i][ + match.start() + len("*/"):].rstrip() + in_multiline_comment = False + else: + line = lines_list[i][0:match.start()].rstrip() + + # If multiline comment is starting + if match.group() == "/*": + line = lines_list[i][0:match.start()] + in_multiline_comment = True + + # If comment ends on same line, handle it immediately + comment_end = lines_list[i].find("*/") + if comment_end != -1: + line += lines_list[i][comment_end + len("*/"):] + line = line.rstrip() + in_multiline_comment = False + + if in_multiline_comment: + continue + + # If line with "}" isn't at end of file + if i + 1 < len(lines_list) and line.endswith("}"): + next_line = lines_list[i + 1].lstrip() + + # If next line is already empty, there's nothing to do + if len(next_line) > 0: + if next_line[0] != "}" and "else" not in next_line and "#endif" not in next_line: + lines_list.insert(i + 1, "") + i += 1 + + output = linesep.join(lines_list) + + if output != lines: + return (output, True, True) + else: + return (lines, False, True) diff --git a/wpiformat/wpiformat/newline.py b/wpiformat/wpiformat/eofnewline.py similarity index 93% rename from wpiformat/wpiformat/newline.py rename to wpiformat/wpiformat/eofnewline.py index 12510c64..b6cfa7bf 100644 --- a/wpiformat/wpiformat/newline.py +++ b/wpiformat/wpiformat/eofnewline.py @@ -3,7 +3,7 @@ from wpiformat.task import Task -class Newline(Task): +class EofNewline(Task): def run_pipeline(self, config_file, name, lines): output = lines.rstrip() + Task.get_linesep(lines)