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

A single leading space should not override an otherwise 100% tab-indented file #507

Merged
merged 7 commits into from
Jan 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions jvm/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Fixed
- A single leading space (such as in the copyright header) should not override an otherwise 100% tab-indented file. ([#506](https://github.com/diffplug/selfie/issues/506))

## [2.4.1] - 2024-10-07
### Fixed
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2024 DiffPlug
* Copyright (C) 2024-2025 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -35,7 +35,7 @@ internal enum class EscapeLeadingWhitespace {
.lineSequence()
.mapNotNull { line ->
val whitespace = line.takeWhile { it.isWhitespace() }
if (whitespace.isEmpty()) null
if (whitespace.isEmpty() || whitespace == " ") null
else if (whitespace.all { it == ' ' }) ' '
else if (whitespace.all { it == '\t' }) '\t' else MIXED
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2024 DiffPlug
* Copyright (C) 2024-2025 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -29,16 +29,28 @@ class EscapeLeadingWhitespaceTest {
appropriateFor("abc\nabc") shouldBe ALWAYS

// all spaces -> only tabs need escape
appropriateFor(" ") shouldBe ONLY_ON_TAB
appropriateFor(" ") shouldBe ALWAYS
appropriateFor(" ") shouldBe ONLY_ON_TAB
appropriateFor(" \n ") shouldBe ONLY_ON_TAB
appropriateFor(" \n ") shouldBe ONLY_ON_TAB

// all tabs -> only space needs escape
appropriateFor("\t") shouldBe ONLY_ON_SPACE
appropriateFor("\t\t") shouldBe ONLY_ON_SPACE
appropriateFor("\t\n\t") shouldBe ONLY_ON_SPACE

// it's a mess -> everything needs escape
appropriateFor("\t\n ") shouldBe ALWAYS
appropriateFor("\t\n ") shouldBe ALWAYS

// single spaces and tabs -> only tabs need escape
appropriateFor(
"""
/*
${' '}* Copyright
${' '}*/
interface Foo {
${'\t'}fun bar()
}
""") shouldBe
ONLY_ON_SPACE
}
}
16 changes: 7 additions & 9 deletions python/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@

Changelog for the selfie Python libraries.

- [`com.diffplug.selfie:selfie-lib:VERSION`](https://pypi.org/project/selfie-lib/)
- [`com.diffplug.selfie:selfie-runner-pytest:VERSION`](https://pypi.org/project/pytest-selfie/)
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- [`selfie-lib:VERSION`](https://pypi.org/project/selfie-lib/)
- [`pytest-selfie:VERSION`](https://pypi.org/project/pytest-selfie/)

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

Allowable headings are `Added`, `Fixed`, and `Changed`.

## [Unreleased]
### Added
- TODO
### Fixed
- TODO
### Changed
- TODO
- A single leading space (such as in the copyright header) should not override an otherwise 100% tab-indented file. ([#506](https://github.com/diffplug/selfie/issues/506))

## [1.0.0] - 2024-12-16

Expand Down
4 changes: 2 additions & 2 deletions python/selfie-lib/selfie_lib/EscapeLeadingWhitespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ def appropriate_for(cls, file_content: str) -> "EscapeLeadingWhitespace":
common_whitespace = None

for line in file_content.splitlines():
whitespace = "".join(c for c in line if c.isspace())
if not whitespace:
whitespace = line[0 : len(line) - len(line.lstrip())]
if whitespace == "" or whitespace == " ":
continue
elif all(c == " " for c in whitespace):
whitespace = " "
Expand Down
1 change: 1 addition & 0 deletions python/selfie-lib/selfie_lib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .CacheSelfie import cache_selfie_binary as cache_selfie_binary
from .CacheSelfie import cache_selfie_json as cache_selfie_json
from .CommentTracker import CommentTracker as CommentTracker
from .EscapeLeadingWhitespace import EscapeLeadingWhitespace as EscapeLeadingWhitespace
from .FS import FS as FS
from .Lens import Camera as Camera
from .Lens import CompoundLens as CompoundLens
Expand Down
59 changes: 59 additions & 0 deletions python/selfie-lib/tests/EscapeLeadingWhitespace_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from selfie_lib import EscapeLeadingWhitespace


def test_detection():
# not enough to detect
assert EscapeLeadingWhitespace.appropriate_for("") == EscapeLeadingWhitespace.ALWAYS
assert (
EscapeLeadingWhitespace.appropriate_for("abc") == EscapeLeadingWhitespace.ALWAYS
)
assert (
EscapeLeadingWhitespace.appropriate_for("abc\nabc")
== EscapeLeadingWhitespace.ALWAYS
)

# all spaces -> only tabs need escape
assert (
EscapeLeadingWhitespace.appropriate_for(" ") == EscapeLeadingWhitespace.ALWAYS
)
assert (
EscapeLeadingWhitespace.appropriate_for(" ")
== EscapeLeadingWhitespace.ONLY_ON_TAB
)
assert (
EscapeLeadingWhitespace.appropriate_for(" \n ")
== EscapeLeadingWhitespace.ONLY_ON_TAB
)

# all tabs -> only space needs escape
assert (
EscapeLeadingWhitespace.appropriate_for("\t")
== EscapeLeadingWhitespace.ONLY_ON_SPACE
)
assert (
EscapeLeadingWhitespace.appropriate_for("\t\t")
== EscapeLeadingWhitespace.ONLY_ON_SPACE
)
assert (
EscapeLeadingWhitespace.appropriate_for("\t\n\t")
== EscapeLeadingWhitespace.ONLY_ON_SPACE
)

# it's a mess -> everything needs escape
assert (
EscapeLeadingWhitespace.appropriate_for("\t\n ")
== EscapeLeadingWhitespace.ALWAYS
)

# single spaces and tabs -> only tabs need escape
tab = "\t"
test_string = f"""/*
* Copyright
*/
interface Foo [
{tab}bar()
]"""
assert (
EscapeLeadingWhitespace.appropriate_for(test_string)
== EscapeLeadingWhitespace.ONLY_ON_SPACE
)
2 changes: 1 addition & 1 deletion python/selfie-lib/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading