Skip to content

Commit 27b9585

Browse files
committed
fix(formatter): formatting long function calls on multiple lines, fixing formatting of begin nodes in condition
1 parent 2c4cba7 commit 27b9585

File tree

67 files changed

+298
-64
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+298
-64
lines changed

.github/workflows/validate-links.yml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,18 @@ jobs:
5151
path: .lycheecache
5252
key: ${{ steps.restore-cache.outputs.cache-primary-key }}
5353

54+
- uses: nickderobertis/check-if-issue-exists-action@8edfc8560578e80eb6061565e5359d85d5d63b74
55+
name: Check if Issue Exists
56+
id: check_if_issue_exists
57+
with:
58+
repo: ArkScript-lang/Ark
59+
token: ${{ secrets.GITHUB_TOKEN }}
60+
title: Link Checker Report
61+
labels: report, automated issue
62+
63+
# todo: update existing issue if it already exists
5464
- name: Create Issue From File
55-
if: steps.lychee.outputs.exit_code != 0
65+
if: steps.lychee.outputs.exit_code != 0 && steps.check_if_issue_exists.outputs.exists == 'false'
5666
uses: peter-evans/create-issue-from-file@v5
5767
with:
5868
title: Link Checker Report

CHANGELOG.md

Lines changed: 8 additions & 0 deletions

include/CLI/Formatter.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77

88
constexpr struct FormatterConfig
99
{
10-
static constexpr std::size_t SpacePerIndent = 2; ///< Indentation level of each node
11-
static constexpr std::size_t LongLineLength = 32; ///< Max number of characters per line segment to consider splitting
10+
static constexpr std::size_t SpacePerIndent = 2; ///< Indentation level of each node
11+
static constexpr std::size_t LongLineLength = 120; ///< Max number of characters per line segment to consider splitting
1212
} FormatterConfig;
1313

1414
class Formatter final
@@ -88,6 +88,8 @@ class Formatter final
8888
*/
8989
static std::size_t lineOfLastNodeIn(const Ark::internal::Node& node);
9090

91+
[[nodiscard]] bool isLongLine(const Ark::internal::Node& node);
92+
9193
/**
9294
* @brief Decide if a node should be split on a newline or not
9395
* @param node

src/arkscript/Formatter.cpp

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
#include <Ark/Error/Exceptions.hpp>
99
#include <Ark/Error/Diagnostics.hpp>
1010
#include <Ark/Compiler/Common.hpp>
11+
#include <Ark/Utils/Literals.hpp>
1112

1213
using namespace Ark;
1314
using namespace Ark::internal;
15+
using namespace Ark::literals;
1416

1517
Formatter::Formatter(const bool dry_run) :
1618
m_dry_run(dry_run), m_parser(/* debug= */ 0, ParserMode::Raw), m_updated(false)
@@ -134,15 +136,28 @@ std::size_t Formatter::lineOfLastNodeIn(const Node& node)
134136
return node.position().start.line;
135137
}
136138

137-
bool Formatter::shouldSplitOnNewline(const Node& node)
139+
bool Formatter::isLongLine(const Node& node)
138140
{
139141
const std::string formatted = format(node, 0, false);
140-
const std::string::size_type sz = formatted.find_first_of('\n');
142+
const std::size_t max_len =
143+
std::ranges::max(
144+
Utils::splitString(formatted, '\n'),
145+
[](const std::string& lhs, const std::string& rhs) {
146+
return lhs.size() < rhs.size();
147+
})
148+
.size();
149+
const std::size_t newlines = std::ranges::count(formatted, '\n');
150+
151+
// split on multiple lines if we have a very long node,
152+
// or if we added many line breaks while doing dumb formatting
153+
return max_len >= FormatterConfig::LongLineLength || (newlines > 0 && node.isListLike() && newlines + 1 >= node.constList().size());
154+
}
141155

142-
const bool is_long_line = !((sz < FormatterConfig::LongLineLength || (sz == std::string::npos && formatted.size() < FormatterConfig::LongLineLength)));
156+
bool Formatter::shouldSplitOnNewline(const Node& node)
157+
{
143158
if (node.comment().empty() && (isBeginBlock(node) || isFuncCall(node)))
144159
return false;
145-
if (is_long_line || (node.isListLike() && node.constList().size() > 1) || !node.comment().empty())
160+
if (isLongLine(node) || (node.isListLike() && node.constList().size() > 1) || !node.comment().empty())
146161
return true;
147162
return false;
148163
}
@@ -302,7 +317,7 @@ std::string Formatter::formatFunction(const Node& node, const std::size_t indent
302317
{
303318
bool comment_in_args = false;
304319
std::string args;
305-
const bool split = shouldSplitOnNewline(args_node);
320+
const bool split = (isLongLine(args_node) || !args_node.comment().empty());
306321

307322
for (std::size_t i = 0, end = args_node.constList().size(); i < end; ++i)
308323
{
@@ -354,7 +369,7 @@ std::string Formatter::formatCondition(const Node& node, const std::size_t inden
354369
cond_on_newline ? "\n" : " ",
355370
formatted_cond);
356371

357-
const bool split_then_newline = shouldSplitOnNewline(then_node);
372+
const bool split_then_newline = shouldSplitOnNewline(then_node) || isBeginBlock(then_node);
358373

359374
// (if cond then)
360375
if (node.constList().size() == 3)
@@ -509,6 +524,19 @@ std::string Formatter::formatCall(const Node& node, const std::size_t indent)
509524
}
510525

511526
std::string result = is_list ? "[" : ("(" + format(node.constList()[0], indent, false));
527+
528+
// Split args on multiple lines even if, individually, they fit in the configured line length, if grouped together
529+
// on a single line they are too long
530+
const std::size_t args_line_length = std::accumulate(
531+
formatted_args.begin(),
532+
formatted_args.end(),
533+
result.size() + 1, // +1 to count the closing paren/bracket
534+
[](const std::size_t acc, const std::string& val) {
535+
return acc + val.size() + 1_z;
536+
});
537+
if (args_line_length >= FormatterConfig::LongLineLength)
538+
is_multiline = true;
539+
512540
for (std::size_t i = 0, end = formatted_args.size(); i < end; ++i)
513541
{
514542
const std::string& formatted_node = formatted_args[i];

tests/unittests/Suites/FormatterSuite.cpp

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,34 @@ using namespace boost;
99
ut::suite<"Formatter"> formatter_suite = [] {
1010
using namespace ut;
1111

12-
iterTestFiles(
13-
"FormatterSuite",
14-
[](TestData&& data) {
15-
std::string formatted_code;
16-
17-
should("output a correctly formatted code for " + data.stem) = [&] {
18-
Formatter formatter(data.path, /* dry_run= */ true);
19-
expect(nothrow([&] {
20-
mut(formatter).run();
21-
}));
22-
23-
formatted_code = formatter.output();
24-
// data.expected is ltrim(rtrim(file content))
25-
// we want to ensure that a blank line has been added
26-
expectOrDiff((data.expected + "\n"), formatted_code);
27-
};
28-
29-
should("not update an already correctly formatted code (" + data.stem + ")") = [&] {
30-
Formatter formatter(/* dry_run= */ true);
31-
expect(nothrow([&] {
32-
mut(formatter).runWithString(formatted_code);
33-
}));
34-
35-
const std::string code = formatter.output();
36-
expectOrDiff(formatted_code, code);
37-
};
38-
});
12+
for (const std::string& subfolder : { "basics", "codeSamples" })
13+
{
14+
iterTestFiles(
15+
"FormatterSuite/" + subfolder,
16+
[](TestData&& data) {
17+
std::string formatted_code;
18+
19+
should("output a correctly formatted code for " + data.stem) = [&] {
20+
Formatter formatter(data.path, /* dry_run= */ true);
21+
expect(nothrow([&] {
22+
mut(formatter).run();
23+
}));
24+
25+
formatted_code = formatter.output();
26+
// data.expected is ltrim(rtrim(file content))
27+
// we want to ensure that a blank line has been added
28+
expectOrDiff((data.expected + "\n"), formatted_code);
29+
};
30+
31+
should("not update an already correctly formatted code (" + data.stem + ")") = [&] {
32+
Formatter formatter(/* dry_run= */ true);
33+
expect(nothrow([&] {
34+
mut(formatter).runWithString(formatted_code);
35+
}));
36+
37+
const std::string code = formatter.output();
38+
expectOrDiff(formatted_code, code);
39+
};
40+
});
41+
}
3942
};

tests/unittests/TestsHelper.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ std::string sanitizeRuntimeError(const std::exception& e)
106106
void expectOrDiff(const std::string& expected, const std::string& received)
107107
{
108108
const bool comparison = expected == received;
109-
boost::ut::expect(comparison) << [&] {
109+
const auto diff = [&] {
110110
dtl::Diff<std::string, std::vector<std::string>> d(
111111
Ark::Utils::splitString(received, '\n'),
112112
Ark::Utils::splitString(expected, '\n'));
@@ -118,4 +118,6 @@ void expectOrDiff(const std::string& expected, const std::string& received)
118118

119119
return stream.str();
120120
};
121+
122+
boost::ut::expect(comparison) << diff;
121123
}

tests/unittests/resources/FormatterSuite/block.ark renamed to tests/unittests/resources/FormatterSuite/basics/block.ark

File renamed without changes.

tests/unittests/resources/FormatterSuite/block.expected renamed to tests/unittests/resources/FormatterSuite/basics/block.expected

File renamed without changes.

tests/unittests/resources/FormatterSuite/calls.ark renamed to tests/unittests/resources/FormatterSuite/basics/calls.ark

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77
(list:forEach _listeners (fun (element)
88
(if (= typ (@ element 0)) {
99
((@ element 1) val)
10-
(set found true)})))
10+
(set found true)})))

0 commit comments

Comments
 (0)