hyprconf2lua 1.2.1__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.1/src/hyprconf2lua.egg-info → hyprconf2lua-1.3.0}/PKG-INFO +1 -1
  2. {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.0}/pyproject.toml +1 -1
  3. hyprconf2lua-1.3.0/src/hyprconf2lua/__init__.py +1 -0
  4. {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.0}/src/hyprconf2lua/codegen.py +62 -26
  5. {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.0}/src/hyprconf2lua/mappings.py +1 -0
  6. {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.0}/src/hyprconf2lua/parser.py +1 -1
  7. {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.0/src/hyprconf2lua.egg-info}/PKG-INFO +1 -1
  8. {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.0}/tests/test_converter.py +45 -0
  9. hyprconf2lua-1.2.1/src/hyprconf2lua/__init__.py +0 -1
  10. {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.0}/LICENSE +0 -0
  11. {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.0}/README.md +0 -0
  12. {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.0}/setup.cfg +0 -0
  13. {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.0}/src/hyprconf2lua/__main__.py +0 -0
  14. {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.0}/src/hyprconf2lua/ast.py +0 -0
  15. {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.0}/src/hyprconf2lua/cli.py +0 -0
  16. {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.0}/src/hyprconf2lua/converter.py +0 -0
  17. {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.0}/src/hyprconf2lua/lexer.py +0 -0
  18. {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.0}/src/hyprconf2lua.egg-info/SOURCES.txt +0 -0
  19. {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.0}/src/hyprconf2lua.egg-info/dependency_links.txt +0 -0
  20. {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.0}/src/hyprconf2lua.egg-info/entry_points.txt +0 -0
  21. {hyprconf2lua-1.2.1 → 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.1
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "hyprconf2lua"
7
- version = "1.2.1"
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.1")
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.1
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
@@ -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.2.1"
File without changes
File without changes
File without changes