diff --git a/components/clarinet-format/src/formatter/mod.rs b/components/clarinet-format/src/formatter/mod.rs index 816b5b39c..6e7e220f4 100644 --- a/components/clarinet-format/src/formatter/mod.rs +++ b/components/clarinet-format/src/formatter/mod.rs @@ -59,7 +59,14 @@ impl ClarityFormatter { pub fn new(settings: Settings) -> Self { Self { settings } } - pub fn format(&mut self, source: &str) -> String { + pub fn format_file(&mut self, source: &str) -> String { + let pse = clarity::vm::ast::parser::v2::parse(source).unwrap(); + let result = format_source_exprs(&self.settings, &pse, "", None, ""); + + // make sure the file ends with a newline + result.trim_end_matches('\n').to_string() + "\n" + } + pub fn format_section(&mut self, source: &str) -> String { let pse = clarity::vm::ast::parser::v2::parse(source).unwrap(); format_source_exprs(&self.settings, &pse, "", None, "") } @@ -72,8 +79,10 @@ pub fn format_source_exprs( previous_expr: Option<&PreSymbolicExpression>, acc: &str, ) -> String { - println!("exprs: {:?}", expressions); + // println!("exprs: {:?}", expressions); // println!("previous: {:?}", previous_expr); + + // use peekable to handle trailing comments nicely let mut iter = expressions.iter().peekable(); let mut result = acc.to_owned(); // Accumulate results here @@ -109,8 +118,11 @@ pub fn format_source_exprs( NativeFunctions::Match => { format_match(settings, list, previous_indentation) } - NativeFunctions::IndexOf | NativeFunctions::IndexOfAlias => { - format_index_of(settings, list, previous_indentation) + NativeFunctions::IndexOf + | NativeFunctions::IndexOfAlias + | NativeFunctions::Asserts + | NativeFunctions::ContractCall => { + format_general(settings, list, previous_indentation) } NativeFunctions::TupleCons => { // if the kv map is defined with (tuple (c 1)) then we strip the @@ -301,32 +313,23 @@ fn is_same_line(expr1: &PreSymbolicExpression, expr2: &PreSymbolicExpression) -> expr1.span().start_line == expr2.span().start_line } -fn format_index_of( +// this is probably un-needed but was getting some weird artifacts for code like +// (something (1 2 3) true) would be formatted as (something (1 2 3)true) +fn format_general( settings: &Settings, exprs: &[PreSymbolicExpression], previous_indentation: &str, ) -> String { - println!("ASDFASDF"); let func_type = display_pse(settings, exprs.first().unwrap(), ""); let mut acc = format!("({func_type}"); acc.push(' '); - acc.push_str(&format!( - "{} {}", - format_source_exprs( - settings, - &[exprs[1].clone()], - previous_indentation, - None, - "" - ), - format_source_exprs( - settings, - &[exprs[2].clone()], - previous_indentation, - None, - "" - ) - )); + for (i, arg) in exprs[1..].iter().enumerate() { + acc.push_str(&format!( + "{}{}", + format_source_exprs(settings, &[arg.clone()], previous_indentation, None, ""), + if i < exprs.len() - 2 { " " } else { "" } + )) + } acc.push(')'); acc.to_owned() } @@ -770,15 +773,58 @@ fn format_function(settings: &Settings, exprs: &[PreSymbolicExpression]) -> Stri mod tests_formatter { use super::{ClarityFormatter, Settings}; use crate::formatter::Indentation; + use std::collections::HashMap; use std::fs; use std::path::Path; + fn from_metadata(metadata: &str) -> Settings { + let mut max_line_length = 80; + let mut indent = Indentation::Space(2); + + let metadata_map: HashMap<&str, &str> = metadata + .split(',') + .map(|pair| pair.trim()) + .filter_map(|kv| kv.split_once(':')) + .map(|(k, v)| (k.trim(), v.trim())) + .collect(); + + if let Some(length) = metadata_map.get("max_line_length") { + max_line_length = length.parse().unwrap_or(max_line_length); + } + + if let Some(&indentation) = metadata_map.get("indentation") { + indent = match indentation { + "tab" => Indentation::Tab, + value => { + if let Ok(spaces) = value.parse::() { + Indentation::Space(spaces) + } else { + Indentation::Space(2) // Fallback to default + } + } + }; + } + + Settings { + max_line_length, + indentation: indent, + } + } fn format_with_default(source: &str) -> String { let mut formatter = ClarityFormatter::new(Settings::default()); - formatter.format(source) + formatter.format_section(source) + } + fn format_file_with_metadata(source: &str) -> String { + let mut lines = source.lines(); + let metadata_line = lines.next().unwrap_or_default(); + let settings = from_metadata(metadata_line); + + let real_source = lines.collect::>().join("\n"); + let mut formatter = ClarityFormatter::new(settings); + formatter.format_file(&real_source) } fn format_with(source: &str, settings: Settings) -> String { let mut formatter = ClarityFormatter::new(settings); - formatter.format(source) + formatter.format_section(source) } #[test] fn test_simplest_formatter() { @@ -1025,7 +1071,9 @@ mod tests_formatter { fs::read_to_string(&intended_path).expect("Failed to read intended file"); // Apply formatting and compare - let result = format_with_default(&src); + let result = format_file_with_metadata(&src); + println!("intended: {:?}", intended); + println!("result: {:?}", result); pretty_assertions::assert_eq!( result, intended, diff --git a/components/clarinet-format/tests/golden-intended/tuple.clar b/components/clarinet-format/tests/golden-intended/tuple.clar index 0fe1813e4..1a5f90ce9 100644 --- a/components/clarinet-format/tests/golden-intended/tuple.clar +++ b/components/clarinet-format/tests/golden-intended/tuple.clar @@ -12,6 +12,6 @@ ) (begin (asserts! (is-lending-pool contract-caller) ERR_UNAUTHORIZED) - (contract-call? pool-reserve-data set-user-reserve-data user asset state) + (contract-call? .pool-reserve-data set-user-reserve-data user asset state) ) ) diff --git a/components/clarinet-format/tests/golden/comments.clar b/components/clarinet-format/tests/golden/comments.clar index 076d99e50..57c76ad7f 100644 --- a/components/clarinet-format/tests/golden/comments.clar +++ b/components/clarinet-format/tests/golden/comments.clar @@ -1,3 +1,4 @@ +;; max_line_length: 80, indentation: 2 ;; comment (define-read-only (get-offer (id uint) (w uint)) (map-get? offers-map id) ) diff --git a/components/clarinet-format/tests/golden/match-or.clar b/components/clarinet-format/tests/golden/match-or.clar index 5aa8c0598..2d4c06df1 100644 --- a/components/clarinet-format/tests/golden/match-or.clar +++ b/components/clarinet-format/tests/golden/match-or.clar @@ -1,3 +1,4 @@ +;; max_line_length: 80, indentation: 2 ;; Determines if a character is a vowel (a, e, i, o, u, and y). (define-private (is-vowel (char (buff 1))) (or diff --git a/components/clarinet-format/tests/golden/nested_map.clar b/components/clarinet-format/tests/golden/nested_map.clar index 8529118bf..442a96992 100644 --- a/components/clarinet-format/tests/golden/nested_map.clar +++ b/components/clarinet-format/tests/golden/nested_map.clar @@ -1,3 +1,4 @@ +;; max_line_length: 80, indentation: 2 (define-public (mng-name-register) (map-set name-properties { diff --git a/components/clarinet-format/tests/golden/test.clar b/components/clarinet-format/tests/golden/test.clar index 3608662f8..2116b8d23 100644 --- a/components/clarinet-format/tests/golden/test.clar +++ b/components/clarinet-format/tests/golden/test.clar @@ -1,9 +1,10 @@ +;; max_line_length: 80, indentation: 2 ;; private functions ;; #[allow(unchecked_data)] (define-private (complete-individual-deposits-helper (deposit {txid: (buff 32), vout-index: uint, amount: uint, recipient: principal, burn-hash: (buff 32), burn-height: uint, sweep-txid: (buff 32)}) (helper-response (response uint uint))) - (match helper-response + (match helper-response index - (begin + (begin (try! (unwrap! (complete-deposit-wrapper (get txid deposit) (get vout-index deposit) (get amount deposit) (get recipient deposit) (get burn-hash deposit) (get burn-height deposit) (get sweep-txid deposit)) (err (+ ERR_DEPOSIT_INDEX_PREFIX (+ u10 index))))) (ok (+ index u1)) ) diff --git a/components/clarinet-format/tests/golden/traits.clar b/components/clarinet-format/tests/golden/traits.clar index 7d2387518..a311ba316 100644 --- a/components/clarinet-format/tests/golden/traits.clar +++ b/components/clarinet-format/tests/golden/traits.clar @@ -1,3 +1,4 @@ +;; max_line_length: 80, indentation: 2 (use-trait token-a-trait 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF.token-a.token-trait) (define-public (forward-get-balance (user principal) (contract )) (begin diff --git a/components/clarinet-format/tests/golden/tuple.clar b/components/clarinet-format/tests/golden/tuple.clar index a1aaf790e..b90942248 100644 --- a/components/clarinet-format/tests/golden/tuple.clar +++ b/components/clarinet-format/tests/golden/tuple.clar @@ -1,3 +1,4 @@ +;; max_line_length: 80, indentation: 2 (define-public (set-user-reserve (user principal) (asset principal)