diff --git a/c_formatter_42/data/.clang-format b/c_formatter_42/data/.clang-format index 2fdc681..4abcb0a 100644 --- a/c_formatter_42/data/.clang-format +++ b/c_formatter_42/data/.clang-format @@ -92,7 +92,7 @@ BreakBeforeTernaryOperators: true # ColumnLimit (unsigned) # The column limit. -ColumnLimit: 80 +ColumnLimit: 0 # FixNamespaceComments (bool) diff --git a/c_formatter_42/formatters/line_breaker.py b/c_formatter_42/formatters/line_breaker.py index dd4ea70..d12feef 100644 --- a/c_formatter_42/formatters/line_breaker.py +++ b/c_formatter_42/formatters/line_breaker.py @@ -1,9 +1,11 @@ import re +from c_formatter_42.formatters import helper def line_breaker(content: str, column_limit: int = 80) -> str: lines = content.split("\n") lines = list(map(lambda s: insert_break(s, column_limit), lines)) + return "\n".join(lines) @@ -11,20 +13,20 @@ def insert_break(line: str, column_limit: int) -> str: if line_length(line) <= column_limit: return line - line_indent_level = indent_level(line) - tabulation = "\t" * (line_indent_level + 1) - # break at all breakable spaces (space after comma or space before binary operators) breakable_space_pattern = r"((?<=,) | (?=[+\-*/%])(?!\*+\S|\+\+|\-\-))" line = re.sub(breakable_space_pattern, "\n", line) segments = line.split("\n") + line_indent_level = indent_level(line) + # join as many segments as it doesn't exceed line length limit line = segments[0] current_line_length = line_length(segments[0]) for segment in segments[1:]: current_line_length += line_length(segment) + 1 if current_line_length > column_limit: + tabulation = "\t" * (line_indent_level + additional_indent_level(line)) line = ("\n" + tabulation).join([line, segment]) current_line_length = line_length(tabulation + segment) else: @@ -33,15 +35,52 @@ def insert_break(line: str, column_limit: int) -> str: return line +# +# additional indent level increases in proportion to corresponds paren depth +# +# (examples) +# foo() * bar() * baz() +# ~~~~~~~~~~~~~^~~~~~~~ when line breaks here, +# foo() * bar() +# > * baz() next line should be indented with 1 tab +# === +# (foo(bar() * baz())) +# ~~~~~~~~~~^~~~~~~~~ when line breaks here, +# (foo(bar() +# > > > * baz())) next line should be indented with 3 tabs +# +# function declaration, user defined type (?), and control statement needs one less tab (discount) +# +def additional_indent_level(s: str) -> int: + additional_indent_level = 1 + + discount_pattern = r"(^\t*{type}\t+.*?[a-zA-Z0-9_]\()|(^\t*typedef)|(^\t*(if|while))" + discount_pattern = discount_pattern.format( + type=helper.REGEX_TYPE, + ) + if re.match(discount_pattern, s): + additional_indent_level = 0 + + is_surrounded_sq = False + is_surrounded_dq = False + + for c in s: + if c == "'": + is_surrounded_sq = not is_surrounded_sq + elif c == '"': + is_surrounded_dq = not is_surrounded_dq + elif c == "(" and not is_surrounded_sq and not is_surrounded_dq: + additional_indent_level += 1 + elif c == ")" and not is_surrounded_sq and not is_surrounded_dq: + additional_indent_level -= 1 + + return additional_indent_level + + def line_length(line: str) -> int: line = line.expandtabs(4) return len(line) def indent_level(line: str) -> int: - last_tab_occurance = line.rfind("\t") - if last_tab_occurance < 0: - return 0 - - line = line[: (last_tab_occurance + 1)] - return int(len(line.expandtabs(4)) / 4) + return line.count("\t") diff --git a/setup.cfg b/setup.cfg index 52f17e2..ab7455c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = c_formatter_42 -version = 0.1.2 +version = 0.1.3 description = formatting tool complient with 42 school's norm long_description = file: README.md long_description_content_type = text/markdown diff --git a/tests/formatters/test_clang_format.py b/tests/formatters/test_clang_format.py index 4b979f2..e87c4b8 100644 --- a/tests/formatters/test_clang_format.py +++ b/tests/formatters/test_clang_format.py @@ -33,26 +33,26 @@ def test_clang_format_empty(): assert clang_format("") == "" -def test_clang_format_dont_join_lines(): - input = """ -if (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -\t|| bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -\t|| cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc) -""" - output = """ -if (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa || -\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb || -\tcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc) -""" - assert clang_format(input) == output - - input = """ -if (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa || -\tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb || -\tcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc) -""" - assert clang_format(input) == input - +# def test_clang_format_dont_join_lines(): +# input = """ +# if (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +# \t|| bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +# \t|| cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc) +# """ +# output = """ +# if (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa || +# \tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb || +# \tcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc) +# """ +# assert clang_format(input) == output +# +# input = """ +# if (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa || +# \tbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb || +# \tcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc) +# """ +# assert clang_format(input) == input +# def test_clang_format_non_array_assignment_packing(): input = """ @@ -71,19 +71,6 @@ def test_clang_format_non_array_assignment_packing(): assert clang_format(input) == input -def test_clang_format_long_function_declaration(): - input = """ -static void st_merge_fields_in_curr( -\t\tchar *strs[3], t_tok_lst **curr, t_tok_lst *fields) -""" - output = """ -static void st_merge_fields_in_curr(char *strs[3], -\t\t\t\t\t\t\t\t\tt_tok_lst **curr, -\t\t\t\t\t\t\t\t\tt_tok_lst *fields) -""" - assert clang_format(input) == output - - @contextmanager def change_temp_dir_context(): tempdir = tempfile.mkdtemp("c_formatter_42") diff --git a/tests/formatters/test_hoist.py b/tests/formatters/test_hoist.py index e35b271..81377b9 100644 --- a/tests/formatters/test_hoist.py +++ b/tests/formatters/test_hoist.py @@ -130,7 +130,20 @@ def test_hoist_ex(): """) # TODO test on weird types + output = """\ +void foo() +{ +\tt_list\t*bar; +\tbar = (t_list *)malloc(sizeof(t_list) * (count_elements(baz) + 1)); +} +""" + assert output == hoist("""\ +void foo() +{ +\tt_list\t*bar = (t_list *)malloc(sizeof(t_list) * (count_elements(baz) + 1)); +} +""") @pytest.mark.skip() def test_hoist_array(): diff --git a/tests/formatters/test_line_breaker.py b/tests/formatters/test_line_breaker.py index 882e710..cc8c9fc 100644 --- a/tests/formatters/test_line_breaker.py +++ b/tests/formatters/test_line_breaker.py @@ -17,7 +17,7 @@ def test_line_indent_depth_basic_3(): input = """\ \t\t\t + 2 + 2 + 2\t """ - assert 7 == indent_level(input) + assert 4 == indent_level(input) def test_insert_line_break_basic_1(): @@ -40,8 +40,8 @@ def test_insert_line_break_basic_2(): def test_insert_line_break_basic_3(): output = """\ \t\t\t\treturn (fooooooooooooooooooooooooo(a, b, cccccccccccc, -\t\t\t\t\tddddddddddddd, eeeeeeeeeeeeeeee, fffffffffffffff, -\t\t\t\t\tgggggggggggg, hhhhhhhhhhhhhhhhhh)); +\t\t\t\t\t\t\tddddddddddddd, eeeeeeeeeeeeeeee, fffffffffffffff, +\t\t\t\t\t\t\tgggggggggggg, hhhhhhhhhhhhhhhhhh)); """ assert output == line_breaker("""\ \t\t\t\treturn (fooooooooooooooooooooooooo(a, b, cccccccccccc, ddddddddddddd, eeeeeeeeeeeeeeee, fffffffffffffff, gggggggggggg, hhhhhhhhhhhhhhhhhh)); @@ -50,7 +50,7 @@ def test_insert_line_break_basic_3(): def test_insert_line_break_basic_4(): output = """\ void\t\tlooooooooooooooooooooooong = 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 -\t\t\t\t+ 2 + 2 + 2; +\t\t\t+ 2 + 2 + 2; """ assert output == line_breaker("""\ void\t\tlooooooooooooooooooooooong = 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2; @@ -151,3 +151,37 @@ def test_insert_line_break_basic_22(): output = ",\n\taaaa* b" assert output == line_breaker(", aaaa* b", 7) +def test_insert_line_break_basic_23(): + output = "foooooo(bar\n\t\t* baz)" + assert output == line_breaker("foooooo(bar * baz)", 7) + + +def test_insert_line_break_long_function_declaration(): + input = """ +static void\tst_merge_fields_in_curr(char *strs[3], t_tok_lst **curr, t_tok_lst *fields) +""" + output = """ +static void\tst_merge_fields_in_curr(char *strs[3], t_tok_lst **curr, +\t\tt_tok_lst *fields) +""" + assert line_breaker(input) == output + +def test_insert_line_break_control_statement_1(): + input = """\ +\twhile (true + false) +""" + output = """\ +\twhile (true +\t\t+ false) +""" + assert line_breaker(input, 7) == output + +def test_insert_line_break_control_statement_2(): + input = """\ +\tif (true + false) +""" + output = """\ +\tif (true +\t\t+ false) +""" + assert line_breaker(input, 5) == output