forked from 7Cav/cScripts
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsqf_linter.py
More file actions
87 lines (72 loc) · 3.08 KB
/
sqf_linter.py
File metadata and controls
87 lines (72 loc) · 3.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#!/usr/bin/env python3
# Requires: https://github.com/LordGolias/sqf
import os
import sys
import argparse
import concurrent.futures
from sqf.parser import parse
import sqf.analyzer
from sqf.exceptions import SQFParserError
addon_base_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
files_to_ignore_lower = [
x.lower() for x in ["initSettings.sqf", "initKeybinds.sqf", "XEH_PREP.sqf"]
]
def get_files_to_process(basePath):
arma_files = []
for (root, _dirs, files) in os.walk(basePath):
for file in files:
if file.endswith(".sqf"):
if file.lower() in files_to_ignore_lower:
continue
filePath = os.path.join(root, file)
arma_files.append(filePath)
return arma_files
def process_file(filePath):
errors = []
warnings = []
try:
with open(filePath, "r", encoding="utf-8", errors="ignore") as file:
content = file.read()
if "#ASC_ignoreFile" in content:
return (filePath, errors, warnings)
sqfLintParse = parse(content)
exceptions = sqf.analyzer.analyze(sqfLintParse).exceptions
if (exceptions):
for e in exceptions:
if ("assigned to an outer scope" in e.message):
warnings.append(f"[{e.position[0]},{e.position[1]}] {e.message}")
if ("is not from this scope" in e.message):
warnings.append(f"[{e.position[0]},{e.position[1]}] {e.message}")
if ("not used" in e.message):
warnings.append(f"[{e.position[0]},{e.position[1]}] {e.message}")
# most of this is just noise about macro parsing:
# if (e.message.startswith("error")):
# errors.append(f"[{e.position[0]},{e.position[1]}] {e.message}")
# else:
# warnings.append(f"[{e.position[0]},{e.position[1]}] {e.message}")
except Exception as e:
# errors.append(f"Exception {e}")
pass
return (filePath, errors, warnings)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-m', '--module', help='only search specified module addon folder', required=False, default=".")
args = parser.parse_args()
error_count = 0
addon_base_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
if (args.module): addon_base_path = os.path.join(addon_base_path, "cScripts", args.module)
arma_files = get_files_to_process(addon_base_path)
print(f"Checking {len(arma_files)} files from {addon_base_path}")
with concurrent.futures.ThreadPoolExecutor(max_workers=12) as executor:
for (filePath, errors, warnings) in executor.map(process_file, arma_files):
if errors or warnings:
error_count += 1
print(f"{filePath}")
for e in errors:
print(f" {e}")
for e in warnings:
print(f" {e}")
print("Errors: {}".format(error_count))
return error_count
if __name__ == "__main__":
sys.exit(main())