mirror of
https://github.com/open-goal/jak-project
synced 2026-08-21 14:54:53 -04:00
formatter: support formatting bindings, for example in a let (#2883)
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "formatter_tree.h"
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/util/string_util.h"
|
||||
|
||||
@@ -15,9 +16,11 @@ extern "C" {
|
||||
extern const TSLanguage* tree_sitter_opengoal();
|
||||
}
|
||||
|
||||
std::string apply_formatting(const FormatterTreeNode& curr_node,
|
||||
std::string output,
|
||||
int tree_depth = 0) {
|
||||
std::string apply_formatting(
|
||||
const FormatterTreeNode& curr_node,
|
||||
std::string output,
|
||||
std::optional<formatter_rules::config::FormFormattingConfig> form_element_config) {
|
||||
using namespace formatter_rules;
|
||||
if (!curr_node.token && curr_node.refs.empty()) {
|
||||
return output;
|
||||
}
|
||||
@@ -34,33 +37,69 @@ std::string apply_formatting(const FormatterTreeNode& curr_node,
|
||||
|
||||
bool inline_form = false;
|
||||
// Also check if the form should be constant-paired
|
||||
const bool constant_pair_form =
|
||||
formatter_rules::constant_pairs::form_should_be_constant_paired(curr_node);
|
||||
const bool constant_pair_form = constant_pairs::form_should_be_constant_paired(curr_node);
|
||||
if (!constant_pair_form) {
|
||||
// Determine if the form should be inlined or hung/flowed
|
||||
// TODO - this isn't entirely accurate, needs current cursor positioning (which is tricky
|
||||
// because recursion!)
|
||||
inline_form = formatter_rules::indent::form_can_be_inlined(curr_form, curr_node);
|
||||
inline_form = indent::form_can_be_inlined(curr_form, curr_node);
|
||||
}
|
||||
const bool flowing = indent::should_form_flow(curr_node, inline_form);
|
||||
std::optional<formatter_rules::config::FormFormattingConfig> form_config;
|
||||
if (!curr_node.refs.empty() && curr_node.refs.at(0).token) {
|
||||
const auto& form_head = curr_node.refs.at(0).token;
|
||||
if (form_head && config::opengoal_form_config.find(form_head.value()) !=
|
||||
config::opengoal_form_config.end()) {
|
||||
form_config = config::opengoal_form_config.at(form_head.value());
|
||||
}
|
||||
}
|
||||
// TODO - might want to make some kind of per-form config struct, simplify the passing around of
|
||||
// info below
|
||||
for (int i = 0; i < curr_node.refs.size(); i++) {
|
||||
const auto& ref = curr_node.refs.at(i);
|
||||
// Append a newline if needed
|
||||
if (!inline_form) {
|
||||
formatter_rules::indent::append_newline(curr_form, ref, curr_node, tree_depth, i,
|
||||
constant_pair_form);
|
||||
// Figure out if the element should be inlined or not
|
||||
bool inline_element = inline_form;
|
||||
if (indent::inline_form_element(curr_node, i)) {
|
||||
inline_element = indent::inline_form_element(curr_node, i).value();
|
||||
}
|
||||
// Append a newline if needed
|
||||
// TODO - cleanup / move
|
||||
bool is_binding_list = false;
|
||||
bool force_newline = false;
|
||||
bool override_force_flow = false;
|
||||
if (form_config) {
|
||||
force_newline = std::find(form_config->force_newline_at_indices.begin(),
|
||||
form_config->force_newline_at_indices.end(),
|
||||
i) != form_config->force_newline_at_indices.end();
|
||||
// Check if it's a small enough binding list, if so we don't force a newline if the element
|
||||
// can be inlined
|
||||
if (inline_element && i > 0 && form_config->bindings_at_index == i - 1 &&
|
||||
curr_node.refs.at(i - 1).refs.size() < form_config->allow_inlining_if_size_less_than) {
|
||||
force_newline = false;
|
||||
override_force_flow = true;
|
||||
}
|
||||
is_binding_list = form_config->bindings_at_index == i;
|
||||
}
|
||||
|
||||
if (!curr_node.metadata.is_top_level &&
|
||||
(!inline_element || is_binding_list || force_newline ||
|
||||
(form_element_config && form_element_config->force_flow))) {
|
||||
indent::append_newline(curr_form, ref, curr_node, i, flowing, constant_pair_form,
|
||||
(form_element_config && form_element_config->force_flow));
|
||||
}
|
||||
// TODO - indent the line (or don't)
|
||||
// Either print the element's token, or recursively format it as well
|
||||
if (ref.token) {
|
||||
// TODO depth hard-coded to 1, i think this can be removed, since
|
||||
// forms are always done bottom-top recursively, they always act
|
||||
// independently as if it was the shallowest depth
|
||||
if (!inline_form) {
|
||||
formatter_rules::indent::flow_line(curr_form, ref, curr_node, 1, i);
|
||||
if (!inline_element || force_newline) {
|
||||
indent::indent_line(curr_form, ref, curr_node, 1, i, flowing);
|
||||
}
|
||||
if (ref.metadata.node_type == "comment" && ref.metadata.is_inline) {
|
||||
curr_form += " " + ref.token.value();
|
||||
} else if (ref.metadata.node_type == "block_comment") {
|
||||
curr_form += formatter_rules::comments::format_block_comment(ref.token.value());
|
||||
curr_form += comments::format_block_comment(ref.token.value());
|
||||
} else {
|
||||
curr_form += ref.token.value();
|
||||
}
|
||||
@@ -68,9 +107,20 @@ std::string apply_formatting(const FormatterTreeNode& curr_node,
|
||||
curr_form += " ";
|
||||
}
|
||||
} else {
|
||||
auto formatted_form = apply_formatting(ref, "", tree_depth + 1);
|
||||
if (!curr_node.metadata.is_top_level && !inline_form) {
|
||||
formatter_rules::indent::hang_lines(formatted_form, ref, curr_node, constant_pair_form);
|
||||
// See if the item at this position has specific formatting
|
||||
std::optional<formatter_rules::config::FormFormattingConfig> config = {};
|
||||
std::string formatted_form;
|
||||
if (form_config && form_config->index_configs.find(i) != form_config->index_configs.end()) {
|
||||
formatted_form = apply_formatting(ref, "", *form_config->index_configs.at(i));
|
||||
} else {
|
||||
formatted_form = apply_formatting(ref, "", {});
|
||||
}
|
||||
// TODO - align inner lines only
|
||||
if (!curr_node.metadata.is_top_level) {
|
||||
indent::align_lines(
|
||||
formatted_form, ref, curr_node, constant_pair_form, flowing,
|
||||
(!override_force_flow && form_config && i >= form_config->start_flow_at_index),
|
||||
inline_element);
|
||||
}
|
||||
curr_form += formatted_form;
|
||||
if (!curr_node.metadata.is_top_level) {
|
||||
@@ -78,7 +128,7 @@ std::string apply_formatting(const FormatterTreeNode& curr_node,
|
||||
}
|
||||
}
|
||||
// Handle blank lines at the top level, skip if it's the final element
|
||||
formatter_rules::blank_lines::separate_by_newline(curr_form, curr_node, ref, i);
|
||||
blank_lines::separate_by_newline(curr_form, curr_node, ref, i);
|
||||
}
|
||||
if (!curr_node.metadata.is_top_level) {
|
||||
curr_form = str_util::rtrim(curr_form) + ")";
|
||||
@@ -104,8 +154,13 @@ std::optional<std::string> formatter::format_code(const std::string& source) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto formatting_tree = FormatterTree(source, root_node);
|
||||
std::string formatted_code = apply_formatting(formatting_tree.root, "");
|
||||
try {
|
||||
const auto formatting_tree = FormatterTree(source, root_node);
|
||||
std::string formatted_code = apply_formatting(formatting_tree.root, "", {});
|
||||
return formatted_code;
|
||||
} catch (std::exception& e) {
|
||||
lg::error("Unable to format code - {}", e.what());
|
||||
}
|
||||
|
||||
return formatted_code;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user