Skip to content

Commit eebb860

Browse files
committed
Port over our lsp's format
1 parent 6c514ca commit eebb860

File tree

1 file changed

+105
-25
lines changed

1 file changed

+105
-25
lines changed

format.py

Lines changed: 105 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
import subprocess
44
import re
55
import os
6+
import tempfile
7+
import pathlib
68

79
SETTINGS_PATH = 'Default.sublime-settings'
810

9-
poorMansErrorParser = re.compile(r'Syntax error!\n.+ \((\d+),(\d+)\):(.+)')
11+
resExt = ".res"
12+
resiExt = ".resi"
1013

1114
def findBsConfigDirFromFilename(filename):
1215
currentDir = os.path.dirname(filename)
@@ -20,6 +23,89 @@ def findBsConfigDirFromFilename(filename):
2023

2124
currentDir = parentDir
2225

26+
def formatUsingValidBscPath(code, bscPath, isInterface):
27+
extension = resiExt if isInterface else resExt
28+
tmpobj = tempfile.NamedTemporaryFile(mode='w+', suffix=extension, delete=False)
29+
tmpobj.write(code)
30+
tmpobj.close()
31+
proc = subprocess.Popen(
32+
[bscPath, "-color", "never", "-format", tmpobj.name],
33+
stderr=subprocess.PIPE,
34+
stdout=subprocess.PIPE,
35+
)
36+
stdout, stderr = proc.communicate()
37+
if proc.returncode == 0:
38+
return {
39+
"kind": "success",
40+
"result": stdout.decode(),
41+
}
42+
else:
43+
return {
44+
"kind": "error",
45+
"result": stderr.decode(),
46+
}
47+
48+
# Copied over from rescript-language-server's server.ts
49+
def parseBsbOutputLocation(location):
50+
# example bsb output location:
51+
# 3:9
52+
# 3:5-8
53+
# 3:9-6:1
54+
55+
# language-server position is 0-based. Ours is 1-based. Don't forget to convert
56+
# also, our end character is inclusive. Language-server's is exclusive
57+
isRange = location.find("-") >= 0
58+
if isRange:
59+
[from_, to] = location.split("-")
60+
[fromLine, fromChar] = from_.split(":")
61+
isSingleLine = to.find(":") >= 0
62+
[toLine, toChar] = to.split(":") if isSingleLine else [fromLine, to]
63+
return {
64+
"start": {"line": int(fromLine) - 1, "character": int(fromChar) - 1},
65+
"end": {"line": int(toLine) - 1, "character": int(toChar)},
66+
}
67+
else:
68+
[line, char] = location.split(":")
69+
start = {"line": int(line) - 1, "character": int(char)}
70+
return {
71+
"start": start,
72+
"end": start,
73+
}
74+
75+
# Copied over from rescript-language-server's server.ts
76+
def parseBsbLogOutput(content):
77+
res = []
78+
lines = content.splitlines()
79+
for line in lines:
80+
if line.startswith(" We've found a bug for you!"):
81+
res.append([])
82+
elif line.startswith(" Warning number "):
83+
res.append([])
84+
elif line.startswith(" Syntax error!"):
85+
res.append([])
86+
elif re.match(r'^ [0-9]+ ', line):
87+
# code display. Swallow
88+
pass
89+
elif line.startswith(" "):
90+
res[len(res) - 1].append(line)
91+
92+
ret = {}
93+
# map of file path to list of diagnosis
94+
for diagnosisLines in res:
95+
fileAndLocation, *diagnosisMessage = diagnosisLines
96+
lastSpace = fileAndLocation.rfind(" ")
97+
file = fileAndLocation[2:lastSpace]
98+
location = fileAndLocation[lastSpace:]
99+
if not file in ret:
100+
ret[file] = []
101+
cleanedUpDiagnosis = "\n".join([line[2:] for line in diagnosisMessage]).strip()
102+
ret[file].append({
103+
"range": parseBsbOutputLocation(location),
104+
"message": cleanedUpDiagnosis,
105+
})
106+
107+
return ret
108+
23109
def findFormatter(view, filename):
24110
# filename = view.file_name()
25111
# globalFormatterExe = sublime.load_settings(SETTINGS_PATH).get('optionalGlobalFormatter')
@@ -72,7 +158,7 @@ def run(self, edit, formatBuffer=True):
72158
view.erase_phantoms("errns")
73159

74160
currentBuffer = sublime.Region(0, view.size())
75-
contents = view.substr(currentBuffer)
161+
code = view.substr(currentBuffer)
76162

77163
filename = view.file_name()
78164
if filename == None:
@@ -85,36 +171,30 @@ def run(self, edit, formatBuffer=True):
85171
if bscExe == None:
86172
return
87173

88-
proc = subprocess.Popen(
89-
[bscExe, "-color", "never", "-format", filename],
90-
stderr=subprocess.PIPE,
91-
stdout=subprocess.PIPE,
174+
extension = pathlib.Path(filename).suffix
175+
formattedResult = formatUsingValidBscPath(
176+
code,
177+
bscExe,
178+
extension == resiExt
92179
)
93-
stdout, stderr = proc.communicate()
94180

95-
# if proc.returncode == 0 and formatBuffer:
96-
if proc.returncode == 0 and formatBuffer:
97-
view.replace(edit, currentBuffer, stdout.decode())
181+
if formattedResult["kind"] == "success":
182+
view.replace(edit, currentBuffer, formattedResult["result"])
98183
else:
99-
errTxt = stderr.decode()
100184
regions = []
101185
phantoms = []
102186

103-
for line in errTxt.splitlines():
104-
# test.ns(29,38):Did you forget to close this template expression with a backtick?
105-
match = poorMansErrorParser.match(line)
106-
if match == None:
107-
# can't parse error... not sure why. Possible that it's a bad binary
108-
sublime.error_message("An unknown error occurred during formatting:\n\n" + errTxt)
109-
else:
110-
# filename = match.group(1)
111-
startCnum = int(match.group(2))
112-
endCnum = int(match.group(3))
113-
explanation = match.group(4)
114-
115-
region = sublime.Region(startCnum, endCnum)
187+
filesAndErrors = parseBsbLogOutput(formattedResult["result"])
188+
for _file, diagnostics in filesAndErrors.items():
189+
for diagnostic in diagnostics:
190+
range_ = diagnostic["range"]
191+
message = diagnostic["message"]
192+
region = sublime.Region(
193+
view.text_point(range_["start"]["line"], range_["start"]["character"]),
194+
view.text_point(range_["end"]["line"], range_["end"]["character"]),
195+
)
116196
regions.append(region)
117-
html = '<body id="my-plugin-feature"> <style> div.error {padding: 5px; } </style> <div class="error">' + explanation + '</div> </body>'
197+
html = '<body id="my-plugin-feature"> <style> div.error {padding: 5px; } </style> <div class="error">' + message + '</div> </body>'
118198
view.add_phantom("errns", region, html, sublime.LAYOUT_BELOW)
119199

120200
view.add_regions('syntaxerror', regions, 'invalid.illegal', 'dot', sublime.DRAW_NO_FILL)

0 commit comments

Comments
 (0)