hyprconf2lua 1.2.0__py3-none-any.whl
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/__init__.py +1 -0
- hyprconf2lua/__main__.py +4 -0
- hyprconf2lua/ast.py +171 -0
- hyprconf2lua/cli.py +173 -0
- hyprconf2lua/codegen.py +931 -0
- hyprconf2lua/converter.py +60 -0
- hyprconf2lua/lexer.py +74 -0
- hyprconf2lua/mappings.py +130 -0
- hyprconf2lua/parser.py +494 -0
- hyprconf2lua-1.2.0.dist-info/METADATA +242 -0
- hyprconf2lua-1.2.0.dist-info/RECORD +15 -0
- hyprconf2lua-1.2.0.dist-info/WHEEL +5 -0
- hyprconf2lua-1.2.0.dist-info/entry_points.txt +2 -0
- hyprconf2lua-1.2.0.dist-info/licenses/LICENSE +21 -0
- hyprconf2lua-1.2.0.dist-info/top_level.txt +1 -0
hyprconf2lua/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.0.0"
|
hyprconf2lua/__main__.py
ADDED
hyprconf2lua/ast.py
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from typing import Dict, List, Optional, Union
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
Value = Union[str, List["Value"]]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class Comment:
|
|
11
|
+
text: str
|
|
12
|
+
line: int
|
|
13
|
+
|
|
14
|
+
def __str__(self) -> str:
|
|
15
|
+
return self.text
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class Directive:
|
|
20
|
+
key: str
|
|
21
|
+
value: List[str]
|
|
22
|
+
line: int
|
|
23
|
+
col: int
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class Section:
|
|
28
|
+
name: str
|
|
29
|
+
body: Block
|
|
30
|
+
line: int
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class VariableDef:
|
|
35
|
+
name: str
|
|
36
|
+
value: str
|
|
37
|
+
line: int
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class ExecDirective:
|
|
42
|
+
kind: str
|
|
43
|
+
command: str
|
|
44
|
+
line: int
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class BindDirective:
|
|
49
|
+
mods: List[str]
|
|
50
|
+
key: str
|
|
51
|
+
dispatcher: str
|
|
52
|
+
params: List[str]
|
|
53
|
+
flags: str
|
|
54
|
+
line: int
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class MonitorDirective:
|
|
59
|
+
name: str
|
|
60
|
+
mode: str
|
|
61
|
+
position: str
|
|
62
|
+
scale: str
|
|
63
|
+
line: int
|
|
64
|
+
extra: Dict[str, str] = field(default_factory=dict)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass
|
|
68
|
+
class WindowRule:
|
|
69
|
+
is_v2: bool
|
|
70
|
+
rule: str
|
|
71
|
+
match_params: List[str]
|
|
72
|
+
line: int
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@dataclass
|
|
76
|
+
class AnimationDirective:
|
|
77
|
+
name: str
|
|
78
|
+
style: str
|
|
79
|
+
speed: str
|
|
80
|
+
curve: str
|
|
81
|
+
line: int
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass
|
|
85
|
+
class BezierDirective:
|
|
86
|
+
name: str
|
|
87
|
+
p1x: str
|
|
88
|
+
p1y: str
|
|
89
|
+
p2x: str
|
|
90
|
+
p2y: str
|
|
91
|
+
line: int
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@dataclass
|
|
95
|
+
class EnvDirective:
|
|
96
|
+
name: str
|
|
97
|
+
value: str
|
|
98
|
+
line: int
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@dataclass
|
|
102
|
+
class SourceDirective:
|
|
103
|
+
path: str
|
|
104
|
+
line: int
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@dataclass
|
|
108
|
+
class DeviceSection:
|
|
109
|
+
name: str
|
|
110
|
+
body: List[Directive]
|
|
111
|
+
line: int
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@dataclass
|
|
115
|
+
class GestureDirective:
|
|
116
|
+
body: List[Directive]
|
|
117
|
+
line: int
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@dataclass
|
|
121
|
+
class WorkspaceDirective:
|
|
122
|
+
name: str
|
|
123
|
+
params: List[str]
|
|
124
|
+
line: int
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@dataclass
|
|
128
|
+
class LayerRuleDirective:
|
|
129
|
+
rule: str
|
|
130
|
+
namespace: str
|
|
131
|
+
line: int
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@dataclass
|
|
135
|
+
class SubmapDef:
|
|
136
|
+
name: str
|
|
137
|
+
body: List[BlockStmt]
|
|
138
|
+
line: int
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@dataclass
|
|
142
|
+
class WindowRuleBlock:
|
|
143
|
+
is_v2: bool
|
|
144
|
+
name: str
|
|
145
|
+
match: Dict[str, str]
|
|
146
|
+
effects: Dict[str, List[str]]
|
|
147
|
+
line: int
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@dataclass
|
|
151
|
+
class LayerRuleBlock:
|
|
152
|
+
name: str
|
|
153
|
+
match: Dict[str, str]
|
|
154
|
+
effects: Dict[str, List[str]]
|
|
155
|
+
line: int
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
BlockStmt = Union[
|
|
159
|
+
Directive, Section, VariableDef, ExecDirective, BindDirective,
|
|
160
|
+
MonitorDirective, WindowRule, AnimationDirective, BezierDirective,
|
|
161
|
+
EnvDirective, SourceDirective, DeviceSection, GestureDirective,
|
|
162
|
+
WorkspaceDirective, LayerRuleDirective, SubmapDef, Comment,
|
|
163
|
+
WindowRuleBlock, LayerRuleBlock,
|
|
164
|
+
]
|
|
165
|
+
|
|
166
|
+
Block = List[BlockStmt]
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@dataclass
|
|
170
|
+
class ConfigFile:
|
|
171
|
+
body: Block
|
hyprconf2lua/cli.py
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import argparse
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from hyprconf2lua.converter import convert
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def convert_file(path: str, check: bool = False, report: bool = False) -> bool:
|
|
10
|
+
try:
|
|
11
|
+
with open(path, "r") as f:
|
|
12
|
+
source = f.read()
|
|
13
|
+
except FileNotFoundError:
|
|
14
|
+
print(f"Error: file not found: {path}", file=sys.stderr)
|
|
15
|
+
return False
|
|
16
|
+
except IOError as e:
|
|
17
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
18
|
+
return False
|
|
19
|
+
|
|
20
|
+
result = convert(source)
|
|
21
|
+
|
|
22
|
+
if result.errors:
|
|
23
|
+
for err in result.errors:
|
|
24
|
+
print(f"Error: {err}", file=sys.stderr)
|
|
25
|
+
return False
|
|
26
|
+
|
|
27
|
+
if result.warnings:
|
|
28
|
+
for w in result.warnings:
|
|
29
|
+
print(f"Warning: {w}", file=sys.stderr)
|
|
30
|
+
|
|
31
|
+
if check:
|
|
32
|
+
if result.report["flagged"] > 0:
|
|
33
|
+
print(f"Check FAILED: {result.report['flagged']} flagged directives",
|
|
34
|
+
file=sys.stderr)
|
|
35
|
+
return False
|
|
36
|
+
return True
|
|
37
|
+
|
|
38
|
+
return result.lua
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def process_dir(dir_path: str, in_place: bool = False, check: bool = False,
|
|
42
|
+
report: bool = False) -> int:
|
|
43
|
+
failed = 0
|
|
44
|
+
for root, dirs, files in os.walk(dir_path):
|
|
45
|
+
for fname in files:
|
|
46
|
+
if not fname.endswith(".conf"):
|
|
47
|
+
continue
|
|
48
|
+
fpath = os.path.join(root, fname)
|
|
49
|
+
lua_path = os.path.splitext(fpath)[0] + ".lua"
|
|
50
|
+
|
|
51
|
+
if not check and not in_place:
|
|
52
|
+
continue
|
|
53
|
+
|
|
54
|
+
lua_output = convert_file(fpath, check=check, report=report)
|
|
55
|
+
if lua_output is False:
|
|
56
|
+
failed += 1
|
|
57
|
+
if check:
|
|
58
|
+
print(f" FAIL: {fpath}", file=sys.stderr)
|
|
59
|
+
continue
|
|
60
|
+
|
|
61
|
+
if isinstance(lua_output, str):
|
|
62
|
+
if check:
|
|
63
|
+
print(f" PASS: {fpath}")
|
|
64
|
+
continue
|
|
65
|
+
if in_place:
|
|
66
|
+
try:
|
|
67
|
+
with open(lua_path, "w") as f:
|
|
68
|
+
f.write(lua_output)
|
|
69
|
+
print(f" WROTE: {lua_path}")
|
|
70
|
+
except IOError as e:
|
|
71
|
+
print(f" ERROR writing {lua_path}: {e}", file=sys.stderr)
|
|
72
|
+
failed += 1
|
|
73
|
+
|
|
74
|
+
return failed
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def main():
|
|
78
|
+
parser = argparse.ArgumentParser(
|
|
79
|
+
description="Convert Hyprland hyprlang .conf to Lua .lua config (v0.55+)",
|
|
80
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
81
|
+
epilog="""
|
|
82
|
+
Examples:
|
|
83
|
+
hyprconf2lua hyprland.conf > hyprland.lua
|
|
84
|
+
hyprconf2lua --in hyprland.conf --out hyprland.lua
|
|
85
|
+
hyprconf2lua --dir ~/.config/hypr --in-place
|
|
86
|
+
cat hyprland.conf | hyprconf2lua > hyprland.lua
|
|
87
|
+
hyprconf2lua --check hyprland.conf
|
|
88
|
+
""",
|
|
89
|
+
)
|
|
90
|
+
parser.add_argument("file", nargs="?", help="Input .conf file (reads stdin if omitted)")
|
|
91
|
+
parser.add_argument("-o", "--out", help="Output file (default: stdout)")
|
|
92
|
+
parser.add_argument("-d", "--dir", help="Walk a directory, writing .lua next to each .conf")
|
|
93
|
+
parser.add_argument("--in-place", action="store_true", help="With --dir, overwrite existing .lua siblings")
|
|
94
|
+
parser.add_argument("--check", action="store_true", help="Exit 3 if any directive is flagged")
|
|
95
|
+
parser.add_argument("--report", action="store_true", help="Print translation stats to stderr")
|
|
96
|
+
parser.add_argument("--version", action="store_true", help="Print version and exit")
|
|
97
|
+
|
|
98
|
+
args = parser.parse_args()
|
|
99
|
+
|
|
100
|
+
if args.version:
|
|
101
|
+
from hyprconf2lua import __version__
|
|
102
|
+
print(f"hyprconf2lua v{__version__}")
|
|
103
|
+
sys.exit(0)
|
|
104
|
+
|
|
105
|
+
if args.dir:
|
|
106
|
+
failed = process_dir(args.dir, in_place=args.in_place, check=args.check, report=args.report)
|
|
107
|
+
if args.check and failed > 0:
|
|
108
|
+
sys.exit(3)
|
|
109
|
+
sys.exit(1 if failed > 0 else 0)
|
|
110
|
+
|
|
111
|
+
source: str
|
|
112
|
+
source_name: str = args.file or "stdin"
|
|
113
|
+
|
|
114
|
+
if args.file:
|
|
115
|
+
try:
|
|
116
|
+
with open(args.file, "r") as f:
|
|
117
|
+
source = f.read()
|
|
118
|
+
except FileNotFoundError:
|
|
119
|
+
print(f"Error: file not found: {args.file}", file=sys.stderr)
|
|
120
|
+
sys.exit(1)
|
|
121
|
+
except IOError as e:
|
|
122
|
+
print(f"Error reading {args.file}: {e}", file=sys.stderr)
|
|
123
|
+
sys.exit(1)
|
|
124
|
+
else:
|
|
125
|
+
if sys.stdin.isatty():
|
|
126
|
+
parser.print_help()
|
|
127
|
+
sys.exit(0)
|
|
128
|
+
source = sys.stdin.read()
|
|
129
|
+
|
|
130
|
+
result = convert(source)
|
|
131
|
+
|
|
132
|
+
for err in result.errors:
|
|
133
|
+
print(f"Error: {err}", file=sys.stderr)
|
|
134
|
+
|
|
135
|
+
if result.errors:
|
|
136
|
+
sys.exit(1)
|
|
137
|
+
|
|
138
|
+
for w in result.warnings:
|
|
139
|
+
print(f"Warning: {w}", file=sys.stderr)
|
|
140
|
+
|
|
141
|
+
if not result.lua.strip():
|
|
142
|
+
print("Error: conversion produced no output", file=sys.stderr)
|
|
143
|
+
sys.exit(1)
|
|
144
|
+
|
|
145
|
+
if args.report:
|
|
146
|
+
r = result.report
|
|
147
|
+
total = r["translated"] + r["passthrough"] + r["flagged"]
|
|
148
|
+
cov = result.coverage
|
|
149
|
+
print(f"Report: {r['translated']} translated, {r['passthrough']} passthrough, "
|
|
150
|
+
f"{r['flagged']} flagged, {total} total, {cov}% coverage",
|
|
151
|
+
file=sys.stderr)
|
|
152
|
+
|
|
153
|
+
if args.check:
|
|
154
|
+
if result.report["flagged"] > 0:
|
|
155
|
+
print(f"Check FAILED: {result.report['flagged']} flagged directive(s)",
|
|
156
|
+
file=sys.stderr)
|
|
157
|
+
sys.exit(3)
|
|
158
|
+
sys.exit(0)
|
|
159
|
+
|
|
160
|
+
if args.out:
|
|
161
|
+
try:
|
|
162
|
+
with open(args.out, "w") as f:
|
|
163
|
+
f.write(result.lua)
|
|
164
|
+
except IOError as e:
|
|
165
|
+
print(f"Error writing {args.out}: {e}", file=sys.stderr)
|
|
166
|
+
sys.exit(1)
|
|
167
|
+
else:
|
|
168
|
+
sys.stdout.write(result.lua)
|
|
169
|
+
sys.stdout.flush()
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
if __name__ == "__main__":
|
|
173
|
+
main()
|