mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-10 19:07:17 +01:00
Allow spaced desktop Name=; restore Sick days
Rule 2 now takes one space between words; the phone label
rule is unchanged. Reverts the 5b36e84 rename.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzxdWY1k4muZfdVJfb7YGJ
This commit is contained in:
parent
af3c98a50e
commit
2bbb428f2c
@ -122,7 +122,10 @@ def desktop_problems(repo: Path) -> list[str]:
|
|||||||
if not names:
|
if not names:
|
||||||
out.append(f"{rel}: no Name= in [Desktop Entry]")
|
out.append(f"{rel}: no Name= in [Desktop Entry]")
|
||||||
for name in names:
|
for name in names:
|
||||||
out += [f"{rel}: Name={p}" for p in launcher_label.label_problems(name)]
|
out += [
|
||||||
|
f"{rel}: Name={p}"
|
||||||
|
for p in launcher_label.label_problems(name, words=True)
|
||||||
|
]
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,8 @@ the launcher font is capped too (`label_widths.py`): FocusOwner, 10
|
|||||||
characters, was still cut. The same rules live
|
characters, was still cut. The same rules live
|
||||||
in `~/src/testsAndMisc/python_pkg/app_icons/labels.py` (`label_problems`);
|
in `~/src/testsAndMisc/python_pkg/app_icons/labels.py` (`label_problems`);
|
||||||
the messages are mirrored here, not imported, because utils must not depend
|
the messages are mirrored here, not imported, because utils must not depend
|
||||||
on another repo.
|
on another repo. A desktop entry's `Name=` (rule 2) may hold single spaces
|
||||||
|
between words (`words=True`); the phone launcher may not.
|
||||||
|
|
||||||
The name is `<application android:label>` of the main manifest, plus the
|
The name is `<application android:label>` of the main manifest, plus the
|
||||||
`android:label` of any activity (or alias) whose intent-filter holds MAIN +
|
`android:label` of any activity (or alias) whose intent-filter holds MAIN +
|
||||||
@ -38,27 +39,39 @@ from repo_contract.label_widths import MAX_WIDTH, label_width
|
|||||||
MAX_LENGTH = 10
|
MAX_LENGTH = 10
|
||||||
STRINGS = Path("android/app/src/main/res/values/strings.xml")
|
STRINGS = Path("android/app/src/main/res/values/strings.xml")
|
||||||
_ASCII_LETTERS = re.compile(r"[A-Za-z]+")
|
_ASCII_LETTERS = re.compile(r"[A-Za-z]+")
|
||||||
|
_ASCII_WORDS = re.compile(r"[A-Za-z]+( [A-Za-z]+)*")
|
||||||
_STRING_REF = re.compile(r"@string/(\w+)")
|
_STRING_REF = re.compile(r"@string/(\w+)")
|
||||||
_PLACEHOLDER = re.compile(r"\$\{(\w+)\}")
|
_PLACEHOLDER = re.compile(r"\$\{(\w+)\}")
|
||||||
_UNRESOLVED = "cannot resolve the launcher name"
|
_UNRESOLVED = "cannot resolve the launcher name"
|
||||||
|
|
||||||
|
|
||||||
def label_problems(name: str) -> list[str]:
|
def label_problems(name: str, *, words: bool = False) -> list[str]:
|
||||||
"""Every launcher-name rule `name` breaks (app_icons labels.py wording)."""
|
"""Every launcher-name rule `name` breaks (app_icons labels.py wording).
|
||||||
|
|
||||||
|
`words=True` is for a desktop entry's `Name=` (rule 2), shown in a menu or
|
||||||
|
dmenu where "Sick days" reads better than "Sickdays": one space between
|
||||||
|
words is fine. The phone launcher (rule 1) never passes it.
|
||||||
|
"""
|
||||||
if not name:
|
if not name:
|
||||||
return ["label is empty"]
|
return ["label is empty"]
|
||||||
problems: list[str] = []
|
problems: list[str] = []
|
||||||
letters_only = _ASCII_LETTERS.fullmatch(name) is not None
|
if words:
|
||||||
if not letters_only:
|
valid = _ASCII_WORDS.fullmatch(name) is not None
|
||||||
problems.append(
|
message = (
|
||||||
f"{name!r} must contain only ASCII letters (no spaces, _ or symbols)"
|
f"{name!r} must be ASCII letters, one space between words (no _ or symbols)"
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
valid = _ASCII_LETTERS.fullmatch(name) is not None
|
||||||
|
message = f"{name!r} must contain only ASCII letters (no spaces, _ or symbols)"
|
||||||
|
if not valid:
|
||||||
|
problems.append(message)
|
||||||
if not name[0].isupper():
|
if not name[0].isupper():
|
||||||
problems.append(f"{name!r} must start with a capital letter")
|
problems.append(f"{name!r} must start with a capital letter")
|
||||||
# The width table only knows ASCII letters; other names already failed.
|
# The width table only knows ASCII letters; other names already failed.
|
||||||
if letters_only and label_width(name) > MAX_WIDTH:
|
letters = name.replace(" ", "")
|
||||||
|
if valid and label_width(letters) > MAX_WIDTH:
|
||||||
problems.append(
|
problems.append(
|
||||||
f"{name!r} is {label_width(name)} wide in the launcher font; "
|
f"{name!r} is {label_width(letters)} wide in the launcher font; "
|
||||||
f"at most {MAX_WIDTH} fit"
|
f"at most {MAX_WIDTH} fit"
|
||||||
)
|
)
|
||||||
if len(name) > MAX_LENGTH:
|
if len(name) > MAX_LENGTH:
|
||||||
|
|||||||
@ -98,6 +98,7 @@ def test_no_flutter_app_no_rules(repo: Path) -> None:
|
|||||||
def test_desktop_names_checked(repo: Path) -> None:
|
def test_desktop_names_checked(repo: Path) -> None:
|
||||||
flutter_app(repo)
|
flutter_app(repo)
|
||||||
write(repo, "linux/good.desktop", DESKTOP.format(name="Todo"))
|
write(repo, "linux/good.desktop", DESKTOP.format(name="Todo"))
|
||||||
|
write(repo, "linux/spaced.desktop", DESKTOP.format(name="Sick days"))
|
||||||
write(repo, "linux/bad.desktop", DESKTOP.format(name="todo app"))
|
write(repo, "linux/bad.desktop", DESKTOP.format(name="todo app"))
|
||||||
write(
|
write(
|
||||||
repo,
|
repo,
|
||||||
@ -108,10 +109,6 @@ def test_desktop_names_checked(repo: Path) -> None:
|
|||||||
track(repo)
|
track(repo)
|
||||||
write(repo, "linux/untracked.desktop", DESKTOP.format(name="bad name"))
|
write(repo, "linux/untracked.desktop", DESKTOP.format(name="bad name"))
|
||||||
assert launcher.problems(repo) == [
|
assert launcher.problems(repo) == [
|
||||||
(
|
|
||||||
"launcher: linux/bad.desktop: Name='todo app' must contain only ASCII"
|
|
||||||
" letters (no spaces, _ or symbols)"
|
|
||||||
),
|
|
||||||
"launcher: linux/bad.desktop: Name='todo app' must start with a capital letter",
|
"launcher: linux/bad.desktop: Name='todo app' must start with a capital letter",
|
||||||
"launcher: linux/none.desktop: no Name= in [Desktop Entry]",
|
"launcher: linux/none.desktop: no Name= in [Desktop Entry]",
|
||||||
]
|
]
|
||||||
|
|||||||
@ -177,3 +177,45 @@ def test_manifest_without_application(tmp_path: Path) -> None:
|
|||||||
assert ll.problems(app) == [
|
assert ll.problems(app) == [
|
||||||
f"{MAIN}: <application> has no android:label; cannot resolve the launcher name"
|
f"{MAIN}: <application> has no android:label; cannot resolve the launcher name"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
WORDS_RULE = "must be ASCII letters, one space between words (no _ or symbols)"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("name", "expected"),
|
||||||
|
[
|
||||||
|
("Sick days", []),
|
||||||
|
("Todo", []),
|
||||||
|
("sick days", ["'sick days' must start with a capital letter"]),
|
||||||
|
(
|
||||||
|
"Sick days",
|
||||||
|
[f"'Sick days' {WORDS_RULE}"],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Sick days ",
|
||||||
|
[f"'Sick days ' {WORDS_RULE}"],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Sick_days",
|
||||||
|
[f"'Sick_days' {WORDS_RULE}"],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Focus owner x",
|
||||||
|
[
|
||||||
|
"'Focus owner x' is 607 wide in the launcher font; at most 522 fit",
|
||||||
|
"'Focus owner x' has 13 characters; at most 10 fit",
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_desktop_names_may_have_spaces_between_words(
|
||||||
|
name: str, expected: list[str]
|
||||||
|
) -> None:
|
||||||
|
assert ll.label_problems(name, words=True) == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_the_phone_label_still_refuses_a_space() -> None:
|
||||||
|
assert ll.label_problems("Sick days") == [
|
||||||
|
"'Sick days' must contain only ASCII letters (no spaces, _ or symbols)"
|
||||||
|
]
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Type=Application
|
Type=Application
|
||||||
Name=Sickdays
|
Name=Sick days
|
||||||
Comment=Mark sick days and choose which lockers still apply
|
Comment=Mark sick days and choose which lockers still apply
|
||||||
Exec=sickdays gui
|
Exec=sickdays gui
|
||||||
Icon=face-sick
|
Icon=face-sick
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user