diff options
Diffstat (limited to 'working/hyprland-lua-port/reader-changes-for-lua.patch')
| -rw-r--r-- | working/hyprland-lua-port/reader-changes-for-lua.patch | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/working/hyprland-lua-port/reader-changes-for-lua.patch b/working/hyprland-lua-port/reader-changes-for-lua.patch new file mode 100644 index 0000000..747b047 --- /dev/null +++ b/working/hyprland-lua-port/reader-changes-for-lua.patch @@ -0,0 +1,241 @@ +diff --git a/common/.local/bin/dotfiles-validate b/common/.local/bin/dotfiles-validate +index 57a7505..d5e6695 100755 +--- a/common/.local/bin/dotfiles-validate ++++ b/common/.local/bin/dotfiles-validate +@@ -3,6 +3,7 @@ + # + # Walks the tree and extracts the commands that configs promise to launch: + # - hypr conf files: exec-once = CMD / exec = CMD / bind* = ..., exec, CMD ++# - hypr lua configs: at_start/at_shutdown/at_reload("CMD"), exec_cmd("CMD") + # - waybar config: "exec(-if)", "on-click*", "on-scroll-*", + # "on-double-click" values + # - systemd user units: Exec*= lines (leading -/@ modifiers stripped) +@@ -45,6 +46,30 @@ find "$root" -path '*/.config/hypr/*.conf' -type f 2>/dev/null | while read -r f + ' "$f" + done >> "$refs_file" + ++# --- hypr lua configs: autostart collectors and exec_cmd dispatchers --- ++# The Lua config manager (Hyprland 0.55+) spells the same two things as function ++# calls rather than assignments, so the .conf walk above sees none of them. Only ++# the first word is taken, as everywhere else here. The two matches are written ++# out rather than folded into a helper: awk cannot take a regex literal as a ++# function parameter -- it collapses to a boolean match against $0, which ++# silently turns every line into a bogus reference. ++find "$root" -path '*/.config/hypr/*.lua' -type f 2>/dev/null | while read -r f; do ++ awk -v file="$f" ' ++ match($0, /at_(start|shutdown|reload)\("/) { ++ rest = substr($0, RSTART + RLENGTH) ++ sub(/".*$/, "", rest) ++ n = split(rest, w, /[ \t]+/) ++ if (n > 0 && w[1] != "") print file ":" FNR ":" w[1] ++ } ++ match($0, /hl\.dsp\.exec_cmd\("/) { ++ rest = substr($0, RSTART + RLENGTH) ++ sub(/".*$/, "", rest) ++ n = split(rest, w, /[ \t]+/) ++ if (n > 0 && w[1] != "") print file ":" FNR ":" w[1] ++ } ++ ' "$f" ++done >> "$refs_file" ++ + # --- waybar configs: command-bearing JSON values --- + find "$root" -path '*/.config/waybar/*' -type f \( -name config -o -name '*.json' -o -name '*.jsonc' \) 2>/dev/null | while read -r f; do + awk -v file="$f" ' +diff --git a/tests/layout-cycle/test_layout_cycle.py b/tests/layout-cycle/test_layout_cycle.py +index 1454817..3740024 100644 +--- a/tests/layout-cycle/test_layout_cycle.py ++++ b/tests/layout-cycle/test_layout_cycle.py +@@ -18,6 +18,7 @@ Run from repo root: + + import json + import os ++import re + import subprocess + import tempfile + import unittest +@@ -25,7 +26,7 @@ import unittest + REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) + SCRIPT = os.path.join(REPO_ROOT, "hyprland/.local/bin/layout-cycle") + FAKE_HYPRCTL = os.path.join(os.path.dirname(__file__), "fake-hyprctl") +-HYPRLAND_CONF = os.path.join(REPO_ROOT, "hyprland/.config/hypr/hyprland.conf") ++HYPRLAND_CFG = os.path.join(REPO_ROOT, "hyprland/.config/hypr/hyprland.lua") + + FLASH = "rgba(ffd24aff)" + ACTIVE = "rgba(daa520ff)" +@@ -326,7 +327,7 @@ class TestFlashAllBorders(LayoutCycleHarness): + + + class TestScrollLayoutDisabledInConfig(unittest.TestCase): +- """Pin the hyprland.conf half of the scrolling disable. ++ """Pin the hyprland.lua half of the scrolling disable. + + The script tests above prove the ring skips scrolling; these prove no + keybinding reaches it either, and that the freed Super+Shift+S chord +@@ -335,10 +336,20 @@ class TestScrollLayoutDisabledInConfig(unittest.TestCase): + + @classmethod + def setUpClass(cls): +- with open(HYPRLAND_CONF) as f: ++ with open(HYPRLAND_CFG) as f: + cls.conf = f.read() ++ # Every bind in the Lua config is a top-level hl.bind() call; the ++ # locked/mouse/repeat variants that hyprlang spelled bindl/bindm/binde ++ # are the same call with an options table, so one prefix covers them. + cls.binds = [l for l in cls.conf.splitlines() +- if l.strip().startswith(("bind", "bindl", "bindm"))] ++ if l.strip().startswith("hl.bind")] ++ ++ def test_the_binds_were_actually_found(self): ++ """Guard the guard: a renamed call would empty the list and make both ++ assertions below pass against nothing.""" ++ self.assertGreater(len(self.binds), 50, ++ "found almost no hl.bind lines -- the two assertions " ++ "below would pass vacuously") + + def test_no_bind_selects_scrolling_layout(self): + offenders = [l for l in self.binds if "general:layout scrolling" in l] +@@ -346,7 +357,7 @@ class TestScrollLayoutDisabledInConfig(unittest.TestCase): + + def test_super_shift_s_is_fullscreen_screenshot(self): + shift_s = [l for l in self.binds +- if "$mod SHIFT, S," in l] ++ if re.search(r'\bmod\s*\.\.\s*"\s*\+\s*SHIFT\s*\+\s*S"', l)] + self.assertEqual(len(shift_s), 1, msg=f"binds found: {shift_s}") + self.assertIn("screenshot fullscreen", shift_s[0]) + +diff --git a/tests/settings/test_session_restore.py b/tests/settings/test_session_restore.py +index a989d52..b2a2721 100644 +--- a/tests/settings/test_session_restore.py ++++ b/tests/settings/test_session_restore.py +@@ -585,12 +585,32 @@ class TestCompositorWiring(unittest.TestCase): + script and passing tests while never being placed in the bar. + """ + +- CONF = os.path.join(REPO_ROOT, "hyprland/.config/hypr/hyprland.conf") ++ CONF = os.path.join(REPO_ROOT, "hyprland/.config/hypr/hyprland.lua") + + def _lines(self): ++ """The autostart commands, in launch order. ++ ++ The Lua config collects startup commands with at_start("CMD") and one ++ hl.on("hyprland.start") handler at the bottom replays the list in order, ++ so position in this file is still position at launch -- which is what ++ the ordering assertion below reads. Returning the command strings rather ++ than raw lines means every entry here is by construction an autostart ++ command, so the old startswith("exec-once") filter has nothing left to do. ++ """ ++ out = [] + with open(self.CONF) as f: +- return [ln.strip() for ln in f +- if ln.strip() and not ln.strip().startswith("#")] ++ for ln in f: ++ m = re.search(r'at_start\("(.*)"\)', ln.strip()) ++ if m: ++ out.append(m.group(1)) ++ return out ++ ++ def test_the_autostart_commands_were_actually_found(self): ++ """Guard the guard: a renamed collector would empty the list and make ++ every assertion below pass against nothing.""" ++ self.assertGreater(len(self._lines()), 10, ++ "found almost no at_start commands -- the assertions " ++ "below would pass vacuously") + + @staticmethod + def _is_toggle_restore(line): +@@ -607,22 +627,19 @@ class TestCompositorWiring(unittest.TestCase): + + def test_restore_runs_at_session_start(self): + self.assertTrue( +- any(ln.startswith("exec-once") and self._is_toggle_restore(ln) +- for ln in self._lines()), +- "hyprland.conf has no exec-once running `settings restore` — " ++ any(self._is_toggle_restore(ln) for ln in self._lines()), ++ "hyprland.lua has no at_start running `settings restore` — " + "remembered toggles would never be replayed") + + def test_wallpaper_restore_runs_at_session_start(self): + self.assertTrue( +- any(ln.startswith("exec-once") and "settings restore-wallpaper" in ln +- for ln in self._lines()), +- "hyprland.conf has no exec-once running `settings restore-wallpaper` " ++ any("settings restore-wallpaper" in ln for ln in self._lines()), ++ "hyprland.lua has no at_start running `settings restore-wallpaper` " + "— the stored wallpaper would never be put back") + + def test_the_toggle_restore_runs_only_once(self): + """Twice means the whole re-assert budget is spent twice per login.""" +- lines = [ln for ln in self._lines() +- if ln.startswith("exec-once") and self._is_toggle_restore(ln)] ++ lines = [ln for ln in self._lines() if self._is_toggle_restore(ln)] + self.assertEqual(len(lines), 1, lines) + + def test_restore_is_ordered_after_the_backings_it_corrects(self): +@@ -631,17 +648,15 @@ class TestCompositorWiring(unittest.TestCase): + # burn attempts on backings that aren't up yet. + lines = self._lines() + restore = next(i for i, ln in enumerate(lines) +- if ln.startswith("exec-once") +- and self._is_toggle_restore(ln)) ++ if self._is_toggle_restore(ln)) + for backing in ("hypridle", "dunst"): +- launch = next(i for i, ln in enumerate(lines) +- if ln.startswith("exec-once") and backing in ln) ++ launch = next(i for i, ln in enumerate(lines) if backing in ln) + self.assertLess(launch, restore, + f"`settings restore` is ordered before {backing}") + + + class DimEnv(TempEnv): +- """Adds the hyprctl fake, whose dim default is 1 -- as hyprland.conf's is.""" ++ """Adds the hyprctl fake, whose dim default is 1 -- as hyprland.lua's is.""" + + def setUp(self): + super().setUp() +diff --git a/tests/waybar-reserve/test_reserve_pairing.py b/tests/waybar-reserve/test_reserve_pairing.py +index 7421692..fbeb95d 100644 +--- a/tests/waybar-reserve/test_reserve_pairing.py ++++ b/tests/waybar-reserve/test_reserve_pairing.py +@@ -26,7 +26,7 @@ import unittest + + REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) + WAYBAR = os.path.join(REPO_ROOT, "hyprland/.config/waybar/config") +-HYPR = os.path.join(REPO_ROOT, "hyprland/.config/hypr/hyprland.conf") ++HYPR = os.path.join(REPO_ROOT, "hyprland/.config/hypr/hyprland.lua") + + + def waybar_config(): +@@ -35,13 +35,19 @@ def waybar_config(): + + + def reserve_exec_wired(): +- """True when hyprland.conf runs waybar-reserve via exec (not exec-once). ++ """True when the config re-runs waybar-reserve on every config reload. + +- Matches waybar-reserve invoked anywhere in an ``exec =`` line, so the +- reload-race-safe retry-loop form (``exec = for i in 1 2 3; do sleep 0.2; +- waybar-reserve; done``) counts the same as a bare ``exec = waybar-reserve``.""" ++ Matches waybar-reserve invoked anywhere in an ``at_reload(...)`` line, so the ++ reload-race-safe retry-loop form (``at_reload("for i in 1 2 3; do sleep 0.2; ++ waybar-reserve; done")``) counts the same as a bare ``at_reload("waybar-reserve")``. ++ ++ at_reload and not at_start, because hyprlang's ``exec`` ran at startup AND on ++ every reload, and the Lua port splits those two jobs. at_reload's handler is ++ ``hl.on("config.reloaded")``, which fires on the initial load as well, so it ++ alone covers both. Matching at_start instead would pass while reservations ++ died on the next reload -- which is exactly the defect this guards.""" + with open(HYPR) as f: +- return any(re.match(r"\s*exec\s*=.*\bwaybar-reserve\b", ln) for ln in f) ++ return any(re.match(r'\s*at_reload\(".*\bwaybar-reserve\b', ln) for ln in f) + + + def reserve_target(): +@@ -61,7 +67,7 @@ class ReservePairingHarness(unittest.TestCase): + + def test_hyprland_wires_the_reserve_script(self): + self.assertTrue(reserve_exec_wired(), +- "hyprland.conf lacks `exec = waybar-reserve`: reservations " ++ "hyprland.lua lacks `at_reload(... waybar-reserve ...)`: reservations " + "die on the next config reload") + + def test_reserve_target_covers_the_bar(self): |
