Skip to content

Commit

Permalink
A single leading space should not override an otherwise 100% tab-inde…
Browse files Browse the repository at this point in the history
…nted file (#507)
  • Loading branch information
nedtwigg authored Jan 1, 2025
2 parents 6183019 + a638736 commit 357996a
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 18 deletions.
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.

0 comments on commit 357996a

Please sign in to comment.