95 lines
2.7 KiB
Python
95 lines
2.7 KiB
Python
|
|
import ast
|
||
|
|
import platform
|
||
|
|
import subprocess
|
||
|
|
import urllib.request
|
||
|
|
import zipfile
|
||
|
|
import io
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
PROJECT_DIR = Path(__file__).parent.parent
|
||
|
|
BIN_DIR = PROJECT_DIR / "bin"
|
||
|
|
|
||
|
|
STYLUA_VERSION = "0.20.0"
|
||
|
|
STYLUA_URL = (
|
||
|
|
f"https://github.com/JohnnyMorganz/StyLua/releases/download/v{STYLUA_VERSION}/"
|
||
|
|
f"stylua-windows-x86_64.zip"
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def format_python(code: str) -> dict:
|
||
|
|
try:
|
||
|
|
ast.parse(code)
|
||
|
|
except SyntaxError as e:
|
||
|
|
return {
|
||
|
|
"ok": False,
|
||
|
|
"error": f"SyntaxError: {e.msg}",
|
||
|
|
"line": e.lineno,
|
||
|
|
"formatted": code,
|
||
|
|
}
|
||
|
|
|
||
|
|
try:
|
||
|
|
import black
|
||
|
|
formatted = black.format_str(code, mode=black.Mode())
|
||
|
|
return {"ok": True, "error": None, "line": None, "formatted": formatted}
|
||
|
|
except Exception as e:
|
||
|
|
return {"ok": False, "error": str(e), "line": None, "formatted": code}
|
||
|
|
|
||
|
|
|
||
|
|
def _stylua_exe() -> Path:
|
||
|
|
return BIN_DIR / ("stylua.exe" if platform.system() == "Windows" else "stylua")
|
||
|
|
|
||
|
|
|
||
|
|
def _ensure_stylua() -> Path | None:
|
||
|
|
exe = _stylua_exe()
|
||
|
|
if exe.exists():
|
||
|
|
return exe
|
||
|
|
|
||
|
|
if platform.system() != "Windows":
|
||
|
|
return None
|
||
|
|
|
||
|
|
BIN_DIR.mkdir(parents=True, exist_ok=True)
|
||
|
|
try:
|
||
|
|
with urllib.request.urlopen(STYLUA_URL, timeout=30) as resp:
|
||
|
|
data = resp.read()
|
||
|
|
with zipfile.ZipFile(io.BytesIO(data)) as zf:
|
||
|
|
for name in zf.namelist():
|
||
|
|
if Path(name).name.lower() == "stylua.exe":
|
||
|
|
exe.write_bytes(zf.read(name))
|
||
|
|
return exe
|
||
|
|
except Exception:
|
||
|
|
return None
|
||
|
|
return None
|
||
|
|
|
||
|
|
|
||
|
|
def format_lua(code: str) -> dict:
|
||
|
|
exe = _ensure_stylua()
|
||
|
|
if not exe:
|
||
|
|
return {
|
||
|
|
"ok": False,
|
||
|
|
"error": "stylua не удалось скачать автоматически — проверь интернет или положи stylua.exe в bin\\ вручную.",
|
||
|
|
"line": None,
|
||
|
|
"formatted": code,
|
||
|
|
}
|
||
|
|
|
||
|
|
try:
|
||
|
|
result = subprocess.run(
|
||
|
|
[str(exe), "-"],
|
||
|
|
input=code,
|
||
|
|
capture_output=True,
|
||
|
|
text=True,
|
||
|
|
timeout=15,
|
||
|
|
)
|
||
|
|
if result.returncode != 0:
|
||
|
|
return {"ok": False, "error": result.stderr.strip(), "line": None, "formatted": code}
|
||
|
|
return {"ok": True, "error": None, "line": None, "formatted": result.stdout}
|
||
|
|
except Exception as e:
|
||
|
|
return {"ok": False, "error": str(e), "line": None, "formatted": code}
|
||
|
|
|
||
|
|
|
||
|
|
def format_code(code: str, language: str) -> dict:
|
||
|
|
if language == "python":
|
||
|
|
return format_python(code)
|
||
|
|
if language == "lua":
|
||
|
|
return format_lua(code)
|
||
|
|
return {"ok": False, "error": f"unsupported language: {language}", "line": None, "formatted": code}
|