hyprconf2lua 1.2.0__tar.gz → 1.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (21) hide show
  1. {hyprconf2lua-1.2.0/src/hyprconf2lua.egg-info → hyprconf2lua-1.3.0}/PKG-INFO +18 -17
  2. {hyprconf2lua-1.2.0 → hyprconf2lua-1.3.0}/README.md +17 -16
  3. {hyprconf2lua-1.2.0 → hyprconf2lua-1.3.0}/pyproject.toml +1 -1
  4. hyprconf2lua-1.3.0/src/hyprconf2lua/__init__.py +1 -0
  5. {hyprconf2lua-1.2.0 → hyprconf2lua-1.3.0}/src/hyprconf2lua/codegen.py +62 -26
  6. {hyprconf2lua-1.2.0 → hyprconf2lua-1.3.0}/src/hyprconf2lua/mappings.py +1 -0
  7. {hyprconf2lua-1.2.0 → hyprconf2lua-1.3.0}/src/hyprconf2lua/parser.py +1 -1
  8. {hyprconf2lua-1.2.0 → hyprconf2lua-1.3.0/src/hyprconf2lua.egg-info}/PKG-INFO +18 -17
  9. {hyprconf2lua-1.2.0 → hyprconf2lua-1.3.0}/tests/test_converter.py +45 -0
  10. hyprconf2lua-1.2.0/src/hyprconf2lua/__init__.py +0 -1
  11. {hyprconf2lua-1.2.0 → hyprconf2lua-1.3.0}/LICENSE +0 -0
  12. {hyprconf2lua-1.2.0 → hyprconf2lua-1.3.0}/setup.cfg +0 -0
  13. {hyprconf2lua-1.2.0 → hyprconf2lua-1.3.0}/src/hyprconf2lua/__main__.py +0 -0
  14. {hyprconf2lua-1.2.0 → hyprconf2lua-1.3.0}/src/hyprconf2lua/ast.py +0 -0
  15. {hyprconf2lua-1.2.0 → hyprconf2lua-1.3.0}/src/hyprconf2lua/cli.py +0 -0
  16. {hyprconf2lua-1.2.0 → hyprconf2lua-1.3.0}/src/hyprconf2lua/converter.py +0 -0
  17. {hyprconf2lua-1.2.0 → hyprconf2lua-1.3.0}/src/hyprconf2lua/lexer.py +0 -0
  18. {hyprconf2lua-1.2.0 → hyprconf2lua-1.3.0}/src/hyprconf2lua.egg-info/SOURCES.txt +0 -0
  19. {hyprconf2lua-1.2.0 → hyprconf2lua-1.3.0}/src/hyprconf2lua.egg-info/dependency_links.txt +0 -0
  20. {hyprconf2lua-1.2.0 → hyprconf2lua-1.3.0}/src/hyprconf2lua.egg-info/entry_points.txt +0 -0
  21. {hyprconf2lua-1.2.0 → hyprconf2lua-1.3.0}/src/hyprconf2lua.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyprconf2lua
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Summary: Convert Hyprland hyprlang .conf files to Lua .lua config format (v0.55+)
5
5
  Author: hyprconf2lua contributors
6
6
  License: MIT
@@ -26,15 +26,14 @@ Dynamic: license-file
26
26
  Hyprland 0.55+ replaced the old hyprlang config with Lua. The old format will be dropped in a future release. **hyprconf2lua** converts your existing config automatically — no manual rewrite needed.
27
27
 
28
28
  ```bash
29
- git clone https://github.com/Prateek-squadron/hyprconf2lua.git
30
- cd hyprconf2lua
31
- ./install.sh
29
+ pip install hyprconf2lua
32
30
  hyprconf2lua ~/.config/hypr/hyprland.conf -o hyprland.lua
33
31
  ```
34
32
 
35
33
  That's it. ~97% of your config converts cleanly. The rest gets flagged with `-- TODO` comments telling you exactly what to touch up.
36
34
 
37
- > **Not on PyPI yet** — the `pip install hyprconf2lua` command you might see in older posts doesn't work because the package hasn't been published there. Use the clone+install method above. It's zero-dependency, works on every distro, and sidesteps PEP 668 ("externally managed environment") completely. If your distro blocks pip, this is the way.
35
+ > **PEP 668 ("externally managed environment")?** Use the clone method instead zero pip needed:
36
+ > `git clone https://github.com/Prateek-squadron/hyprconf2lua.git && cd hyprconf2lua && ./install.sh`
38
37
 
39
38
  ---
40
39
 
@@ -88,33 +87,35 @@ end)
88
87
 
89
88
  ## Installation
90
89
 
91
- ### Quick install (recommended)
90
+ ### pip install (recommended)
92
91
 
93
92
  ```bash
94
- git clone https://github.com/Prateek-squadron/hyprconf2lua.git
95
- cd hyprconf2lua
96
- ./install.sh # symlinks hyprconf2lua -> ~/.local/bin
93
+ pip install hyprconf2lua # or: pip install --user hyprconf2lua
97
94
  hyprconf2lua ~/.config/hypr/hyprland.conf -o hyprland.lua
98
95
  ```
99
96
 
100
- ### One-shot (no install)
97
+ ### pipx install
98
+
99
+ ```bash
100
+ pipx install hyprconf2lua # isolated, no PEP 668 issues
101
+ ```
102
+
103
+ ### Clone + install (no pip, works everywhere)
101
104
 
102
105
  ```bash
103
106
  git clone https://github.com/Prateek-squadron/hyprconf2lua.git
104
107
  cd hyprconf2lua
105
- PYTHONPATH=src python3 -m hyprconf2lua ~/.config/hypr/hyprland.conf > hyprland.lua
108
+ ./install.sh # symlinks to ~/.local/bin
106
109
  ```
107
110
 
108
- ### pip install (if your distro allows it)
111
+ ### One-shot (no install at all)
109
112
 
110
113
  ```bash
111
- pip install --user .
112
- # or
113
- pip install --break-system-packages .
114
+ git clone https://github.com/Prateek-squadron/hyprconf2lua.git
115
+ cd hyprconf2lua
116
+ PYTHONPATH=src python3 -m hyprconf2lua ~/.config/hypr/hyprland.conf > hyprland.lua
114
117
  ```
115
118
 
116
- > **"externally managed environment" error?** That's PEP 668 — modern distros protect the system Python from pip. Use the clone+install method above instead — zero pip required.
117
-
118
119
  ---
119
120
 
120
121
  ## Full migration guide
@@ -5,15 +5,14 @@
5
5
  Hyprland 0.55+ replaced the old hyprlang config with Lua. The old format will be dropped in a future release. **hyprconf2lua** converts your existing config automatically — no manual rewrite needed.
6
6
 
7
7
  ```bash
8
- git clone https://github.com/Prateek-squadron/hyprconf2lua.git
9
- cd hyprconf2lua
10
- ./install.sh
8
+ pip install hyprconf2lua
11
9
  hyprconf2lua ~/.config/hypr/hyprland.conf -o hyprland.lua
12
10
  ```
13
11
 
14
12
  That's it. ~97% of your config converts cleanly. The rest gets flagged with `-- TODO` comments telling you exactly what to touch up.
15
13
 
16
- > **Not on PyPI yet** — the `pip install hyprconf2lua` command you might see in older posts doesn't work because the package hasn't been published there. Use the clone+install method above. It's zero-dependency, works on every distro, and sidesteps PEP 668 ("externally managed environment") completely. If your distro blocks pip, this is the way.
14
+ > **PEP 668 ("externally managed environment")?** Use the clone method instead zero pip needed:
15
+ > `git clone https://github.com/Prateek-squadron/hyprconf2lua.git && cd hyprconf2lua && ./install.sh`
17
16
 
18
17
  ---
19
18
 
@@ -67,33 +66,35 @@ end)
67
66
 
68
67
  ## Installation
69
68
 
70
- ### Quick install (recommended)
69
+ ### pip install (recommended)
71
70
 
72
71
  ```bash
73
- git clone https://github.com/Prateek-squadron/hyprconf2lua.git
74
- cd hyprconf2lua
75
- ./install.sh # symlinks hyprconf2lua -> ~/.local/bin
72
+ pip install hyprconf2lua # or: pip install --user hyprconf2lua
76
73
  hyprconf2lua ~/.config/hypr/hyprland.conf -o hyprland.lua
77
74
  ```
78
75
 
79
- ### One-shot (no install)
76
+ ### pipx install
77
+
78
+ ```bash
79
+ pipx install hyprconf2lua # isolated, no PEP 668 issues
80
+ ```
81
+
82
+ ### Clone + install (no pip, works everywhere)
80
83
 
81
84
  ```bash
82
85
  git clone https://github.com/Prateek-squadron/hyprconf2lua.git
83
86
  cd hyprconf2lua
84
- PYTHONPATH=src python3 -m hyprconf2lua ~/.config/hypr/hyprland.conf > hyprland.lua
87
+ ./install.sh # symlinks to ~/.local/bin
85
88
  ```
86
89
 
87
- ### pip install (if your distro allows it)
90
+ ### One-shot (no install at all)
88
91
 
89
92
  ```bash
90
- pip install --user .
91
- # or
92
- pip install --break-system-packages .
93
+ git clone https://github.com/Prateek-squadron/hyprconf2lua.git
94
+ cd hyprconf2lua
95
+ PYTHONPATH=src python3 -m hyprconf2lua ~/.config/hypr/hyprland.conf > hyprland.lua
93
96
  ```
94
97
 
95
- > **"externally managed environment" error?** That's PEP 668 — modern distros protect the system Python from pip. Use the clone+install method above instead — zero pip required.
96
-
97
98
  ---
98
99
 
99
100
  ## Full migration guide
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "hyprconf2lua"
7
- version = "1.2.0"
7
+ version = "1.3.0"
8
8
  description = "Convert Hyprland hyprlang .conf files to Lua .lua config format (v0.55+)"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -0,0 +1 @@
1
+ __version__ = "1.3.0"
@@ -51,7 +51,7 @@ class Codegen:
51
51
 
52
52
  def generate(self, config: ConfigFile) -> str:
53
53
  self.lines = []
54
- self.emit("-- Generated by hyprconf2lua v1.2.0")
54
+ self.emit("-- Generated by hyprconf2lua v1.3.0")
55
55
  self.emit("-- https://github.com/yourusername/hyprconf2lua")
56
56
  self.emit("-- Manual review may be needed for complex directives")
57
57
  self.emit("")
@@ -222,6 +222,53 @@ class Codegen:
222
222
  self.emit("end)")
223
223
  self.emit("")
224
224
 
225
+ def _is_gradient(self, val: str) -> bool:
226
+ parts = val.strip().split()
227
+ if len(parts) < 3:
228
+ return False
229
+ angle = parts[-1]
230
+ if not angle.endswith("deg"):
231
+ return False
232
+ try:
233
+ float(angle[:-3])
234
+ except ValueError:
235
+ return False
236
+ return True
237
+
238
+ def _format_gradient(self, val: str) -> str:
239
+ parts = val.strip().split()
240
+ angle = parts[-1][:-3]
241
+ colors = [self.to_lua_val(p) for p in parts[:-1]]
242
+ return f"{{ colors = {{ {', '.join(colors)} }}, angle = {angle} }}"
243
+
244
+ def _emit_directive(self, key: str, values: List[str]):
245
+ if len(values) == 1 and self._is_gradient(values[0]):
246
+ self.translated_count += 1
247
+ self.emit(f"{key} = {self._format_gradient(values[0])},")
248
+ elif len(values) == 1:
249
+ self.translated_count += 1
250
+ val = self.to_lua_val(values[0])
251
+ self.emit(f"{key} = {val},")
252
+ else:
253
+ self.translated_count += 1
254
+ vals = [self.to_lua_val(v) for v in values]
255
+ self.emit(f"{key} = {{ {', '.join(vals)} }},")
256
+
257
+ def _group_section_body(self, directives: List[BlockStmt]) -> List[BlockStmt]:
258
+ grouped: List[BlockStmt] = []
259
+ dot_groups = {}
260
+ for d in directives:
261
+ if isinstance(d, Directive) and "." in d.key:
262
+ prefix, suffix = d.key.split(".", 1)
263
+ if prefix not in dot_groups:
264
+ dot_groups[prefix] = []
265
+ dot_groups[prefix].append(Directive(suffix, d.value, d.line, d.col))
266
+ else:
267
+ grouped.append(d)
268
+ for prefix, subs in dot_groups.items():
269
+ grouped.append(Section(prefix, subs, 0))
270
+ return grouped
271
+
225
272
  def emit_section_config(self, section_name: str, directives: List[BlockStmt]):
226
273
  if not directives:
227
274
  return
@@ -231,7 +278,7 @@ class Codegen:
231
278
  self.emit(f"{section_name} = {{")
232
279
  self.indent()
233
280
 
234
- for d in directives:
281
+ for d in self._group_section_body(directives):
235
282
  if isinstance(d, Comment):
236
283
  self.emit(f"--{d.text[1:]}")
237
284
  elif isinstance(d, Section):
@@ -242,14 +289,7 @@ class Codegen:
242
289
  if isinstance(sd, Comment):
243
290
  self.emit(f"--{sd.text[1:]}")
244
291
  elif isinstance(sd, Directive):
245
- self.translated_count += 1
246
- key = sd.key
247
- if len(sd.value) == 1:
248
- val = self.to_lua_val(sd.value[0])
249
- self.emit(f"{key} = {val},")
250
- else:
251
- vals = [self.to_lua_val(v) for v in sd.value]
252
- self.emit(f"{key} = {{ {', '.join(vals)} }},")
292
+ self._emit_directive(sd.key, sd.value)
253
293
  elif isinstance(sd, Section):
254
294
  self.passthrough_count += 1
255
295
  self.emit(f"-- Nested subsection {sd.name}:")
@@ -261,14 +301,7 @@ class Codegen:
261
301
  self.dedent()
262
302
  self.emit("},")
263
303
  elif isinstance(d, Directive):
264
- self.translated_count += 1
265
- key = d.key
266
- if len(d.value) == 1:
267
- val = self.to_lua_val(d.value[0])
268
- self.emit(f"{key} = {val},")
269
- else:
270
- vals = [self.to_lua_val(v) for v in d.value]
271
- self.emit(f"{key} = {{ {', '.join(vals)} }},")
304
+ self._emit_directive(d.key, d.value)
272
305
 
273
306
  self.dedent()
274
307
  self.emit("},")
@@ -322,6 +355,15 @@ class Codegen:
322
355
  if not has_plugins:
323
356
  self.emit("-- TODO: manual review (plugin config)")
324
357
  return
358
+ if stmt.name == "device":
359
+ name = ""
360
+ body = []
361
+ for child in stmt.body:
362
+ if isinstance(child, Directive) and child.key == "name":
363
+ name = child.value[0] if child.value else ""
364
+ else:
365
+ body.append(child)
366
+ return self.visit(DeviceSection(name, body, stmt.line))
325
367
  if stmt.name in KNOWN_SECTIONS:
326
368
  self.emit_section_config(stmt.name, stmt.body)
327
369
  else:
@@ -330,17 +372,11 @@ class Codegen:
330
372
  self.indent()
331
373
  self.emit(f"{stmt.name} = {{")
332
374
  self.indent()
333
- for child in stmt.body:
375
+ for child in self._group_section_body(stmt.body):
334
376
  if isinstance(child, Comment):
335
377
  self.emit(f"--{child.text[1:]}")
336
378
  elif isinstance(child, Directive):
337
- k = child.key
338
- if len(child.value) == 1:
339
- v = self.to_lua_val(child.value[0])
340
- self.emit(f"{k} = {v},")
341
- else:
342
- vals = [self.to_lua_val(v) for v in child.value]
343
- self.emit(f"{k} = {{ {', '.join(vals)} }},")
379
+ self._emit_directive(child.key, child.value)
344
380
  self.dedent()
345
381
  self.emit("},")
346
382
  self.dedent()
@@ -51,6 +51,7 @@ DISPATCHER_MAP = {
51
51
  "moveoutofgroup": ("hl.dsp.window.move", True),
52
52
  "focusurgentorlast": ("hl.dsp.focus", True),
53
53
  "focusonemonitor": ("hl.dsp.focus", True),
54
+ "forcekillactive": ("hl.dsp.window.kill", False),
54
55
  }
55
56
 
56
57
  BIND_FLAGS_TO_OPTIONS = {
@@ -302,7 +302,7 @@ class Parser:
302
302
  params = [cmd] if cmd else []
303
303
  else:
304
304
  params = [v.strip() for v in values[3:]]
305
- mods = [m.strip() for m in mods_str.replace(",", " ").split()] if mods_str else []
305
+ mods = [m.strip() for m in mods_str.replace(",", " ").split() if m.strip() and m.strip() != "&"] if mods_str else []
306
306
  return BindDirective(mods, key, dispatcher, params, flags, line)
307
307
 
308
308
  def parse_monitor(self, line: int) -> MonitorDirective:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyprconf2lua
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Summary: Convert Hyprland hyprlang .conf files to Lua .lua config format (v0.55+)
5
5
  Author: hyprconf2lua contributors
6
6
  License: MIT
@@ -26,15 +26,14 @@ Dynamic: license-file
26
26
  Hyprland 0.55+ replaced the old hyprlang config with Lua. The old format will be dropped in a future release. **hyprconf2lua** converts your existing config automatically — no manual rewrite needed.
27
27
 
28
28
  ```bash
29
- git clone https://github.com/Prateek-squadron/hyprconf2lua.git
30
- cd hyprconf2lua
31
- ./install.sh
29
+ pip install hyprconf2lua
32
30
  hyprconf2lua ~/.config/hypr/hyprland.conf -o hyprland.lua
33
31
  ```
34
32
 
35
33
  That's it. ~97% of your config converts cleanly. The rest gets flagged with `-- TODO` comments telling you exactly what to touch up.
36
34
 
37
- > **Not on PyPI yet** — the `pip install hyprconf2lua` command you might see in older posts doesn't work because the package hasn't been published there. Use the clone+install method above. It's zero-dependency, works on every distro, and sidesteps PEP 668 ("externally managed environment") completely. If your distro blocks pip, this is the way.
35
+ > **PEP 668 ("externally managed environment")?** Use the clone method instead zero pip needed:
36
+ > `git clone https://github.com/Prateek-squadron/hyprconf2lua.git && cd hyprconf2lua && ./install.sh`
38
37
 
39
38
  ---
40
39
 
@@ -88,33 +87,35 @@ end)
88
87
 
89
88
  ## Installation
90
89
 
91
- ### Quick install (recommended)
90
+ ### pip install (recommended)
92
91
 
93
92
  ```bash
94
- git clone https://github.com/Prateek-squadron/hyprconf2lua.git
95
- cd hyprconf2lua
96
- ./install.sh # symlinks hyprconf2lua -> ~/.local/bin
93
+ pip install hyprconf2lua # or: pip install --user hyprconf2lua
97
94
  hyprconf2lua ~/.config/hypr/hyprland.conf -o hyprland.lua
98
95
  ```
99
96
 
100
- ### One-shot (no install)
97
+ ### pipx install
98
+
99
+ ```bash
100
+ pipx install hyprconf2lua # isolated, no PEP 668 issues
101
+ ```
102
+
103
+ ### Clone + install (no pip, works everywhere)
101
104
 
102
105
  ```bash
103
106
  git clone https://github.com/Prateek-squadron/hyprconf2lua.git
104
107
  cd hyprconf2lua
105
- PYTHONPATH=src python3 -m hyprconf2lua ~/.config/hypr/hyprland.conf > hyprland.lua
108
+ ./install.sh # symlinks to ~/.local/bin
106
109
  ```
107
110
 
108
- ### pip install (if your distro allows it)
111
+ ### One-shot (no install at all)
109
112
 
110
113
  ```bash
111
- pip install --user .
112
- # or
113
- pip install --break-system-packages .
114
+ git clone https://github.com/Prateek-squadron/hyprconf2lua.git
115
+ cd hyprconf2lua
116
+ PYTHONPATH=src python3 -m hyprconf2lua ~/.config/hypr/hyprland.conf > hyprland.lua
114
117
  ```
115
118
 
116
- > **"externally managed environment" error?** That's PEP 668 — modern distros protect the system Python from pip. Use the clone+install method above instead — zero pip required.
117
-
118
119
  ---
119
120
 
120
121
  ## Full migration guide
@@ -528,6 +528,45 @@ def test_bracket_in_windowrule_params():
528
528
  assert result.success
529
529
 
530
530
 
531
+ def test_forcekillactive_dispatcher():
532
+ result = convert('bind = SUPER, Q, forcekillactive\n')
533
+ assert result.success
534
+ assert "hl.dsp.window.kill" in result.lua
535
+
536
+
537
+ def test_ampersand_in_bind_mods():
538
+ result = convert('bind = $mainMod & SHIFT & A, exec, kitty\n')
539
+ assert result.success
540
+ assert "hl.bind" in result.lua
541
+ assert "&" not in result.lua.split("hl.bind")[-1].split("hl.dsp")[0]
542
+
543
+
544
+ def test_ampersand_in_bind_mods_nospace():
545
+ result = convert('bind = $mainMod&$SHIFT&A, exec, kitty\n')
546
+ assert result.success
547
+
548
+
549
+ def test_device_without_prefix():
550
+ result = convert('device {\n name = razer-cobra-pro\n sensitivity = 0.0\n}\n')
551
+ assert result.success
552
+ assert "hl.device" in result.lua
553
+ assert "razer-cobra-pro" in result.lua
554
+
555
+
556
+ def test_gradient_conversion():
557
+ result = convert('general {\n col.active_border = rgba(33ccffee) rgba(00ff99ee) 45deg\n}\n')
558
+ assert result.success
559
+ assert "colors" in result.lua
560
+ assert "angle = 45" in result.lua
561
+
562
+
563
+ def test_col_dot_prefix_grouped():
564
+ result = convert('general {\n col.active_border = rgba(33ccffee) rgba(00ff99ee) 45deg\n col.inactive_border = rgba(595959aa)\n gaps_in = 5\n}\n')
565
+ assert result.success
566
+ # Both col.* keys should be grouped under one 'col' section
567
+ assert result.lua.count("col = {") == 1
568
+
569
+
531
570
  if __name__ == "__main__":
532
571
  test_lexer_basic()
533
572
  test_lexer_comment()
@@ -574,4 +613,10 @@ if __name__ == "__main__":
574
613
  test_bracket_in_workspace()
575
614
  test_bracket_in_exec_on()
576
615
  test_bracket_in_windowrule_params()
616
+ test_forcekillactive_dispatcher()
617
+ test_ampersand_in_bind_mods()
618
+ test_ampersand_in_bind_mods_nospace()
619
+ test_device_without_prefix()
620
+ test_gradient_conversion()
621
+ test_col_dot_prefix_grouped()
577
622
  print("All tests passed!")
@@ -1 +0,0 @@
1
- __version__ = "1.0.0"
File without changes
File without changes