diff --git a/crates/config/src/fmt.rs b/crates/config/src/fmt.rs index 15d4209da2bc2..673b692fbe4fd 100644 --- a/crates/config/src/fmt.rs +++ b/crates/config/src/fmt.rs @@ -42,6 +42,8 @@ pub struct FormatterConfig { /// Style that determines if a broken list, should keep its elements together on their own /// line, before breaking individually. pub prefer_compact: PreferCompact, + /// Keep single imports on a single line even if they exceed line length. + pub single_line_imports: bool, } /// Style of integer types. @@ -251,6 +253,7 @@ impl Default for FormatterConfig { pow_no_space: false, prefer_compact: PreferCompact::default(), docs_style: DocCommentStyle::default(), + single_line_imports: false, } } } diff --git a/crates/fmt/README.md b/crates/fmt/README.md index 5e7e82d19a720..65f05d6c82847 100644 --- a/crates/fmt/README.md +++ b/crates/fmt/README.md @@ -128,6 +128,7 @@ The formatter supports multiple configuration options defined in `foundry.toml`. | `contract_new_lines` | `false` | Add a new line at the start and end of contract declarations. | | `sort_imports` | `false` | Sort import statements alphabetically in groups. A group is a set of imports separated by a newline. | | `pow_no_space` | `false` | Suppress spaces around the power operator (`**`). | +| `single_line_imports` | `false` | Keep single imports on a single line, even if they exceed the line length limit. | > Check [`FormatterConfig`](../config/src/fmt.rs) for a more detailed explanation. diff --git a/crates/fmt/src/state/sol.rs b/crates/fmt/src/state/sol.rs index 7303d6fb7d3d3..6e82ef5dd482d 100644 --- a/crates/fmt/src/state/sol.rs +++ b/crates/fmt/src/state/sol.rs @@ -215,9 +215,19 @@ impl<'ast> State<'_, 'ast> { } ast::ImportItems::Aliases(aliases) => { - self.s.cbox(self.ind); - self.word("{"); - self.braces_break(); + // Check if we should keep single imports on one line + let use_single_line = self.config.single_line_imports && aliases.len() == 1; + + if use_single_line { + self.word("{"); + if self.config.bracket_spacing { + self.nbsp(); + } + } else { + self.s.cbox(self.ind); + self.word("{"); + self.braces_break(); + } if self.config.sort_imports { let mut sorted: Vec<_> = aliases.iter().collect(); @@ -227,10 +237,17 @@ impl<'ast> State<'_, 'ast> { self.print_commasep_aliases(aliases.iter()); }; - self.braces_break(); - self.s.offset(-self.ind); - self.word("}"); - self.end(); + if use_single_line { + if self.config.bracket_spacing { + self.nbsp(); + } + self.word("}"); + } else { + self.braces_break(); + self.s.offset(-self.ind); + self.word("}"); + self.end(); + } self.word(" from "); self.print_ast_str_lit(path); } diff --git a/crates/fmt/testdata/ImportDirective/bracket-spacing.fmt.sol b/crates/fmt/testdata/ImportDirective/bracket-spacing.fmt.sol index 5c5ae93e9a692..b0f4fd067d465 100644 --- a/crates/fmt/testdata/ImportDirective/bracket-spacing.fmt.sol +++ b/crates/fmt/testdata/ImportDirective/bracket-spacing.fmt.sol @@ -19,3 +19,8 @@ import { symbol3 as alias3, symbol4 } from "File2.sol"; + +// Single import that exceeds line length (121 chars) +import { + ITransparentUpgradeableProxy +} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; diff --git a/crates/fmt/testdata/ImportDirective/fmt.sol b/crates/fmt/testdata/ImportDirective/fmt.sol index 83a739f4e1e73..c4ce5f3166750 100644 --- a/crates/fmt/testdata/ImportDirective/fmt.sol +++ b/crates/fmt/testdata/ImportDirective/fmt.sol @@ -18,3 +18,8 @@ import { symbol3 as alias3, symbol4 } from "File2.sol"; + +// Single import that exceeds line length (121 chars) +import { + ITransparentUpgradeableProxy +} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; diff --git a/crates/fmt/testdata/ImportDirective/original.sol b/crates/fmt/testdata/ImportDirective/original.sol index f027174512196..2a18f88f0fa33 100644 --- a/crates/fmt/testdata/ImportDirective/original.sol +++ b/crates/fmt/testdata/ImportDirective/original.sol @@ -8,3 +8,6 @@ import {symbol1 as alias0, symbol2} from "File.sol"; import {symbol1 as alias0, symbol2} from 'File.sol'; import {symbol1 as alias1, symbol2 as alias2, symbol3 as alias3, symbol4} from "File2.sol"; import {symbol1 as alias1, symbol2 as alias2, symbol3 as alias3, symbol4} from 'File2.sol'; + +// Single import that exceeds line length (121 chars) +import { ITransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; diff --git a/crates/fmt/testdata/ImportDirective/preserve-quote.fmt.sol b/crates/fmt/testdata/ImportDirective/preserve-quote.fmt.sol index 66d2a1d1ec6c1..c759965928ec2 100644 --- a/crates/fmt/testdata/ImportDirective/preserve-quote.fmt.sol +++ b/crates/fmt/testdata/ImportDirective/preserve-quote.fmt.sol @@ -19,3 +19,8 @@ import { symbol3 as alias3, symbol4 } from 'File2.sol'; + +// Single import that exceeds line length (121 chars) +import { + ITransparentUpgradeableProxy +} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; diff --git a/crates/fmt/testdata/ImportDirective/single-quote.fmt.sol b/crates/fmt/testdata/ImportDirective/single-quote.fmt.sol index d72e043f4f5d7..1820bd5166841 100644 --- a/crates/fmt/testdata/ImportDirective/single-quote.fmt.sol +++ b/crates/fmt/testdata/ImportDirective/single-quote.fmt.sol @@ -19,3 +19,8 @@ import { symbol3 as alias3, symbol4 } from 'File2.sol'; + +// Single import that exceeds line length (121 chars) +import { + ITransparentUpgradeableProxy +} from '@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol'; diff --git a/crates/fmt/testdata/ImportDirective/single_line_import.fmt.sol b/crates/fmt/testdata/ImportDirective/single_line_import.fmt.sol new file mode 100644 index 0000000000000..5644e336dbb97 --- /dev/null +++ b/crates/fmt/testdata/ImportDirective/single_line_import.fmt.sol @@ -0,0 +1,24 @@ +// config: single_line_imports = true +import "SomeFile.sol"; +import "SomeFile.sol"; +import "SomeFile.sol" as SomeOtherFile; +import "SomeFile.sol" as SomeOtherFile; +import "AnotherFile.sol" as SomeSymbol; +import "AnotherFile.sol" as SomeSymbol; +import {symbol1 as alias0, symbol2} from "File.sol"; +import {symbol1 as alias0, symbol2} from "File.sol"; +import { + symbol1 as alias1, + symbol2 as alias2, + symbol3 as alias3, + symbol4 +} from "File2.sol"; +import { + symbol1 as alias1, + symbol2 as alias2, + symbol3 as alias3, + symbol4 +} from "File2.sol"; + +// Single import that exceeds line length (121 chars) +import {ITransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 7eaecf19c346b..f7d96f292efff 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -139,6 +139,7 @@ contract_new_lines = false sort_imports = false pow_no_space = false prefer_compact = "all" +single_line_imports = false [lint] severity = [] @@ -1317,7 +1318,8 @@ forgetest_init!(test_default_config, |prj, cmd| { "contract_new_lines": false, "sort_imports": false, "pow_no_space": false, - "prefer_compact": "all" + "prefer_compact": "all", + "single_line_imports": false }, "lint": { "severity": [],