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.
- package/.vscode/settings.json +1 -1
- package/compiler/Inference.ff +17 -1
- package/compiler/LspHook.ff +15 -5
- package/core/BuildSystem.ff +1 -1
- package/core/Ordering.ff +1 -1
- package/experimental/inceptron/Run.ff +47 -0
- package/experimental/mattis/Tree.ff +44 -0
- package/experimental/tests/TestListOrdering.ff +7 -0
- package/experimental/tests/TestOrder.ff +4 -0
- package/experimental/tests/TestSql.ff +18 -0
- package/lsp/.firefly/package.ff +1 -0
- package/lsp/CompletionHandler.ff +112 -6
- package/lsp/Handler.ff +205 -7
- package/lsp/LanguageServer.ff +5 -0
- package/lsp/SemanticTokenTypes.ff +62 -0
- package/lsp/Sql.ff +1395 -0
- package/lsp/SqlStatement.ff +34 -0
- package/lsp/SqlSuggestion.ff +295 -0
- package/output/js/ff/compiler/Builder.mjs +2 -2
- package/output/js/ff/compiler/Builder.mjs.map +1 -1
- package/output/js/ff/compiler/Dependencies.mjs +2 -2
- package/output/js/ff/compiler/Dependencies.mjs.map +1 -1
- package/output/js/ff/compiler/Inference.mjs +36 -16
- package/output/js/ff/compiler/Inference.mjs.map +4 -2
- package/output/js/ff/compiler/LspHook.mjs +10 -10
- package/output/js/ff/compiler/LspHook.mjs.map +4 -3
- package/output/js/ff/compiler/Main.mjs +4 -4
- package/output/js/ff/compiler/Main.mjs.map +1 -1
- package/output/js/ff/core/BuildSystem.mjs +2 -2
- package/output/js/ff/core/BuildSystem.mjs.map +3 -2
- package/output/js/ff/core/Ordering.mjs +2 -2
- package/output/js/ff/core/Ordering.mjs.map +1 -1
- package/package.json +1 -1
- package/postgresql/Pg.ff +49 -26
- package/vscode/client/src/extension.ts +5 -0
- 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
|
|
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(
|
|
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
|
-
}
|
package/lsp/LanguageServer.ff
CHANGED
|
@@ -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
|
+
}
|