Skip to content

Commit 7aa44cd

Browse files
authored
Add logic for inserting comments for sway-fmt-v2 (#2311)
1 parent aa5dc0d commit 7aa44cd

24 files changed

+1730
-54
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sway-fmt-v2/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ description = "Sway sway-fmt-v2."
1212
[dependencies]
1313
anyhow = "1"
1414
forc-util = { version = "0.18.1", path = "../forc-util" }
15+
ropey = "1.5"
1516
serde = { version = "1.0", features = ["derive"] }
1617
serde_ignored = "0.1"
1718
sway-core = { version = "0.18.1", path = "../sway-core" }

sway-fmt-v2/src/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ pub enum FormatterError {
99
FormatError(#[from] std::fmt::Error),
1010
#[error("Error while lexing file: {0}")]
1111
LexError(#[from] sway_parse::LexError),
12+
#[error("Error while adding comments")]
13+
CommentError,
1214
}
1315

1416
#[derive(Debug, Error)]

sway-fmt-v2/src/fmt.rs

Lines changed: 205 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::utils::{
2-
indent_style::Shape, newline_style::apply_newline_style, program_type::insert_program_type,
2+
comments::handle_comments, indent_style::Shape, newline_style::apply_newline_style,
3+
program_type::insert_program_type,
34
};
45
use std::{path::Path, sync::Arc};
56
use sway_core::BuildConfig;
@@ -42,11 +43,11 @@ impl Formatter {
4243
) -> Result<FormattedCode, FormatterError> {
4344
let path = build_config.map(|build_config| build_config.canonical_root_module());
4445
let src_len = src.len();
45-
let module = sway_parse::parse_file(src, path)?;
46+
let module = sway_parse::parse_file(src.clone(), path.clone())?;
4647
// Get parsed items
47-
let items = module.items;
48+
let items = &module.items;
4849
// Get the program type (script, predicate, contract or library)
49-
let program_type = module.kind;
50+
let program_type = &module.kind;
5051

5152
// Formatted code will be pushed here with raw newline stlye.
5253
// Which means newlines are not converted into system-specific versions until `apply_newline_style()`.
@@ -68,6 +69,14 @@ impl Formatter {
6869
}
6970

7071
let mut formatted_code = String::from(&raw_formatted_code);
72+
// Add comments
73+
handle_comments(
74+
src,
75+
&module,
76+
Arc::from(formatted_code.clone()),
77+
path,
78+
&mut formatted_code,
79+
)?;
7180
// Replace newlines with specified `NewlineStyle`
7281
apply_newline_style(
7382
self.config.whitespace.newline_style,
@@ -337,8 +346,10 @@ storage {
337346
let correct_sway_code = r#"contract;
338347
339348
storage {
340-
long_var_name : Type1,
341-
var2 : Type2,
349+
long_var_name : Type1 = Type1 {
350+
},
351+
var2 : Type2 = Type2 {
352+
},
342353
}"#;
343354

344355
let mut formatter = Formatter::default();
@@ -358,10 +369,12 @@ storage {
358369
"#;
359370
let correct_sway_code = r#"contract;
360371
361-
storage { long_var_name: Type1, var2: Type2 }"#;
372+
storage { long_var_name: Type1 = Type1 {
373+
}, var2: Type2 = Type2 {
374+
} }"#;
362375
let mut formatter = Formatter::default();
363376
formatter.config.structures.small_structures_single_line = true;
364-
formatter.config.whitespace.max_width = 300;
377+
formatter.config.whitespace.max_width = 700;
365378
let formatted_sway_code =
366379
Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap();
367380
assert_eq!(correct_sway_code, formatted_sway_code)
@@ -391,12 +404,10 @@ struct Type1 {
391404
x: u64,
392405
y: u64,
393406
}
394-
395407
struct Type2 {
396408
w: b256,
397409
z: bool,
398410
}
399-
400411
storage {
401412
var1: Type1 = Type1 {
402413
x: 0,
@@ -485,4 +496,188 @@ trait CompSciStudent: Programmer + Student {
485496
Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap();
486497
assert_eq!(correct_sway_code, formatted_sway_code)
487498
}
499+
500+
#[test]
501+
fn test_struct_comments() {
502+
let sway_code_to_format = r#"contract;
503+
// This is a comment, for this one to be placed correctly we need to have Module visitor implemented
504+
pub struct Foo { // Here is a comment
505+
506+
507+
508+
// Trying some ASCII art
509+
baz:u64,
510+
511+
512+
513+
514+
bazzz:u64// ________ ___ ___ _______ ___ ___ ________ ________ ________
515+
// |\ _____\\ \|\ \|\ ___ \ |\ \ |\ \ |\ __ \|\ __ \|\ ____\
516+
// \ \ \__/\ \ \\\ \ \ __/|\ \ \ \ \ \ \ \ \|\ \ \ \|\ /\ \ \___|_
517+
// \ \ __\\ \ \\\ \ \ \_|/_\ \ \ \ \ \ \ \ __ \ \ __ \ \_____ \
518+
// \ \ \_| \ \ \\\ \ \ \_|\ \ \ \____ \ \ \____\ \ \ \ \ \ \|\ \|____|\ \
519+
// \ \__\ \ \_______\ \_______\ \_______\ \ \_______\ \__\ \__\ \_______\____\_\ \
520+
// \|__| \|_______|\|_______|\|_______| \|_______|\|__|\|__|\|_______|\_________\
521+
// \|_________|
522+
}
523+
// This is a comment
524+
"#;
525+
let correct_sway_code = r#"contract;
526+
527+
// This is a comment, for this one to be placed correctly we need to have Module visitor implemented
528+
pub struct Foo { // Here is a comment
529+
530+
531+
532+
// Trying some ASCII art
533+
baz: u64,
534+
bazzz: u64,// ________ ___ ___ _______ ___ ___ ________ ________ ________
535+
// |\ _____\\ \|\ \|\ ___ \ |\ \ |\ \ |\ __ \|\ __ \|\ ____\
536+
// \ \ \__/\ \ \\\ \ \ __/|\ \ \ \ \ \ \ \ \|\ \ \ \|\ /\ \ \___|_
537+
// \ \ __\\ \ \\\ \ \ \_|/_\ \ \ \ \ \ \ \ __ \ \ __ \ \_____ \
538+
// \ \ \_| \ \ \\\ \ \ \_|\ \ \ \____ \ \ \____\ \ \ \ \ \ \|\ \|____|\ \
539+
// \ \__\ \ \_______\ \_______\ \_______\ \ \_______\ \__\ \__\ \_______\____\_\ \
540+
// \|__| \|_______|\|_______|\|_______| \|_______|\|__|\|__|\|_______|\_________\
541+
// \|_________|
542+
}
543+
// This is a comment"#;
544+
let mut formatter = Formatter::default();
545+
let formatted_sway_code =
546+
Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap();
547+
assert_eq!(correct_sway_code, formatted_sway_code)
548+
}
549+
550+
#[test]
551+
fn test_enum_comments() {
552+
let sway_code_to_format = r#"contract;
553+
pub enum Bazz { // Here is a comment
554+
// Trying some ASCII art
555+
baz: (),
556+
557+
558+
559+
560+
561+
bazzz: (),//-----
562+
//--D--
563+
//-----
564+
}
565+
"#;
566+
let correct_sway_code = r#"contract;
567+
568+
pub enum Bazz { // Here is a comment
569+
// Trying some ASCII art
570+
baz: (),
571+
bazzz: (),//-----
572+
//--D--
573+
//-----
574+
}"#;
575+
let mut formatter = Formatter::default();
576+
let formatted_sway_code =
577+
Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap();
578+
assert_eq!(correct_sway_code, formatted_sway_code);
579+
}
580+
581+
#[test]
582+
fn test_fn_comments() {
583+
let sway_code_to_format = r#"contract;
584+
// This is a comment before a fn
585+
// This is another comment before a fn
586+
fn hello_world( baz: /* this is a comment */ u64) { // This is a comment inside the block
587+
}
588+
"#;
589+
let correct_sway_code = r#"contract;
590+
591+
// This is a comment before a fn
592+
// This is another comment before a fn
593+
fn hello_world(baz: /* this is a comment */ u64) { // This is a comment inside the block
594+
}"#;
595+
596+
let mut formatter = Formatter::default();
597+
let formatted_sway_code =
598+
Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap();
599+
assert_eq!(correct_sway_code, formatted_sway_code);
600+
}
601+
602+
#[test]
603+
fn test_abi_comments() {
604+
let sway_code_to_format = r#"contract;
605+
// This is an abi
606+
abi StorageMapExample {
607+
// insert_into_map is blah blah
608+
#[storage(write)] // this is some other comment
609+
fn insert_into_map(key: u64, value: u64); // this is the last comment inside the StorageMapExample
610+
}"#;
611+
let correct_sway_code = r#"contract;
612+
613+
// This is an abi
614+
abi StorageMapExample {
615+
// insert_into_map is blah blah
616+
#[storage(write)] // this is some other comment
617+
fn insert_into_map(key: u64, value: u64); // this is the last comment inside the StorageMapExample
618+
}"#;
619+
let mut formatter = Formatter::default();
620+
let formatted_sway_code =
621+
Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap();
622+
assert_eq!(correct_sway_code, formatted_sway_code);
623+
}
624+
625+
#[test]
626+
fn test_const_comments() {
627+
let sway_code_to_format = r#"contract;
628+
pub const /* TEST: blah blah tests */ TEST: u16 = 10; // This is a comment next to a const"#;
629+
let correct_sway_code = r#"contract;
630+
631+
pub const /* TEST: blah blah tests */ TEST: u16 = 10;"#; // Comment next to const is not picked up by the lexer see: #2356
632+
let mut formatter = Formatter::default();
633+
let formatted_sway_code =
634+
Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap();
635+
assert_eq!(correct_sway_code, formatted_sway_code);
636+
}
637+
#[test]
638+
fn test_storage_comments() {
639+
let sway_code_to_format = r#"contract;
640+
storage {
641+
// Testing a comment inside storage
642+
long_var_name: Type1=Type1{},
643+
// Testing another comment
644+
var2: Type2 = Type2{} // This is the last comment
645+
}"#;
646+
let correct_sway_code = r#"contract;
647+
648+
storage {
649+
// Testing a comment inside storage
650+
long_var_name: Type1 = Type1 {
651+
},
652+
// Testing another comment
653+
var2: Type2 = Type2 {
654+
}, // This is the last comment
655+
}"#;
656+
let mut formatter = Formatter::default();
657+
let formatted_sway_code =
658+
Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap();
659+
assert_eq!(correct_sway_code, formatted_sway_code);
660+
}
661+
662+
#[test]
663+
fn test_trait_comments() {
664+
let sway_code_to_format = r#"contract;
665+
// This is the programmer trait
666+
trait Programmer {
667+
// Returns fav languages of this Programmer.
668+
fn fav_language(self) -> String;
669+
}"#;
670+
let correct_sway_code = r#"contract;
671+
672+
// This is the programmer trait
673+
trait Programmer {
674+
// Returns fav languages of this Programmer.
675+
fn fav_language(self) -> String;
676+
}"#;
677+
678+
let mut formatter = Formatter::default();
679+
let formatted_sway_code =
680+
Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap();
681+
assert_eq!(correct_sway_code, formatted_sway_code)
682+
}
488683
}

sway-fmt-v2/src/items/item_abi.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
use crate::{
22
config::items::ItemBraceStyle,
33
fmt::{Format, FormattedCode, Formatter},
4-
utils::{attribute::FormatDecl, bracket::CurlyBrace},
4+
utils::{
5+
attribute::FormatDecl,
6+
bracket::CurlyBrace,
7+
comments::{ByteSpan, LeafSpans},
8+
},
59
FormatterError,
610
};
711
use std::fmt::Write;
@@ -130,3 +134,15 @@ impl CurlyBrace for ItemAbi {
130134
Ok(())
131135
}
132136
}
137+
138+
impl LeafSpans for ItemAbi {
139+
fn leaf_spans(&self) -> Vec<ByteSpan> {
140+
let mut collected_spans = vec![ByteSpan::from(self.abi_token.span())];
141+
collected_spans.push(ByteSpan::from(self.name.span()));
142+
collected_spans.append(&mut self.abi_items.leaf_spans());
143+
if let Some(abi_defs) = &self.abi_defs_opt {
144+
collected_spans.append(&mut abi_defs.leaf_spans());
145+
}
146+
collected_spans
147+
}
148+
}

sway-fmt-v2/src/items/item_const.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::{
22
fmt::{Format, FormattedCode, Formatter},
3+
utils::comments::{ByteSpan, LeafSpans},
34
FormatterError,
45
};
56
use std::fmt::Write;
@@ -44,3 +45,22 @@ impl Format for ItemConst {
4445
Ok(())
4546
}
4647
}
48+
49+
impl LeafSpans for ItemConst {
50+
fn leaf_spans(&self) -> Vec<ByteSpan> {
51+
let mut collected_spans = Vec::new();
52+
if let Some(visibility) = &self.visibility {
53+
collected_spans.push(ByteSpan::from(visibility.span()));
54+
}
55+
collected_spans.push(ByteSpan::from(self.const_token.span()));
56+
collected_spans.push(ByteSpan::from(self.name.span()));
57+
if let Some(ty) = &self.ty_opt {
58+
collected_spans.append(&mut ty.leaf_spans());
59+
// TODO: determine if we allow comments in between `:` and ty
60+
}
61+
collected_spans.push(ByteSpan::from(self.eq_token.span()));
62+
collected_spans.append(&mut self.expr.leaf_spans());
63+
collected_spans.push(ByteSpan::from(self.semicolon_token.span()));
64+
collected_spans
65+
}
66+
}

sway-fmt-v2/src/items/item_enum.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
use crate::{
22
config::{items::ItemBraceStyle, user_def::FieldAlignment},
33
fmt::{Format, FormattedCode, Formatter},
4-
utils::{bracket::CurlyBrace, item::ItemLenChars},
4+
utils::{
5+
bracket::CurlyBrace,
6+
comments::{ByteSpan, LeafSpans},
7+
item::ItemLenChars,
8+
},
59
FormatterError,
610
};
711
use std::fmt::Write;
@@ -235,3 +239,18 @@ impl CurlyBrace for ItemEnum {
235239
Ok(())
236240
}
237241
}
242+
impl LeafSpans for ItemEnum {
243+
fn leaf_spans(&self) -> Vec<ByteSpan> {
244+
let mut collected_spans = Vec::new();
245+
if let Some(visibility) = &self.visibility {
246+
collected_spans.push(ByteSpan::from(visibility.span()));
247+
}
248+
collected_spans.push(ByteSpan::from(self.enum_token.span()));
249+
collected_spans.push(ByteSpan::from(self.name.span()));
250+
if let Some(generics) = &self.generics {
251+
collected_spans.push(ByteSpan::from(generics.parameters.span()))
252+
}
253+
collected_spans.append(&mut self.fields.leaf_spans());
254+
collected_spans
255+
}
256+
}

0 commit comments

Comments
 (0)