hyprconf2lua 1.2.1__tar.gz → 1.3.1__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.
- {hyprconf2lua-1.2.1/src/hyprconf2lua.egg-info → hyprconf2lua-1.3.1}/PKG-INFO +1 -1
- {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.1}/pyproject.toml +1 -1
- hyprconf2lua-1.3.1/src/hyprconf2lua/__init__.py +1 -0
- {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.1}/src/hyprconf2lua/codegen.py +68 -28
- {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.1}/src/hyprconf2lua/mappings.py +1 -0
- {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.1}/src/hyprconf2lua/parser.py +36 -9
- {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.1/src/hyprconf2lua.egg-info}/PKG-INFO +1 -1
- {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.1}/tests/test_converter.py +82 -0
- hyprconf2lua-1.2.1/src/hyprconf2lua/__init__.py +0 -1
- {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.1}/LICENSE +0 -0
- {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.1}/README.md +0 -0
- {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.1}/setup.cfg +0 -0
- {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.1}/src/hyprconf2lua/__main__.py +0 -0
- {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.1}/src/hyprconf2lua/ast.py +0 -0
- {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.1}/src/hyprconf2lua/cli.py +0 -0
- {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.1}/src/hyprconf2lua/converter.py +0 -0
- {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.1}/src/hyprconf2lua/lexer.py +0 -0
- {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.1}/src/hyprconf2lua.egg-info/SOURCES.txt +0 -0
- {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.1}/src/hyprconf2lua.egg-info/dependency_links.txt +0 -0
- {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.1}/src/hyprconf2lua.egg-info/entry_points.txt +0 -0
- {hyprconf2lua-1.2.1 → hyprconf2lua-1.3.1}/src/hyprconf2lua.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.3.1"
|
|
@@ -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.
|
|
54
|
+
self.emit("-- Generated by hyprconf2lua v1.3.1")
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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()
|
|
@@ -470,8 +506,12 @@ class Codegen:
|
|
|
470
506
|
if dispatcher in DISPATCHER_MAP:
|
|
471
507
|
func, needs_args = DISPATCHER_MAP[dispatcher]
|
|
472
508
|
|
|
509
|
+
if dispatcher == "movetoworkspace":
|
|
510
|
+
args = self.build_dispatcher_args(params, needs_args, func)
|
|
511
|
+
return f'{func}({args})'
|
|
512
|
+
|
|
473
513
|
if dispatcher == "movetoworkspacesilent":
|
|
474
|
-
args = self.build_dispatcher_args(params, needs_args)
|
|
514
|
+
args = self.build_dispatcher_args(params, needs_args, func)
|
|
475
515
|
return f'{func}({args}, {{ follow = false }})'
|
|
476
516
|
|
|
477
517
|
if dispatcher == "movefocus":
|
|
@@ -535,7 +575,7 @@ class Codegen:
|
|
|
535
575
|
resolved = self.resolve_val(params[0]) if params else ""
|
|
536
576
|
return f'{func}({self.quote(resolved)})'
|
|
537
577
|
|
|
538
|
-
args = self.build_dispatcher_args(params, needs_args)
|
|
578
|
+
args = self.build_dispatcher_args(params, needs_args, func)
|
|
539
579
|
return f'{func}({args})' if needs_args else f'{func}()'
|
|
540
580
|
|
|
541
581
|
if dispatcher == "mouse":
|
|
@@ -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:
|
|
@@ -344,15 +344,42 @@ class Parser:
|
|
|
344
344
|
break
|
|
345
345
|
key = ":".join(key_parts)
|
|
346
346
|
|
|
347
|
-
self.
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
347
|
+
if key == "match" and self.peek().type == "BLOCK_OPEN":
|
|
348
|
+
self.advance()
|
|
349
|
+
self.skip_newlines()
|
|
350
|
+
while self.peek().type not in ("BLOCK_CLOSE", "EOF"):
|
|
351
|
+
t2 = self.peek()
|
|
352
|
+
if t2.type == "COMMENT":
|
|
353
|
+
self.advance()
|
|
354
|
+
elif t2.type == "IDENT":
|
|
355
|
+
mk_parts = [self.advance().value]
|
|
356
|
+
while self.peek().type == "COLON":
|
|
357
|
+
self.advance()
|
|
358
|
+
if self.peek().type == "IDENT":
|
|
359
|
+
mk_parts.append(self.advance().value)
|
|
360
|
+
else:
|
|
361
|
+
break
|
|
362
|
+
mk_key = ":".join(mk_parts)
|
|
363
|
+
self.expect("EQUALS")
|
|
364
|
+
mk_val = self.parse_value_rest()
|
|
365
|
+
if mk_key.startswith("match:"):
|
|
366
|
+
match[mk_key[len("match:"):]] = mk_val
|
|
367
|
+
else:
|
|
368
|
+
match[mk_key] = mk_val
|
|
369
|
+
else:
|
|
370
|
+
self.advance()
|
|
371
|
+
self.skip_newlines()
|
|
372
|
+
self.expect("BLOCK_CLOSE")
|
|
354
373
|
else:
|
|
355
|
-
|
|
374
|
+
self.expect("EQUALS")
|
|
375
|
+
val = self.parse_value_rest()
|
|
376
|
+
|
|
377
|
+
if key == "name":
|
|
378
|
+
name = val
|
|
379
|
+
elif key.startswith("match:"):
|
|
380
|
+
match[key[len("match:"):]] = val
|
|
381
|
+
else:
|
|
382
|
+
effects[key] = [val]
|
|
356
383
|
else:
|
|
357
384
|
self.advance()
|
|
358
385
|
self.skip_newlines()
|
|
@@ -528,6 +528,77 @@ 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
|
+
assert result.lua.count("col = {") == 1
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
def test_match_block_in_windowrule():
|
|
570
|
+
result = convert('windowrule {\n name = my-rule\n float = on\n match {\n class = .*\n title = my-window\n }\n}\n')
|
|
571
|
+
assert result.success
|
|
572
|
+
assert "class = \".*\"" in result.lua
|
|
573
|
+
assert "title = \"my-window\"" in result.lua
|
|
574
|
+
assert "float = true" in result.lua
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
def test_match_block_in_windowrulev2():
|
|
578
|
+
result = convert('windowrulev2 {\n name = v2-match\n opacity = 0.9 0.8\n match {\n class = (kitty)\n }\n}\n')
|
|
579
|
+
assert result.success
|
|
580
|
+
assert "class = \"(kitty)\"" in result.lua
|
|
581
|
+
assert "opacity = { 0.9, 0.8 }" in result.lua
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
def test_match_block_in_layerrule():
|
|
585
|
+
result = convert('layerrule {\n name = my-layer\n blur = true\n match {\n namespace = rofi\n }\n}\n')
|
|
586
|
+
assert result.success
|
|
587
|
+
assert "namespace" in result.lua
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
def test_movetoworkspace_conversion():
|
|
591
|
+
result = convert('bind = $mainMod SHIFT, 1, movetoworkspace, 1\n')
|
|
592
|
+
assert result.success
|
|
593
|
+
assert "workspace = 1" in result.lua
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
def test_movetoworkspacesilent_conversion():
|
|
597
|
+
result = convert('bind = $mainMod SHIFT, 1, movetoworkspacesilent, 1\n')
|
|
598
|
+
assert result.success
|
|
599
|
+
assert "workspace = 1" in result.lua
|
|
600
|
+
|
|
601
|
+
|
|
531
602
|
if __name__ == "__main__":
|
|
532
603
|
test_lexer_basic()
|
|
533
604
|
test_lexer_comment()
|
|
@@ -574,4 +645,15 @@ if __name__ == "__main__":
|
|
|
574
645
|
test_bracket_in_workspace()
|
|
575
646
|
test_bracket_in_exec_on()
|
|
576
647
|
test_bracket_in_windowrule_params()
|
|
648
|
+
test_forcekillactive_dispatcher()
|
|
649
|
+
test_ampersand_in_bind_mods()
|
|
650
|
+
test_ampersand_in_bind_mods_nospace()
|
|
651
|
+
test_device_without_prefix()
|
|
652
|
+
test_gradient_conversion()
|
|
653
|
+
test_col_dot_prefix_grouped()
|
|
654
|
+
test_match_block_in_windowrule()
|
|
655
|
+
test_match_block_in_windowrulev2()
|
|
656
|
+
test_match_block_in_layerrule()
|
|
657
|
+
test_movetoworkspace_conversion()
|
|
658
|
+
test_movetoworkspacesilent_conversion()
|
|
577
659
|
print("All tests passed!")
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.2.1"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|