3
3
import subprocess
4
4
import re
5
5
import os
6
+ import tempfile
7
+ import pathlib
6
8
7
9
SETTINGS_PATH = 'Default.sublime-settings'
8
10
9
- poorMansErrorParser = re .compile (r'Syntax error!\n.+ \((\d+),(\d+)\):(.+)' )
11
+ resExt = ".res"
12
+ resiExt = ".resi"
10
13
11
14
def findBsConfigDirFromFilename (filename ):
12
15
currentDir = os .path .dirname (filename )
@@ -20,6 +23,89 @@ def findBsConfigDirFromFilename(filename):
20
23
21
24
currentDir = parentDir
22
25
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
+
23
109
def findFormatter (view , filename ):
24
110
# filename = view.file_name()
25
111
# globalFormatterExe = sublime.load_settings(SETTINGS_PATH).get('optionalGlobalFormatter')
@@ -72,7 +158,7 @@ def run(self, edit, formatBuffer=True):
72
158
view .erase_phantoms ("errns" )
73
159
74
160
currentBuffer = sublime .Region (0 , view .size ())
75
- contents = view .substr (currentBuffer )
161
+ code = view .substr (currentBuffer )
76
162
77
163
filename = view .file_name ()
78
164
if filename == None :
@@ -85,36 +171,30 @@ def run(self, edit, formatBuffer=True):
85
171
if bscExe == None :
86
172
return
87
173
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
92
179
)
93
- stdout , stderr = proc .communicate ()
94
180
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" ])
98
183
else :
99
- errTxt = stderr .decode ()
100
184
regions = []
101
185
phantoms = []
102
186
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
+ )
116
196
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>'
118
198
view .add_phantom ("errns" , region , html , sublime .LAYOUT_BELOW )
119
199
120
200
view .add_regions ('syntaxerror' , regions , 'invalid.illegal' , 'dot' , sublime .DRAW_NO_FILL )
0 commit comments