firefly-compiler 0.6.20 → 0.6.24

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.
Files changed (36) hide show
  1. package/.vscode/settings.json +1 -1
  2. package/compiler/Inference.ff +17 -1
  3. package/compiler/LspHook.ff +15 -5
  4. package/core/BuildSystem.ff +1 -1
  5. package/core/Ordering.ff +1 -1
  6. package/experimental/inceptron/Run.ff +47 -0
  7. package/experimental/mattis/Tree.ff +44 -0
  8. package/experimental/tests/TestListOrdering.ff +7 -0
  9. package/experimental/tests/TestOrder.ff +4 -0
  10. package/experimental/tests/TestSql.ff +18 -0
  11. package/lsp/.firefly/package.ff +1 -0
  12. package/lsp/CompletionHandler.ff +112 -6
  13. package/lsp/Handler.ff +205 -7
  14. package/lsp/LanguageServer.ff +5 -0
  15. package/lsp/SemanticTokenTypes.ff +62 -0
  16. package/lsp/Sql.ff +1395 -0
  17. package/lsp/SqlStatement.ff +34 -0
  18. package/lsp/SqlSuggestion.ff +295 -0
  19. package/output/js/ff/compiler/Builder.mjs +2 -2
  20. package/output/js/ff/compiler/Builder.mjs.map +1 -1
  21. package/output/js/ff/compiler/Dependencies.mjs +2 -2
  22. package/output/js/ff/compiler/Dependencies.mjs.map +1 -1
  23. package/output/js/ff/compiler/Inference.mjs +36 -16
  24. package/output/js/ff/compiler/Inference.mjs.map +4 -2
  25. package/output/js/ff/compiler/LspHook.mjs +10 -10
  26. package/output/js/ff/compiler/LspHook.mjs.map +4 -3
  27. package/output/js/ff/compiler/Main.mjs +4 -4
  28. package/output/js/ff/compiler/Main.mjs.map +1 -1
  29. package/output/js/ff/core/BuildSystem.mjs +2 -2
  30. package/output/js/ff/core/BuildSystem.mjs.map +3 -2
  31. package/output/js/ff/core/Ordering.mjs +2 -2
  32. package/output/js/ff/core/Ordering.mjs.map +1 -1
  33. package/package.json +1 -1
  34. package/postgresql/Pg.ff +49 -26
  35. package/vscode/client/src/extension.ts +5 -0
  36. package/vscode/package.json +7 -1
package/lsp/Handler.ff CHANGED
@@ -10,10 +10,15 @@ import Unification from ff:compiler
10
10
  import LspHook from ff:compiler
11
11
  import ModuleCache from ff:compiler
12
12
  import DependencyLock from ff:compiler
13
+ import Pg from ff:postgresql
13
14
  import HoverHandler
14
15
  import CompletionHandler
15
16
  import SignatureHelpHandler
16
17
  import SymbolHandler
18
+ import SemanticTokenTypes
19
+ import Sql
20
+ import SqlStatement
21
+
17
22
 
18
23
  capability Handler(
19
24
  fireflyPath: Path
@@ -23,10 +28,20 @@ capability Handler(
23
28
  mutable responseCache: Map[TokenRequestCacheKey, ResultOrError]
24
29
  mutable fileSymbolsCache: Map[String, List[DocumentSymbol]]
25
30
  mutable importSymbolsCache: Pair[String, List[ImportSymbolInfo]]
31
+ mutable sqlConnectionString: Option[String]
32
+ mutable sqlData: Option[SqlData]
33
+ mutable sqlConnectionPool: Option[PgPool]
34
+ sqlDiagnosticsCache: StringMap[Map[String, Option[SqlError]]]
35
+ sqlLock: Lock
26
36
  moduleCache: ModuleCache
27
37
  dependencyLock: DependencyLock
28
38
  )
29
39
 
40
+ data SqlCacheKey(
41
+ sql: String
42
+ parameters: Option[List[SqlParameter]]
43
+ )
44
+
30
45
  data TokenRequestCacheKey(
31
46
  method: String
32
47
  targetAt: Location
@@ -57,8 +72,12 @@ data TokenLocation(
57
72
 
58
73
  data Definition(at: Location, name: String, local: Bool)
59
74
 
75
+ data SqlError(token: SqlToken, message: String)
60
76
 
61
77
  extend self: Handler {
78
+
79
+
80
+
62
81
 
63
82
  handleNotification(system: NodeSystem, method: String, parameters: Map[String, Json], version: Int): List[Pair[String, Json]] {
64
83
  method.{
@@ -126,6 +145,8 @@ extend self: Handler {
126
145
  self.printTime(system.mainTask(), "handleWorkspaceSymbol") {
127
146
  self.handleWorkspaceSymbol(system, parameters)
128
147
  }
148
+ | "textDocument/semanticTokens/full" =>
149
+ self.handleSemanticTokensFull(system, parameters, version)
129
150
  | "shutdown" =>
130
151
  Result(Json.null().write())
131
152
  | _ =>
@@ -160,6 +181,10 @@ extend self: Handler {
160
181
 
161
182
  handleInitialize(system: NodeSystem, parameters: Map[String, Json]): ResultOrError {
162
183
  self.rootPath = parameters.grab("rootUri").getString().map {system.pathFromUrl(_)}
184
+ self.sqlConnectionString = parameters.get("initializationOptions").{
185
+ | Some(opts) => opts.getField("sqlConnectionString").map {_.grabString()}.filter {_ != ""}
186
+ | None => None
187
+ }
163
188
 
164
189
  let anyFireflyFile = Json.object()
165
190
  .with("filters", [
@@ -212,6 +237,15 @@ extend self: Handler {
212
237
  )
213
238
  )*/
214
239
  .with("renameProvider", True)
240
+ .with("semanticTokensProvider", Json.object()
241
+ .with("legend", Json.object()
242
+ .with("tokenTypes", SemanticTokenTypes.new().second)
243
+ .with("tokenModifiers", Json.array([]))
244
+ )
245
+ .with("full", Json.object()
246
+ .with("delta", False)
247
+ )
248
+ )
215
249
  )
216
250
  .with("serverInfo", Json.object()
217
251
  .with("name", "Firefly Language Server")
@@ -244,7 +278,8 @@ extend self: Handler {
244
278
  let uri = parameters.grab("textDocument").field("uri").grabString()
245
279
  let path = system.pathFromUrl(uri)
246
280
  let fireflyPath = system.path(".")
247
- let errors = self.check(system, fireflyPath, path, None, Set.new(), self.virtualFiles, version, LspHook.disabled(), True)
281
+ let lspHook = LspHook.disabled(sql = True)
282
+ let errors = self.check(system, fireflyPath, path, None, Set.new(), self.virtualFiles, version, lspHook, True)
248
283
  let diagnostics = errors.filter {_.at.file == path.absolute()}.map {| CompileError(at, message) =>
249
284
  let tokenLocation = self.findToken(system, at)
250
285
  Json.object()
@@ -252,10 +287,101 @@ extend self: Handler {
252
287
  .with("severity", 1)
253
288
  .with("message", message)
254
289
  }
290
+
291
+ let sqlStatements = SqlStatement.extractStatements(lspHook.sqlStrings.toList())
292
+ let sqlDiagnistics = if(sqlStatements.isEmpty()) {[]} else {
293
+ if(self.sqlConnectionString.isEmpty()) {
294
+ sqlStatements.map {
295
+ | SqlStatement(_, at, _, _) =>
296
+ let tokenLocation = self.findToken(system, at)
297
+ Json.object()
298
+ .with("range", self.tokenLocationToLspRange(tokenLocation))
299
+ .with("severity", 3)
300
+ .with("message", "Configure firefly.sqlConnectionString to enable SQL diagnostics")
301
+ .with("source", "firefly")
302
+ }
303
+ } else:
304
+ self.sqlLock.do {
305
+ mutable sqlCache = self.sqlDiagnosticsCache.getOrSet(path.absolute()) {Map.new()}
306
+ mutable seenSql = Set.new()
307
+ let issues = try {
308
+ let pool = self.getSqlConnectionPool(system)
309
+ pool.transaction(readOnly = True, body = {connection =>
310
+ sqlStatements.collect {
311
+ | SqlStatement(_, at, sql, parameters) =>
312
+ let cacheKey = Show.show(SqlCacheKey(sql, parameters))
313
+ seenSql = seenSql.add(cacheKey)
314
+ let sqlError = sqlCache.get(cacheKey).else {
315
+ let error = Sql.buildExplainStatement(sql, parameters).flatMap {
316
+ | ExplainedStatement(explainSql, deltas, usedParameters) =>
317
+ let missingParameters = parameters.map {parsedParameters =>
318
+ usedParameters.filter {| Pair(used, o) => parsedParameters.all {passed => passed.name != used}}
319
+ }.else {[]}
320
+ try {
321
+ connection.statement(explainSql).map {_ => }
322
+ missingParameters.first().map {| Pair(name, position) =>
323
+ let tokens = Sql.tokenize(sql)
324
+ let t = tokens.find {_.startOffset >= position - 1}.else {tokens.grabLast()} // TODO
325
+ SqlError(t, "Parameter not defined: " + name)
326
+ }
327
+ } catchAny {e =>
328
+ let errorMessage = e.message()
329
+ system.writeErrorLine(Json(e!).write())
330
+ let errorPosition = if(e!->position.nullish()) {0} else {
331
+ let position = e!->position.grabString().grabInt()
332
+ position + deltas.filter {_.first < position}.map {_.second}.foldLeft(0) {_ + _}
333
+ }
334
+ let tokens = Sql.tokenize(sql)
335
+ let t = tokens.find {_.startOffset >= errorPosition - 1}.else {tokens.grabLast()} // TODO
336
+ Some(SqlError(t, errorMessage))
337
+ }
338
+ }
339
+ sqlCache = sqlCache.add(cacheKey, error)
340
+ error
341
+ }
342
+ sqlError.map {e =>
343
+ let absoluteLine = at.line - 1 + e.token.startLine - 1 + 1
344
+ let absoluteColumnExtra = if(e.token.at().line == 1) {at.column - 1} else {0}
345
+ let absoluteColumn = e.token.startOffset - e.token.startLineOffset + absoluteColumnExtra + 1
346
+ let tokenLocation = TokenLocation(
347
+ file = at.file
348
+ startLine = absoluteLine
349
+ startColumn = absoluteColumn
350
+ endLine = absoluteLine
351
+ endColumn = absoluteColumn + (e.token.stopOffset - e.token.startOffset)
352
+ raw = e.token.raw()
353
+ followedByLeftBracket = False
354
+ )
355
+ Json.object()
356
+ .with("range", self.tokenLocationToLspRange(tokenLocation))
357
+ .with("severity", 1)
358
+ .with("message", e.message)
359
+ }
360
+ | _ => None
361
+ }
362
+ })
363
+ } catchAny {error =>
364
+ system.writeErrorLine("Error while running SQL diagnostics: " + error.message())
365
+ system.writeErrorLine(error.stack())
366
+ []
367
+ }
368
+ if(seenSql.size() == 0) {
369
+ self.sqlDiagnosticsCache.remove(path.absolute())
370
+ } else {
371
+ sqlCache.keys().each {
372
+ | cachedSql {!seenSql.contains(cachedSql)} => sqlCache = sqlCache.remove(cachedSql)
373
+ | _ =>
374
+ }
375
+ self.sqlDiagnosticsCache.set(path.absolute(), sqlCache)
376
+ }
377
+ issues
378
+ }
379
+ }
380
+
255
381
  Json.object()
256
382
  .with("uri", uri)
257
383
  .with("version", parameters.grab("textDocument").field("version"))
258
- .with("diagnostics", diagnostics)
384
+ .with("diagnostics", [...diagnostics, ...sqlDiagnistics])
259
385
  }
260
386
 
261
387
  handleClearDiagnostic(system: NodeSystem, parameters: Map[String, Json]): List[Json] {
@@ -310,6 +436,63 @@ extend self: Handler {
310
436
  symbols
311
437
  }
312
438
  }
439
+
440
+ handleSemanticTokensFull(system: NodeSystem, parameters: Map[String, Json], version: Int): ResultOrError {
441
+ let js = system.js()
442
+ let uri = parameters.grab("textDocument").field("uri").grabString()
443
+ let path = system.pathFromUrl(uri)
444
+ let fireflyPath = system.path(".")
445
+ let lspHook = LspHook.disabled(sql = True)
446
+ self.check(system, fireflyPath, path, None, Set.new(), self.virtualFiles, version, lspHook, True)
447
+ let sqlStatements = SqlStatement.extractStatements(lspHook.sqlStrings.toList())
448
+ if(!sqlStatements.isEmpty() && self.sqlData.isEmpty()) {
449
+ self.sqlData = Some(SqlData([], [], [], []))
450
+ system.mainTask().spawn {_ =>
451
+ try {
452
+ let pool = self.getSqlConnectionPool(system)
453
+ let data = Sql.load(system, pool)
454
+ self.sqlData = Some(data)
455
+ } catchAny {error =>
456
+ system.writeErrorLine("Error while running Sql.load: " + error.message())
457
+ }
458
+ }
459
+ }
460
+ mutable lastAbsoluteLine = -1
461
+ mutable lastAbsoluteColumn = 0
462
+ let tokens = sqlStatements.flatMap {
463
+ | SqlStatement(_, at, sql, _) =>
464
+ let tokens = Sql.tokenize(sql)
465
+ tokens.map {token =>
466
+ let absoluteLine = at.line - 1 + token.startLine - 1
467
+ let absoluteColumnExtra = if(token.at().line == 1) {at.column - 1} else {0}
468
+ let absoluteColumn = token.startOffset - token.startLineOffset + absoluteColumnExtra
469
+ let length = token.stopOffset - token.startOffset
470
+ let relativeLine = if(lastAbsoluteLine != -1) {absoluteLine - lastAbsoluteLine} else {absoluteLine}
471
+ let relativeColumn = if(lastAbsoluteLine == absoluteLine) {absoluteColumn - lastAbsoluteColumn} else {absoluteColumn}
472
+ let tokenTypes = SemanticTokenTypes.new().first
473
+ let tokenType = token.kind.{
474
+ | SqlReserved => tokenTypes.keyword.second
475
+ | SqlUnreserved => tokenTypes.macro.second
476
+ | SqlIdentifier => tokenTypes.variable.second
477
+ | SqlNumber => tokenTypes.number.second
478
+ | SqlString => tokenTypes.string.second
479
+ | SqlOperator => tokenTypes.operator.second
480
+ | SqlPunctuation => tokenTypes.operator.second
481
+ | SqlComment => tokenTypes.comment.second
482
+ | SqlPlaceholder => tokenTypes.typeParameter.second
483
+ }
484
+ let tokenModifiers = 0
485
+ lastAbsoluteLine = absoluteLine
486
+ lastAbsoluteColumn = absoluteColumn
487
+ [relativeLine, relativeColumn, length, tokenType, tokenModifiers]
488
+ }
489
+ | _ => []
490
+ }
491
+
492
+ let o = Json.object()
493
+ .with("data", Json.array(tokens.flatten().map(Json.int)))
494
+ Result(o.write())
495
+ }
313
496
 
314
497
  handleWorkspaceSymbol(system: NodeSystem, parameters: Map[String, Json]): ResultOrError {
315
498
  self.rootPath.or {Error(-32803 /*Request Failed*/, "Open a folder to enable workspace symbols")}: rootPath =>
@@ -407,7 +590,9 @@ extend self: Handler {
407
590
  line = token.startLine
408
591
  column = token.startColumn
409
592
  )
410
- let lspHook = LspHook.new(at = Some(completionAt), definedAt = None, insertIdentifier = True, trackSymbols = False)
593
+ let lspHook = LspHook.new(
594
+ at = Some(completionAt), definedAt = None, insertIdentifier = True, trackSymbols = False, sql = True
595
+ )
411
596
  let path = system.path(completionAt.file)
412
597
  let errors = self.check(system, self.fireflyPath, path, None, Set.new(), self.virtualFiles, version, lspHook, True)
413
598
  errors.each {| CompileError(at, message) =>
@@ -416,6 +601,7 @@ extend self: Handler {
416
601
  let importSymbols = self.printTime(system.mainTask(), "findModulesToImport") {
417
602
  self.findModulesToImport(system, path, lspHook)
418
603
  }
604
+ let sqlStatement = SqlStatement.extractStatements(lspHook.sqlStrings.toList()).find {_.termAt == completionAt}
419
605
  let o = CompletionHandler.handleCompletion(
420
606
  lspHook = lspHook
421
607
  toplevel = completionAt.column == 1
@@ -425,6 +611,8 @@ extend self: Handler {
425
611
  importSymbols = importSymbols.first
426
612
  importLine = importSymbols.second
427
613
  )
614
+ sqlStatement = sqlStatement.map {Pair(location, _)}
615
+ sqlData = self.sqlData.else {SqlData([], [], [], [])}
428
616
  )
429
617
  Result(o.write())
430
618
  }
@@ -792,9 +980,19 @@ extend self: Handler {
792
980
  infer
793
981
  )
794
982
  }
983
+
984
+ getSqlConnectionPool(system: NodeSystem): PgPool {
985
+ let connectionString = self.sqlConnectionString.else {
986
+ throw(LanguageServerException("No SQL connection string configured. Set firefly.sqlConnectionString in VS Code settings."))
987
+ }
988
+ self.sqlLock.do {
989
+ self.sqlConnectionPool.else {
990
+ let pool = Pg.newPoolFromConnectionString(system, connectionString)
991
+ self.sqlConnectionPool = Some(pool)
992
+ pool
993
+ }
994
+ }
995
+ }
996
+
795
997
 
796
998
  }
797
-
798
- foo(bb: Int): Int {
799
- bb
800
- }
@@ -39,6 +39,11 @@ main(system: NodeSystem) {
39
39
  responseCache = Map.new()
40
40
  fileSymbolsCache = Map.new()
41
41
  importSymbolsCache = Pair("", [])
42
+ sqlConnectionString = None
43
+ sqlData = None
44
+ sqlConnectionPool = None
45
+ sqlLock = system.mainTask().lock()
46
+ sqlDiagnosticsCache = StringMap.new()
42
47
  moduleCache = cache
43
48
  dependencyLock = DependencyLock.new(system.mainTask())
44
49
  )
@@ -0,0 +1,62 @@
1
+ data SemanticTokenTypes(
2
+ namespace: Pair[String, Int]
3
+ type: Pair[String, Int]
4
+ class: Pair[String, Int]
5
+ enum: Pair[String, Int]
6
+ interface: Pair[String, Int]
7
+ struct: Pair[String, Int]
8
+ typeParameter: Pair[String, Int]
9
+ parameter: Pair[String, Int]
10
+ variable: Pair[String, Int]
11
+ property: Pair[String, Int]
12
+ enumMember: Pair[String, Int]
13
+ event: Pair[String, Int]
14
+ function: Pair[String, Int]
15
+ method: Pair[String, Int]
16
+ macro: Pair[String, Int]
17
+ keyword: Pair[String, Int]
18
+ modifier: Pair[String, Int]
19
+ comment: Pair[String, Int]
20
+ string: Pair[String, Int]
21
+ number: Pair[String, Int]
22
+ regexp: Pair[String, Int]
23
+ operator: Pair[String, Int]
24
+ decorator: Pair[String, Int]
25
+ label: Pair[String, Int]
26
+ )
27
+
28
+ new(): Pair[SemanticTokenTypes, List[String]] {
29
+ let names = Array.new()
30
+ function add(name: String): Pair[String, Int] {
31
+ let index = names.size()
32
+ names.push(name)
33
+ Pair(name, index)
34
+ }
35
+ let types = SemanticTokenTypes(
36
+ namespace = add("namespace")
37
+ type = add("type")
38
+ class = add("class")
39
+ enum = add("enum")
40
+ interface = add("interface")
41
+ struct = add("struct")
42
+ typeParameter = add("typeParameter")
43
+ parameter = add("parameter")
44
+ variable = add("variable")
45
+ property = add("property")
46
+ enumMember = add("enumMember")
47
+ event = add("event")
48
+ function = add("function")
49
+ method = add("method")
50
+ macro = add("macro")
51
+ keyword = add("keyword")
52
+ modifier = add("modifier")
53
+ comment = add("comment")
54
+ string = add("string")
55
+ number = add("number")
56
+ regexp = add("regexp")
57
+ operator = add("operator")
58
+ decorator = add("decorator")
59
+ label = add("label")
60
+ )
61
+ Pair(types, names.drain())
62
+ }