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

Add a configuration to preserve whitespace on lines containing only whitespace #804

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

TTOzzi
Copy link
Contributor

@TTOzzi TTOzzi commented Sep 5, 2024

Resolve #801

I've implemented an option to preserve whitespace on lines that consist solely of whitespace.
I believe this option will be useful for many people, but I'm not sure if I've implemented it in the correct way 🤔
If the implementation direction is not correct, please feel free to let me know. Thank you 🙇

Comment on lines 452 to 455
if configuration.allowWhitespaceOnlyLines, outputBuffer.isAtStartOfLine {
let currentIndentationSpaceSize = outputBuffer.currentIndentation.indentation().count
outputBuffer.write(String(repeating: " ", count: size - currentIndentationSpaceSize))
} else {
Copy link
Contributor Author

@TTOzzi TTOzzi Sep 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current implementation preserves whitespace exactly as it is.

Input (Output is the same as the input)

class A {
  func foo() -> Int {
    return 1
  }
     // 5 spaces
  func bar() -> Int {
    return 2
  }
}

Would it be better to modify it so that only the current level of indentation is preserved, like this?

Suggested change
if configuration.allowWhitespaceOnlyLines, outputBuffer.isAtStartOfLine {
let currentIndentationSpaceSize = outputBuffer.currentIndentation.indentation().count
outputBuffer.write(String(repeating: " ", count: size - currentIndentationSpaceSize))
} else {
if configuration.allowWhitespaceOnlyLines, outputBuffer.isAtStartOfLine {
let currentIndentationSpaceSize = outputBuffer.currentIndentation.indentation().count
outputBuffer.write(String())
} else {

Output will be changed to

class A {
  func foo() -> Int {
    return 1
  }
  // 2 spaces(same as indentation)
  func bar() -> Int {
    return 2
  }
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since people want this to match the behavior in Xcode (which I personally think is undesirable, but...), then the number of spaces should be changed to match the expected indentation if someone were to type on that line.

For example, if I have this:

func f() {}
     // <- 5 spaces
func g() {}

Then I wrap it in a struct, once I type the closing }, Xcode will re-indent the whole thing. When that happens, the line has changed to this:

struct S {
  func f() {}
  // <- 2 spaces
  func g() {}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To completely match the behavior with Xcode, we need to create spaces according to the indentation even when there are no existing spaces. For now, I've modified it so that the indentation is adjusted only when spaces are present.

Do you think we need to generate spaces to match the indentation even when there are no spaces?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like Xcode does insert spaces there, even when there aren't any.

@ahoppen @bnbarham Do you all have any strong opinions here? I've never liked this behavior in Xcode anyway (I always tell it to strip whitespace-only lines) but I could see myself arguing for either direction here. (And I'd rather not make it another separate knob on top of the one already being added here.)

Copy link
Contributor

@bnbarham bnbarham Sep 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I too would prefer not having an additional option for this, but I can definitely see some people wanting "ident all the things including blank lines" and others wanting "keep my trailing whitespace but don't add indentation to blank lines". So I'm not sure there's a good way around it :(.

Though TBH I don't fully understand the use case for either of these - might ask that in the issue, they do say "this is my favorite feature".

If we do just have the one, rather than allowWhitespaceOnlyLines maybe indentBlankLines/formatBlankLines is clearer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good to me!
Once we've finalized the implementation direction from the ongoing discussion, I'll update the configuration name as you suggested to make it clearer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed the configuration to indentBlankLines for better clarity and updated the comments accordingly.

Additionally, I modified it so that when indentBlankLines is set to true, it adds spaces that match the current indent, even if there are no spaces in the blank lines.

input

struct S {
  func f() {}
// <- no space
  func g() {}
}

output

struct S {
  func f() {}
  // <- 2 spaces
  func g() {}
}

final class AllowWhitespaceOnlyLinesTests: PrettyPrintTestCase {
func testAllowWhitespaceOnlyLinesEnabled() {
let input =
"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this file, can you replace any trailing whitespace with literal \u{0020} escape sequences? That makes it clear to the reader what's being tested, and we don't risk the spaces being lost if someone's editor strips trailing spaces.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, that part was exactly what I was struggling with, and I didn’t know there was such a great solution! Thank you!

@@ -449,7 +449,11 @@ public class PrettyPrinter {

// Print out the number of spaces according to the size, and adjust spaceRemaining.
case .space(let size, _):
outputBuffer.enqueueSpaces(size)
if configuration.allowWhitespaceOnlyLines, outputBuffer.isAtStartOfLine {
outputBuffer.write(String())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you leave a brief comment here explaining why we do this? To a random observer, asking it to write an empty string feels odd, but it's because we need the buffer to still do its other logic (flushing the pending indentation, etc.).

I'd also just write it as outputBuffer.write(""); same effect but easier to read/scan.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a comment and made the modification!

@ValentinWalter
Copy link

Really looking forward to this!

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

Successfully merging this pull request may close these issues.

Don't trim whitespace-only lines
4 participants