firefly-compiler 0.4.10 → 0.4.12
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.
- package/core/Array.ff +8 -0
- package/core/HttpClient.ff +3 -0
- package/core/JsValue.ff +0 -3
- package/core/Json.ff +241 -11
- package/lsp/CompletionHandler.ff +7 -7
- package/lsp/Handler.ff +81 -90
- package/lsp/HoverHandler.ff +7 -10
- package/lsp/LanguageServer.ff +32 -35
- package/lsp/SignatureHelpHandler.ff +12 -13
- package/lsp/SymbolHandler.ff +18 -18
- package/lux/Main.ff +11 -12
- package/output/js/ff/core/Array.mjs +16 -0
- package/output/js/ff/core/HttpClient.mjs +8 -0
- package/output/js/ff/core/JsValue.mjs +0 -8
- package/output/js/ff/core/Json.mjs +579 -17
- package/package.json +2 -2
- package/vscode/package.json +1 -1
package/core/Array.ff
CHANGED
|
@@ -180,6 +180,14 @@ extend self[T]: Array[T] {
|
|
|
180
180
|
}
|
|
181
181
|
return result;
|
|
182
182
|
"""
|
|
183
|
+
|
|
184
|
+
flatMap[S](body: T => Array[S]): Array[S] {
|
|
185
|
+
let results = Stack.make()
|
|
186
|
+
self.each {x =>
|
|
187
|
+
results.pushArray(body(x))
|
|
188
|
+
}
|
|
189
|
+
results.drain()
|
|
190
|
+
}
|
|
183
191
|
|
|
184
192
|
sortBy[S: Order](body: T => S): Array[T] {
|
|
185
193
|
self.sortWith {Ordering.compare(body(_), body(_))}
|
package/core/HttpClient.ff
CHANGED
|
@@ -78,6 +78,9 @@ extend self: FetchResponse {
|
|
|
78
78
|
readText(): String
|
|
79
79
|
target js async "return await self_.text()"
|
|
80
80
|
|
|
81
|
+
readJson(): Json
|
|
82
|
+
target js async "return await self_.json()"
|
|
83
|
+
|
|
81
84
|
readBuffer(): Buffer
|
|
82
85
|
target js async "return new DataView(await self_.arrayBuffer())"
|
|
83
86
|
|
package/core/JsValue.ff
CHANGED
|
@@ -213,9 +213,6 @@ extend self: JsValue {
|
|
|
213
213
|
target js sync "return new self_(a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_)"
|
|
214
214
|
|
|
215
215
|
|
|
216
|
-
toJson(space: Option[String] = None): String
|
|
217
|
-
target js sync "return JSON.stringify(self_, null, space_.value_)"
|
|
218
|
-
|
|
219
216
|
grabPairs(): List[Pair[String, JsValue]]
|
|
220
217
|
target js sync """
|
|
221
218
|
if(!(self_ instanceof Object)) throw new Error('Expected object, got '+ typeof self_);;
|
package/core/Json.ff
CHANGED
|
@@ -9,11 +9,6 @@ read(json: String): Option[Json]
|
|
|
9
9
|
}
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
write(json: Json, space: Option[String] = None): String
|
|
13
|
-
target js sync """
|
|
14
|
-
return JSON.stringify(json_, null, space_.value_);
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
12
|
string(json: String): Json
|
|
18
13
|
target js sync """
|
|
19
14
|
return json_;
|
|
@@ -53,17 +48,30 @@ object(): Json
|
|
|
53
48
|
return {};
|
|
54
49
|
"""
|
|
55
50
|
|
|
51
|
+
fields(body: ((String, Json) => Unit) => Unit): Json
|
|
52
|
+
target js sync """
|
|
53
|
+
const result = {};
|
|
54
|
+
body_((k, v) => {result[k] = v});
|
|
55
|
+
return result;
|
|
56
|
+
"""
|
|
57
|
+
target js async """
|
|
58
|
+
const result = {};
|
|
59
|
+
await body_((k, v) => {result[k] = v}, $task);
|
|
60
|
+
return result;
|
|
61
|
+
"""
|
|
62
|
+
|
|
56
63
|
extend self: Json {
|
|
57
64
|
|
|
58
|
-
|
|
65
|
+
write(indentation: Option[String] = None): String
|
|
59
66
|
target js sync """
|
|
60
|
-
|
|
61
|
-
throw new Error('Not an object: ' + JSON.stringify(self_));
|
|
62
|
-
}
|
|
63
|
-
return {...self_, [field_]: value_};
|
|
67
|
+
return JSON.stringify(self_, null, space_.value_);
|
|
64
68
|
"""
|
|
69
|
+
|
|
70
|
+
with[T: JsonLike](field: String, value: T): Json {
|
|
71
|
+
internalWith(self, field, toJson(value))
|
|
72
|
+
}
|
|
65
73
|
|
|
66
|
-
merge(that: Json)
|
|
74
|
+
merge(that: Json): Json
|
|
67
75
|
target js sync """
|
|
68
76
|
if(typeof self_ !== 'object' || self_ === null || Array.isArray(self_)) {
|
|
69
77
|
throw new Error('Not an object: ' + JSON.stringify(self_));
|
|
@@ -104,6 +112,38 @@ extend self: Json {
|
|
|
104
112
|
if(!Array.isArray(self_)) throw new Error('Not an array: ' + JSON.stringify(self_));
|
|
105
113
|
return self_;
|
|
106
114
|
"""
|
|
115
|
+
|
|
116
|
+
grabMap(): Map[String, Json] {
|
|
117
|
+
mutable map = Map.empty()
|
|
118
|
+
self.each {key, value =>
|
|
119
|
+
map = map.add(key, value)
|
|
120
|
+
}
|
|
121
|
+
map
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
getString(): Option[String] {
|
|
125
|
+
if(self.isString()) {self.grabString()}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
getInt(): Option[Int] {
|
|
129
|
+
if(self.isInt()) {self.grabInt()}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
getFloat(): Option[Float] {
|
|
133
|
+
if(self.isFloat()) {self.grabFloat()}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
getBool(): Option[Bool] {
|
|
137
|
+
if(self.isBool()) {self.grabBool()}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
getArray(): Option[Array[Json]] {
|
|
141
|
+
if(self.isArray()) {self.grabArray()}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
getMap(): Option[Map[String, Json]] {
|
|
145
|
+
if(self.isObject()) {self.grabMap()}
|
|
146
|
+
}
|
|
107
147
|
|
|
108
148
|
isString(): Bool
|
|
109
149
|
target js sync """
|
|
@@ -151,6 +191,11 @@ extend self: Json {
|
|
|
151
191
|
return typeof self_ === 'array' ? self_[key] ?? null : null;
|
|
152
192
|
"""
|
|
153
193
|
|
|
194
|
+
hasField(key: String): Bool
|
|
195
|
+
target js sync """
|
|
196
|
+
return typeof self_ === 'object' && self_ !== null && !Array.isArray(self_) && Object.hasOwn(self_, key_);
|
|
197
|
+
"""
|
|
198
|
+
|
|
154
199
|
getField(key: String): Option[Json]
|
|
155
200
|
target js sync """
|
|
156
201
|
return typeof self_ === 'object' && self_ !== null && !Array.isArray(self_) && Object.hasOwn(self_, key_)
|
|
@@ -191,6 +236,12 @@ extend self: Json {
|
|
|
191
236
|
self.each {field, value => stack.push(body(field, value))}
|
|
192
237
|
stack.drain()
|
|
193
238
|
}
|
|
239
|
+
|
|
240
|
+
flatMap[T](body: (String, Json) => Array[T]): Array[T] {
|
|
241
|
+
let stack = Stack.make()
|
|
242
|
+
self.each {field, value => stack.pushArray(body(field, value))}
|
|
243
|
+
stack.drain()
|
|
244
|
+
}
|
|
194
245
|
|
|
195
246
|
each(body: (String, Json) => Unit) {
|
|
196
247
|
if(!self.isObject()) {throw(GrabException())}
|
|
@@ -204,6 +255,14 @@ extend self: Json {
|
|
|
204
255
|
|
|
205
256
|
}
|
|
206
257
|
|
|
258
|
+
internalWith(self: Json, field: String, json: Json): Json
|
|
259
|
+
target js sync """
|
|
260
|
+
if(typeof self_ !== 'object' || self_ === null || Array.isArray(self_)) {
|
|
261
|
+
throw new Error('Not an object: ' + JSON.stringify(self_));
|
|
262
|
+
}
|
|
263
|
+
return {...self_, [field_]: value_};
|
|
264
|
+
"""
|
|
265
|
+
|
|
207
266
|
internalEach(self: Json, body: (String, Json) => Unit): Unit
|
|
208
267
|
target js sync "for(const [key, value] of Object.entries(self_)) body_(key, value)"
|
|
209
268
|
target js async "for(const [key, value] of Object.entries(self_)) await body_(key, value, $task)"
|
|
@@ -211,3 +270,174 @@ internalEach(self: Json, body: (String, Json) => Unit): Unit
|
|
|
211
270
|
internalEachWhile(self: Json, body: (String, Json) => Bool): Unit
|
|
212
271
|
target js sync "for(const [key, value] of Object.entries(self_)) if(!body_(key, value)) break"
|
|
213
272
|
target js async "for(const [key, value] of Object.entries(self_)) if(!await body_(key, value, $task)) break"
|
|
273
|
+
|
|
274
|
+
trait T: JsonLike {
|
|
275
|
+
toJson(value: T): Json
|
|
276
|
+
fromJson(json: Json): Option[T]
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
instance Json: JsonLike {
|
|
280
|
+
toJson(value: Json): Json {value}
|
|
281
|
+
fromJson(json: Json): Option[Json] {Some(json)}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
instance String: JsonLike {
|
|
285
|
+
toJson(value: String): Json {string(value)}
|
|
286
|
+
fromJson(json: Json): Option[String] {_.getString()}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
instance Int: JsonLike {
|
|
290
|
+
toJson(value: Int): Json {int(value)}
|
|
291
|
+
fromJson(json: Json): Option[Int] {_.getInt()}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
instance Float: JsonLike {
|
|
295
|
+
toJson(value: Float): Json {float(value)}
|
|
296
|
+
fromJson(json: Json): Option[Float] {_.getFloat()}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
instance Bool: JsonLike {
|
|
300
|
+
toJson(value: Bool): Json {bool(value)}
|
|
301
|
+
fromJson(json: Json): Option[Bool] {_.getBool()}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
instance Array[T: JsonLike]: JsonLike {
|
|
305
|
+
toJson(value: Array[T]): Json {array(value.map(toJson))}
|
|
306
|
+
fromJson(json: Json): Option[Array[T]] {
|
|
307
|
+
json.getArray().flatMap {array =>
|
|
308
|
+
mutable convertible = True
|
|
309
|
+
let stack = Stack.make()
|
|
310
|
+
array.eachWhile {item =>
|
|
311
|
+
fromJson(item).{
|
|
312
|
+
| None => convertible = False
|
|
313
|
+
| Some(value) => stack.push(value)
|
|
314
|
+
}
|
|
315
|
+
convertible
|
|
316
|
+
}
|
|
317
|
+
if(convertible) {stack.drain()}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
instance List[T: JsonLike]: JsonLike {
|
|
323
|
+
toJson(value: List[T]): Json {list(value.map(toJson))}
|
|
324
|
+
fromJson(json: Json): Option[List[T]] {fromJson[Array[T]](json).map {_.toList()}}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
instance StringMap[T: JsonLike]: JsonLike {
|
|
328
|
+
toJson(value: StringMap[T]): Json {
|
|
329
|
+
Json.fields {setField =>
|
|
330
|
+
value.each {key, value => setField(key, toJson(value))}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
fromJson(json: Json): Option[StringMap[T]] {
|
|
334
|
+
if(json.isObject()) {
|
|
335
|
+
let map = StringMap.make()
|
|
336
|
+
mutable convertible = True
|
|
337
|
+
json.eachWhile {key, value =>
|
|
338
|
+
fromJson(value).{
|
|
339
|
+
| None => convertible = False
|
|
340
|
+
| Some(v) => map.set(key, v)
|
|
341
|
+
}
|
|
342
|
+
convertible
|
|
343
|
+
}
|
|
344
|
+
if(convertible) {map}
|
|
345
|
+
}.flatten()
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
instance Json: HasAnyTag {
|
|
351
|
+
anyTag(): AnyTag[Json] {Any.internalAnyTag("ff:core/Json.Json[]")}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
instance Json: Show {
|
|
355
|
+
show(value: Json): String {value.write(Some(" "))}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
instance Json: Equal {
|
|
359
|
+
equals(a: Json, b: Json): Bool {
|
|
360
|
+
internalEquals(a, b)
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
instance Json: Order {
|
|
365
|
+
compare(a: Json, b: Json): Ordering {
|
|
366
|
+
Ordering.fromInt(internalCompare(a, b))
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
instance Json: Serializable {
|
|
371
|
+
serializeUsing(serialization: Serialization, value: Json): Unit {
|
|
372
|
+
Serializable.serializeUsing(serialization, value.write())
|
|
373
|
+
}
|
|
374
|
+
deserializeUsing(serialization: Serialization): Json {
|
|
375
|
+
Json.read(Serializable.deserializeUsing[String](serialization)).grab()
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
internalEquals(a: Json, b: Json): Bool
|
|
381
|
+
target js sync """
|
|
382
|
+
if(a_ === b_) {
|
|
383
|
+
return true;
|
|
384
|
+
} else if(Array.isArray(a_) || Array.isArray(b_)) {
|
|
385
|
+
if(!Array.isArray(a_) || !Array.isArray(b_)) return false;
|
|
386
|
+
if(a_.length !== b_.length) return false;
|
|
387
|
+
for(let i = 0; i < a_.length; i++) {
|
|
388
|
+
if(!internalEquals_(a_[i], b_[i])) return false;
|
|
389
|
+
}
|
|
390
|
+
return true;
|
|
391
|
+
} else if(typeof a_ === 'object' && typeof b_ === 'object' && a_ !== null && b_ !== null) {
|
|
392
|
+
const aKeys = Object.keys(a_);
|
|
393
|
+
const bKeys = Object.keys(b_);
|
|
394
|
+
if(aKeys.length !== bKeys.length) return false;
|
|
395
|
+
for(const key of aKeys) {
|
|
396
|
+
if(!Object.hasOwn(b_, key) || !internalEquals_(a_[key], b_[key])) return false;
|
|
397
|
+
}
|
|
398
|
+
return true;
|
|
399
|
+
} else {
|
|
400
|
+
return false;
|
|
401
|
+
}
|
|
402
|
+
"""
|
|
403
|
+
|
|
404
|
+
internalCompare(a: Json, b: Json): Int
|
|
405
|
+
target js sync """
|
|
406
|
+
if(a_ === b_) {
|
|
407
|
+
return 0;
|
|
408
|
+
} else if (a_ === null || b_ === null) {
|
|
409
|
+
return a_ === null ? -1 : 1;
|
|
410
|
+
} else if (typeof a_ === 'boolean' || typeof b_ === 'boolean') {
|
|
411
|
+
if(typeof b_ !== 'boolean') return -1;
|
|
412
|
+
if(typeof a_ !== 'boolean') return 1;
|
|
413
|
+
return a_ < b_ ? -1 : 1;
|
|
414
|
+
} else if (typeof a_ === 'number' || typeof b_ === 'number') {
|
|
415
|
+
if(typeof b_ !== 'number') return -1;
|
|
416
|
+
if(typeof a_ !== 'number') return 1;
|
|
417
|
+
if(isNaB(a_)) return isNaB(b_) ? 0 : -1;
|
|
418
|
+
if(isNaB(b_)) return 1;
|
|
419
|
+
return a_ < b_ ? -1 : 1;
|
|
420
|
+
} else if (typeof a_ === 'string' || typeof b_ === 'string') {
|
|
421
|
+
if(typeof b_ !== 'string') return -1;
|
|
422
|
+
if(typeof a_ !== 'string') return 1;
|
|
423
|
+
return a_.localeCompare(b_, 'en');
|
|
424
|
+
} else if(Array.isArray(a_) || Array.isArray(b_)) {
|
|
425
|
+
if(!Array.isArray(a_) || !Array.isArray(b_)) return a_ < b_ ? -1 : 1;
|
|
426
|
+
const length = Math.min(a_.length, b_.length);
|
|
427
|
+
for(let i = 0; i < length; i++) {
|
|
428
|
+
const cmp = internalCompare_(a_[i], b_[i]);
|
|
429
|
+
if(cmp !== 0) return cmp;
|
|
430
|
+
}
|
|
431
|
+
return a_.length - b_.length;
|
|
432
|
+
} else {
|
|
433
|
+
const aKeys = Object.keys(a_).sort();
|
|
434
|
+
const bKeys = Object.keys(b_).sort();
|
|
435
|
+
const keyResult = internalCompare_(aKeys, bKeys);
|
|
436
|
+
if(keyResult !== 0) return keyResult;
|
|
437
|
+
for(const key of aKeys) {
|
|
438
|
+
const result = internalCompare_(a_[key], b_[key]);
|
|
439
|
+
if(result !== 0) return result;
|
|
440
|
+
}
|
|
441
|
+
return 0;
|
|
442
|
+
}
|
|
443
|
+
"""
|
package/lsp/CompletionHandler.ff
CHANGED
|
@@ -15,7 +15,7 @@ data CompletionInfo(
|
|
|
15
15
|
secondarySort: Int = 5
|
|
16
16
|
)
|
|
17
17
|
|
|
18
|
-
handleCompletion(
|
|
18
|
+
handleCompletion(lspHook: LspHook, toplevel: Bool, followedByOpenBracket: Bool): Json {
|
|
19
19
|
let topLevelCompletions = if(!toplevel) {[]} else {toplevelCompletion(lspHook)}
|
|
20
20
|
let patternCompletions = lspHook.results().collectFirst {
|
|
21
21
|
| InferPatternHook h =>
|
|
@@ -156,11 +156,11 @@ handleCompletion(system: NodeSystem, lspHook: LspHook, toplevel: Bool, followedB
|
|
|
156
156
|
} else {c}
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
|
-
completionsToJson(
|
|
159
|
+
completionsToJson([...fixedCompletions, ...fixedTypeCompletions, ...topLevelCompletions])
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
completionsToJson(
|
|
163
|
-
|
|
162
|
+
completionsToJson(completions: List[CompletionInfo]): Json {
|
|
163
|
+
Json.object()
|
|
164
164
|
.with("isIncomplete", False)
|
|
165
165
|
.with("items", completions.distinct().toArray().map {| CompletionInfo i =>
|
|
166
166
|
let shownType = i.type.show([])
|
|
@@ -179,7 +179,7 @@ completionsToJson(js: JsSystem, completions: List[CompletionInfo]): JsValue {
|
|
|
179
179
|
| Pair(TConstructor(_, n1, _), Some(TConstructor(_, n2, _))) => n1 == n2
|
|
180
180
|
| _ => False
|
|
181
181
|
}
|
|
182
|
-
|
|
182
|
+
Json.object()
|
|
183
183
|
// Namespace or Property or Constructor or EnumMember or Constructor or Method/Function or Field/Variable
|
|
184
184
|
.with("kind"
|
|
185
185
|
if(shownType == "") {3} else:
|
|
@@ -192,12 +192,12 @@ completionsToJson(js: JsSystem, completions: List[CompletionInfo]): JsValue {
|
|
|
192
192
|
.with("sortText", sortText)
|
|
193
193
|
.with("preselect", preselect)
|
|
194
194
|
.with("label", i.label)
|
|
195
|
-
.with("labelDetails",
|
|
195
|
+
.with("labelDetails", Json.object()
|
|
196
196
|
.with("detail", i.extra + if(shownType == "") {""} else {": " + shownType})
|
|
197
197
|
)
|
|
198
198
|
.with("insertText", i.snippet)
|
|
199
199
|
.with("insertTextFormat", 2 /* Snippet */)
|
|
200
|
-
.with("documentation",
|
|
200
|
+
.with("documentation", Json.object()
|
|
201
201
|
.with("kind", "markdown")
|
|
202
202
|
.with("value", "```\n" + i.documentation + "\n```")
|
|
203
203
|
)
|