Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Syntax aware formatting #3

Open
aheze opened this issue Jun 28, 2022 · 8 comments
Open

Syntax aware formatting #3

aheze opened this issue Jun 28, 2022 · 8 comments
Labels
enhancement New feature or request

Comments

@aheze
Copy link
Owner

aheze commented Jun 28, 2022

Currently when you select a chunk of code that contains parameters nested inside, formatting is weird:

Memberwise init generated by Xcode

This is because Multiliner splits parameters by commas , — this includes commas inside nested inits.

/// The text inside the braces.
let contentsString = text[openingContentIndex ..< closingContentIndex]
let contents = contentsString
.components(separatedBy: ",")

Possibly a split using Regex?

@aheze aheze added the enhancement New feature or request label Jun 29, 2022
@calimarkus
Copy link

Maybe you could parse the code into an AST using apple/swift-syntax to understand the code better. The resulting type information could be helpful to make more informed decisions on how to rewrite the code. (Maybe you could also use this to identify the initial range?)

@aheze
Copy link
Owner Author

aheze commented Jul 8, 2022

Worth a try. Not sure how the performance will be impacted though

@calimarkus
Copy link

It's designed for "performance-critical applications" - whatever that may mean:

SwiftSyntax is a set of Swift bindings for the libSyntax library. [...] Its API is designed for performance-critical applications.

@aheze
Copy link
Owner Author

aheze commented Jul 9, 2022

Right. Thanks for the suggestion, I'll do some experimenting this week!

@orchetect
Copy link
Contributor

orchetect commented Jul 9, 2022

Yeah, because of assumptions the parsing makes, it can format things in an unexpected way so you have to be fairly specific about what you highlight sometimes. I find myself needing to hit CtrlI afterward which then indents the results correctly.

Adopting swift-syntax would be great - could help with the heavy lifting of handling recursive and complex formatting, and would evolve as the Swift language evolves too.

It would be hugely beneficial to be adding unit tests to ensure formatting continues to work as expected as the extension is updated over time, since there's so many edge cases and variations in code formatting.

(I would be game for helping out with writing unit tests at some point.)

@aheze aheze changed the title Recursive formatting Syntax aware formatting Jul 10, 2022
@orchetect
Copy link
Contributor

orchetect commented Jul 24, 2022

One major benefit to using a formal AST is that you could format an entire file in one go. I often come across code I inherit or old code I wrote where parameters were all on single lines. It would be amazing to just CmdA and run the formatter.

I did a bit of snooping and it looks like some linters/formatters are already capable of this:

apple/swift-format:

  • lineBreakBeforeEachArgument (boolean): Determines the line-breaking behavior for generic arguments and function arguments when a declaration is wrapped onto multiple lines. If true, a line break will be added before each argument, forcing the entire argument list to be laid out vertically. If false (the default), arguments will be laid out horizontally first, with line breaks only being fired when the line length would be exceeded.

  • lineBreakBeforeEachGenericRequirement (boolean): Determines the line-breaking behavior for generic requirements when the requirements list is wrapped onto multiple lines. If true, a line break will be added before each requirement, forcing the entire requirements list to be laid out vertically. If false (the default), requirements will be laid out horizontally first, with line breaks only being fired when the line length would be exceeded.

  • lineBreakAroundMultilineExpressionChainComponents (boolean): Determines whether line breaks should be forced before and after multiline components of dot-chained expressions, such as function calls and subscripts chained together through member access (i.e. "." expressions). When any component is multiline and this option is true, a line break is forced before the "." of the component and after the component's closing delimiter (i.e. right paren, right bracket, right brace, etc.).

SwiftFormat:

--wraparguments before-first

- foo(bar: Int,
-     baz: String)

+ foo(
+   bar: Int,
+   baz: String
+ )
- class Foo<Bar,
-           Baz>

+ class Foo<
+   Bar,
+   Baz
+ >

--wrapparameters after-first

- func foo(
-   bar: Int,
-   baz: String
- ) {
    ...
  }

+ func foo(bar: Int,
+          baz: String) {
    ...
  }

--wrapcollections before-first:

- let foo = [bar,
             baz,
-            quuz]

+ let foo = [
+   bar,
    baz,
+   quuz
+ ]

@aheze
Copy link
Owner Author

aheze commented Jul 24, 2022

@orchetect ah, so both SwiftFormats can kind of replace Multiliner. Nice investigating!

I guess if you want full file formatting it's better to go with one of the above. Multiliner was originally meant to be a quick shortcut to expand whatever line you're currently on. This makes it pretty fast actually (since it doesn't need to parse the whole file, it formats instantly even if your file is huge).

I've thought about whole file formatting, but the SwiftFormats can do this already and are great at it. Maybe it would be better to keep Multiliner lightweight and focus more on small shortcuts. I'll still look into AST though

@orchetect
Copy link
Contributor

I don't think it's a replacement necessarily, after testing them out. It's also not exactly the same as Multiliner, you have to tweak the rules to get them to force it and it only works under certain circumstances. Whereas Multiliner does it every time. So I'd still probably use both depending on circumstances.

What I was thinking is it might be possible to learn from their source code how they're doing it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants