Current section
37 Versions
Jump to
Current section
37 Versions
Compare versions
5
files changed
+129
additions
-32
deletions
| @@ -15,4 +15,4 @@ | |
| 15 15 | [{<<"app">>,<<"providers">>}, |
| 16 16 | {<<"optional">>,false}, |
| 17 17 | {<<"requirement">>,<<"1.8.1">>}]}]}. |
| 18 | - {<<"version">>,<<"0.9.0">>}. |
| 18 | + {<<"version">>,<<"0.9.1">>}. |
| @@ -1,6 +1,6 @@ | |
| 1 1 | {application,steamroller, |
| 2 2 | [{description,"An opinionated Erlang code formatter."}, |
| 3 | - {vsn,"0.9.0"}, |
| 3 | + {vsn,"0.9.1"}, |
| 4 4 | {registered,[]}, |
| 5 5 | {applications,[kernel,stdlib]}, |
| 6 6 | {env,[]}, |
| @@ -38,7 +38,7 @@ | |
| 38 38 | | function |
| 39 39 | | module_comment |
| 40 40 | | function_comment |
| 41 | - | dot. |
| 41 | + | expr. |
| 42 42 | |
| 43 43 | -define(sp, <<" ">>). |
| 44 44 | -define(nl, <<"\n">>). |
| @@ -208,13 +208,18 @@ generate_doc_([{'-', _}, {atom, _, Atom} | Tokens], Doc0, PrevTerm) -> | |
| 208 208 | case {Atom, PrevTerm} of |
| 209 209 | {_, function_comment} -> newline(Doc0, Group); |
| 210 210 | {Atom, {attribute, Atom}} -> newline(Doc0, Group); |
| 211 | - {define, {attribute, IfDef}} when IfDef == ifdef orelse IfDef == else -> |
| 211 | + {define, {attribute, IfDef}} |
| 212 | + when IfDef == ifdef orelse IfDef == else orelse IfDef == 'if' -> |
| 212 213 | newline(Doc0, Group); |
| 213 214 | {IfDef, {attribute, define}} when IfDef == else orelse IfDef == endif -> |
| 214 215 | newline(Doc0, Group); |
| 215 216 | _ -> newlines(Doc0, Group) |
| 216 217 | end, |
| 217 218 | generate_doc_(Rest, Doc1, {attribute, Atom}); |
| 219 | + generate_doc_([{'-', _}, {'if' = Atom, _} | Tokens], Doc0, _) -> |
| 220 | + % :'( |
| 221 | + {Group, Rest} = attribute(Atom, Tokens), |
| 222 | + generate_doc_(Rest, newlines(Doc0, Group), {attribute, Atom}); |
| 218 223 | generate_doc_([{atom, _, _Atom} | _] = Tokens, Doc0, PrevTerm) -> |
| 219 224 | % Function |
| 220 225 | {Group, Rest} = function(Tokens), |
| @@ -228,14 +233,14 @@ generate_doc_([{atom, _, _Atom} | _] = Tokens, Doc0, PrevTerm) -> | |
| 228 233 | generate_doc_([{C, _} | _] = Tokens, Doc0, PrevTerm) when ?IS_LIST_OPEN_CHAR(C) -> |
| 229 234 | % List |
| 230 235 | % If this is at the top level this is probably a config file |
| 231 | - {ForceBreak, Group0, Rest} = list_group(Tokens), |
| 236 | + {ForceBreak, Group0, [{dot, _} | Rest]} = list_group(Tokens), |
| 232 237 | Group1 = force_break(ForceBreak, Group0), |
| 233 238 | Doc1 = |
| 234 239 | case PrevTerm of |
| 235 240 | function_comment -> newline(Doc0, Group1); |
| 236 241 | _ -> newlines(Doc0, Group1) |
| 237 242 | end, |
| 238 | - generate_doc_(Rest, Doc1, list); |
| 243 | + generate_doc_(Rest, cons(Doc1, text(?dot)), list); |
| 239 244 | generate_doc_([{comment, _, "%%" ++ _ = CommentText} | Rest], Doc0, PrevTerm) -> |
| 240 245 | % Module Comment |
| 241 246 | Comment = comment(CommentText), |
| @@ -257,9 +262,13 @@ generate_doc_([{comment, _, CommentText} | Rest], Doc0, PrevTerm) -> | |
| 257 262 | _ -> newlines(Doc0, Comment) |
| 258 263 | end, |
| 259 264 | generate_doc_(Rest, Doc1, function_comment); |
| 260 | - generate_doc_([{dot, _} | Rest], Doc, _PrevTerm) -> |
| 261 | - % Any undhandled dots, for example at the end of terms in config files. |
| 262 | - generate_doc_(Rest, cons(Doc, text(?dot)), dot). |
| 265 | + generate_doc_(Tokens, Doc, _PrevTerm) -> |
| 266 | + % Anything unhandled gets treated as an expression. |
| 267 | + % Things which fall through to here: |
| 268 | + % - Macros: can appear at the top level |
| 269 | + {_End, ForceBreak, Exprs, Rest} = exprs(Tokens), |
| 270 | + Group = force_break(ForceBreak, group(space(Exprs), inherit)), |
| 271 | + generate_doc_(Rest, newline(Doc, Group), expr). |
| 263 272 | |
| 264 273 | %% |
| 265 274 | %% Erlang Source Elements |
| @@ -272,6 +281,14 @@ attribute(Att, [{dot, _} | Rest]) -> | |
| 272 281 | % -endif. |
| 273 282 | Attribute = group(cons([text(<<"-">>), text(a2b(Att)), text(?dot)])), |
| 274 283 | {Attribute, Rest}; |
| 284 | + attribute('if', Tokens) -> |
| 285 | + % Easiest to handle this here... |
| 286 | + % a2b/1 will print 'if' with the single quotes. |
| 287 | + % op2b/1 will print `dot` as `.`. |
| 288 | + % so pattern match the 'if' and convert it to <<"if">>. |
| 289 | + {_ForceBreak, Expr, [{dot, _} | Rest]} = list_group(Tokens), |
| 290 | + Attribute = group(cons([text(<<"-">>), text(<<"if">>), Expr, text(?dot)])), |
| 291 | + {Attribute, Rest}; |
| 275 292 | attribute(Att, Tokens) -> |
| 276 293 | {_ForceBreak, Expr, [{dot, _} | Rest]} = list_group(Tokens), |
| 277 294 | Attribute = group(cons([text(<<"-">>), text(a2b(Att)), Expr, text(?dot)])), |
| @@ -416,8 +433,8 @@ try_([{'try', _} | Tokens]) -> | |
| 416 433 | % There is no 'of' for this 'try' |
| 417 434 | {exprs, text(<<"try">>), TryTokens}; |
| 418 435 | {TryArgTokens, Rest0, _} -> |
| 419 | - {empty, _, TryArg, []} = expr(TryArgTokens, no_force_break), |
| 420 | - {clauses, group(space(text(<<"try">>), TryArg)), Rest0} |
| 436 | + {empty, _, TryArgs, []} = exprs(TryArgTokens), |
| 437 | + {clauses, group(space(text(<<"try">>), space(TryArgs))), Rest0} |
| 421 438 | end, |
| 422 439 | {TryTokens1, AfterClauseTokens} = |
| 423 440 | case get_until('after', Rest1) of |
| @@ -531,17 +548,34 @@ try_after_([{'after', _} | Tokens]) -> | |
| 531 548 | |
| 532 549 | -spec fun_(tokens()) -> {force_break(), doc(), tokens()}. |
| 533 550 | fun_([{'fun', _} | Tokens]) -> |
| 534 | - {ClauseTokens, Rest1, _} = get_until_end(Tokens), |
| 535 | - {ForceBreak, Clauses} = handle_trailing_comments(clauses(ClauseTokens)), |
| 536 | - Doc = |
| 537 | - force_break( |
| 538 | - ForceBreak, |
| 539 | - group( |
| 540 | - space(nest(?indent, space([text(<<"fun">>) | Clauses])), text(<<"end">>)), |
| 541 | - inherit |
| 542 | - ) |
| 543 | - ), |
| 544 | - {ForceBreak, Doc, Rest1}. |
| 551 | + {ForceBreak, Doc, Rest} = |
| 552 | + case get_until_end(Tokens) of |
| 553 | + {[{'(', _}, {')', _}], [], {dot, _}} -> |
| 554 | + % This case can happen in typedefs if the type looks like |
| 555 | + % -type x() :: fun(). |
| 556 | + {no_force_break, text(<<"fun().">>), []}; |
| 557 | + {[{'(', _}, {')', _}, {'|', _} | _] = Tokens0, Rest0, End} -> |
| 558 | + % This case can happen in typedefs if the type looks like |
| 559 | + % -type x() :: fun() | map(). |
| 560 | + [_, _ | Tokens1] = Tokens0, |
| 561 | + Rest1 = Tokens1 ++ [End] ++ Rest0, |
| 562 | + {no_force_break, text(<<"fun()">>), Rest1}; |
| 563 | + {ClauseTokens, Rest0, _} -> |
| 564 | + {ForceBreak0, Clauses} = handle_trailing_comments(clauses(ClauseTokens)), |
| 565 | + Doc0 = |
| 566 | + force_break( |
| 567 | + ForceBreak0, |
| 568 | + group( |
| 569 | + space( |
| 570 | + nest(?indent, space([text(<<"fun">>) | Clauses])), |
| 571 | + text(<<"end">>) |
| 572 | + ), |
| 573 | + inherit |
| 574 | + ) |
| 575 | + ), |
| 576 | + {ForceBreak0, Doc0, Rest0} |
| 577 | + end, |
| 578 | + {ForceBreak, Doc, Rest}. |
| 545 579 | |
| 546 580 | -spec begin_(tokens()) -> {force_break(), doc(), tokens()}. |
| 547 581 | begin_([{'begin', _} | Tokens]) -> |
| @@ -705,8 +739,9 @@ head_and_clause(Rest0, Doc0) -> | |
| 705 739 | % The first case happens most of the time. The second case happens when we have |
| 706 740 | % things like: |
| 707 741 | % if X =:= test; X =:= other -> |
| 708 | - {_End, _ForceBreak, Expr, Leftovers} = expr(Tokens, no_force_break), |
| 709 | - head_and_clause(Leftovers ++ [Token | Rest1], space(Doc0, Expr)). |
| 742 | + {_End, ForceBreak, Exprs, Leftovers} = exprs(Tokens), |
| 743 | + Group = force_break(ForceBreak, group(space(Exprs), inherit)), |
| 744 | + head_and_clause(Leftovers ++ [Token | Rest1], space(Doc0, Group)). |
| 710 745 | |
| 711 746 | -spec clause(tokens()) -> {continue(), force_break(), doc(), tokens()}. |
| 712 747 | clause(Tokens) -> |
| @@ -903,10 +938,18 @@ expr_( | |
| 903 938 | ] |
| 904 939 | ), |
| 905 940 | expr_(Rest, space(Doc, Fun), ForceBreak); |
| 941 | + expr_( |
| 942 | + [{'fun', _}, {var, LineNum, Var}, {'/', LineNum}, {integer, LineNum, Arity} | Rest], |
| 943 | + Doc, |
| 944 | + ForceBreak |
| 945 | + ) -> |
| 946 | + % Handle `fun Var/arity` |
| 947 | + Fun = cons([text(<<"fun ">>), text(v2b(Var)), text(<<"/">>), text(i2b(Arity))]), |
| 948 | + expr_(Rest, space(Doc, Fun), ForceBreak); |
| 906 949 | expr_( |
| 907 950 | [ |
| 908 951 | {'fun', _}, |
| 909 | - {var, LineNum, MacroName}, |
| 952 | + {var, LineNum, Var}, |
| 910 953 | {':', LineNum}, |
| 911 954 | {atom, LineNum, FunctionName}, |
| 912 955 | {'/', LineNum}, |
| @@ -920,7 +963,7 @@ expr_( | |
| 920 963 | cons( |
| 921 964 | [ |
| 922 965 | text(<<"fun ">>), |
| 923 | - text(v2b(MacroName)), |
| 966 | + text(v2b(Var)), |
| 924 967 | text(<<":">>), |
| 925 968 | text(a2b(FunctionName)), |
| 926 969 | text(<<"/">>), |
| @@ -1213,6 +1256,14 @@ get_until(End, [{C, _} = Token | Rest], Acc, [C | Stack]) when ?IS_LIST_CLOSE_CH | |
| 1213 1256 | get_until(End, [{C, _} = Token | Rest], Acc, Stack) when ?IS_LIST_OPEN_CHAR(C) -> |
| 1214 1257 | % If we hit an open bracket we ignore until the close bracket |
| 1215 1258 | get_until(End, Rest, [Token | Acc], [close_bracket(C) | Stack]); |
| 1259 | + get_until(End, [{'fun', _} = Token, {'(', _}, {')', _}, {Op, _} | _] = Rest0, Acc, Stack) |
| 1260 | + when Op == '|' orelse Op == dot -> |
| 1261 | + % 'fun' without 'end' |
| 1262 | + % -type x() :: fun(). |
| 1263 | + % or |
| 1264 | + % -type x() :: fun() | y(). |
| 1265 | + Rest1 = tl(Rest0), |
| 1266 | + get_until(End, Rest1, [Token | Acc], Stack); |
| 1216 1267 | get_until(End, [{'fun', _} = Token, {'(', _}, {'(', _} | _] = Rest0, Acc, Stack) -> |
| 1217 1268 | % 'fun' without 'end' |
| 1218 1269 | % fun((Arg :: type) -> other_type()) |
| @@ -1223,6 +1274,11 @@ get_until(End, [{'fun', _} = Token, {atom, _, _} | _] = Rest0, Acc, Stack) -> | |
| 1223 1274 | % fun local/1 |
| 1224 1275 | Rest1 = tl(Rest0), |
| 1225 1276 | get_until(End, Rest1, [Token | Acc], Stack); |
| 1277 | + get_until(End, [{'fun', _} = Token, {var, _, _}, {'/', _} | _] = Rest0, Acc, Stack) -> |
| 1278 | + % 'fun' without 'end' |
| 1279 | + % fun X/1 |
| 1280 | + Rest1 = tl(Rest0), |
| 1281 | + get_until(End, Rest1, [Token | Acc], Stack); |
| 1226 1282 | get_until(End, [{'fun', _} = Token, {'?', _}, {var, _, _}, {':', _} | _] = Rest0, Acc, Stack) -> |
| 1227 1283 | % 'fun' without 'end' |
| 1228 1284 | % fun ?MACRO:x/1 |
| @@ -1370,11 +1426,36 @@ get_end_of_expr( | |
| 1370 1426 | % fun((Arg :: type) -> other_type()) |
| 1371 1427 | Rest1 = tl(Rest0), |
| 1372 1428 | get_end_of_expr(Rest1, [Token | Acc], LineNum, KeywordStack, Guard); |
| 1429 | + get_end_of_expr( |
| 1430 | + [{'fun', LineNum} = Token, {'(', _}, {')', _}, {Op, _} | _] = Rest0, |
| 1431 | + Acc, |
| 1432 | + _, |
| 1433 | + KeywordStack, |
| 1434 | + Guard |
| 1435 | + ) |
| 1436 | + when Op == dot orelse Op == '|' -> |
| 1437 | + % 'fun' without 'end' |
| 1438 | + % -type x() :: fun(). |
| 1439 | + % or |
| 1440 | + % -type x() :: fun() | y(). |
| 1441 | + Rest1 = tl(Rest0), |
| 1442 | + get_end_of_expr(Rest1, [Token | Acc], LineNum, KeywordStack, Guard); |
| 1373 1443 | get_end_of_expr([{'fun', LineNum} = Token, {atom, _, _} | _] = Rest0, Acc, _, KeywordStack, Guard) -> |
| 1374 1444 | % 'fun' without 'end' |
| 1375 1445 | % fun local/1 |
| 1376 1446 | Rest1 = tl(Rest0), |
| 1377 1447 | get_end_of_expr(Rest1, [Token | Acc], LineNum, KeywordStack, Guard); |
| 1448 | + get_end_of_expr( |
| 1449 | + [{'fun', LineNum} = Token, {var, _, _}, {'/', _} | _] = Rest0, |
| 1450 | + Acc, |
| 1451 | + _, |
| 1452 | + KeywordStack, |
| 1453 | + Guard |
| 1454 | + ) -> |
| 1455 | + % 'fun' without 'end' |
| 1456 | + % fun X/1 |
| 1457 | + Rest1 = tl(Rest0), |
| 1458 | + get_end_of_expr(Rest1, [Token | Acc], LineNum, KeywordStack, Guard); |
| 1378 1459 | get_end_of_expr( |
| 1379 1460 | [{'fun', LineNum} = Token, {'?', _}, {var, _, _}, {':', _} | _] = Rest0, |
| 1380 1461 | Acc, |
| @@ -87,11 +87,5 @@ eq_({_Left, _Right}, _) -> | |
| 87 87 | false. |
| 88 88 | |
| 89 89 | check_for_errors([], _) -> ok; |
| 90 | - check_for_errors([{error, {LineNum, erl_parse, ["syntax error before: ", Str]}} | _], File) -> |
| 91 | - Err = |
| 92 | - list_to_binary( |
| 93 | - io_lib:format("~s: Error on line ~p: syntax error before ~s", [File, LineNum, Str]) |
| 94 | - ), |
| 95 | - {error, Err}; |
| 96 90 | check_for_errors([{error, Msg} | _], File) -> {error, {File, Msg}}; |
| 97 91 | check_for_errors([_ | Rest], File) -> check_for_errors(Rest, File). |
| @@ -121,6 +121,28 @@ format_files([File | Rest], Opts) -> | |
| 121 121 | [File, IncludeFile, Line] |
| 122 122 | ), |
| 123 123 | format_files(Rest, Opts); |
| 124 | + {error, {File, {Line, epp, Err}}} -> |
| 125 | + % These errors typically mean that the sorce does not compile. |
| 126 | + % Not really our problem so we warn instead of erroring. |
| 127 | + rebar_api:warn( |
| 128 | + "Steamroller Warn: File: ~s: epp error ~p on line ~p. Skipping...", |
| 129 | + [File, Err, Line] |
| 130 | + ), |
| 131 | + format_files(Rest, Opts); |
| 132 | + {error, {File, {Line, erl_parse, ["syntax error before: ", Str]}}} -> |
| 133 | + rebar_api:warn( |
| 134 | + "Steamroller Warn: File: ~s: Syntax error before ~s on line ~p. Skipping...", |
| 135 | + [File, Str, Line] |
| 136 | + ), |
| 137 | + format_files(Rest, Opts); |
| 138 | + {error, {File, {Line, erl_parse, Err}}} -> |
| 139 | + % These errors typically mean that the sorce does not compile. |
| 140 | + % Not really our problem so we warn instead of erroring. |
| 141 | + rebar_api:warn( |
| 142 | + "Steamroller Warn: File: ~s: erl_parse error ~p on line ~p. Skipping...", |
| 143 | + [File, Err, Line] |
| 144 | + ), |
| 145 | + format_files(Rest, Opts); |
| 124 146 | {error, _} = Err -> Err |
| 125 147 | end; |
| 126 148 | format_files([], _) -> ok. |