Private
Public Access
Preserve annotation spacing and tight unary and range operators.
This commit is contained in:
1 parent
2de408ce4c
commit
68c8b0141d
4 files changed
+52
-6
No files matched your search
@@ -14,7 +14,7 @@ A line comment starts with `//` and ends at the next newline.
|
||||
|
||||
`capyc --format -` reads standard input and writes standard output. `capyc --format --check PATH...` writes each changed path and returns 1.
|
||||
|
||||
The formatter uses a 100-byte call-width threshold. It uses tabs for indentation. It puts function, `if`, `else`, `for`, and `while` braces on their own lines. It expands block statements. It keeps short calls on one line. It puts each argument of a longer call on its own indented line. It preserves comments, strings, markup, and directive lines byte-for-byte. It keeps at most one blank line between source items. Colons have no preceding space and one following space. It strips other trailing whitespace.
|
||||
The formatter uses a 100-byte call-width threshold. It uses tabs for indentation. It puts function, `if`, `else`, `for`, and `while` braces on their own lines. It expands block statements. It keeps short calls on one line. It puts each argument of a longer call on its own indented line. It preserves comments, strings, markup, and directive lines byte-for-byte. It keeps at most one blank line between source items. Annotation colons have one space on each side, as in `name : Type`. Map-entry colons have no space before them and one space after them, as in `key: value`. It strips other trailing whitespace.
|
||||
|
||||
An identifier starts with a letter or underscore. Later characters can also be decimal digits. Names that start with `__bearer_` are reserved for the standard-library boundary. Public standard-library names are also reserved.
|
||||
|
||||
|
||||
@@ -317,7 +317,7 @@ def format_test(workspace):
|
||||
result = subprocess.run([CAPYC, "--format", "--check", source], cwd=ROOT, capture_output=True, timeout=TIMEOUT)
|
||||
assert result.returncode == 0 and result.stdout == b"" and result.stderr == b"", result
|
||||
result = subprocess.run([CAPYC, "--format", "-"], cwd=ROOT, input=b'function CLI(request : dval) { return }\n', capture_output=True, timeout=TIMEOUT)
|
||||
assert result.returncode == 0 and result.stdout == b"function CLI(request: dval)\n{\n\treturn\n}\n" and result.stderr == b"", result
|
||||
assert result.returncode == 0 and result.stdout == b"function CLI(request : dval)\n{\n\treturn\n}\n" and result.stderr == b"", result
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -338,7 +338,7 @@ int main(int argc, char** argv)
|
||||
{
|
||||
const std::string source = "#import \"child.capy\" as child\nfunction CLI(request : dval) { var text := \"héllo\\nworld\" // keep\nif true { print(\"this string makes the call exceed the formatter line limit and proves that every map entry receives one extra tab\", page: 1, limit: 2) } else { return }\n}\n";
|
||||
const std::string formatted = format_source(source);
|
||||
assert(formatted == "#import \"child.capy\" as child\nfunction CLI(request: dval)\n{\n\tvar text := \"héllo\\nworld\" // keep\n\tif true\n\t{\n\t\tprint(\n\t\t\t\"this string makes the call exceed the formatter line limit and proves that every map entry receives one extra tab\",\n\t\t\tpage: 1,\n\t\t\tlimit: 2\n\t\t)\n\t}\n\telse\n\t{\n\t\treturn\n\t}\n}\n");
|
||||
assert(formatted == "#import \"child.capy\" as child\nfunction CLI(request : dval)\n{\n\tvar text := \"héllo\\nworld\" // keep\n\tif true\n\t{\n\t\tprint(\n\t\t\t\"this string makes the call exceed the formatter line limit and proves that every map entry receives one extra tab\",\n\t\t\tpage: 1,\n\t\t\tlimit: 2\n\t\t)\n\t}\n\telse\n\t{\n\t\treturn\n\t}\n}\n");
|
||||
assert(format_source(formatted) == formatted);
|
||||
assert(parse(source).items.size() == parse(formatted).items.size());
|
||||
auto input_tokens = Lexer(source).tokens(), output_tokens = Lexer(formatted).tokens();
|
||||
@@ -347,7 +347,7 @@ int main(int argc, char** argv)
|
||||
assert(input_tokens.size() == output_tokens.size());
|
||||
for (std::size_t index = 0; index < input_tokens.size(); ++index)
|
||||
assert(input_tokens[index].kind == output_tokens[index].kind && input_tokens[index].text == output_tokens[index].text);
|
||||
assert(format_source("function CLI(request : dval) { <> raw markup <?= string(\"x\") ?> </> }\n") == "function CLI(request: dval)\n{\n\t<> raw markup <?= string(\"x\") ?> </>\n}\n");
|
||||
assert(format_source("function CLI(request : dval) { <> raw markup <?= string(\"x\") ?> </> }\n") == "function CLI(request : dval)\n{\n\t<> raw markup <?= string(\"x\") ?> </>\n}\n");
|
||||
}
|
||||
{
|
||||
const std::string source = "#exports CLI \n\n\nfunction CLI(request : dval) { if !bool(false) { print(\":\", \")\", \",\", \"}\", \"function\") } }\n// last comment \n";
|
||||
@@ -360,6 +360,20 @@ int main(int argc, char** argv)
|
||||
const std::string host = "host function example(value : s32) s32 // host \nfunction next() {}\n";
|
||||
assert(parse(format_source(host)).items.size() == 2);
|
||||
}
|
||||
{
|
||||
const std::string source = "function f(n : s32) s32 { var a := -1; var b := n * -2; if !(a < b) { return -a }; for i := 0..n { a = a + i }; -> a - b }";
|
||||
const std::string expected = "function f(n : s32) s32\n{\n\tvar a := -1;\n\tvar b := n * -2;\n\tif !(a < b)\n\t{\n\t\treturn -a\n\t}\n\t;\n\tfor i := 0..n\n\t{\n\t\ta = a + i\n\t}\n\t;\n\t-> a - b\n}\n";
|
||||
assert(format_source(source) == expected);
|
||||
assert(format_source(expected) == expected);
|
||||
const std::string annotations = "struct Pair { x : s32\ny : s32 }\nfunction f(n : as s32) { var x : dval = {key: -1}\nprint(key: x) }\n";
|
||||
const std::string formatted = format_source(annotations);
|
||||
assert(formatted.find("x : s32") != std::string::npos);
|
||||
assert(formatted.find("y : s32") != std::string::npos);
|
||||
assert(formatted.find("n : as s32") != std::string::npos);
|
||||
assert(formatted.find("var x : dval = {key: -1}") != std::string::npos);
|
||||
assert(formatted.find("print(key: x)") != std::string::npos);
|
||||
assert(format_source(formatted) == formatted);
|
||||
}
|
||||
for (int index = 1; index < argc; ++index)
|
||||
{
|
||||
std::ifstream input(argv[index], std::ios::binary);
|
||||
|
||||
+34
-2
@@ -1210,10 +1210,42 @@ std::string format_source(std::string_view source)
|
||||
}
|
||||
if (is_symbol(token, ",")) { write(","); if (!multiline_calls.empty() && multiline_calls.back()) newline(); else space(); continue; }
|
||||
if (is_symbol(token, ";")) { write(";"); newline(); continue; }
|
||||
if (is_symbol(token, ":")) { write(":"); space(); continue; }
|
||||
if (is_symbol(token, ":"))
|
||||
{
|
||||
bool annotation = false;
|
||||
for (std::size_t at = index; at;)
|
||||
{
|
||||
const Token& item = tokens[--at];
|
||||
if (is_symbol(item, ";") || is_symbol(item, "{") || is_symbol(item, "}"))
|
||||
{
|
||||
const Token* name = previous(at);
|
||||
annotation = name && name->kind == TokenKind::identifier && name != tokens.data() && is_identifier(*previous(static_cast<std::size_t>(name - tokens.data())), "struct");
|
||||
break;
|
||||
}
|
||||
if (is_symbol(item, "("))
|
||||
{
|
||||
const Token* name = previous(at);
|
||||
annotation = name && is_identifier(*name, "function");
|
||||
if (!annotation && name && name->kind == TokenKind::identifier && name != tokens.data())
|
||||
{
|
||||
const Token* keyword = previous(static_cast<std::size_t>(name - tokens.data()));
|
||||
annotation = keyword && is_identifier(*keyword, "function");
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (is_identifier(item, "var")) { annotation = true; break; }
|
||||
}
|
||||
if (annotation) space();
|
||||
write(":");
|
||||
space();
|
||||
continue;
|
||||
}
|
||||
if (is_symbol(token, ".") || is_symbol(token, "::") || is_symbol(token, "?") || is_symbol(token, "]")) { write(text); continue; }
|
||||
if (is_symbol(token, "[")) { if (before && (before->kind == TokenKind::identifier || is_symbol(*before, ")") || is_symbol(*before, "]"))) write("["); else { if (before && is_symbol(*before, "=")) space(); write("["); } continue; }
|
||||
if (is_symbol(token, "!") || (is_symbol(token, "-") && (!before || is_symbol(*before, "(") || is_symbol(*before, "[") || is_symbol(*before, "=") || is_symbol(*before, ",")))) { if (is_symbol(token, "!") && before && before->kind == TokenKind::identifier) space(); write(text); continue; }
|
||||
const bool value_end = before && ((before->kind == TokenKind::identifier && before->text != "return" && before->text != "if" && before->text != "while" && before->text != "else" && before->text != "as") || before->kind == TokenKind::integer || before->kind == TokenKind::floating || before->kind == TokenKind::string || before->kind == TokenKind::markup || is_symbol(*before, ")") || is_symbol(*before, "]") || is_symbol(*before, "}"));
|
||||
const bool unary_minus = is_symbol(token, "-") && !value_end;
|
||||
if (is_symbol(token, "..")) { write(text); continue; }
|
||||
if (is_symbol(token, "!") || unary_minus) { if (before && before->kind == TokenKind::identifier) space(); write(text); continue; }
|
||||
if (token.kind == TokenKind::symbol) { space(); write(text); space(); continue; }
|
||||
if (before && (before->kind == TokenKind::identifier || before->kind == TokenKind::integer || before->kind == TokenKind::floating || before->kind == TokenKind::string || before->kind == TokenKind::markup || is_symbol(*before, ")") || is_symbol(*before, "]") || is_symbol(*before, "}"))) space();
|
||||
write(raw(token));
|
||||
|
||||
Reference in new issue
Block a user