forked from FuelLabs/sway
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support for configurable blocks in the formatter (FuelLabs#4105)
## Description Closes FuelLabs#3679 This is based on how `storage` blocks are handled.. Most of the code is copied from `item_storage`. We should probably consider making as much as that code common between the two because `storage` and `configurable` blocks look pretty much the same. Making this change now so that the formatter does not panic when we add an example using `configurable` under `sway/examples`. ## Checklist - [x] I have linked to any relevant issues. - [x] I have commented my code, particularly in hard-to-understand areas. - [ ] I have updated the documentation where relevant (API docs, the reference, and the Sway book). - [x] I have added tests that prove my fix is effective or that my feature works. - [x] I have added (or requested a maintainer to add) the necessary `Breaking*` or `New Feature` labels where relevant. - [x] I have done my best to ensure that my PR adheres to [the Fuel Labs Code Review Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md). - [x] I have requested a review from the relevant team or maintainers. Co-authored-by: Kaya Gökalp <[email protected]>
- Loading branch information
1 parent
ceb384b
commit ebe7afe
Showing
14 changed files
with
334 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
use crate::{ | ||
config::{items::ItemBraceStyle, user_def::FieldAlignment}, | ||
formatter::{ | ||
shape::{ExprKind, LineStyle}, | ||
*, | ||
}, | ||
utils::{ | ||
map::byte_span::{ByteSpan, LeafSpans}, | ||
CurlyBrace, | ||
}, | ||
}; | ||
use std::fmt::Write; | ||
use sway_ast::{keywords::Token, token::Delimiter, ConfigurableField, ItemConfigurable}; | ||
use sway_types::Spanned; | ||
|
||
#[cfg(test)] | ||
mod tests; | ||
|
||
impl Format for ItemConfigurable { | ||
fn format( | ||
&self, | ||
formatted_code: &mut FormattedCode, | ||
formatter: &mut Formatter, | ||
) -> Result<(), FormatterError> { | ||
formatter.with_shape( | ||
formatter | ||
.shape | ||
.with_code_line_from(LineStyle::Multiline, ExprKind::default()), | ||
|formatter| -> Result<(), FormatterError> { | ||
// Add configurable token | ||
write!( | ||
formatted_code, | ||
"{}", | ||
self.configurable_token.span().as_str() | ||
)?; | ||
let fields = self.fields.get(); | ||
|
||
// Handle openning brace | ||
Self::open_curly_brace(formatted_code, formatter)?; | ||
|
||
// Determine alignment tactic | ||
match formatter.config.structures.field_alignment { | ||
FieldAlignment::AlignFields(configurable_field_align_threshold) => { | ||
writeln!(formatted_code)?; | ||
let value_pairs = &fields | ||
.value_separator_pairs | ||
.iter() | ||
// TODO: Handle annotations instead of stripping them | ||
.map(|(configurable_field, comma_token)| { | ||
(&configurable_field.value, comma_token) | ||
}) | ||
.collect::<Vec<_>>(); | ||
// In first iteration we are going to be collecting the lengths of the | ||
// struct fields. | ||
let field_length: Vec<usize> = value_pairs | ||
.iter() | ||
.map(|(configurable_field, _)| configurable_field.name.as_str().len()) | ||
.collect(); | ||
|
||
// Find the maximum length in the `field_length` vector that is still | ||
// smaller than `configurable_field_align_threshold`. | ||
// `max_valid_field_length`: the length of the field that we are taking as | ||
// a reference to align. | ||
let mut max_valid_field_length = 0; | ||
field_length.iter().for_each(|length| { | ||
if *length > max_valid_field_length | ||
&& *length < configurable_field_align_threshold | ||
{ | ||
max_valid_field_length = *length; | ||
} | ||
}); | ||
|
||
let value_pairs_iter = value_pairs.iter().enumerate(); | ||
for (field_index, (configurable_field, comma_token)) in | ||
value_pairs_iter.clone() | ||
{ | ||
write!( | ||
formatted_code, | ||
"{}", | ||
&formatter.shape.indent.to_string(&formatter.config)? | ||
)?; | ||
|
||
// Add name | ||
configurable_field.name.format(formatted_code, formatter)?; | ||
|
||
// `current_field_length`: the length of the current field that we are | ||
// trying to format. | ||
let current_field_length = field_length[field_index]; | ||
if current_field_length < max_valid_field_length { | ||
// We need to add alignment between `:` and `ty` | ||
let mut required_alignment = | ||
max_valid_field_length - current_field_length; | ||
while required_alignment != 0 { | ||
write!(formatted_code, " ")?; | ||
required_alignment -= 1; | ||
} | ||
} | ||
// Add `:`, `ty` & `CommaToken` | ||
write!( | ||
formatted_code, | ||
" {} ", | ||
configurable_field.colon_token.ident().as_str(), | ||
)?; | ||
configurable_field.ty.format(formatted_code, formatter)?; | ||
write!( | ||
formatted_code, | ||
" {} ", | ||
configurable_field.eq_token.ident().as_str() | ||
)?; | ||
configurable_field | ||
.initializer | ||
.format(formatted_code, formatter)?; | ||
writeln!(formatted_code, "{}", comma_token.ident().as_str())?; | ||
} | ||
if let Some(final_value) = &fields.final_value_opt { | ||
final_value.format(formatted_code, formatter)?; | ||
} | ||
} | ||
FieldAlignment::Off => fields.format(formatted_code, formatter)?, | ||
} | ||
// Handle closing brace | ||
Self::close_curly_brace(formatted_code, formatter)?; | ||
|
||
Ok(()) | ||
}, | ||
)?; | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
impl CurlyBrace for ItemConfigurable { | ||
fn open_curly_brace( | ||
line: &mut String, | ||
formatter: &mut Formatter, | ||
) -> Result<(), FormatterError> { | ||
let brace_style = formatter.config.items.item_brace_style; | ||
formatter.shape.block_indent(&formatter.config); | ||
let open_brace = Delimiter::Brace.as_open_char(); | ||
match brace_style { | ||
ItemBraceStyle::AlwaysNextLine => { | ||
// Add opening brace to the next line. | ||
write!(line, "\n{open_brace}")?; | ||
} | ||
_ => { | ||
// Add opening brace to the same line | ||
write!(line, " {open_brace}")?; | ||
} | ||
} | ||
|
||
Ok(()) | ||
} | ||
fn close_curly_brace( | ||
line: &mut String, | ||
formatter: &mut Formatter, | ||
) -> Result<(), FormatterError> { | ||
// shrink_left would return error if the current indentation level is becoming < 0, in that | ||
// case we should use the Shape::default() which has 0 indentation level. | ||
formatter.shape.block_unindent(&formatter.config); | ||
write!( | ||
line, | ||
"{}{}", | ||
formatter.shape.indent.to_string(&formatter.config)?, | ||
Delimiter::Brace.as_close_char() | ||
)?; | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
impl LeafSpans for ItemConfigurable { | ||
fn leaf_spans(&self) -> Vec<ByteSpan> { | ||
let mut collected_spans = vec![ByteSpan::from(self.configurable_token.span())]; | ||
collected_spans.append(&mut self.fields.leaf_spans()); | ||
collected_spans | ||
} | ||
} | ||
|
||
impl LeafSpans for ConfigurableField { | ||
fn leaf_spans(&self) -> Vec<ByteSpan> { | ||
let mut collected_spans = vec![ByteSpan::from(self.name.span())]; | ||
collected_spans.push(ByteSpan::from(self.colon_token.span())); | ||
collected_spans.append(&mut self.ty.leaf_spans()); | ||
collected_spans.push(ByteSpan::from(self.eq_token.span())); | ||
collected_spans.append(&mut self.initializer.leaf_spans()); | ||
collected_spans | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
use forc_tracing::{println_green, println_red}; | ||
use paste::paste; | ||
use prettydiff::{basic::DiffOp, diff_lines}; | ||
|
||
macro_rules! fmt_test { | ||
($scope:ident $desired_output:expr, $($name:ident $y:expr),+) => { | ||
fmt_test_inner!($scope $desired_output, | ||
$($name $y)+ | ||
, | ||
remove_trailing_whitespace format!("{} \n\n\t ", $desired_output).as_str(), | ||
remove_beginning_whitespace format!(" \n\t{}", $desired_output).as_str(), | ||
identity $desired_output, /* test return is valid */ | ||
remove_beginning_and_trailing_whitespace format!(" \n\t {} \n\t ", $desired_output).as_str() | ||
); | ||
}; | ||
} | ||
|
||
macro_rules! fmt_test_inner { | ||
($scope:ident $desired_output:expr, $($name:ident $y:expr),+) => { | ||
$( | ||
paste! { | ||
#[test] | ||
fn [<$scope _ $name>] () { | ||
let formatted_code = crate::parse::parse_format::<sway_ast::ItemConfigurable>($y); | ||
let changeset = diff_lines(&formatted_code, $desired_output); | ||
let diff = changeset.diff(); | ||
let count_of_updates = diff.len(); | ||
if count_of_updates != 0 { | ||
println!("FAILED: {count_of_updates} diff items."); | ||
} | ||
for diff in diff { | ||
match diff { | ||
DiffOp::Equal(old) => { | ||
for o in old { | ||
println!("{}", o) | ||
} | ||
} | ||
DiffOp::Insert(new) => { | ||
for n in new { | ||
println_green(&format!("+{}", n)); | ||
} | ||
} | ||
DiffOp::Remove(old) => { | ||
for o in old { | ||
println_red(&format!("-{}", o)); | ||
} | ||
} | ||
DiffOp::Replace(old, new) => { | ||
for o in old { | ||
println_red(&format!("-{}", o)); | ||
} | ||
for n in new { | ||
println_green(&format!("+{}", n)); | ||
} | ||
} | ||
} | ||
} | ||
assert_eq!(&formatted_code, $desired_output) | ||
} | ||
} | ||
)+ | ||
} | ||
} | ||
|
||
fmt_test!( configurables | ||
"configurable { | ||
C0: bool = true, | ||
C1: u64 = 42, | ||
C2: b256 = 0x1111111111111111111111111111111111111111111111111111111111111111, | ||
C3: MyStruct = MyStruct { x: 42, y: true }, | ||
C4: MyEnum = MyEnum::A(42), | ||
C5: MyEnum = MyEnum::B(true), | ||
C6: str[4] = \"fuel\", | ||
C7: [u64; 4] = [1, 2, 3, 4], | ||
C8: u64 = 0, | ||
}", | ||
wrong_new_lines | ||
"configurable { | ||
C0: bool = true, C1: u64 = 42, | ||
C2: b256 = | ||
0x1111111111111111111111111111111111111111111111111111111111111111, | ||
C3: MyStruct = | ||
MyStruct { x: 42, | ||
y: true }, | ||
C4: MyEnum | ||
= MyEnum::A(42), | ||
C5: MyEnum = MyEnum::B(true), | ||
C6: str[4] = \"fuel\", | ||
C7: [u64; 4] = [1, 2, | ||
3, 4], C8: u64 = 0, | ||
}" | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
mod item_abi; | ||
mod item_configurable; | ||
mod item_const; | ||
mod item_enum; | ||
mod item_fn; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 2 additions & 2 deletions
4
test/src/e2e_vm_tests/test_programs/should_fail/configurables_are_immutable/Forc.toml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
[project] | ||
authors = ["Fuel Labs <[email protected]>"] | ||
license = "Apache-2.0" | ||
entry = "main.sw" | ||
implicit-std = false | ||
license = "Apache-2.0" | ||
name = "configurables_are_immutable" | ||
entry = "main.sw" |
2 changes: 1 addition & 1 deletion
2
test/src/e2e_vm_tests/test_programs/should_fail/configurables_are_immutable/src/main.sw
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
script; | ||
|
||
configurable { | ||
C1:u64 = 5, | ||
C1: u64 = 5, | ||
} | ||
|
||
fn main() -> u64 { | ||
|
Oops, something went wrong.