From 7aa44cdd437152348e80c86a8fe7649dec275d09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kaya=20G=C3=B6kalp?= Date: Tue, 26 Jul 2022 01:08:22 +0300 Subject: [PATCH] Add logic for inserting comments for sway-fmt-v2 (#2311) --- Cargo.lock | 1 + sway-fmt-v2/Cargo.toml | 1 + sway-fmt-v2/src/error.rs | 2 + sway-fmt-v2/src/fmt.rs | 215 +++++++++- sway-fmt-v2/src/items/item_abi.rs | 18 +- sway-fmt-v2/src/items/item_const.rs | 20 + sway-fmt-v2/src/items/item_enum.rs | 21 +- sway-fmt-v2/src/items/item_fn.rs | 82 +++- sway-fmt-v2/src/items/item_impl.rs | 17 + sway-fmt-v2/src/items/item_storage.rs | 38 +- sway-fmt-v2/src/items/item_struct.rs | 23 +- sway-fmt-v2/src/items/item_trait.rs | 36 +- sway-fmt-v2/src/items/item_use.rs | 51 ++- sway-fmt-v2/src/utils/attribute.rs | 23 +- sway-fmt-v2/src/utils/comments.rs | 384 +++++++++++++++++- sway-fmt-v2/src/utils/expr.rs | 545 +++++++++++++++++++++++++- sway-fmt-v2/src/utils/item.rs | 23 +- sway-fmt-v2/src/utils/literal.rs | 16 +- sway-fmt-v2/src/utils/path.rs | 19 +- sway-fmt-v2/src/utils/pattern.rs | 62 ++- sway-fmt-v2/src/utils/program_type.rs | 58 ++- sway-fmt-v2/src/utils/punctuated.rs | 35 ++ sway-fmt-v2/src/utils/statement.rs | 45 ++- sway-fmt-v2/src/utils/ty.rs | 49 ++- 24 files changed, 1730 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c7a35d7224e..f758133b5ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3658,6 +3658,7 @@ version = "0.18.1" dependencies = [ "anyhow", "forc-util", + "ropey", "serde", "serde_ignored", "sway-core", diff --git a/sway-fmt-v2/Cargo.toml b/sway-fmt-v2/Cargo.toml index 0c5cc1f925a..37be8a55e1b 100644 --- a/sway-fmt-v2/Cargo.toml +++ b/sway-fmt-v2/Cargo.toml @@ -12,6 +12,7 @@ description = "Sway sway-fmt-v2." [dependencies] anyhow = "1" forc-util = { version = "0.18.1", path = "../forc-util" } +ropey = "1.5" serde = { version = "1.0", features = ["derive"] } serde_ignored = "0.1" sway-core = { version = "0.18.1", path = "../sway-core" } diff --git a/sway-fmt-v2/src/error.rs b/sway-fmt-v2/src/error.rs index d9b37cc5728..5fda59ad932 100644 --- a/sway-fmt-v2/src/error.rs +++ b/sway-fmt-v2/src/error.rs @@ -9,6 +9,8 @@ pub enum FormatterError { FormatError(#[from] std::fmt::Error), #[error("Error while lexing file: {0}")] LexError(#[from] sway_parse::LexError), + #[error("Error while adding comments")] + CommentError, } #[derive(Debug, Error)] diff --git a/sway-fmt-v2/src/fmt.rs b/sway-fmt-v2/src/fmt.rs index 578721638f1..f40d2d70028 100644 --- a/sway-fmt-v2/src/fmt.rs +++ b/sway-fmt-v2/src/fmt.rs @@ -1,5 +1,6 @@ use crate::utils::{ - indent_style::Shape, newline_style::apply_newline_style, program_type::insert_program_type, + comments::handle_comments, indent_style::Shape, newline_style::apply_newline_style, + program_type::insert_program_type, }; use std::{path::Path, sync::Arc}; use sway_core::BuildConfig; @@ -42,11 +43,11 @@ impl Formatter { ) -> Result { let path = build_config.map(|build_config| build_config.canonical_root_module()); let src_len = src.len(); - let module = sway_parse::parse_file(src, path)?; + let module = sway_parse::parse_file(src.clone(), path.clone())?; // Get parsed items - let items = module.items; + let items = &module.items; // Get the program type (script, predicate, contract or library) - let program_type = module.kind; + let program_type = &module.kind; // Formatted code will be pushed here with raw newline stlye. // Which means newlines are not converted into system-specific versions until `apply_newline_style()`. @@ -68,6 +69,14 @@ impl Formatter { } let mut formatted_code = String::from(&raw_formatted_code); + // Add comments + handle_comments( + src, + &module, + Arc::from(formatted_code.clone()), + path, + &mut formatted_code, + )?; // Replace newlines with specified `NewlineStyle` apply_newline_style( self.config.whitespace.newline_style, @@ -337,8 +346,10 @@ storage { let correct_sway_code = r#"contract; storage { - long_var_name : Type1, - var2 : Type2, + long_var_name : Type1 = Type1 { + }, + var2 : Type2 = Type2 { + }, }"#; let mut formatter = Formatter::default(); @@ -358,10 +369,12 @@ storage { "#; let correct_sway_code = r#"contract; -storage { long_var_name: Type1, var2: Type2 }"#; +storage { long_var_name: Type1 = Type1 { + }, var2: Type2 = Type2 { + } }"#; let mut formatter = Formatter::default(); formatter.config.structures.small_structures_single_line = true; - formatter.config.whitespace.max_width = 300; + formatter.config.whitespace.max_width = 700; let formatted_sway_code = Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap(); assert_eq!(correct_sway_code, formatted_sway_code) @@ -391,12 +404,10 @@ struct Type1 { x: u64, y: u64, } - struct Type2 { w: b256, z: bool, } - storage { var1: Type1 = Type1 { x: 0, @@ -485,4 +496,188 @@ trait CompSciStudent: Programmer + Student { Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap(); assert_eq!(correct_sway_code, formatted_sway_code) } + + #[test] + fn test_struct_comments() { + let sway_code_to_format = r#"contract; +// This is a comment, for this one to be placed correctly we need to have Module visitor implemented +pub struct Foo { // Here is a comment + + + + // Trying some ASCII art + baz:u64, + + + + + bazzz:u64// ________ ___ ___ _______ ___ ___ ________ ________ ________ + // |\ _____\\ \|\ \|\ ___ \ |\ \ |\ \ |\ __ \|\ __ \|\ ____\ + // \ \ \__/\ \ \\\ \ \ __/|\ \ \ \ \ \ \ \ \|\ \ \ \|\ /\ \ \___|_ + // \ \ __\\ \ \\\ \ \ \_|/_\ \ \ \ \ \ \ \ __ \ \ __ \ \_____ \ + // \ \ \_| \ \ \\\ \ \ \_|\ \ \ \____ \ \ \____\ \ \ \ \ \ \|\ \|____|\ \ + // \ \__\ \ \_______\ \_______\ \_______\ \ \_______\ \__\ \__\ \_______\____\_\ \ + // \|__| \|_______|\|_______|\|_______| \|_______|\|__|\|__|\|_______|\_________\ + // \|_________| +} +// This is a comment +"#; + let correct_sway_code = r#"contract; + +// This is a comment, for this one to be placed correctly we need to have Module visitor implemented +pub struct Foo { // Here is a comment + + + + // Trying some ASCII art + baz: u64, + bazzz: u64,// ________ ___ ___ _______ ___ ___ ________ ________ ________ + // |\ _____\\ \|\ \|\ ___ \ |\ \ |\ \ |\ __ \|\ __ \|\ ____\ + // \ \ \__/\ \ \\\ \ \ __/|\ \ \ \ \ \ \ \ \|\ \ \ \|\ /\ \ \___|_ + // \ \ __\\ \ \\\ \ \ \_|/_\ \ \ \ \ \ \ \ __ \ \ __ \ \_____ \ + // \ \ \_| \ \ \\\ \ \ \_|\ \ \ \____ \ \ \____\ \ \ \ \ \ \|\ \|____|\ \ + // \ \__\ \ \_______\ \_______\ \_______\ \ \_______\ \__\ \__\ \_______\____\_\ \ + // \|__| \|_______|\|_______|\|_______| \|_______|\|__|\|__|\|_______|\_________\ + // \|_________| +} +// This is a comment"#; + let mut formatter = Formatter::default(); + let formatted_sway_code = + Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap(); + assert_eq!(correct_sway_code, formatted_sway_code) + } + + #[test] + fn test_enum_comments() { + let sway_code_to_format = r#"contract; +pub enum Bazz { // Here is a comment + // Trying some ASCII art + baz: (), + + + + + + bazzz: (),//----- + //--D-- + //----- +} +"#; + let correct_sway_code = r#"contract; + +pub enum Bazz { // Here is a comment + // Trying some ASCII art + baz: (), + bazzz: (),//----- + //--D-- + //----- +}"#; + let mut formatter = Formatter::default(); + let formatted_sway_code = + Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap(); + assert_eq!(correct_sway_code, formatted_sway_code); + } + + #[test] + fn test_fn_comments() { + let sway_code_to_format = r#"contract; +// This is a comment before a fn +// This is another comment before a fn +fn hello_world( baz: /* this is a comment */ u64) { // This is a comment inside the block +} +"#; + let correct_sway_code = r#"contract; + +// This is a comment before a fn +// This is another comment before a fn +fn hello_world(baz: /* this is a comment */ u64) { // This is a comment inside the block +}"#; + + let mut formatter = Formatter::default(); + let formatted_sway_code = + Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap(); + assert_eq!(correct_sway_code, formatted_sway_code); + } + + #[test] + fn test_abi_comments() { + let sway_code_to_format = r#"contract; +// This is an abi +abi StorageMapExample { + // insert_into_map is blah blah + #[storage(write)] // this is some other comment + fn insert_into_map(key: u64, value: u64); // this is the last comment inside the StorageMapExample +}"#; + let correct_sway_code = r#"contract; + +// This is an abi +abi StorageMapExample { + // insert_into_map is blah blah + #[storage(write)] // this is some other comment + fn insert_into_map(key: u64, value: u64); // this is the last comment inside the StorageMapExample +}"#; + let mut formatter = Formatter::default(); + let formatted_sway_code = + Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap(); + assert_eq!(correct_sway_code, formatted_sway_code); + } + + #[test] + fn test_const_comments() { + let sway_code_to_format = r#"contract; +pub const /* TEST: blah blah tests */ TEST: u16 = 10; // This is a comment next to a const"#; + let correct_sway_code = r#"contract; + +pub const /* TEST: blah blah tests */ TEST: u16 = 10;"#; // Comment next to const is not picked up by the lexer see: #2356 + let mut formatter = Formatter::default(); + let formatted_sway_code = + Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap(); + assert_eq!(correct_sway_code, formatted_sway_code); + } + #[test] + fn test_storage_comments() { + let sway_code_to_format = r#"contract; +storage { + // Testing a comment inside storage + long_var_name: Type1=Type1{}, + // Testing another comment + var2: Type2 = Type2{} // This is the last comment +}"#; + let correct_sway_code = r#"contract; + +storage { + // Testing a comment inside storage + long_var_name: Type1 = Type1 { + }, + // Testing another comment + var2: Type2 = Type2 { + }, // This is the last comment +}"#; + let mut formatter = Formatter::default(); + let formatted_sway_code = + Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap(); + assert_eq!(correct_sway_code, formatted_sway_code); + } + + #[test] + fn test_trait_comments() { + let sway_code_to_format = r#"contract; +// This is the programmer trait +trait Programmer { + // Returns fav languages of this Programmer. + fn fav_language(self) -> String; +}"#; + let correct_sway_code = r#"contract; + +// This is the programmer trait +trait Programmer { + // Returns fav languages of this Programmer. + fn fav_language(self) -> String; +}"#; + + let mut formatter = Formatter::default(); + let formatted_sway_code = + Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap(); + assert_eq!(correct_sway_code, formatted_sway_code) + } } diff --git a/sway-fmt-v2/src/items/item_abi.rs b/sway-fmt-v2/src/items/item_abi.rs index 3113bdfff7e..c55ae82eca8 100644 --- a/sway-fmt-v2/src/items/item_abi.rs +++ b/sway-fmt-v2/src/items/item_abi.rs @@ -1,7 +1,11 @@ use crate::{ config::items::ItemBraceStyle, fmt::{Format, FormattedCode, Formatter}, - utils::{attribute::FormatDecl, bracket::CurlyBrace}, + utils::{ + attribute::FormatDecl, + bracket::CurlyBrace, + comments::{ByteSpan, LeafSpans}, + }, FormatterError, }; use std::fmt::Write; @@ -130,3 +134,15 @@ impl CurlyBrace for ItemAbi { Ok(()) } } + +impl LeafSpans for ItemAbi { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = vec![ByteSpan::from(self.abi_token.span())]; + collected_spans.push(ByteSpan::from(self.name.span())); + collected_spans.append(&mut self.abi_items.leaf_spans()); + if let Some(abi_defs) = &self.abi_defs_opt { + collected_spans.append(&mut abi_defs.leaf_spans()); + } + collected_spans + } +} diff --git a/sway-fmt-v2/src/items/item_const.rs b/sway-fmt-v2/src/items/item_const.rs index 17f2d9d6661..acc589c130c 100644 --- a/sway-fmt-v2/src/items/item_const.rs +++ b/sway-fmt-v2/src/items/item_const.rs @@ -1,5 +1,6 @@ use crate::{ fmt::{Format, FormattedCode, Formatter}, + utils::comments::{ByteSpan, LeafSpans}, FormatterError, }; use std::fmt::Write; @@ -44,3 +45,22 @@ impl Format for ItemConst { Ok(()) } } + +impl LeafSpans for ItemConst { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + if let Some(visibility) = &self.visibility { + collected_spans.push(ByteSpan::from(visibility.span())); + } + collected_spans.push(ByteSpan::from(self.const_token.span())); + collected_spans.push(ByteSpan::from(self.name.span())); + if let Some(ty) = &self.ty_opt { + collected_spans.append(&mut ty.leaf_spans()); + // TODO: determine if we allow comments in between `:` and ty + } + collected_spans.push(ByteSpan::from(self.eq_token.span())); + collected_spans.append(&mut self.expr.leaf_spans()); + collected_spans.push(ByteSpan::from(self.semicolon_token.span())); + collected_spans + } +} diff --git a/sway-fmt-v2/src/items/item_enum.rs b/sway-fmt-v2/src/items/item_enum.rs index 3d869a7e407..d84a1dc06a8 100644 --- a/sway-fmt-v2/src/items/item_enum.rs +++ b/sway-fmt-v2/src/items/item_enum.rs @@ -1,7 +1,11 @@ use crate::{ config::{items::ItemBraceStyle, user_def::FieldAlignment}, fmt::{Format, FormattedCode, Formatter}, - utils::{bracket::CurlyBrace, item::ItemLenChars}, + utils::{ + bracket::CurlyBrace, + comments::{ByteSpan, LeafSpans}, + item::ItemLenChars, + }, FormatterError, }; use std::fmt::Write; @@ -235,3 +239,18 @@ impl CurlyBrace for ItemEnum { Ok(()) } } +impl LeafSpans for ItemEnum { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + if let Some(visibility) = &self.visibility { + collected_spans.push(ByteSpan::from(visibility.span())); + } + collected_spans.push(ByteSpan::from(self.enum_token.span())); + collected_spans.push(ByteSpan::from(self.name.span())); + if let Some(generics) = &self.generics { + collected_spans.push(ByteSpan::from(generics.parameters.span())) + } + collected_spans.append(&mut self.fields.leaf_spans()); + collected_spans + } +} diff --git a/sway-fmt-v2/src/items/item_fn.rs b/sway-fmt-v2/src/items/item_fn.rs index 754ecba7a3b..e7682ca6461 100644 --- a/sway-fmt-v2/src/items/item_fn.rs +++ b/sway-fmt-v2/src/items/item_fn.rs @@ -1,7 +1,10 @@ use crate::{ config::items::ItemBraceStyle, fmt::{Format, FormattedCode, Formatter, FormatterError}, - utils::bracket::{CurlyBrace, Parenthesis}, + utils::{ + bracket::{CurlyBrace, Parenthesis}, + comments::{ByteSpan, LeafSpans}, + }, }; use std::fmt::Write; use sway_parse::{token::Delimiter, CodeBlockContents, FnArg, FnArgs, FnSignature, ItemFn}; @@ -203,3 +206,80 @@ impl Format for FnArg { Ok(()) } } + +impl LeafSpans for ItemFn { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + collected_spans.append(&mut self.fn_signature.leaf_spans()); + collected_spans.append(&mut self.body.leaf_spans()); + collected_spans + } +} + +impl LeafSpans for CodeBlockContents { + fn leaf_spans(&self) -> Vec { + let mut collected_span = Vec::new(); + for statement in self.statements.iter() { + collected_span.append(&mut statement.leaf_spans()); + } + if let Some(expr) = &self.final_expr_opt { + collected_span.append(&mut expr.leaf_spans()); + } + collected_span + } +} + +impl LeafSpans for FnSignature { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + if let Some(visibility) = &self.visibility { + collected_spans.push(ByteSpan::from(visibility.span())); + } + collected_spans.push(ByteSpan::from(self.fn_token.span())); + collected_spans.push(ByteSpan::from(self.name.span())); + if let Some(generics) = &self.generics { + collected_spans.push(ByteSpan::from(generics.parameters.span())); + } + collected_spans.append(&mut self.arguments.leaf_spans()); + if let Some(return_type) = &self.return_type_opt { + collected_spans.append(&mut return_type.leaf_spans()); + } + // TODO add where, I will add where for all items at once. + collected_spans + } +} + +impl LeafSpans for FnArgs { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + match &self { + FnArgs::Static(arg_static) => { + collected_spans.append(&mut arg_static.leaf_spans()); + } + FnArgs::NonStatic { + self_token, + mutable_self, + args_opt, + } => { + collected_spans.push(ByteSpan::from(self_token.span())); + if let Some(mutable) = mutable_self { + collected_spans.push(ByteSpan::from(mutable.span())); + } + if let Some(args) = args_opt { + collected_spans.append(&mut args.leaf_spans()); + } + } + }; + collected_spans + } +} + +impl LeafSpans for FnArg { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + collected_spans.append(&mut self.pattern.leaf_spans()); + collected_spans.push(ByteSpan::from(self.colon_token.span())); + collected_spans.push(ByteSpan::from(self.ty.span())); + collected_spans + } +} diff --git a/sway-fmt-v2/src/items/item_impl.rs b/sway-fmt-v2/src/items/item_impl.rs index a14aedad852..07b85c6b590 100644 --- a/sway-fmt-v2/src/items/item_impl.rs +++ b/sway-fmt-v2/src/items/item_impl.rs @@ -1,8 +1,10 @@ use crate::{ fmt::{Format, FormattedCode, Formatter}, + utils::comments::{ByteSpan, LeafSpans}, FormatterError, }; use sway_parse::ItemImpl; +use sway_types::Spanned; impl Format for ItemImpl { fn format( @@ -13,3 +15,18 @@ impl Format for ItemImpl { todo!() } } +impl LeafSpans for ItemImpl { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = vec![ByteSpan::from(self.impl_token.span())]; + if let Some(generic) = &self.generic_params_opt { + collected_spans.push(ByteSpan::from(generic.parameters.span())); + } + if let Some(trait_tuple) = &self.trait_opt { + collected_spans.append(&mut trait_tuple.leaf_spans()); + } + collected_spans.append(&mut self.ty.leaf_spans()); + // TODO add where + collected_spans.append(&mut self.contents.leaf_spans()); + collected_spans + } +} diff --git a/sway-fmt-v2/src/items/item_storage.rs b/sway-fmt-v2/src/items/item_storage.rs index cd8208d2a1d..9d4189ffd5c 100644 --- a/sway-fmt-v2/src/items/item_storage.rs +++ b/sway-fmt-v2/src/items/item_storage.rs @@ -1,13 +1,17 @@ use crate::{ config::{items::ItemBraceStyle, user_def::FieldAlignment}, fmt::{Format, FormattedCode, Formatter}, - utils::{bracket::CurlyBrace, item::ItemLenChars}, + utils::{ + bracket::CurlyBrace, + comments::{ByteSpan, LeafSpans}, + item::ItemLenChars, + }, FormatterError, }; use std::fmt::Write; use sway_parse::{ token::{Delimiter, PunctKind}, - ItemStorage, + ItemStorage, StorageField, }; use sway_types::Spanned; @@ -111,6 +115,14 @@ fn format_storage( storage_field.colon_token.ident().as_str(), )?; storage_field.ty.format(formatted_code, formatter)?; + write!( + formatted_code, + " {} ", + storage_field.eq_token.ident().as_str() + )?; + storage_field + .initializer + .format(formatted_code, formatter)?; if value_pairs_iter.peek().is_some() { writeln!(formatted_code, "{}", field.1.span().as_str())?; } else if let Some(final_value) = &fields.final_value_opt { @@ -157,7 +169,8 @@ fn format_storage( field.0.colon_token.span().as_str(), )?; field.0.ty.format(formatted_code, formatter)?; - + write!(formatted_code, " {} ", field.0.eq_token.ident().as_str())?; + field.0.initializer.format(formatted_code, formatter)?; if value_pairs_iter.peek().is_some() { write!(formatted_code, "{} ", field.1.span().as_str())?; } @@ -216,3 +229,22 @@ impl CurlyBrace for ItemStorage { Ok(()) } } + +impl LeafSpans for ItemStorage { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = vec![ByteSpan::from(self.storage_token.span())]; + collected_spans.append(&mut self.fields.leaf_spans()); + collected_spans + } +} + +impl LeafSpans for StorageField { + fn leaf_spans(&self) -> Vec { + 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 + } +} diff --git a/sway-fmt-v2/src/items/item_struct.rs b/sway-fmt-v2/src/items/item_struct.rs index 57358ea062d..c714fa2a37b 100644 --- a/sway-fmt-v2/src/items/item_struct.rs +++ b/sway-fmt-v2/src/items/item_struct.rs @@ -1,7 +1,11 @@ use crate::{ config::{items::ItemBraceStyle, user_def::FieldAlignment}, fmt::{Format, FormattedCode, Formatter}, - utils::{bracket::CurlyBrace, item::ItemLenChars}, + utils::{ + bracket::CurlyBrace, + comments::{ByteSpan, LeafSpans}, + item::ItemLenChars, + }, FormatterError, }; use std::fmt::Write; @@ -189,7 +193,6 @@ fn format_struct( // Handle closing brace ItemStruct::close_curly_brace(formatted_code, formatter)?; - writeln!(formatted_code)?; Ok(()) } @@ -241,3 +244,19 @@ impl CurlyBrace for ItemStruct { Ok(()) } } + +impl LeafSpans for ItemStruct { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + if let Some(visibility) = &self.visibility { + collected_spans.push(ByteSpan::from(visibility.span())); + } + collected_spans.push(ByteSpan::from(self.struct_token.span())); + collected_spans.push(ByteSpan::from(self.name.span())); + if let Some(generics) = &self.generics { + collected_spans.push(ByteSpan::from(generics.parameters.span())) + } + collected_spans.append(&mut self.fields.leaf_spans()); + collected_spans + } +} diff --git a/sway-fmt-v2/src/items/item_trait.rs b/sway-fmt-v2/src/items/item_trait.rs index 882067e654d..1bdc05b5392 100644 --- a/sway-fmt-v2/src/items/item_trait.rs +++ b/sway-fmt-v2/src/items/item_trait.rs @@ -1,4 +1,11 @@ -use crate::{config::items::ItemBraceStyle, fmt::*, utils::bracket::CurlyBrace}; +use crate::{ + config::items::ItemBraceStyle, + fmt::*, + utils::{ + bracket::CurlyBrace, + comments::{ByteSpan, LeafSpans}, + }, +}; use std::fmt::Write; use sway_parse::{token::Delimiter, ItemTrait, Traits}; use sway_types::Spanned; @@ -113,3 +120,30 @@ impl Format for Traits { Ok(()) } } + +impl LeafSpans for ItemTrait { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + if let Some(visibility) = &self.visibility { + collected_spans.push(ByteSpan::from(visibility.span())); + } + collected_spans.push(ByteSpan::from(self.trait_token.span())); + collected_spans.push(ByteSpan::from(self.name.span())); + if let Some(super_traits) = &self.super_traits { + collected_spans.append(&mut super_traits.leaf_spans()); + } + collected_spans.append(&mut self.trait_items.leaf_spans()); + if let Some(trait_defs) = &self.trait_defs_opt { + collected_spans.append(&mut trait_defs.leaf_spans()); + } + collected_spans + } +} + +impl LeafSpans for Traits { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = self.prefix.leaf_spans(); + collected_spans.append(&mut self.suffixes.leaf_spans()); + collected_spans + } +} diff --git a/sway-fmt-v2/src/items/item_use.rs b/sway-fmt-v2/src/items/item_use.rs index cf23ebc89dd..9448a136322 100644 --- a/sway-fmt-v2/src/items/item_use.rs +++ b/sway-fmt-v2/src/items/item_use.rs @@ -1,8 +1,12 @@ +use std::vec; + use crate::{ fmt::{Format, FormattedCode, Formatter}, + utils::comments::{ByteSpan, LeafSpans}, FormatterError, }; -use sway_parse::ItemUse; +use sway_parse::{ItemUse, UseTree}; +use sway_types::Spanned; impl Format for ItemUse { fn format( @@ -13,3 +17,48 @@ impl Format for ItemUse { todo!() } } + +impl LeafSpans for ItemUse { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + if let Some(visibility) = &self.visibility { + collected_spans.push(ByteSpan::from(visibility.span())); + } + collected_spans.push(ByteSpan::from(self.use_token.span())); + if let Some(root_import) = &self.root_import { + collected_spans.push(ByteSpan::from(root_import.span())); + } + collected_spans.append(&mut self.tree.leaf_spans()); + collected_spans.push(ByteSpan::from(self.semicolon_token.span())); + collected_spans + } +} + +impl LeafSpans for UseTree { + fn leaf_spans(&self) -> Vec { + match self { + UseTree::Group { imports } => imports.leaf_spans(), + UseTree::Name { name } => vec![ByteSpan::from(name.span())], + UseTree::Rename { + name, + as_token, + alias, + } => vec![ + ByteSpan::from(name.span()), + ByteSpan::from(as_token.span()), + ByteSpan::from(alias.span()), + ], + UseTree::Glob { star_token } => vec![ByteSpan::from(star_token.span())], + UseTree::Path { + prefix, + double_colon_token, + suffix, + } => { + let mut collected_spans = vec![ByteSpan::from(prefix.span())]; + collected_spans.push(ByteSpan::from(double_colon_token.span())); + collected_spans.append(&mut suffix.leaf_spans()); + collected_spans + } + } + } +} diff --git a/sway-fmt-v2/src/utils/attribute.rs b/sway-fmt-v2/src/utils/attribute.rs index 7e10cfd1775..eb70567a008 100644 --- a/sway-fmt-v2/src/utils/attribute.rs +++ b/sway-fmt-v2/src/utils/attribute.rs @@ -4,13 +4,16 @@ use crate::{ }; use std::fmt::Write; use sway_parse::{ - attribute::{Annotated, AttributeDecl}, + attribute::{Annotated, Attribute, AttributeDecl}, token::Delimiter, Parse, }; use sway_types::Spanned; -use super::bracket::{Parenthesis, SquareBracket}; +use super::{ + bracket::{Parenthesis, SquareBracket}, + comments::{ByteSpan, LeafSpans}, +}; impl Format for Annotated { fn format( @@ -91,3 +94,19 @@ impl Parenthesis for AttributeDecl { Ok(()) } } +impl LeafSpans for AttributeDecl { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = vec![ByteSpan::from(self.hash_token.span())]; + collected_spans.append(&mut self.attribute.leaf_spans()); + collected_spans + } +} +impl LeafSpans for Attribute { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = vec![ByteSpan::from(self.name.span())]; + if let Some(args) = &self.args { + collected_spans.append(&mut args.leaf_spans()); + } + collected_spans + } +} diff --git a/sway-fmt-v2/src/utils/comments.rs b/sway-fmt-v2/src/utils/comments.rs index b867a787328..c5f9f765488 100644 --- a/sway-fmt-v2/src/utils/comments.rs +++ b/sway-fmt-v2/src/utils/comments.rs @@ -1,18 +1,51 @@ -use crate::fmt::FormatterError; -use std::{cmp::Ordering, collections::BTreeMap, sync::Arc}; -use sway_parse::token::{lex_commented, Comment, CommentedTokenTree, CommentedTree}; - +use crate::fmt::{FormattedCode, FormatterError}; +use ropey::Rope; +use std::{ + cmp::Ordering, + collections::BTreeMap, + fmt::Write, + ops::Bound::{Excluded, Included}, + path::PathBuf, + sync::Arc, +}; +use sway_parse::{ + attribute::Annotated, + brackets::{Parens, SquareBrackets}, + keywords::{ + AddToken, ColonToken, CommaToken, ForToken, ForwardSlashToken, RightArrowToken, + SemicolonToken, + }, + token::{lex_commented, Comment, CommentedTokenTree, CommentedTree}, + Braces, Module, Parse, TypeField, +}; +use sway_types::{Ident, Span, Spanned}; /// Represents a span for the comments in a spesific file /// A stripped down version of sway-types::src::Span #[derive(PartialEq, Eq, Debug, Clone, Default)] -pub struct CommentSpan { +pub struct ByteSpan { // The byte position in the string of the start of the span. pub start: usize, // The byte position in the string of the end of the span. pub end: usize, } -impl Ord for CommentSpan { +impl From for ByteSpan { + /// Takes `start` and `end` from `sway::types::Span` and constructs a `ByteSpan` + fn from(span: Span) -> Self { + ByteSpan { + start: span.start(), + end: span.end(), + } + } +} + +impl ByteSpan { + pub fn len(&self) -> usize { + self.end - self.start + } +} + +impl Ord for ByteSpan { fn cmp(&self, other: &Self) -> Ordering { // If the starting position is the same encapsulatig span (i.e, wider one) should come // first @@ -23,13 +56,13 @@ impl Ord for CommentSpan { } } -impl PartialOrd for CommentSpan { - fn partial_cmp(&self, other: &CommentSpan) -> Option { +impl PartialOrd for ByteSpan { + fn partial_cmp(&self, other: &ByteSpan) -> Option { Some(self.cmp(other)) } } -pub type CommentMap = BTreeMap; +pub type CommentMap = BTreeMap; /// Get the CommentedTokenStream and collect the spans -> Comment mapping for the input source /// code. @@ -54,7 +87,7 @@ fn collect_comments_from_token_stream( ) { match commented_token_tree { CommentedTokenTree::Comment(comment) => { - let comment_span = CommentSpan { + let comment_span = ByteSpan { start: comment.span.start(), end: comment.span.end(), }; @@ -69,16 +102,327 @@ fn collect_comments_from_token_stream( } } +impl LeafSpans for Braces +where + T: LeafSpans + Clone, +{ + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + let mut opening_brace_span = ByteSpan::from(self.span()); + opening_brace_span.end = opening_brace_span.start + 1; + // Add opening brace's ByteSpan + collected_spans.push(opening_brace_span); + // Add T's collected ByteSpan + collected_spans.append(&mut self.clone().into_inner().leaf_spans()); + let mut closing_brace_span = ByteSpan::from(self.span()); + closing_brace_span.start = closing_brace_span.end - 1; + // Add closing brace's ByteSpan + collected_spans.push(closing_brace_span); + collected_spans + } +} + +impl LeafSpans for Parens +where + T: LeafSpans + Clone, +{ + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + let mut opening_paren_span = ByteSpan::from(self.span()); + opening_paren_span.end = opening_paren_span.start + 1; + // Add opening paren's span + collected_spans.push(opening_paren_span); + // Add T's collected ByteSpan + collected_spans.append(&mut self.clone().into_inner().leaf_spans()); + let mut closing_paren_span = ByteSpan::from(self.span()); + closing_paren_span.start = closing_paren_span.end - 1; + // Add closing paren's ByteSpan + collected_spans.push(closing_paren_span); + collected_spans + } +} + +impl LeafSpans for SquareBrackets +where + T: LeafSpans + Clone, +{ + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + let mut opening_bracket_span = ByteSpan::from(self.span()); + opening_bracket_span.end = opening_bracket_span.start + 1; + // Add opening bracket's span + collected_spans.push(opening_bracket_span); + // Add T's collected ByteSpan + collected_spans.append(&mut self.clone().into_inner().leaf_spans()); + let mut closing_bracket_span = ByteSpan::from(self.span()); + closing_bracket_span.start = closing_bracket_span.end - 1; + // Add closing bracket's ByteSpan + collected_spans.push(closing_bracket_span); + collected_spans + } +} + +impl LeafSpans for (T, P) +where + T: LeafSpans, + P: LeafSpans, +{ + fn leaf_spans(&self) -> Vec { + let mut collected_spans = self.0.leaf_spans(); + collected_spans.append(&mut self.1.leaf_spans()); + collected_spans + } +} +impl LeafSpans for Vec +where + T: LeafSpans, +{ + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + for t in self { + collected_spans.append(&mut t.leaf_spans()); + } + collected_spans + } +} +impl LeafSpans for Annotated +where + T: LeafSpans + Parse, +{ + fn leaf_spans(&self) -> Vec { + let mut collected_spans = self.attribute_list.leaf_spans(); + collected_spans.append(&mut self.value.leaf_spans()); + collected_spans + } +} + +impl LeafSpans for Ident { + fn leaf_spans(&self) -> Vec { + vec![ByteSpan::from(self.span())] + } +} +impl LeafSpans for CommaToken { + fn leaf_spans(&self) -> Vec { + vec![(ByteSpan::from(self.span()))] + } +} + +impl LeafSpans for TypeField { + fn leaf_spans(&self) -> Vec { + 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 + } +} + +impl LeafSpans for AddToken { + fn leaf_spans(&self) -> Vec { + vec![ByteSpan::from(self.span())] + } +} + +impl LeafSpans for SemicolonToken { + fn leaf_spans(&self) -> Vec { + vec![ByteSpan::from(self.span())] + } +} + +impl LeafSpans for ColonToken { + fn leaf_spans(&self) -> Vec { + vec![ByteSpan::from(self.span())] + } +} + +impl LeafSpans for RightArrowToken { + fn leaf_spans(&self) -> Vec { + vec![ByteSpan::from(self.span())] + } +} + +impl LeafSpans for ForToken { + fn leaf_spans(&self) -> Vec { + vec![ByteSpan::from(self.span())] + } +} + +impl LeafSpans for ForwardSlashToken { + fn leaf_spans(&self) -> Vec { + vec![ByteSpan::from(self.span())] + } +} +/// Handles comments by first creating the CommentMap which is used for fast seaching comments. +/// Traverses items for finding a comment in unformatted input and placing it in correct place in formatted output. +pub fn handle_comments( + unformatted_input: Arc, + unformatted_module: &Module, + formatted_input: Arc, + path: Option>, + formatted_code: &mut FormattedCode, +) -> Result<(), FormatterError> { + // Collect Span -> Comment mapping from unformatted input + let comment_map = comment_map_from_src(unformatted_input.clone())?; + // After the formatting existing items should be the same (type of the item) but their spans will be changed since we applied formatting to them. + let formatted_module = sway_parse::parse_file(formatted_input, path)?; + // Actually find & insert the comments + add_comments( + comment_map, + unformatted_module, + &formatted_module, + formatted_code, + unformatted_input, + )?; + Ok(()) +} + +/// Adds the comments from comment_map to correct places in the formatted code. This requires us +/// both the unformatted and formatted code's modules as they will have different spans for their +/// visitable positions. While traversing the unformatted module, `add_comments` searches for comments. If there is a comment found +/// places the comment to the correct place at formatted_code. +/// +/// This requires both the unformatted_code itself and the parsed version of it, because +/// unformatted_code is used for context lookups and unformatted_module is required for actual +/// traversal. When `add_comments` is called we have already parsed the unformatted_code so there is no need +/// to parse it again. +fn add_comments( + comment_map: CommentMap, + unformatted_module: &Module, + formatted_module: &Module, + formatted_code: &mut FormattedCode, + unformatted_code: Arc, +) -> Result<(), FormatterError> { + let mut unformatted_comment_spans = unformatted_module.leaf_spans(); + let mut formatted_comment_spans = formatted_module.leaf_spans(); + // Adding end of file to both spans so that the last comment(s) after an item would also be + // found & included + unformatted_comment_spans.push(ByteSpan { + start: unformatted_code.len(), + end: unformatted_code.len(), + }); + formatted_comment_spans.push(ByteSpan { + start: formatted_code.len(), + end: formatted_code.len(), + }); + + // Since we are adding comments into formatted code, in the next iteration the spans we find for the formatted code needs to be offsetted + // as the total length of comments we added in previous iterations. + let mut offset = 0; + + // We will definetly have a span in the collected span since for a source code to be parsed there should be some tokens present. + let mut previous_unformatted_comment_span = unformatted_comment_spans + .first() + .ok_or(FormatterError::CommentError)?; + let mut previous_formatted_comment_span = formatted_comment_spans + .first() + .ok_or(FormatterError::CommentError)?; + for (unformatted_comment_span, formatted_comment_span) in unformatted_comment_spans + .iter() + .skip(1) + .zip(formatted_comment_spans.iter().skip(1)) + { + let comments_found = get_comments_between_spans( + previous_unformatted_comment_span, + unformatted_comment_span, + &comment_map, + &unformatted_code, + ); + if !comments_found.is_empty() { + offset += insert_after_span( + previous_formatted_comment_span, + comments_found, + offset, + formatted_code, + )?; + } + previous_unformatted_comment_span = unformatted_comment_span; + previous_formatted_comment_span = formatted_comment_span; + } + Ok(()) +} + +// A CommentWithContext is the Comment and the offset before it. The offset can be between the (from) item we searched for this comment or from the last comment inside range +type CommentWithContext = (Comment, String); + +/// Returns a list of comments between given spans. For each comment returns the Context +/// Context of a comment is basically the offset (the characters between the last item/comment) to the current comment +fn get_comments_between_spans( + from: &ByteSpan, + to: &ByteSpan, + comment_map: &CommentMap, + unformatted_code: &Arc, +) -> Vec { + let mut comments_with_context: Vec = Vec::new(); + for (index, (comment_span, comment)) in comment_map + .range((Included(from), Excluded(to))) + .enumerate() + { + let starting_position_for_context = if index == 0 { + // This is the first comment in the current range the context should be collected between from's end and comment's beginning + from.end + } else { + // There is a comment before this one, so we should get the context starting from the last comment's end to the beginning of the current comment + comments_with_context[index - 1].0.span.end() + }; + comments_with_context.push(( + comment.clone(), + unformatted_code[starting_position_for_context..comment_span.start].to_string(), + )); + } + comments_with_context +} + +/// Inserts after given span and returns the offset. While inserting comments this also inserts Context of the comments so that the alignment whitespaces/newlines are intact +fn insert_after_span( + from: &ByteSpan, + comments_to_insert: Vec, + offset: usize, + formatted_code: &mut FormattedCode, +) -> Result { + let iter = comments_to_insert.iter(); + let mut offset = offset; + let mut comment_str = String::new(); + for comment_with_context in iter { + let (comment_value, comment_context) = comment_with_context; + write!( + comment_str, + "{}{}", + comment_context, + &format_comment(comment_value) + )?; + } + let mut src_rope = Rope::from_str(formatted_code); + // If the position we are going to be inserting from + 1 is a \n we are moving that \n after + // this comment so if that is the case we are inserting after the \n + if formatted_code.chars().nth(from.end + offset + 1) == Some('\n') { + offset += 1; + } + src_rope.insert(from.end + offset, &comment_str); + formatted_code.clear(); + formatted_code.push_str(&src_rope.to_string()); + Ok(comment_str.len()) +} + +/// Applies formatting to the comment. +/// Currently does not apply any formatting and directly returns the raw comment str +fn format_comment(comment: &Comment) -> String { + comment.span().str() +} +/// While searching for a comment we need the possible places a comment can be placed in a structure +/// `leaf_spans` collects all field's spans so that we can check in between them. +pub trait LeafSpans { + fn leaf_spans(&self) -> Vec; +} + #[cfg(test)] mod tests { - use super::{comment_map_from_src, CommentSpan}; + use super::{comment_map_from_src, ByteSpan}; use std::{ops::Bound::Included, sync::Arc}; #[test] fn test_comment_span_ordering() { - let first_span = CommentSpan { start: 2, end: 6 }; - let second_span = CommentSpan { start: 2, end: 4 }; - let third_span = CommentSpan { start: 4, end: 7 }; + let first_span = ByteSpan { start: 2, end: 6 }; + let second_span = ByteSpan { start: 2, end: 4 }; + let third_span = ByteSpan { start: 4, end: 7 }; let mut vec = vec![second_span.clone(), third_span.clone(), first_span.clone()]; vec.sort(); @@ -102,8 +446,8 @@ mod tests { "#; let map = comment_map_from_src(Arc::from(input)).unwrap(); assert!(!map.is_empty()); - let range_start_span = CommentSpan { start: 0, end: 32 }; - let range_end_span = CommentSpan { start: 33, end: 34 }; + let range_start_span = ByteSpan { start: 0, end: 32 }; + let range_end_span = ByteSpan { start: 33, end: 34 }; let found_comment = map .range((Included(range_start_span), Included(range_end_span))) .last() @@ -124,8 +468,8 @@ mod tests { "#; let map = comment_map_from_src(Arc::from(input)).unwrap(); assert!(!map.is_empty()); - let range_start_span = CommentSpan { start: 40, end: 54 }; - let range_end_span = CommentSpan { + let range_start_span = ByteSpan { start: 40, end: 54 }; + let range_end_span = ByteSpan { start: 100, end: 115, }; @@ -149,11 +493,11 @@ mod tests { "#; let map = comment_map_from_src(Arc::from(input)).unwrap(); assert!(!map.is_empty()); - let range_start_span = CommentSpan { + let range_start_span = ByteSpan { start: 110, end: 116, }; - let range_end_span = CommentSpan { + let range_end_span = ByteSpan { start: 200, end: 201, }; diff --git a/sway-fmt-v2/src/utils/expr.rs b/sway-fmt-v2/src/utils/expr.rs index aa9a85aed72..7220c91cb6f 100644 --- a/sway-fmt-v2/src/utils/expr.rs +++ b/sway-fmt-v2/src/utils/expr.rs @@ -1,8 +1,15 @@ -use crate::{config::items::ItemBraceStyle, fmt::*}; -use std::fmt::Write; +use crate::{ + config::items::ItemBraceStyle, + fmt::*, + utils::comments::{ByteSpan, LeafSpans}, +}; +use std::{fmt::Write, vec}; use sway_parse::{ + expr::asm::{AsmBlockContents, AsmFinalExpr}, token::{Delimiter, PunctKind}, - Expr, ExprStructField, + AbiCastArgs, AsmBlock, AsmRegisterDeclaration, Assignable, Expr, ExprArrayDescriptor, + ExprStructField, ExprTupleDescriptor, IfCondition, IfExpr, Instruction, MatchBranch, + MatchBranchKind, }; use sway_types::Spanned; @@ -250,3 +257,535 @@ impl CurlyBrace for ExprStructField { Ok(()) } } + +// TODO: Find a better way of handling Boxed version +impl LeafSpans for Box { + fn leaf_spans(&self) -> Vec { + visit_expr(self) + } +} + +impl LeafSpans for Expr { + fn leaf_spans(&self) -> Vec { + visit_expr(self) + } +} + +/// Collects various expr field's ByteSpans. +fn visit_expr(expr: &Expr) -> Vec { + match expr { + Expr::Path(path) => path.leaf_spans(), + Expr::Literal(literal) => literal.leaf_spans(), + Expr::AbiCast { abi_token, args } => { + let mut collected_spans = vec![ByteSpan::from(abi_token.span())]; + collected_spans.append(&mut args.leaf_spans()); + collected_spans + } + Expr::Struct { path, fields } => { + let mut collected_spans = path.leaf_spans(); + collected_spans.append(&mut fields.leaf_spans()); + collected_spans + } + Expr::Tuple(tuple) => tuple.leaf_spans(), + Expr::Parens(parens) => parens.leaf_spans(), + Expr::Block(block) => block.leaf_spans(), + Expr::Array(array) => array.leaf_spans(), + Expr::Asm(asm) => asm.leaf_spans(), + Expr::Return { + return_token, + expr_opt, + } => { + let mut collected_spans = vec![ByteSpan::from(return_token.span())]; + if let Some(expr) = expr_opt { + collected_spans.append(&mut expr.leaf_spans()); + } + collected_spans + } + Expr::If(expr_if) => expr_if.leaf_spans(), + Expr::Match { + match_token, + value, + branches, + } => { + let mut collected_spans = vec![ByteSpan::from(match_token.span())]; + collected_spans.append(&mut value.leaf_spans()); + collected_spans.append(&mut branches.leaf_spans()); + collected_spans + } + Expr::While { + while_token, + condition, + block, + } => { + let mut collected_spans = vec![ByteSpan::from(while_token.span())]; + collected_spans.append(&mut condition.leaf_spans()); + collected_spans.append(&mut block.leaf_spans()); + collected_spans + } + Expr::FuncApp { func, args } => { + let mut collected_spans = Vec::new(); + collected_spans.append(&mut func.leaf_spans()); + collected_spans.append(&mut args.leaf_spans()); + collected_spans + } + Expr::Index { target, arg } => { + let mut collected_spans = Vec::new(); + collected_spans.append(&mut target.leaf_spans()); + collected_spans.append(&mut arg.leaf_spans()); + collected_spans + } + Expr::MethodCall { + target, + dot_token, + name, + contract_args_opt, + args, + } => { + let mut collected_spans = Vec::new(); + collected_spans.append(&mut target.leaf_spans()); + collected_spans.push(ByteSpan::from(dot_token.span())); + collected_spans.push(ByteSpan::from(name.span())); + if let Some(contract_args) = contract_args_opt { + collected_spans.append(&mut contract_args.leaf_spans()); + } + collected_spans.append(&mut args.leaf_spans()); + collected_spans + } + Expr::FieldProjection { + target, + dot_token, + name, + } => { + let mut collected_spans = Vec::new(); + collected_spans.append(&mut target.leaf_spans()); + collected_spans.push(ByteSpan::from(dot_token.span())); + collected_spans.push(ByteSpan::from(name.span())); + collected_spans + } + Expr::TupleFieldProjection { + target, + dot_token, + field: _field, + field_span, + } => { + let mut collected_spans = Vec::new(); + collected_spans.append(&mut target.leaf_spans()); + collected_spans.push(ByteSpan::from(dot_token.span())); + collected_spans.push(ByteSpan::from(field_span.clone())); + collected_spans + } + Expr::Ref { ref_token, expr } => { + let mut collected_spans = vec![ByteSpan::from(ref_token.span())]; + collected_spans.append(&mut expr.leaf_spans()); + collected_spans + } + Expr::Deref { deref_token, expr } => { + let mut collected_spans = vec![ByteSpan::from(deref_token.span())]; + collected_spans.append(&mut expr.leaf_spans()); + collected_spans + } + Expr::Not { bang_token, expr } => { + let mut collected_spans = vec![ByteSpan::from(bang_token.span())]; + collected_spans.append(&mut expr.leaf_spans()); + collected_spans + } + Expr::Mul { + lhs, + star_token, + rhs, + } => { + let mut collected_spans = lhs.leaf_spans(); + collected_spans.push(ByteSpan::from(star_token.span())); + collected_spans.append(&mut rhs.leaf_spans()); + collected_spans + } + Expr::Div { + lhs, + forward_slash_token, + rhs, + } => { + let mut collected_spans = lhs.leaf_spans(); + collected_spans.push(ByteSpan::from(forward_slash_token.span())); + collected_spans.append(&mut rhs.leaf_spans()); + collected_spans + } + Expr::Modulo { + lhs, + percent_token, + rhs, + } => { + let mut collected_spans = lhs.leaf_spans(); + collected_spans.push(ByteSpan::from(percent_token.span())); + collected_spans.append(&mut rhs.leaf_spans()); + collected_spans + } + Expr::Add { + lhs, + add_token, + rhs, + } => { + let mut collected_spans = lhs.leaf_spans(); + collected_spans.push(ByteSpan::from(add_token.span())); + collected_spans.append(&mut rhs.leaf_spans()); + collected_spans + } + Expr::Sub { + lhs, + sub_token, + rhs, + } => { + let mut collected_spans = lhs.leaf_spans(); + collected_spans.push(ByteSpan::from(sub_token.span())); + collected_spans.append(&mut rhs.leaf_spans()); + collected_spans + } + Expr::Shl { + lhs, + shl_token, + rhs, + } => { + let mut collected_spans = lhs.leaf_spans(); + collected_spans.push(ByteSpan::from(shl_token.span())); + collected_spans.append(&mut rhs.leaf_spans()); + collected_spans + } + Expr::Shr { + lhs, + shr_token, + rhs, + } => { + let mut collected_spans = lhs.leaf_spans(); + collected_spans.push(ByteSpan::from(shr_token.span())); + collected_spans.append(&mut rhs.leaf_spans()); + collected_spans + } + Expr::BitAnd { + lhs, + ampersand_token, + rhs, + } => { + let mut collected_spans = lhs.leaf_spans(); + collected_spans.push(ByteSpan::from(ampersand_token.span())); + collected_spans.append(&mut rhs.leaf_spans()); + collected_spans + } + Expr::BitXor { + lhs, + caret_token, + rhs, + } => { + let mut collected_spans = lhs.leaf_spans(); + collected_spans.push(ByteSpan::from(caret_token.span())); + collected_spans.append(&mut rhs.leaf_spans()); + collected_spans + } + Expr::BitOr { + lhs, + pipe_token, + rhs, + } => { + let mut collected_spans = lhs.leaf_spans(); + collected_spans.push(ByteSpan::from(pipe_token.span())); + collected_spans.append(&mut rhs.leaf_spans()); + collected_spans + } + Expr::Equal { + lhs, + double_eq_token, + rhs, + } => { + let mut collected_spans = lhs.leaf_spans(); + collected_spans.push(ByteSpan::from(double_eq_token.span())); + collected_spans.append(&mut rhs.leaf_spans()); + collected_spans + } + Expr::NotEqual { + lhs, + bang_eq_token, + rhs, + } => { + let mut collected_spans = lhs.leaf_spans(); + collected_spans.push(ByteSpan::from(bang_eq_token.span())); + collected_spans.append(&mut rhs.leaf_spans()); + collected_spans + } + Expr::LessThan { + lhs, + less_than_token, + rhs, + } => { + let mut collected_spans = lhs.leaf_spans(); + collected_spans.push(ByteSpan::from(less_than_token.span())); + collected_spans.append(&mut rhs.leaf_spans()); + collected_spans + } + Expr::GreaterThan { + lhs, + greater_than_token, + rhs, + } => { + let mut collected_spans = lhs.leaf_spans(); + collected_spans.push(ByteSpan::from(greater_than_token.span())); + collected_spans.append(&mut rhs.leaf_spans()); + collected_spans + } + Expr::LessThanEq { + lhs, + less_than_eq_token, + rhs, + } => { + let mut collected_spans = lhs.leaf_spans(); + collected_spans.push(ByteSpan::from(less_than_eq_token.span())); + collected_spans.append(&mut rhs.leaf_spans()); + collected_spans + } + Expr::GreaterThanEq { + lhs, + greater_than_eq_token, + rhs, + } => { + let mut collected_spans = lhs.leaf_spans(); + collected_spans.push(ByteSpan::from(greater_than_eq_token.span())); + collected_spans.append(&mut rhs.leaf_spans()); + collected_spans + } + Expr::LogicalAnd { + lhs, + double_ampersand_token, + rhs, + } => { + let mut collected_spans = lhs.leaf_spans(); + collected_spans.push(ByteSpan::from(double_ampersand_token.span())); + collected_spans.append(&mut rhs.leaf_spans()); + collected_spans + } + Expr::LogicalOr { + lhs, + double_pipe_token, + rhs, + } => { + let mut collected_spans = lhs.leaf_spans(); + collected_spans.push(ByteSpan::from(double_pipe_token.span())); + collected_spans.append(&mut rhs.leaf_spans()); + collected_spans + } + Expr::Reassignment { + assignable, + reassignment_op, + expr, + } => { + let mut collected_spans = assignable.leaf_spans(); + collected_spans.push(ByteSpan::from(reassignment_op.span.clone())); + collected_spans.append(&mut expr.leaf_spans()); + collected_spans + } + } +} + +impl LeafSpans for AbiCastArgs { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = vec![ByteSpan::from(self.name.span())]; + collected_spans.push(ByteSpan::from(self.comma_token.span())); + collected_spans.append(&mut self.address.leaf_spans()); + collected_spans + } +} + +impl LeafSpans for ExprStructField { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = vec![ByteSpan::from(self.field_name.span())]; + if let Some(expr) = &self.expr_opt { + collected_spans.push(ByteSpan::from(expr.0.span())); + // TODO: determine if we are allowing comments between `:` and expr + collected_spans.append(&mut expr.1.leaf_spans()); + } + collected_spans + } +} + +impl LeafSpans for ExprTupleDescriptor { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + if let ExprTupleDescriptor::Cons { + head, + comma_token, + tail, + } = self + { + collected_spans.append(&mut head.leaf_spans()); + collected_spans.push(ByteSpan::from(comma_token.span())); + collected_spans.append(&mut tail.leaf_spans()); + } + collected_spans + } +} + +impl LeafSpans for ExprArrayDescriptor { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + if let ExprArrayDescriptor::Repeat { + value, + semicolon_token, + length, + } = self + { + collected_spans.append(&mut value.leaf_spans()); + collected_spans.push(ByteSpan::from(semicolon_token.span())); + collected_spans.append(&mut length.leaf_spans()); + } + collected_spans + } +} + +impl LeafSpans for AsmBlock { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = vec![ByteSpan::from(self.asm_token.span())]; + collected_spans.append(&mut self.registers.leaf_spans()); + collected_spans.append(&mut self.contents.leaf_spans()); + collected_spans + } +} + +impl LeafSpans for AsmRegisterDeclaration { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = vec![ByteSpan::from(self.register.span())]; + if let Some(value) = &self.value_opt { + collected_spans.append(&mut value.leaf_spans()); + // TODO: determine if we are allowing comments between `:` and expr + } + collected_spans + } +} + +impl LeafSpans for AsmBlockContents { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + for instruction in &self.instructions { + collected_spans.append(&mut instruction.leaf_spans()); + // TODO: probably we shouldn't allow for comments in between the instruction and comma since it may/will result in build failure after formatting + } + collected_spans + } +} + +impl LeafSpans for Instruction { + fn leaf_spans(&self) -> Vec { + // Visit instructions as a whole unit, meaning we cannot insert comments inside an instruction. + vec![ByteSpan::from(self.span())] + } +} + +impl LeafSpans for AsmFinalExpr { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = vec![ByteSpan::from(self.register.span())]; + if let Some(ty) = &self.ty_opt { + collected_spans.append(&mut ty.leaf_spans()); + // TODO: determine if we are allowing comments between `:` and ty + } + collected_spans + } +} + +impl LeafSpans for IfExpr { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = vec![ByteSpan::from(self.if_token.span())]; + collected_spans.append(&mut self.condition.leaf_spans()); + collected_spans.append(&mut self.then_block.leaf_spans()); + if let Some(else_block) = &self.else_opt { + collected_spans.push(ByteSpan::from(else_block.0.span())); + let mut else_body_spans = match &else_block.1 { + std::ops::ControlFlow::Continue(if_expr) => if_expr.leaf_spans(), + std::ops::ControlFlow::Break(else_body) => else_body.leaf_spans(), + }; + collected_spans.append(&mut else_body_spans); + } + collected_spans + } +} + +impl LeafSpans for IfCondition { + fn leaf_spans(&self) -> Vec { + match self { + IfCondition::Expr(expr) => expr.leaf_spans(), + IfCondition::Let { + let_token, + lhs, + eq_token, + rhs, + } => { + let mut collected_spans = vec![ByteSpan::from(let_token.span())]; + collected_spans.append(&mut lhs.leaf_spans()); + collected_spans.push(ByteSpan::from(eq_token.span())); + collected_spans.append(&mut rhs.leaf_spans()); + collected_spans + } + } + } +} + +impl LeafSpans for MatchBranch { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + collected_spans.append(&mut self.pattern.leaf_spans()); + collected_spans.push(ByteSpan::from(self.fat_right_arrow_token.span())); + collected_spans.append(&mut self.kind.leaf_spans()); + collected_spans + } +} + +impl LeafSpans for MatchBranchKind { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + match self { + MatchBranchKind::Block { + block, + comma_token_opt, + } => { + collected_spans.append(&mut block.leaf_spans()); + // TODO: determine if we allow comments between block and comma_token + if let Some(comma_token) = comma_token_opt { + collected_spans.push(ByteSpan::from(comma_token.span())); + } + } + MatchBranchKind::Expr { expr, comma_token } => { + collected_spans.append(&mut expr.leaf_spans()); + // TODO: determine if we allow comments between expr and comma_token + collected_spans.push(ByteSpan::from(comma_token.span())); + } + }; + collected_spans + } +} + +impl LeafSpans for Assignable { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + match self { + Assignable::Var(var) => collected_spans.push(ByteSpan::from(var.span())), + Assignable::Index { target, arg } => { + collected_spans.append(&mut target.leaf_spans()); + collected_spans.append(&mut arg.leaf_spans()); + } + Assignable::FieldProjection { + target, + dot_token, + name, + } => { + collected_spans.append(&mut target.leaf_spans()); + collected_spans.push(ByteSpan::from(dot_token.span())); + collected_spans.push(ByteSpan::from(name.span())); + } + Assignable::TupleFieldProjection { + target, + dot_token, + field: _field, + field_span, + } => { + collected_spans.append(&mut target.leaf_spans()); + collected_spans.push(ByteSpan::from(dot_token.span())); + collected_spans.push(ByteSpan::from(field_span.clone())); + } + }; + collected_spans + } +} diff --git a/sway-fmt-v2/src/utils/item.rs b/sway-fmt-v2/src/utils/item.rs index fbcc270e141..d7a66673a69 100644 --- a/sway-fmt-v2/src/utils/item.rs +++ b/sway-fmt-v2/src/utils/item.rs @@ -1,6 +1,27 @@ -use crate::fmt::{Format, FormattedCode, Formatter, FormatterError}; +use crate::{ + fmt::{Format, FormattedCode, Formatter, FormatterError}, + utils::comments::{ByteSpan, LeafSpans}, +}; use sway_parse::{Item, ItemKind::*}; +impl LeafSpans for Item { + fn leaf_spans(&self) -> Vec { + match &self.value { + Struct(item_struct) => item_struct.leaf_spans(), + Enum(item_enum) => item_enum.leaf_spans(), + Fn(item_fn) => item_fn.leaf_spans(), + Abi(item_abi) => item_abi.leaf_spans(), + Const(item_const) => item_const.leaf_spans(), + Storage(item_storage) => item_storage.leaf_spans(), + Trait(item_trait) => item_trait.leaf_spans(), + Impl(item_impl) => item_impl.leaf_spans(), + Use(item_use) => item_use.leaf_spans(), + Break(_) => todo!(), + Continue(_) => todo!(), + } + } +} + impl Format for Item { fn format( &self, diff --git a/sway-fmt-v2/src/utils/literal.rs b/sway-fmt-v2/src/utils/literal.rs index 732e334063c..2101da95163 100644 --- a/sway-fmt-v2/src/utils/literal.rs +++ b/sway-fmt-v2/src/utils/literal.rs @@ -1,4 +1,7 @@ -use crate::fmt::*; +use crate::{ + fmt::*, + utils::comments::{ByteSpan, LeafSpans}, +}; use std::fmt::Write; use sway_parse::Literal; @@ -18,3 +21,14 @@ impl Format for Literal { Ok(()) } } + +impl LeafSpans for Literal { + fn leaf_spans(&self) -> Vec { + match self { + Literal::String(str_lit) => vec![ByteSpan::from(str_lit.span.clone())], + Literal::Char(chr_lit) => vec![ByteSpan::from(chr_lit.span.clone())], + Literal::Int(int_lit) => vec![ByteSpan::from(int_lit.span.clone())], + Literal::Bool(bool_lit) => vec![ByteSpan::from(bool_lit.span.clone())], + } + } +} diff --git a/sway-fmt-v2/src/utils/path.rs b/sway-fmt-v2/src/utils/path.rs index f71205ee10a..5d4ff00a3bf 100644 --- a/sway-fmt-v2/src/utils/path.rs +++ b/sway-fmt-v2/src/utils/path.rs @@ -1,5 +1,8 @@ -use crate::fmt::*; -use std::fmt::Write; +use crate::{ + fmt::*, + utils::comments::{ByteSpan, LeafSpans}, +}; +use std::{fmt::Write, vec}; use sway_parse::{PathExpr, PathExprSegment, PathType, PathTypeSegment, QualifiedPathRoot}; use sway_types::Spanned; @@ -132,3 +135,15 @@ impl Format for PathTypeSegment { Ok(()) } } + +impl LeafSpans for PathExpr { + fn leaf_spans(&self) -> Vec { + vec![ByteSpan::from(self.span())] + } +} + +impl LeafSpans for PathType { + fn leaf_spans(&self) -> Vec { + vec![ByteSpan::from(self.span())] + } +} diff --git a/sway-fmt-v2/src/utils/pattern.rs b/sway-fmt-v2/src/utils/pattern.rs index 8f5ed66ae49..8cc7bf2e5ed 100644 --- a/sway-fmt-v2/src/utils/pattern.rs +++ b/sway-fmt-v2/src/utils/pattern.rs @@ -1,4 +1,7 @@ -use crate::fmt::*; +use crate::{ + fmt::*, + utils::comments::{ByteSpan, LeafSpans}, +}; use std::fmt::Write; use sway_parse::{token::Delimiter, Pattern, PatternStructField}; use sway_types::Spanned; @@ -114,3 +117,60 @@ impl Format for PatternStructField { Ok(()) } } + +impl LeafSpans for Pattern { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + match self { + Pattern::Wildcard { underscore_token } => { + collected_spans.push(ByteSpan::from(underscore_token.span())); + } + Pattern::Var { mutable, name } => { + if let Some(mutable) = mutable { + collected_spans.push(ByteSpan::from(mutable.span())); + } + collected_spans.push(ByteSpan::from(name.span())); + } + Pattern::Literal(literal) => { + collected_spans.append(&mut literal.leaf_spans()); + } + Pattern::Constant(constant) => { + collected_spans.append(&mut constant.leaf_spans()); + } + Pattern::Constructor { path, args } => { + collected_spans.append(&mut path.leaf_spans()); + collected_spans.append(&mut args.leaf_spans()); + } + Pattern::Struct { path, fields } => { + collected_spans.append(&mut path.leaf_spans()); + collected_spans.append(&mut fields.leaf_spans()); + } + Pattern::Tuple(tuple) => { + collected_spans.append(&mut tuple.leaf_spans()); + } + } + collected_spans + } +} + +impl LeafSpans for PatternStructField { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + match self { + PatternStructField::Rest { token } => { + collected_spans.push(ByteSpan::from(token.span())); + } + PatternStructField::Field { + field_name, + pattern_opt, + } => { + collected_spans.push(ByteSpan::from(field_name.span())); + if let Some(pattern) = pattern_opt { + collected_spans.push(ByteSpan::from(pattern.0.span())); + collected_spans.append(&mut pattern.1.leaf_spans()); + } + } + } + collected_spans + } +} diff --git a/sway-fmt-v2/src/utils/program_type.rs b/sway-fmt-v2/src/utils/program_type.rs index e7eab15083c..72fdb6c976c 100644 --- a/sway-fmt-v2/src/utils/program_type.rs +++ b/sway-fmt-v2/src/utils/program_type.rs @@ -1,9 +1,11 @@ use std::fmt::Write; -use sway_parse::{token::PunctKind, ModuleKind}; +use sway_parse::{dependency::DependencyPath, token::PunctKind, Dependency, Module, ModuleKind}; use sway_types::Spanned; use crate::FormatterError; +use super::comments::{ByteSpan, LeafSpans}; + /// Insert the program type without applying a formatting to it. /// /// Possible list of program types: @@ -13,7 +15,7 @@ use crate::FormatterError; /// - Library pub(crate) fn insert_program_type( formatted_code: &mut String, - module_kind: ModuleKind, + module_kind: &ModuleKind, ) -> Result<(), FormatterError> { match module_kind { ModuleKind::Script { script_token } => { @@ -40,3 +42,55 @@ pub(crate) fn insert_program_type( Ok(()) } + +impl LeafSpans for ModuleKind { + fn leaf_spans(&self) -> Vec { + match self { + ModuleKind::Script { script_token } => { + vec![ByteSpan::from(script_token.span())] + } + ModuleKind::Contract { contract_token } => { + vec![ByteSpan::from(contract_token.span())] + } + ModuleKind::Predicate { predicate_token } => { + vec![ByteSpan::from(predicate_token.span())] + } + ModuleKind::Library { + library_token, + name, + } => { + vec![ + ByteSpan::from(library_token.span()), + ByteSpan::from(name.span()), + ] + } + } + } +} + +impl LeafSpans for Module { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = self.kind.leaf_spans(); + collected_spans.push(ByteSpan::from(self.semicolon_token.span())); + collected_spans.append(&mut self.dependencies.leaf_spans()); + collected_spans.append(&mut self.items.leaf_spans()); + collected_spans + } +} + +impl LeafSpans for Dependency { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = vec![ByteSpan::from(self.dep_token.span())]; + collected_spans.append(&mut self.path.leaf_spans()); + collected_spans.push(ByteSpan::from(self.semicolon_token.span())); + collected_spans + } +} + +impl LeafSpans for DependencyPath { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = self.prefix.leaf_spans(); + collected_spans.append(&mut self.suffixes.leaf_spans()); + collected_spans + } +} diff --git a/sway-fmt-v2/src/utils/punctuated.rs b/sway-fmt-v2/src/utils/punctuated.rs index ddf14660488..503a6cb65e0 100644 --- a/sway-fmt-v2/src/utils/punctuated.rs +++ b/sway-fmt-v2/src/utils/punctuated.rs @@ -1,11 +1,46 @@ use crate::{ fmt::{Format, FormattedCode, Formatter}, + utils::comments::{ByteSpan, LeafSpans}, FormatterError, }; use std::fmt::Write; use sway_parse::{keywords::CommaToken, punctuated::Punctuated, StorageField, TypeField}; use sway_types::{Ident, Spanned}; +impl LeafSpans for Punctuated +where + T: LeafSpans + Clone, + P: LeafSpans + Clone, +{ + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + let value_pairs = &self.value_separator_pairs; + for pair in value_pairs.iter() { + let p_comment_spans = pair.1.leaf_spans(); + // Since we do not want to have comments between T and P we are extending the ByteSpans coming from T with spans coming from P + // Since formatter can insert a trailing comma after a field, comments next to a field can be falsely inserted between the comma and the field + // So we shouldn't allow inserting comments (or searching for one) between T and P as in Punctuated scenerio this can/will result in formattings that breaks the build process + let mut comment_spans = pair + .0 + .leaf_spans() + .iter_mut() + .map(|comment_map| { + // Since the length of P' ByteSpan is same for each pair we are using the first one's length for all of the pairs. + // This assumtion always holds because for each pair P is formatted to same str so the length is going to be the same. + // For example when P is CommaToken, the length of P is always 1. + comment_map.end += p_comment_spans[0].len(); + comment_map.clone() + }) + .collect(); + collected_spans.append(&mut comment_spans) + } + if let Some(final_value) = &self.final_value_opt { + collected_spans.append(&mut final_value.leaf_spans()); + } + collected_spans + } +} + impl Format for Punctuated where T: Format, diff --git a/sway-fmt-v2/src/utils/statement.rs b/sway-fmt-v2/src/utils/statement.rs index dc1da828f84..32411492d42 100644 --- a/sway-fmt-v2/src/utils/statement.rs +++ b/sway-fmt-v2/src/utils/statement.rs @@ -1,4 +1,7 @@ -use crate::fmt::*; +use crate::{ + fmt::*, + utils::comments::{ByteSpan, LeafSpans}, +}; use std::fmt::Write; use sway_parse::{Statement, StatementLet}; use sway_types::Spanned; @@ -56,3 +59,43 @@ impl Format for StatementLet { Ok(()) } } + +impl LeafSpans for Statement { + fn leaf_spans(&self) -> Vec { + match self { + Statement::Let(statement_let) => statement_let.leaf_spans(), + Statement::Item(item) => item.leaf_spans(), + Statement::Expr { + expr, + semicolon_token_opt, + } => { + let mut collected_spans = expr.leaf_spans(); + if let Some(semicolon_token) = semicolon_token_opt { + collected_spans.push(ByteSpan::from(semicolon_token.span())); + } + collected_spans + } + } + } +} + +impl LeafSpans for StatementLet { + fn leaf_spans(&self) -> Vec { + // Add let token's ByteSpan + let mut collected_spans = vec![ByteSpan::from(self.let_token.span())]; + // Add pattern's ByteSpan + collected_spans.append(&mut self.pattern.leaf_spans()); + // Add ty's ByteSpan if it exists + if let Some(ty) = &self.ty_opt { + collected_spans.push(ByteSpan::from(ty.0.span())); + // TODO: determine if we are allowing comments between `:` and ty + collected_spans.append(&mut ty.1.leaf_spans()); + } + // Add eq token's ByteSpan + collected_spans.push(ByteSpan::from(self.eq_token.span())); + // Add Expr's ByteSpan + collected_spans.append(&mut self.expr.leaf_spans()); + collected_spans.push(ByteSpan::from(self.semicolon_token.span())); + collected_spans + } +} diff --git a/sway-fmt-v2/src/utils/ty.rs b/sway-fmt-v2/src/utils/ty.rs index db370c793b3..8c0959960d5 100644 --- a/sway-fmt-v2/src/utils/ty.rs +++ b/sway-fmt-v2/src/utils/ty.rs @@ -1,4 +1,7 @@ -use crate::fmt::{Format, FormattedCode, Formatter, FormatterError}; +use crate::{ + fmt::{Format, FormattedCode, Formatter, FormatterError}, + utils::comments::{ByteSpan, LeafSpans}, +}; use std::fmt::Write; use sway_parse::{ brackets::SquareBrackets, @@ -58,6 +61,7 @@ impl Format for TyArrayDescriptor { formatter: &mut Formatter, ) -> Result<(), FormatterError> { self.ty.format(formatted_code, formatter)?; + // TODO: once expr formatting is completly implemented switch this to use the actual formatting rather than the raw str coming from span write!( formatted_code, "{} {}", @@ -103,3 +107,46 @@ impl Format for TyTupleDescriptor { Ok(()) } } + +impl LeafSpans for Ty { + fn leaf_spans(&self) -> Vec { + match self { + Ty::Path(path) => path.leaf_spans(), + Ty::Tuple(tuple) => tuple.leaf_spans(), + Ty::Array(array) => array.leaf_spans(), + Ty::Str { str_token, length } => { + let mut collected_spans = vec![ByteSpan::from(str_token.span())]; + collected_spans.append(&mut length.leaf_spans()); + collected_spans + } + Ty::Infer { underscore_token } => vec![ByteSpan::from(underscore_token.span())], + } + } +} + +impl LeafSpans for TyTupleDescriptor { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + if let TyTupleDescriptor::Cons { + head, + comma_token, + tail, + } = self + { + collected_spans.append(&mut head.leaf_spans()); + collected_spans.push(ByteSpan::from(comma_token.span())); + collected_spans.append(&mut tail.leaf_spans()); + } + collected_spans + } +} + +impl LeafSpans for TyArrayDescriptor { + fn leaf_spans(&self) -> Vec { + let mut collected_spans = Vec::new(); + collected_spans.append(&mut self.ty.leaf_spans()); + collected_spans.push(ByteSpan::from(self.semicolon_token.span())); + collected_spans.append(&mut self.length.leaf_spans()); + collected_spans + } +}