@arcgis/coding-components 4.31.0-next.21 → 4.31.0-next.23

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 (119) hide show
  1. package/dist/arcgis-coding-components/arcgis-coding-components.esm.js +2 -2
  2. package/dist/arcgis-coding-components/assets/code-editor/sql-expr.worker.js +433 -22
  3. package/dist/arcgis-coding-components/index.esm.js +2 -2
  4. package/dist/arcgis-coding-components/p-08b8fd1f.js +6 -0
  5. package/dist/arcgis-coding-components/{p-cdc52d2f.js → p-1402a8f3.js} +1 -1
  6. package/dist/arcgis-coding-components/{p-8eb7e694.js → p-1635ee06.js} +2 -2
  7. package/dist/arcgis-coding-components/{p-57b8bcd7.js → p-1d45a6dd.js} +1 -1
  8. package/dist/arcgis-coding-components/{p-5b67b62b.js → p-22c2d840.js} +2 -2
  9. package/dist/arcgis-coding-components/{p-cbee273f.js → p-2cfa8f19.js} +2 -2
  10. package/dist/arcgis-coding-components/{p-ffce0588.entry.js → p-3f1b5f73.entry.js} +2 -2
  11. package/dist/arcgis-coding-components/{p-a2341e0c.js → p-50e53c02.js} +2 -2
  12. package/dist/arcgis-coding-components/{p-1b799777.js → p-5872baf9.js} +2 -2
  13. package/dist/arcgis-coding-components/{p-d7d1f130.js → p-776d550d.js} +9 -9
  14. package/dist/arcgis-coding-components/{p-4d5cf512.entry.js → p-9c58968d.entry.js} +2 -2
  15. package/dist/arcgis-coding-components/{p-b715834b.js → p-9c7cef40.js} +2 -2
  16. package/dist/arcgis-coding-components/{p-6621a80f.js → p-9e1c9066.js} +2 -2
  17. package/dist/arcgis-coding-components/{p-56266a64.js → p-9edc9c4b.js} +2 -2
  18. package/dist/arcgis-coding-components/{p-64137fec.js → p-a444264e.js} +2 -2
  19. package/dist/arcgis-coding-components/{p-e5a924a1.js → p-b4912aa0.js} +1 -1
  20. package/dist/arcgis-coding-components/{p-f2d64a9d.js → p-c4521d1d.js} +2 -2
  21. package/dist/arcgis-coding-components/{p-e1f9b463.entry.js → p-d99efb76.entry.js} +2 -2
  22. package/dist/arcgis-coding-components/{p-ff62d134.js → p-f18cfc50.js} +2 -2
  23. package/dist/cjs/{app-globals-a9ef3ca8.js → app-globals-b0c856f6.js} +1 -1
  24. package/dist/cjs/{arcade-defaults-f5241680.js → arcade-defaults-25d38bc0.js} +4 -4
  25. package/dist/cjs/{arcade-language-features-0b9f3947.js → arcade-language-features-0422f79b.js} +2 -2
  26. package/dist/cjs/{arcade-mode-cc08d9cd.js → arcade-mode-151f7044.js} +3 -3
  27. package/dist/cjs/arcgis-arcade-editor_6.cjs.entry.js +5 -5
  28. package/dist/cjs/arcgis-coding-components.cjs.js +3 -3
  29. package/dist/cjs/arcgis-sql-expression-editor.cjs.entry.js +4 -4
  30. package/dist/cjs/arcgis-sql-expression-fields.cjs.entry.js +4 -4
  31. package/dist/cjs/{css-bb6a49ec.js → css-c3bfee00.js} +1 -1
  32. package/dist/cjs/{cssMode-e7fac7d7.js → cssMode-c7d6c64b.js} +2 -2
  33. package/dist/cjs/{html-7ff4071d.js → html-3ff5bd3b.js} +2 -2
  34. package/dist/cjs/{htmlMode-c89193ec.js → htmlMode-71714c3e.js} +2 -2
  35. package/dist/cjs/{index-4b7880ab.js → index-d386d3e2.js} +1 -1
  36. package/dist/cjs/index.cjs.js +4 -4
  37. package/dist/cjs/{javascript-864c0220.js → javascript-d1a20c9f.js} +3 -3
  38. package/dist/cjs/{jsonMode-ac4a7be7.js → jsonMode-2b25e5f4.js} +2 -2
  39. package/dist/cjs/{language-defaults-base-57b37f9f.js → language-defaults-base-fe1d850e.js} +29 -9
  40. package/dist/cjs/loader.cjs.js +3 -3
  41. package/dist/cjs/{sql-expr-defaults-245b036d.js → sql-expr-defaults-0acd5dfe.js} +4 -4
  42. package/dist/cjs/sql-expr-mode-bc156416.js +1825 -0
  43. package/dist/cjs/{tsMode-d30d4ab1.js → tsMode-9cfe75b9.js} +2 -2
  44. package/dist/cjs/{typescript-6c87ddb3.js → typescript-fc9b2e83.js} +2 -2
  45. package/dist/components/arcade-defaults.js +1 -1
  46. package/dist/components/arcade-language-features.js +1 -1
  47. package/dist/components/arcade-mode.js +1 -1
  48. package/dist/components/arcade-results.js +1 -1
  49. package/dist/components/arcade-suggestions.js +1 -1
  50. package/dist/components/arcade-variables.js +1 -1
  51. package/dist/components/arcgis-arcade-editor.js +1 -1
  52. package/dist/components/arcgis-arcade-results.js +1 -1
  53. package/dist/components/arcgis-arcade-suggestions.js +1 -1
  54. package/dist/components/arcgis-arcade-variables.js +1 -1
  55. package/dist/components/arcgis-assets.d.ts +1 -1
  56. package/dist/components/arcgis-assets.js +1 -1
  57. package/dist/components/arcgis-code-editor.js +1 -1
  58. package/dist/components/arcgis-language-api-panel.js +1 -1
  59. package/dist/components/arcgis-sql-expression-editor.js +1 -1
  60. package/dist/components/arcgis-sql-expression-fields.js +1 -1
  61. package/dist/components/chunk-2JTWBRMN.js +1 -1
  62. package/dist/components/code-editor.js +1 -1
  63. package/dist/components/fields.js +20 -2
  64. package/dist/components/index.js +1 -1
  65. package/dist/components/index2.js +1 -1
  66. package/dist/components/language-api-panel.js +1 -1
  67. package/dist/components/language-defaults-base.js +1 -1
  68. package/dist/components/markdown.js +1 -1
  69. package/dist/components/sql-expr-defaults.js +1 -1
  70. package/dist/components/sql-expr-mode.js +33 -19051
  71. package/dist/components/sql-expression-fields.js +1 -1
  72. package/dist/components/useT9n.js +1 -1
  73. package/dist/components/utilities.js +1 -1
  74. package/dist/esm/{app-globals-6d0ca11d.js → app-globals-1d6b10d3.js} +1 -1
  75. package/dist/esm/{arcade-defaults-066445c4.js → arcade-defaults-059a08f1.js} +4 -4
  76. package/dist/esm/{arcade-language-features-0e00c199.js → arcade-language-features-749f495a.js} +2 -2
  77. package/dist/esm/{arcade-mode-abf1e1cf.js → arcade-mode-5e2da07e.js} +3 -3
  78. package/dist/esm/arcgis-arcade-editor_6.entry.js +5 -5
  79. package/dist/esm/arcgis-coding-components.js +4 -4
  80. package/dist/esm/arcgis-sql-expression-editor.entry.js +4 -4
  81. package/dist/esm/arcgis-sql-expression-fields.entry.js +4 -4
  82. package/dist/esm/{css-c6dae12d.js → css-0fa06f45.js} +1 -1
  83. package/dist/esm/{cssMode-052bb603.js → cssMode-7d51359f.js} +2 -2
  84. package/dist/esm/{html-572696a1.js → html-900c5e2a.js} +2 -2
  85. package/dist/esm/{htmlMode-fc184f2d.js → htmlMode-aaba1915.js} +2 -2
  86. package/dist/esm/{index-0edd9846.js → index-51f840b4.js} +1 -1
  87. package/dist/esm/index.js +4 -4
  88. package/dist/esm/{javascript-8bfc0096.js → javascript-4a0ddd5a.js} +3 -3
  89. package/dist/esm/{jsonMode-7adf94ff.js → jsonMode-bcfe66f5.js} +2 -2
  90. package/dist/esm/{language-defaults-base-85a7f476.js → language-defaults-base-5cb37b1e.js} +28 -10
  91. package/dist/esm/loader.js +4 -4
  92. package/dist/esm/{sql-expr-defaults-be84ec7f.js → sql-expr-defaults-f1fd7291.js} +4 -4
  93. package/dist/esm/sql-expr-mode-0f087c3e.js +1821 -0
  94. package/dist/esm/{tsMode-d10773c8.js → tsMode-6deb8933.js} +2 -2
  95. package/dist/esm/{typescript-9491f23e.js → typescript-11a558e0.js} +2 -2
  96. package/dist/loader/cdn.js +1 -1
  97. package/dist/loader/index.cjs.js +1 -1
  98. package/dist/loader/index.es2017.js +1 -1
  99. package/dist/loader/index.js +1 -1
  100. package/dist/types/utils/sql-expr-monaco/sql-expr-completion.d.ts +1 -1
  101. package/dist/types/utils/sql-expr-monaco/sql-expr-validation-utils.d.ts +7 -8
  102. package/dist/types/utils/sql-expr-monaco/sql-expr-validation.d.ts +2 -3
  103. package/dist/types/utils/sql-expr-monaco/types.d.ts +2 -6
  104. package/package.json +13 -14
  105. package/dist/arcgis-coding-components/p-77dd5521.js +0 -6
  106. package/dist/cjs/sql-expr-mode-304f5ce2.js +0 -20844
  107. package/dist/esm/sql-expr-mode-a4413e5c.js +0 -20840
  108. package/dist/types/utils/sql-expr-monaco/DependentFiles/DateOnly.d.ts +0 -41
  109. package/dist/types/utils/sql-expr-monaco/DependentFiles/SqlInterval.d.ts +0 -16
  110. package/dist/types/utils/sql-expr-monaco/DependentFiles/SqlTimestampOffset.d.ts +0 -26
  111. package/dist/types/utils/sql-expr-monaco/DependentFiles/TimeOnly.d.ts +0 -37
  112. package/dist/types/utils/sql-expr-monaco/DependentFiles/UnknownTimeZone.d.ts +0 -11
  113. package/dist/types/utils/sql-expr-monaco/DependentFiles/WhereGrammar.d.ts +0 -122
  114. package/dist/types/utils/sql-expr-monaco/DependentFiles/sqlCompareUtils.d.ts +0 -5
  115. package/dist/types/utils/sql-expr-monaco/DependentFiles/sqlDateParsingUtils.d.ts +0 -18
  116. package/dist/types/utils/sql-expr-monaco/DependentFiles/sqlUtils.d.ts +0 -6
  117. package/dist/types/utils/sql-expr-monaco/DependentFiles/standardizedFunctions.d.ts +0 -156
  118. package/dist/types/utils/sql-expr-monaco/DependentFiles/support.d.ts +0 -150
  119. package/dist/types/utils/sql-expr-monaco/PeggyGrammar/sql92grammar.d.ts +0 -1397
@@ -0,0 +1,1821 @@
1
+ /*!
2
+ * All material copyright Esri, All Rights Reserved, unless otherwise specified.
3
+ * See https://js.arcgis.com/4.31/esri/copyright.txt for details.
4
+ * v4.31.0-next.23
5
+ */
6
+ import { g as editor, T as importCoreSql, V as newLayersSupportFieldsIndex, z as languages, m as main, Q as debounce, O as Emitter } from './language-defaults-base-5cb37b1e.js';
7
+ import { b as getFieldsFromLayerVariable, s as sqlExprDefaults } from './sql-expr-defaults-f1fd7291.js';
8
+ import { t as toCompletionItemKind } from './arcade-language-features-749f495a.js';
9
+ import './index-51f840b4.js';
10
+
11
+ /**
12
+ * Manager to create our sql-expression worker and client proxy
13
+ */
14
+ class SqlExprWorkerManager {
15
+ constructor(_defaults) {
16
+ this._defaults = _defaults;
17
+ this._worker = null;
18
+ this._client = null;
19
+ // Observes the sql-expression defaults. If modified then stop the worker.
20
+ this._configChangeListener = this._defaults.onDidChange(() => this.stopWorker());
21
+ }
22
+ dispose() {
23
+ this._configChangeListener.dispose();
24
+ this.stopWorker();
25
+ }
26
+ stopWorker() {
27
+ if (this._worker) {
28
+ this._worker.dispose();
29
+ this._worker = null;
30
+ }
31
+ this._client = null;
32
+ }
33
+ async _getClientProxy() {
34
+ // We used createWebWorker to create or run the web worker if it isn’t already created.
35
+ // Otherwise, we get and return the proxy client.
36
+ if (!this._client) {
37
+ const { languageId, languageOptions } = this._defaults;
38
+ this._worker = editor.createWebWorker({
39
+ moduleId: "SqlExprWorker",
40
+ label: languageId,
41
+ createData: {
42
+ languageId,
43
+ languageOptions,
44
+ },
45
+ });
46
+ this._client = this._worker.getProxy();
47
+ }
48
+ return await this._client;
49
+ }
50
+ async getLanguageServiceWorker(...resources) {
51
+ const _client = await this._getClientProxy();
52
+ await this._worker?.withSyncedResources(resources);
53
+ return _client;
54
+ }
55
+ }
56
+
57
+ const sqlExprLanguageConfig = {
58
+ comments: {
59
+ lineComment: "--",
60
+ blockComment: ["/*", "*/"],
61
+ },
62
+ brackets: [
63
+ ["{", "}"],
64
+ ["[", "]"],
65
+ ["(", ")"],
66
+ ],
67
+ autoClosingPairs: [
68
+ { open: "{", close: "}" },
69
+ { open: "[", close: "]" },
70
+ { open: "(", close: ")" },
71
+ { open: '"', close: '"' },
72
+ { open: "'", close: "'" },
73
+ ],
74
+ surroundingPairs: [
75
+ { open: "{", close: "}" },
76
+ { open: "[", close: "]" },
77
+ { open: "(", close: ")" },
78
+ { open: '"', close: '"' },
79
+ { open: "'", close: "'" },
80
+ ],
81
+ };
82
+ // TODO: trim down
83
+ const sqlExprLanguageSyntax = {
84
+ defaultToken: "",
85
+ tokenPostfix: ".sql",
86
+ ignoreCase: true,
87
+ brackets: [
88
+ { open: "[", close: "]", token: "delimiter.square" },
89
+ { open: "(", close: ")", token: "delimiter.parenthesis" },
90
+ ],
91
+ keywords: [
92
+ // This list is generated using `keywords.js`
93
+ "ABORT",
94
+ "ABSOLUTE",
95
+ "ACTION",
96
+ "ADA",
97
+ "ADD",
98
+ "AFTER",
99
+ "ALL",
100
+ "ALLOCATE",
101
+ "ALTER",
102
+ "ALWAYS",
103
+ "ANALYZE",
104
+ "AND",
105
+ "ANY",
106
+ "ARE",
107
+ "AS",
108
+ "ASC",
109
+ "ASSERTION",
110
+ "AT",
111
+ "ATTACH",
112
+ "AUTHORIZATION",
113
+ "AUTOINCREMENT",
114
+ "AVG",
115
+ "BACKUP",
116
+ "BEFORE",
117
+ "BEGIN",
118
+ "BETWEEN",
119
+ "BIT",
120
+ "BIT_LENGTH",
121
+ "BOTH",
122
+ "BREAK",
123
+ "BROWSE",
124
+ "BULK",
125
+ "BY",
126
+ "CASCADE",
127
+ "CASCADED",
128
+ "CASE",
129
+ "CAST",
130
+ "CATALOG",
131
+ "CHAR",
132
+ "CHARACTER",
133
+ "CHARACTER_LENGTH",
134
+ "CHAR_LENGTH",
135
+ "CHECK",
136
+ "CHECKPOINT",
137
+ "CLOSE",
138
+ "CLUSTERED",
139
+ "COALESCE",
140
+ "COLLATE",
141
+ "COLLATION",
142
+ "COLUMN",
143
+ "COMMIT",
144
+ "COMPUTE",
145
+ "CONFLICT",
146
+ "CONNECT",
147
+ "CONNECTION",
148
+ "CONSTRAINT",
149
+ "CONSTRAINTS",
150
+ "CONTAINS",
151
+ "CONTAINSTABLE",
152
+ "CONTINUE",
153
+ "CONVERT",
154
+ "CORRESPONDING",
155
+ "COUNT",
156
+ "CREATE",
157
+ "CROSS",
158
+ "CURRENT",
159
+ "CURRENT_DATE",
160
+ "CURRENT_TIME",
161
+ "CURRENT_TIMESTAMP",
162
+ "CURRENT_USER",
163
+ "CURSOR",
164
+ "DATABASE",
165
+ "DATE",
166
+ "DAY",
167
+ "DBCC",
168
+ "DEALLOCATE",
169
+ "DEC",
170
+ "DECIMAL",
171
+ "DECLARE",
172
+ "DEFAULT",
173
+ "DEFERRABLE",
174
+ "DEFERRED",
175
+ "DELETE",
176
+ "DENY",
177
+ "DESC",
178
+ "DESCRIBE",
179
+ "DESCRIPTOR",
180
+ "DETACH",
181
+ "DIAGNOSTICS",
182
+ "DISCONNECT",
183
+ "DISK",
184
+ "DISTINCT",
185
+ "DISTRIBUTED",
186
+ "DO",
187
+ "DOMAIN",
188
+ "DOUBLE",
189
+ "DROP",
190
+ "DUMP",
191
+ "EACH",
192
+ "ELSE",
193
+ "END",
194
+ "END-EXEC",
195
+ "ERRLVL",
196
+ "ESCAPE",
197
+ "EXCEPT",
198
+ "EXCEPTION",
199
+ "EXCLUDE",
200
+ "EXCLUSIVE",
201
+ "EXEC",
202
+ "EXECUTE",
203
+ "EXISTS",
204
+ "EXIT",
205
+ "EXPLAIN",
206
+ "EXTERNAL",
207
+ "EXTRACT",
208
+ "FAIL",
209
+ "FALSE",
210
+ "FETCH",
211
+ "FILE",
212
+ "FILLFACTOR",
213
+ "FILTER",
214
+ "FIRST",
215
+ "FLOAT",
216
+ "FOLLOWING",
217
+ "FOR",
218
+ "FOREIGN",
219
+ "FORTRAN",
220
+ "FOUND",
221
+ "FREETEXT",
222
+ "FREETEXTTABLE",
223
+ "FROM",
224
+ "FULL",
225
+ "FUNCTION",
226
+ "GENERATED",
227
+ "GET",
228
+ "GLOB",
229
+ "GLOBAL",
230
+ "GO",
231
+ "GOTO",
232
+ "GRANT",
233
+ "GROUP",
234
+ "GROUPS",
235
+ "HAVING",
236
+ "HOLDLOCK",
237
+ "HOUR",
238
+ "IDENTITY",
239
+ "IDENTITYCOL",
240
+ "IDENTITY_INSERT",
241
+ "IF",
242
+ "IGNORE",
243
+ "IMMEDIATE",
244
+ "IN",
245
+ "INCLUDE",
246
+ "INDEX",
247
+ "INDEXED",
248
+ "INDICATOR",
249
+ "INITIALLY",
250
+ "INNER",
251
+ "INPUT",
252
+ "INSENSITIVE",
253
+ "INSERT",
254
+ "INSTEAD",
255
+ "INT",
256
+ "INTEGER",
257
+ "INTERSECT",
258
+ "INTERVAL",
259
+ "INTO",
260
+ "IS",
261
+ "ISNULL",
262
+ "ISOLATION",
263
+ "JOIN",
264
+ "KEY",
265
+ "KILL",
266
+ "LANGUAGE",
267
+ "LAST",
268
+ "LEADING",
269
+ "LEFT",
270
+ "LEVEL",
271
+ "LIKE",
272
+ "LIMIT",
273
+ "LINENO",
274
+ "LOAD",
275
+ "LOCAL",
276
+ "LOWER",
277
+ "MATCH",
278
+ "MATERIALIZED",
279
+ "MAX",
280
+ "MERGE",
281
+ "MIN",
282
+ "MINUTE",
283
+ "MODULE",
284
+ "MONTH",
285
+ "NAMES",
286
+ "NATIONAL",
287
+ "NATURAL",
288
+ "NCHAR",
289
+ "NEXT",
290
+ "NO",
291
+ "NOCHECK",
292
+ "NONCLUSTERED",
293
+ "NONE",
294
+ "NOT",
295
+ "NOTHING",
296
+ "NOTNULL",
297
+ "NULL",
298
+ "NULLIF",
299
+ "NULLS",
300
+ "NUMERIC",
301
+ "OCTET_LENGTH",
302
+ "OF",
303
+ "OFF",
304
+ "OFFSET",
305
+ "OFFSETS",
306
+ "ON",
307
+ "ONLY",
308
+ "OPEN",
309
+ "OPENDATASOURCE",
310
+ "OPENQUERY",
311
+ "OPENROWSET",
312
+ "OPENXML",
313
+ "OPTION",
314
+ "OR",
315
+ "ORDER",
316
+ "OTHERS",
317
+ "OUTER",
318
+ "OUTPUT",
319
+ "OVER",
320
+ "OVERLAPS",
321
+ "PAD",
322
+ "PARTIAL",
323
+ "PARTITION",
324
+ "PASCAL",
325
+ "PERCENT",
326
+ "PIVOT",
327
+ "PLAN",
328
+ "POSITION",
329
+ "PRAGMA",
330
+ "PRECEDING",
331
+ "PRECISION",
332
+ "PREPARE",
333
+ "PRESERVE",
334
+ "PRIMARY",
335
+ "PRINT",
336
+ "PRIOR",
337
+ "PRIVILEGES",
338
+ "PROC",
339
+ "PROCEDURE",
340
+ "PUBLIC",
341
+ "QUERY",
342
+ "RAISE",
343
+ "RAISERROR",
344
+ "RANGE",
345
+ "READ",
346
+ "READTEXT",
347
+ "REAL",
348
+ "RECONFIGURE",
349
+ "RECURSIVE",
350
+ "REFERENCES",
351
+ "REGEXP",
352
+ "REINDEX",
353
+ "RELATIVE",
354
+ "RELEASE",
355
+ "RENAME",
356
+ "REPLACE",
357
+ "REPLICATION",
358
+ "RESTORE",
359
+ "RESTRICT",
360
+ "RETURN",
361
+ "RETURNING",
362
+ "REVERT",
363
+ "REVOKE",
364
+ "RIGHT",
365
+ "ROLLBACK",
366
+ "ROW",
367
+ "ROWCOUNT",
368
+ "ROWGUIDCOL",
369
+ "ROWS",
370
+ "RULE",
371
+ "SAVE",
372
+ "SAVEPOINT",
373
+ "SCHEMA",
374
+ "SCROLL",
375
+ "SECOND",
376
+ "SECTION",
377
+ "SECURITYAUDIT",
378
+ "SELECT",
379
+ "SEMANTICKEYPHRASETABLE",
380
+ "SEMANTICSIMILARITYDETAILSTABLE",
381
+ "SEMANTICSIMILARITYTABLE",
382
+ "SESSION",
383
+ "SESSION_USER",
384
+ "SET",
385
+ "SETUSER",
386
+ "SHUTDOWN",
387
+ "SIZE",
388
+ "SMALLINT",
389
+ "SOME",
390
+ "SPACE",
391
+ "SQL",
392
+ "SQLCA",
393
+ "SQLCODE",
394
+ "SQLERROR",
395
+ "SQLSTATE",
396
+ "SQLWARNING",
397
+ "STATISTICS",
398
+ "SUBSTRING",
399
+ "SUM",
400
+ "SYSTEM_USER",
401
+ "TABLE",
402
+ "TABLESAMPLE",
403
+ "TEMP",
404
+ "TEMPORARY",
405
+ "TEXTSIZE",
406
+ "THEN",
407
+ "TIES",
408
+ "TIME",
409
+ "TIMESTAMP",
410
+ "TIMEZONE_HOUR",
411
+ "TIMEZONE_MINUTE",
412
+ "TO",
413
+ "TOP",
414
+ "TRAILING",
415
+ "TRAN",
416
+ "TRANSACTION",
417
+ "TRANSLATE",
418
+ "TRANSLATION",
419
+ "TRIGGER",
420
+ "TRIM",
421
+ "TRUE",
422
+ "TRUNCATE",
423
+ "TRY_CONVERT",
424
+ "TSEQUAL",
425
+ "UNBOUNDED",
426
+ "UNION",
427
+ "UNIQUE",
428
+ "UNKNOWN",
429
+ "UNPIVOT",
430
+ "UPDATE",
431
+ "UPDATETEXT",
432
+ "UPPER",
433
+ "USAGE",
434
+ "USE",
435
+ "USER",
436
+ "USING",
437
+ "VACUUM",
438
+ "VALUE",
439
+ "VALUES",
440
+ "VARCHAR",
441
+ "VARYING",
442
+ "VIEW",
443
+ "VIRTUAL",
444
+ "WAITFOR",
445
+ "WHEN",
446
+ "WHENEVER",
447
+ "WHERE",
448
+ "WHILE",
449
+ "WINDOW",
450
+ "WITH",
451
+ "WITHIN GROUP",
452
+ "WITHOUT",
453
+ "WORK",
454
+ "WRITE",
455
+ "WRITETEXT",
456
+ "YEAR",
457
+ "ZONE",
458
+ ],
459
+ operators: [
460
+ // Logical
461
+ "ALL",
462
+ "AND",
463
+ "ANY",
464
+ "BETWEEN",
465
+ "EXISTS",
466
+ "IN",
467
+ "LIKE",
468
+ "NOT",
469
+ "OR",
470
+ "SOME",
471
+ // Set
472
+ "EXCEPT",
473
+ "INTERSECT",
474
+ "UNION",
475
+ // Join
476
+ "APPLY",
477
+ "CROSS",
478
+ "FULL",
479
+ "INNER",
480
+ "JOIN",
481
+ "LEFT",
482
+ "OUTER",
483
+ "RIGHT",
484
+ // Predicates
485
+ "CONTAINS",
486
+ "FREETEXT",
487
+ "IS",
488
+ "NULL",
489
+ // Pivoting
490
+ "PIVOT",
491
+ "UNPIVOT",
492
+ // Merging
493
+ "MATCHED",
494
+ ],
495
+ builtinFunctions: [
496
+ // Aggregate
497
+ "AVG",
498
+ "CHECKSUM_AGG",
499
+ "COUNT",
500
+ "COUNT_BIG",
501
+ "GROUPING",
502
+ "GROUPING_ID",
503
+ "MAX",
504
+ "MIN",
505
+ "SUM",
506
+ "STDEV",
507
+ "STDEVP",
508
+ "VAR",
509
+ "VARP",
510
+ // Analytic
511
+ "CUME_DIST",
512
+ "FIRST_VALUE",
513
+ "LAG",
514
+ "LAST_VALUE",
515
+ "LEAD",
516
+ "PERCENTILE_CONT",
517
+ "PERCENTILE_DISC",
518
+ "PERCENT_RANK",
519
+ // Collation
520
+ "COLLATE",
521
+ "COLLATIONPROPERTY",
522
+ "TERTIARY_WEIGHTS",
523
+ // Azure
524
+ "FEDERATION_FILTERING_VALUE",
525
+ // Conversion
526
+ "CAST",
527
+ "CONVERT",
528
+ "PARSE",
529
+ "TRY_CAST",
530
+ "TRY_CONVERT",
531
+ "TRY_PARSE",
532
+ // Cryptographic
533
+ "ASYMKEY_ID",
534
+ "ASYMKEYPROPERTY",
535
+ "CERTPROPERTY",
536
+ "CERT_ID",
537
+ "CRYPT_GEN_RANDOM",
538
+ "DECRYPTBYASYMKEY",
539
+ "DECRYPTBYCERT",
540
+ "DECRYPTBYKEY",
541
+ "DECRYPTBYKEYAUTOASYMKEY",
542
+ "DECRYPTBYKEYAUTOCERT",
543
+ "DECRYPTBYPASSPHRASE",
544
+ "ENCRYPTBYASYMKEY",
545
+ "ENCRYPTBYCERT",
546
+ "ENCRYPTBYKEY",
547
+ "ENCRYPTBYPASSPHRASE",
548
+ "HASHBYTES",
549
+ "IS_OBJECTSIGNED",
550
+ "KEY_GUID",
551
+ "KEY_ID",
552
+ "KEY_NAME",
553
+ "SIGNBYASYMKEY",
554
+ "SIGNBYCERT",
555
+ "SYMKEYPROPERTY",
556
+ "VERIFYSIGNEDBYCERT",
557
+ "VERIFYSIGNEDBYASYMKEY",
558
+ // Cursor
559
+ "CURSOR_STATUS",
560
+ // Datatype
561
+ "DATALENGTH",
562
+ "IDENT_CURRENT",
563
+ "IDENT_INCR",
564
+ "IDENT_SEED",
565
+ "IDENTITY",
566
+ "SQL_VARIANT_PROPERTY",
567
+ // Datetime
568
+ "CURRENT_TIMESTAMP",
569
+ "DATEADD",
570
+ "DATEDIFF",
571
+ "DATEFROMPARTS",
572
+ "DATENAME",
573
+ "DATEPART",
574
+ "DATETIME2FROMPARTS",
575
+ "DATETIMEFROMPARTS",
576
+ "DATETIMEOFFSETFROMPARTS",
577
+ "DAY",
578
+ "EOMONTH",
579
+ "GETDATE",
580
+ "GETUTCDATE",
581
+ "ISDATE",
582
+ "MONTH",
583
+ "SMALLDATETIMEFROMPARTS",
584
+ "SWITCHOFFSET",
585
+ "SYSDATETIME",
586
+ "SYSDATETIMEOFFSET",
587
+ "SYSUTCDATETIME",
588
+ "TIMEFROMPARTS",
589
+ "TODATETIMEOFFSET",
590
+ "YEAR",
591
+ // Logical
592
+ "CHOOSE",
593
+ "COALESCE",
594
+ "IIF",
595
+ "NULLIF",
596
+ // Mathematical
597
+ "ABS",
598
+ "ACOS",
599
+ "ASIN",
600
+ "ATAN",
601
+ "ATN2",
602
+ "CEILING",
603
+ "COS",
604
+ "COT",
605
+ "DEGREES",
606
+ "EXP",
607
+ "FLOOR",
608
+ "LOG",
609
+ "LOG10",
610
+ "PI",
611
+ "POWER",
612
+ "RADIANS",
613
+ "RAND",
614
+ "ROUND",
615
+ "SIGN",
616
+ "SIN",
617
+ "SQRT",
618
+ "SQUARE",
619
+ "TAN",
620
+ // Metadata
621
+ "APP_NAME",
622
+ "APPLOCK_MODE",
623
+ "APPLOCK_TEST",
624
+ "ASSEMBLYPROPERTY",
625
+ "COL_LENGTH",
626
+ "COL_NAME",
627
+ "COLUMNPROPERTY",
628
+ "DATABASE_PRINCIPAL_ID",
629
+ "DATABASEPROPERTYEX",
630
+ "DB_ID",
631
+ "DB_NAME",
632
+ "FILE_ID",
633
+ "FILE_IDEX",
634
+ "FILE_NAME",
635
+ "FILEGROUP_ID",
636
+ "FILEGROUP_NAME",
637
+ "FILEGROUPPROPERTY",
638
+ "FILEPROPERTY",
639
+ "FULLTEXTCATALOGPROPERTY",
640
+ "FULLTEXTSERVICEPROPERTY",
641
+ "INDEX_COL",
642
+ "INDEXKEY_PROPERTY",
643
+ "INDEXPROPERTY",
644
+ "OBJECT_DEFINITION",
645
+ "OBJECT_ID",
646
+ "OBJECT_NAME",
647
+ "OBJECT_SCHEMA_NAME",
648
+ "OBJECTPROPERTY",
649
+ "OBJECTPROPERTYEX",
650
+ "ORIGINAL_DB_NAME",
651
+ "PARSENAME",
652
+ "SCHEMA_ID",
653
+ "SCHEMA_NAME",
654
+ "SCOPE_IDENTITY",
655
+ "SERVERPROPERTY",
656
+ "STATS_DATE",
657
+ "TYPE_ID",
658
+ "TYPE_NAME",
659
+ "TYPEPROPERTY",
660
+ // Ranking
661
+ "DENSE_RANK",
662
+ "NTILE",
663
+ "RANK",
664
+ "ROW_NUMBER",
665
+ // Replication
666
+ "PUBLISHINGSERVERNAME",
667
+ // Rowset
668
+ "OPENDATASOURCE",
669
+ "OPENQUERY",
670
+ "OPENROWSET",
671
+ "OPENXML",
672
+ // Security
673
+ "CERTENCODED",
674
+ "CERTPRIVATEKEY",
675
+ "CURRENT_USER",
676
+ "HAS_DBACCESS",
677
+ "HAS_PERMS_BY_NAME",
678
+ "IS_MEMBER",
679
+ "IS_ROLEMEMBER",
680
+ "IS_SRVROLEMEMBER",
681
+ "LOGINPROPERTY",
682
+ "ORIGINAL_LOGIN",
683
+ "PERMISSIONS",
684
+ "PWDENCRYPT",
685
+ "PWDCOMPARE",
686
+ "SESSION_USER",
687
+ "SESSIONPROPERTY",
688
+ "SUSER_ID",
689
+ "SUSER_NAME",
690
+ "SUSER_SID",
691
+ "SUSER_SNAME",
692
+ "SYSTEM_USER",
693
+ "USER",
694
+ "USER_ID",
695
+ "USER_NAME",
696
+ // String
697
+ "ASCII",
698
+ "CHAR",
699
+ "CHARINDEX",
700
+ "CONCAT",
701
+ "DIFFERENCE",
702
+ "FORMAT",
703
+ "LEFT",
704
+ "LEN",
705
+ "LOWER",
706
+ "LTRIM",
707
+ "NCHAR",
708
+ "PATINDEX",
709
+ "QUOTENAME",
710
+ "REPLACE",
711
+ "REPLICATE",
712
+ "REVERSE",
713
+ "RIGHT",
714
+ "RTRIM",
715
+ "SOUNDEX",
716
+ "SPACE",
717
+ "STR",
718
+ "STUFF",
719
+ "SUBSTRING",
720
+ "UNICODE",
721
+ "UPPER",
722
+ // System
723
+ "BINARY_CHECKSUM",
724
+ "CHECKSUM",
725
+ "CONNECTIONPROPERTY",
726
+ "CONTEXT_INFO",
727
+ "CURRENT_REQUEST_ID",
728
+ "ERROR_LINE",
729
+ "ERROR_NUMBER",
730
+ "ERROR_MESSAGE",
731
+ "ERROR_PROCEDURE",
732
+ "ERROR_SEVERITY",
733
+ "ERROR_STATE",
734
+ "FORMATMESSAGE",
735
+ "GETANSINULL",
736
+ "GET_FILESTREAM_TRANSACTION_CONTEXT",
737
+ "HOST_ID",
738
+ "HOST_NAME",
739
+ "ISNULL",
740
+ "ISNUMERIC",
741
+ "MIN_ACTIVE_ROWVERSION",
742
+ "NEWID",
743
+ "NEWSEQUENTIALID",
744
+ "ROWCOUNT_BIG",
745
+ "XACT_STATE",
746
+ // TextImage
747
+ "TEXTPTR",
748
+ "TEXTVALID",
749
+ // Trigger
750
+ "COLUMNS_UPDATED",
751
+ "EVENTDATA",
752
+ "TRIGGER_NESTLEVEL",
753
+ "UPDATE",
754
+ // ChangeTracking
755
+ "CHANGETABLE",
756
+ "CHANGE_TRACKING_CONTEXT",
757
+ "CHANGE_TRACKING_CURRENT_VERSION",
758
+ "CHANGE_TRACKING_IS_COLUMN_IN_MASK",
759
+ "CHANGE_TRACKING_MIN_VALID_VERSION",
760
+ // FullTextSearch
761
+ "CONTAINSTABLE",
762
+ "FREETEXTTABLE",
763
+ // SemanticTextSearch
764
+ "SEMANTICKEYPHRASETABLE",
765
+ "SEMANTICSIMILARITYDETAILSTABLE",
766
+ "SEMANTICSIMILARITYTABLE",
767
+ // FileStream
768
+ "FILETABLEROOTPATH",
769
+ "GETFILENAMESPACEPATH",
770
+ "GETPATHLOCATOR",
771
+ "PATHNAME",
772
+ // ServiceBroker
773
+ "GET_TRANSMISSION_STATUS",
774
+ ],
775
+ builtinVariables: [
776
+ // Configuration
777
+ "@@DATEFIRST",
778
+ "@@DBTS",
779
+ "@@LANGID",
780
+ "@@LANGUAGE",
781
+ "@@LOCK_TIMEOUT",
782
+ "@@MAX_CONNECTIONS",
783
+ "@@MAX_PRECISION",
784
+ "@@NESTLEVEL",
785
+ "@@OPTIONS",
786
+ "@@REMSERVER",
787
+ "@@SERVERNAME",
788
+ "@@SERVICENAME",
789
+ "@@SPID",
790
+ "@@TEXTSIZE",
791
+ "@@VERSION",
792
+ // Cursor
793
+ "@@CURSOR_ROWS",
794
+ "@@FETCH_STATUS",
795
+ // Datetime
796
+ "@@DATEFIRST",
797
+ // Metadata
798
+ "@@PROCID",
799
+ // System
800
+ "@@ERROR",
801
+ "@@IDENTITY",
802
+ "@@ROWCOUNT",
803
+ "@@TRANCOUNT",
804
+ // Stats
805
+ "@@CONNECTIONS",
806
+ "@@CPU_BUSY",
807
+ "@@IDLE",
808
+ "@@IO_BUSY",
809
+ "@@PACKET_ERRORS",
810
+ "@@PACK_RECEIVED",
811
+ "@@PACK_SENT",
812
+ "@@TIMETICKS",
813
+ "@@TOTAL_ERRORS",
814
+ "@@TOTAL_READ",
815
+ "@@TOTAL_WRITE",
816
+ ],
817
+ pseudoColumns: ["$ACTION", "$IDENTITY", "$ROWGUID", "$PARTITION"],
818
+ tokenizer: {
819
+ root: [
820
+ { include: "@comments" },
821
+ { include: "@whitespace" },
822
+ { include: "@pseudoColumns" },
823
+ { include: "@numbers" },
824
+ { include: "@strings" },
825
+ { include: "@complexIdentifiers" },
826
+ { include: "@scopes" },
827
+ [/[;,.]/, "delimiter"],
828
+ [/[()]/, "@brackets"],
829
+ [
830
+ /[\w@#$]+/,
831
+ {
832
+ cases: {
833
+ "@operators": "operator",
834
+ "@builtinVariables": "predefined",
835
+ "@builtinFunctions": "predefined",
836
+ "@keywords": "keyword",
837
+ "@default": "identifier",
838
+ },
839
+ },
840
+ ],
841
+ [/[<>=!%&+\-*/|~^]/, "operator"],
842
+ ],
843
+ whitespace: [[/\s+/, "white"]],
844
+ comments: [
845
+ [/--+.*/, "comment"],
846
+ [/\/\*/, { token: "comment.quote", next: "@comment" }],
847
+ ],
848
+ comment: [
849
+ [/[^*/]+/, "comment"],
850
+ // Not supporting nested comments, as nested comments seem to not be standard?
851
+ // i.e. http://stackoverflow.com/questions/728172/are-there-multiline-comment-delimiters-in-sql-that-are-vendor-agnostic
852
+ // [/\/\*/, { token: 'comment.quote', next: '@push' }], // nested comment not allowed :-(
853
+ [/\*\//, { token: "comment.quote", next: "@pop" }],
854
+ [/./, "comment"],
855
+ ],
856
+ // TODO: discuss
857
+ pseudoColumns: [
858
+ [
859
+ /[$][A-Za-z_][\w@#$]*/,
860
+ {
861
+ cases: {
862
+ "@pseudoColumns": "predefined",
863
+ "@default": "identifier",
864
+ },
865
+ },
866
+ ],
867
+ ],
868
+ numbers: [
869
+ [/0[xX][0-9a-fA-F]*/, "number"],
870
+ [/[$][+-]*\d*(\.\d*)?/, "number"],
871
+ [/((\d+(\.\d*)?)|(\.\d+))([eE][\-+]?\d+)?/, "number"],
872
+ ],
873
+ strings: [
874
+ [/N'/, { token: "string", next: "@string" }],
875
+ [/'/, { token: "string", next: "@string" }],
876
+ ],
877
+ string: [
878
+ [/[^']+/, "string"],
879
+ [/''/, "string"],
880
+ [/'/, { token: "string", next: "@pop" }],
881
+ ],
882
+ complexIdentifiers: [
883
+ [/\[/, { token: "identifier.quote", next: "@bracketedIdentifier" }],
884
+ [/"/, { token: "identifier.quote", next: "@quotedIdentifier" }],
885
+ ],
886
+ bracketedIdentifier: [
887
+ [/[^\]]+/, "identifier"],
888
+ [/]]/, "identifier"],
889
+ [/]/, { token: "identifier.quote", next: "@pop" }],
890
+ ],
891
+ quotedIdentifier: [
892
+ [/[^"]+/, "identifier"],
893
+ [/""/, "identifier"],
894
+ [/"/, { token: "identifier.quote", next: "@pop" }],
895
+ ],
896
+ scopes: [
897
+ [/BEGIN\s+(DISTRIBUTED\s+)?TRAN(SACTION)?\b/i, "keyword"],
898
+ [/BEGIN\s+TRY\b/i, { token: "keyword.try" }],
899
+ [/END\s+TRY\b/i, { token: "keyword.try" }],
900
+ [/BEGIN\s+CATCH\b/i, { token: "keyword.catch" }],
901
+ [/END\s+CATCH\b/i, { token: "keyword.catch" }],
902
+ [/(BEGIN|CASE)\b/i, { token: "keyword.block" }],
903
+ [/END\b/i, { token: "keyword.block" }],
904
+ [/WHEN\b/i, { token: "keyword.choice" }],
905
+ [/THEN\b/i, { token: "keyword.choice" }],
906
+ ],
907
+ },
908
+ };
909
+
910
+ /* eslint-disable @typescript-eslint/no-magic-numbers */
911
+ async function sqlExpressionCompletion(range, model, position, sdkFunctions) {
912
+ try {
913
+ /*
914
+ Handles the case where we have a valid SQL expression in the editor and is
915
+ able to generate an AST from it and traverse it to provide completion items.
916
+ */
917
+ const editorValue = model.getValue();
918
+ const sql = await importCoreSql();
919
+ const fieldIndex = await newLayersSupportFieldsIndex([]);
920
+ const { parseTree } = await sql.parseWhereClause(editorValue, fieldIndex);
921
+ const completionItems = getCompletionItems(model, parseTree, range, position, sdkFunctions);
922
+ return completionItems;
923
+ }
924
+ catch {
925
+ /*
926
+ Handles the case where we have an incomplete SQL expression (i.e. if users are
927
+ still typing or has erroneous syntax). We fall back to a more lexical approach
928
+ in this way to "fail gracefully" in order to still provide completion items.
929
+ */
930
+ const errorRange = {
931
+ startColumn: 1,
932
+ endColumn: position.column,
933
+ startLineNumber: 1,
934
+ endLineNumber: position.lineNumber,
935
+ };
936
+ /* Matching a '(', ')', ' ', or ',' */
937
+ const delimiter = /(?:\(|\)|\s|,)/u;
938
+ /*
939
+ Filter callback function to filter out all non-function words, non-sql keywords, empty
940
+ strings and single quotes ('). Preserve all '(', ')', ',' and 'END' tokens
941
+ within our array.
942
+ */
943
+ const filterToken = (token) => {
944
+ const tokenIsAFunction = sdkFunctions[token] !== undefined;
945
+ const isParenLessFunction = isSqlParenLessFunction(token);
946
+ const excludedTokens = token !== "" && token !== "'";
947
+ const includedTokens = ["(", ")", ",", "END"].includes(token);
948
+ /*
949
+ Returns the tokens if they are either a standardized function, parenthesis-less function
950
+ or is one of the tokens that we specified to keep. Filters all empty string and
951
+ single quotations.
952
+ */
953
+ return excludedTokens && (tokenIsAFunction || isParenLessFunction || includedTokens);
954
+ };
955
+ const textValueUpToCursor = model.getValueInRange(errorRange).toUpperCase();
956
+ // TODO: test this...
957
+ const tokens = textValueUpToCursor
958
+ .split(delimiter)
959
+ .map((token) => token.trim())
960
+ .filter((token) => filterToken(token));
961
+ /* Keeps track of our current context item */
962
+ const contextStack = [];
963
+ for (let i = 0; i < tokens.length; i++) {
964
+ const token = tokens[i];
965
+ const nonEmptyContextStack = contextStack.length > 0;
966
+ /*
967
+ As long as we have not hit the last token in our list yet, we can do a
968
+ lookahead and see if the next token makes a valid sub-expression.
969
+ */
970
+ if (i < tokens.length - 1) {
971
+ const nextToken = tokens[i + 1];
972
+ const invalidFunctionCall = sdkFunctions[token] && nextToken !== "(";
973
+ if (invalidFunctionCall) {
974
+ continue;
975
+ }
976
+ }
977
+ if (token === "(" || token === ",") {
978
+ if (nonEmptyContextStack) {
979
+ const currentContext = contextStack[contextStack.length - 1];
980
+ /*
981
+ This marks the end of our parenthesis-less function's scope, hence
982
+ we cleanse the context and let the next token update the context.
983
+ */
984
+ if (isSqlParenLessFunction(currentContext)) {
985
+ contextStack.pop();
986
+ }
987
+ }
988
+ continue;
989
+ }
990
+ else if (token === ")" || token === "END") {
991
+ /*
992
+ If we have a non empty context stack, that means we are within another
993
+ function's scope right now (another context). We will return to the outer
994
+ scope to provide us the necessary completion items.
995
+ */
996
+ if (nonEmptyContextStack) {
997
+ contextStack.pop();
998
+ }
999
+ continue;
1000
+ }
1001
+ /*
1002
+ If we end off with a standardized function without a '(', we should not
1003
+ update the context. We should only trigger completion if they have a '('
1004
+ to indicate that it's a function call.
1005
+ */
1006
+ if (i === tokens.length - 1 && sdkFunctions[token]) {
1007
+ continue;
1008
+ }
1009
+ /*
1010
+ Very specific case where we have something like
1011
+
1012
+ | TRIM(INTERVAL INTERVAL, ... |
1013
+
1014
+ and we do not want the completion items for "INTERVAL" to persist even after
1015
+ the context ends.
1016
+
1017
+ In essence, we only update the context once.
1018
+ */
1019
+ if (i > 0) {
1020
+ const prevTokenIsParenLess = isSqlParenLessFunction(tokens[i - 1]);
1021
+ const curTokenIsParenLess = isSqlParenLessFunction(token);
1022
+ if (!(curTokenIsParenLess && prevTokenIsParenLess)) {
1023
+ contextStack.push(token);
1024
+ }
1025
+ }
1026
+ else if (i === 0) {
1027
+ contextStack.push(token);
1028
+ }
1029
+ }
1030
+ let completionItems = [];
1031
+ [...new Set(contextStack)].forEach((context, index) => {
1032
+ const autoCompletionItem = generateCompletionList(context, range, index);
1033
+ completionItems = [...completionItems, ...autoCompletionItem];
1034
+ });
1035
+ return completionItems;
1036
+ }
1037
+ }
1038
+ /*
1039
+ Helper function that takes in our model/position information and traverse the
1040
+ completed AST to generate an array of Monaco's completion items.
1041
+
1042
+ This function is invoked only if we have a valid SQL expression that can be parsed to
1043
+ generate an AST. In other words, this is only invoked for our "success" case.
1044
+ */
1045
+ const getCompletionItems = (model, node, range, position, sdkFunctions) => {
1046
+ /*
1047
+ Using an object here so that we can pass by reference to the function to
1048
+ handle updating the state instead of pass by value if we use a primitive
1049
+ type like string. This also allows us to extend the context and add more
1050
+ data and attributes if we need to in the future.
1051
+ */
1052
+ const context = { contextString: "NONE", additionalContext: "NONE" };
1053
+ traverseAST({ model, node, position, context, sdkFunctions });
1054
+ const { contextString, additionalContext } = context;
1055
+ const completionItems = generateCompletionList(contextString, range, 0);
1056
+ const additionalItems = generateCompletionList(additionalContext, range, 1);
1057
+ return [...completionItems, ...additionalItems];
1058
+ };
1059
+ /*
1060
+ This function traverses the AST and finds which node and context we are currently
1061
+ in to help provide the necessary completion items. This follows from having a complete
1062
+ and valid SQL expression. The main purpose of this function is to provide the correct
1063
+ completion items when users trigger IntelliSense via CTRL + Space.
1064
+
1065
+ This is a recursive function.
1066
+ */
1067
+ // eslint-disable-next-line complexity
1068
+ const traverseAST = (
1069
+ /*
1070
+ Keeping the node type as generic 'any' here as __esri.SQLNode does not have updated types
1071
+ with the addition of the range property. Documentation is outdated and may or may not
1072
+ contain mismatching property names (ListNode uses "expr" as property for all expressions
1073
+ but representation is via "value") and some missing cases (e.g. case "parameter" is not
1074
+ identified).
1075
+ */
1076
+ { model, node, position, context, sdkFunctions, delimiterPos, }) => {
1077
+ /* Our current cursor position within the editor */
1078
+ // const cursorRow: number = position.lineNumber;
1079
+ // const cursorColumn: number = position.column;
1080
+ switch (node.type) {
1081
+ case "interval": {
1082
+ /*
1083
+ Here, we are using an interval node as a standalone and not within any
1084
+ function call or expression list.
1085
+ */
1086
+ if (!delimiterPos) {
1087
+ updateContext(context, "INTERVAL", false);
1088
+ return;
1089
+ }
1090
+ /*
1091
+ We can ensure that the match is never null since a fully generated AST
1092
+ follows from a syntactically correct SQL expression.
1093
+ */
1094
+ const nextDelimPos = model.findNextMatch(
1095
+ // @ts-ignore
1096
+ /\)|,/u, {
1097
+ lineNumber: node.location.end.line,
1098
+ column: node.location.end.column,
1099
+ }, true, true, null, true).range;
1100
+ const [inlineFunc, funcWrapped] = getCursorWithinRange(position, delimiterPos, nextDelimPos);
1101
+ if (inlineFunc ?? funcWrapped) {
1102
+ updateContext(context, "INTERVAL", false);
1103
+ }
1104
+ return;
1105
+ }
1106
+ case "case-expression": {
1107
+ /* Only update context if we are within the range */
1108
+ // Representing the range for the "CASE" keyword for starting the case expression
1109
+ const startPos = {
1110
+ startColumn: node.location.start.column,
1111
+ endColumn: node.location.start.column + 4,
1112
+ startLineNumber: node.location.start.line,
1113
+ endLineNumber: node.location.start.line,
1114
+ };
1115
+ // Representing the range for the "END" keyword for ending the case expression
1116
+ const endPos = {
1117
+ startColumn: node.location.end.column - 3,
1118
+ endColumn: node.location.end.column,
1119
+ startLineNumber: node.location.end.line,
1120
+ endLineNumber: node.location.end.line,
1121
+ };
1122
+ const [inline, wrapped] = getCursorWithinRange(position, startPos, endPos);
1123
+ if (inline ?? wrapped) {
1124
+ updateContext(context, "CASE", false);
1125
+ }
1126
+ return;
1127
+ }
1128
+ case "expression-list": {
1129
+ const exprList = node.value;
1130
+ let delimiter;
1131
+ exprList.forEach((expr, index) => {
1132
+ /* The position of our opening delimiter '(' */
1133
+ if (index === 0) {
1134
+ const match = model.findMatches("(", {
1135
+ startLineNumber: 1,
1136
+ endLineNumber: node.location.start.line,
1137
+ startColumn: 1,
1138
+ endColumn: node.location.start.column,
1139
+ }, false, true, null, true);
1140
+ delimiter = match[match.length - 1].range;
1141
+ }
1142
+ traverseAST({ model, node: expr, position, context, sdkFunctions, delimiterPos: delimiter });
1143
+ /*
1144
+ Finds and updates our next delimiter position (i.e. the delimiter that immediately
1145
+ follows this current node)
1146
+ */
1147
+ delimiter = getDelimiterPosition(index + 1, expr, model, exprList.length);
1148
+ });
1149
+ return;
1150
+ }
1151
+ case "unary-expression":
1152
+ traverseAST({ model, node: node.expr, position, context, sdkFunctions });
1153
+ return;
1154
+ case "binary-expression":
1155
+ traverseAST({ model, node: node.left, position, context, sdkFunctions });
1156
+ traverseAST({ model, node: node.right, position, context, sdkFunctions });
1157
+ return;
1158
+ case "null":
1159
+ case "boolean":
1160
+ case "string":
1161
+ case "timestamp":
1162
+ case "date":
1163
+ case "time":
1164
+ case "number":
1165
+ case "current-time":
1166
+ return;
1167
+ case "column-reference": {
1168
+ const columnName = node.column;
1169
+ const isParenLessFunction = isSqlParenLessFunction(columnName);
1170
+ /*
1171
+ In this case, we are using a standalone column reference, not a part of any
1172
+ expression list or function param. Update the context accordingly if it is
1173
+ any of the parenthesis-less functions.
1174
+ */
1175
+ if (!delimiterPos) {
1176
+ if (isParenLessFunction) {
1177
+ updateContext(context, columnName, false);
1178
+ }
1179
+ return;
1180
+ }
1181
+ /*
1182
+ The delimiter right before we start our expression. This delimiter is useful for us
1183
+ as it gives positional information for when the expression starts if it's used as a
1184
+ parameter in a function.
1185
+
1186
+ For example, if we have something like Function(a, b), then the range of b would have
1187
+ a start column value of 13 (offset of 12) whereas "," would be 11 (offset of 10).
1188
+
1189
+ This will be useful in the case where we want to provide completion items before our
1190
+ keyword appears (if any).
1191
+ */
1192
+ const delimiterCharacter = model.getValueInRange(delimiterPos);
1193
+ const nextDelimiterPos = model.findNextMatch(
1194
+ /*
1195
+ Function call only accepts string even though docs noted that RegExp is allowed.
1196
+ "@param searchString — The string used to search. If it is a regular expression,
1197
+ set isRegex to true"
1198
+ */
1199
+ // @ts-ignore
1200
+ /\)|,/u, {
1201
+ column: node.location.start.column,
1202
+ lineNumber: node.location.start.line,
1203
+ }, true, true, null, true).range;
1204
+ const [inlineWithFunction, wrappedWithinFunction] = getCursorWithinRange(position, delimiterPos, nextDelimiterPos);
1205
+ if (inlineWithFunction ?? wrappedWithinFunction) {
1206
+ /*
1207
+ We updated our additional context here if this is our first argument within a function call.
1208
+ This is so we don't override the previous context with our current context, which may
1209
+ strip away certain completion items. For example, if we have the SQL expression
1210
+ CAST(INTERVAL), we still want the completion items for CAST as well as INTERVAL.
1211
+ */
1212
+ if (isParenLessFunction) {
1213
+ updateContext(context, columnName, delimiterCharacter === "(");
1214
+ }
1215
+ }
1216
+ return;
1217
+ }
1218
+ case "data-type":
1219
+ return;
1220
+ case "function": {
1221
+ const functionName = node.name;
1222
+ const argsList = node.args.value;
1223
+ const startRange = {
1224
+ startLineNumber: node.location.start.line,
1225
+ endLineNumber: node.location.start.line,
1226
+ startColumn: node.location.start.column,
1227
+ endColumn: node.location.start.column + functionName.length,
1228
+ };
1229
+ const endRange = {
1230
+ startLineNumber: node.location.end.line,
1231
+ endLineNumber: node.location.end.line,
1232
+ startColumn: node.location.end.column,
1233
+ endColumn: node.location.end.column,
1234
+ };
1235
+ const [inlineWithFunc, wrappedWithinFunc] = getCursorWithinRange(position, startRange, endRange);
1236
+ if (inlineWithFunc ?? wrappedWithinFunc) {
1237
+ updateContext(context, functionName, false);
1238
+ }
1239
+ let delimiterPosition;
1240
+ argsList.forEach((arg, index) => {
1241
+ const nodeType = arg.type;
1242
+ let completionNodeType = false;
1243
+ switch (nodeType) {
1244
+ case "function":
1245
+ case "column-reference":
1246
+ case "interval":
1247
+ case "case-expression":
1248
+ completionNodeType = true;
1249
+ break;
1250
+ }
1251
+ /*
1252
+ We can ensure that the match is never undefined since a fully generated AST
1253
+ follows from a syntactically correct SQL expression.
1254
+ */
1255
+ if (index === 0) {
1256
+ delimiterPosition = model.findNextMatch("(", {
1257
+ column: node.location.start.column,
1258
+ lineNumber: node.location.start.line,
1259
+ }, false, true, null, true).range;
1260
+ }
1261
+ /* TRIM(abc) === TRIM(BOTH ' ' FROM abc). In this case, our next delimiter is the end */
1262
+ const trimFuncDefault = arg.type === "string" && arg.value === "BOTH";
1263
+ /*
1264
+ If the current node is not a completion node type, and is the default trim function,
1265
+ then we will just use the current node as a reference to get the next delimiter position.
1266
+ */
1267
+ if (!completionNodeType && trimFuncDefault) {
1268
+ return;
1269
+ }
1270
+ traverseAST({ model, node: arg, position, context, sdkFunctions, delimiterPos: delimiterPosition });
1271
+ delimiterPosition = getDelimiterPosition(index + 1, arg, model, argsList.length);
1272
+ });
1273
+ return;
1274
+ }
1275
+ /* Currently no support for parameter type (@) */
1276
+ case "parameter":
1277
+ case "when-clause":
1278
+ case "interval-period":
1279
+ case "interval-qualifier":
1280
+ return;
1281
+ default:
1282
+ throw new Error("Invalid syntax.");
1283
+ }
1284
+ };
1285
+ /*
1286
+ Helper function that generates our list of completion items after traversing
1287
+ our AST and updating the context.
1288
+
1289
+ The sorting number is used as a way of grouping and organizing our completion lists.
1290
+
1291
+ - All default completion items have a sort value of 'z' (ASCII 122) + their corresponding
1292
+ grouping value (e.g. 'za', 'zb', etc...)
1293
+ - All completion items found by cursor matching have a value between 'a' - 'y' (ASCII 97-121)
1294
+ - The higher the sorting number, the higher it will appear on our completion list
1295
+ */
1296
+ const generateCompletionList = (functionName, range, sortingNumber) => {
1297
+ const identifiers = getKeywords(functionName);
1298
+ const completionItems = identifiers?.map((identifier) => {
1299
+ const dataKind = dataTypes.includes(identifier.toUpperCase())
1300
+ ? languages.CompletionItemKind.TypeParameter
1301
+ : languages.CompletionItemKind.Keyword;
1302
+ return {
1303
+ label: identifier,
1304
+ kind: dataKind,
1305
+ insertText: identifier,
1306
+ range,
1307
+ sortText: String.fromCharCode(121 - sortingNumber),
1308
+ };
1309
+ });
1310
+ return completionItems ?? [];
1311
+ };
1312
+ /*
1313
+ Function to help us update our current context depending on where our cursor
1314
+ is within the editor. This will allow us to gather the relevant completion items
1315
+ depending on the value of the context (i.e. the function that our cursor is within).
1316
+
1317
+ This is invoked only during our traversal of the AST (i.e. "success" case).
1318
+ */
1319
+ const updateContext = (oldContext, newContext, updateAdditionalContext) => {
1320
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
1321
+ updateAdditionalContext ? (oldContext.additionalContext = newContext) : (oldContext.contextString = newContext);
1322
+ };
1323
+ /** source */
1324
+ const sqlBinOps = [
1325
+ "AND",
1326
+ "OR",
1327
+ "IS",
1328
+ "ISNOT",
1329
+ "IN",
1330
+ "NOT IN",
1331
+ "BETWEEN",
1332
+ "NOTBETWEEN",
1333
+ "LIKE",
1334
+ "NOT LIKE",
1335
+ ];
1336
+ /*
1337
+ The current state of sorting completion items.
1338
+
1339
+ All fields that are provided by default (keywords, standardized functions, constants, etc.)
1340
+ all have a sort value of "z". As we parse our SQL Expression and traverse an AST (if we can),
1341
+ we will have new completion items depending on our cursor context. These completion items
1342
+ should appear first at the top of our list, with the most recent context having the highest
1343
+ appearance on our list. In this manner, we add new completion items with a smaller
1344
+ lexicographic value than the previous to promote the correct ordering. (I.e. add in new completion
1345
+ items with a sort value of "y" next, then "x", then "w", all the way until "a". It is unlikely
1346
+ that we will need more than 26 difference context but if we do require more, we can make use of
1347
+ double-lettered sort values).
1348
+ */
1349
+ const sortTextValues = {
1350
+ identifiers: "a",
1351
+ fields: "aa",
1352
+ constants: "ab",
1353
+ literals: "ab",
1354
+ functions: "ac",
1355
+ snippets: "b",
1356
+ keywords: "c",
1357
+ dataType: "d",
1358
+ };
1359
+ function generateCompletionItems(fields, range, sdkFunctions) {
1360
+ const fieldsCompletion = fields.map((field) => ({
1361
+ label: field.completion?.label ?? field.name,
1362
+ insertText: field.completion?.insertText ?? field.name,
1363
+ sortText: `z${sortTextValues.fields}`,
1364
+ detail: field.completion?.detail,
1365
+ range,
1366
+ kind: languages.CompletionItemKind.Variable,
1367
+ documentation: field.completion?.documentation,
1368
+ }));
1369
+ // {
1370
+ // const funcName = func.toLowerCase();
1371
+ // const apiContext = apiFunctions[funcName as keyof typeof apiFunctions] as ApiContextType;
1372
+ // const { name, snippet, parameters, returnValue, description, examples } = apiContext;
1373
+ // const completionItemData = {
1374
+ // kind: monaco.languages.CompletionItemKind.Function,
1375
+ // range,
1376
+ // sortText: `z${sortTextValues.functions}`,
1377
+ // detail: generateTypeDescription(name, parameters, returnValue),
1378
+ // documentation: generateCompletionDocumentation(description, examples),
1379
+ // };
1380
+ // /*
1381
+ // We take 2 different forms of the same function over our REST API.
1382
+ // e.g. CURRENT_TIME and CURRENT_TIME() are both accepted.
1383
+ // */
1384
+ // if (Array.isArray(snippet) && snippet.length) {
1385
+ // return [
1386
+ // { ...completionItemData, label: name, insertText: snippet[0]! },
1387
+ // snippet[1] && { ...completionItemData, label: name.slice(0, -2), insertText: snippet[1] },
1388
+ // ];
1389
+ // }
1390
+ // return [{ ...completionItemData, label: name, insertText: snippet }];
1391
+ // }
1392
+ // );
1393
+ const sqlBinOpsCompletion = sqlBinOps.map((binOp) => {
1394
+ const formatOperator = (binOp) => {
1395
+ if (binOp === "ISNOT") {
1396
+ return "IS NOT";
1397
+ }
1398
+ if (binOp === "NOTBETWEEN") {
1399
+ return "NOT BETWEEN";
1400
+ }
1401
+ return binOp;
1402
+ };
1403
+ return {
1404
+ label: formatOperator(binOp),
1405
+ kind: languages.CompletionItemKind.Keyword,
1406
+ insertText: formatOperator(binOp),
1407
+ range,
1408
+ sortText: `z${sortTextValues.keywords}`,
1409
+ };
1410
+ });
1411
+ const sqlKeywordsCompletion = sqlParenLessFunctions.map((keyword) => {
1412
+ if (keyword === "CASE") {
1413
+ return {
1414
+ label: keyword,
1415
+ kind: languages.CompletionItemKind.Snippet,
1416
+ insertText: `CASE
1417
+ WHEN condition1 THEN result1
1418
+ ELSE result
1419
+ END`,
1420
+ range,
1421
+ sortText: `z${sortTextValues.snippets}`,
1422
+ };
1423
+ }
1424
+ return {
1425
+ label: keyword,
1426
+ kind: languages.CompletionItemKind.Keyword,
1427
+ insertText: keyword,
1428
+ range,
1429
+ sortText: `z${sortTextValues.keywords}`,
1430
+ };
1431
+ });
1432
+ const sqlDataTypeCompletion = dataTypes.map((dataType) => ({
1433
+ label: dataType,
1434
+ kind: languages.CompletionItemKind.TypeParameter,
1435
+ insertText: dataType,
1436
+ range,
1437
+ sortText: `z${sortTextValues.dataType}`,
1438
+ }));
1439
+ const sqlFunctionCompletion = Object.values(sdkFunctions).map((apiItem) => {
1440
+ const entry = apiItem.completion;
1441
+ const item = {
1442
+ label: entry.label,
1443
+ insertText: entry.insertText || entry.label,
1444
+ sortText: `z${sortTextValues.functions}`,
1445
+ filterText: entry.filterText,
1446
+ detail: entry.detail,
1447
+ range,
1448
+ kind: toCompletionItemKind(entry.kind),
1449
+ };
1450
+ if (entry.insertTextFormat === main.exports.InsertTextFormat.Snippet) {
1451
+ item.insertTextRules = languages.CompletionItemInsertTextRule.InsertAsSnippet;
1452
+ }
1453
+ if (entry.documentation) {
1454
+ if (typeof entry.documentation === "string") {
1455
+ item.documentation = entry.documentation;
1456
+ }
1457
+ else {
1458
+ item.documentation = {
1459
+ supportThemeIcons: false,
1460
+ value: entry.documentation.value,
1461
+ supportHtml: true,
1462
+ };
1463
+ }
1464
+ }
1465
+ return item;
1466
+ });
1467
+ return {
1468
+ fieldsCompletion,
1469
+ sqlFunctionCompletion,
1470
+ sqlBinOpsCompletion,
1471
+ sqlKeywordsCompletion,
1472
+ sqlDataTypeCompletion,
1473
+ };
1474
+ }
1475
+
1476
+ // list of properties that sit on a SQLNode and can have children
1477
+ var ChildBearingProperties;
1478
+ (function (ChildBearingProperties) {
1479
+ ChildBearingProperties["ARGS"] = "args";
1480
+ ChildBearingProperties["CLAUSES"] = "clauses";
1481
+ ChildBearingProperties["ELSE"] = "else";
1482
+ ChildBearingProperties["END"] = "end";
1483
+ ChildBearingProperties["EXPR"] = "expr";
1484
+ ChildBearingProperties["LEFT"] = "left";
1485
+ ChildBearingProperties["OPERAND"] = "operand";
1486
+ ChildBearingProperties["QUALIFIER"] = "qualifier";
1487
+ ChildBearingProperties["RIGHT"] = "right";
1488
+ ChildBearingProperties["START"] = "start";
1489
+ ChildBearingProperties["VALUE"] = "value";
1490
+ })(ChildBearingProperties || (ChildBearingProperties = {}));
1491
+ // list of node types that can have children
1492
+ var ChildBearingNodeTypes;
1493
+ (function (ChildBearingNodeTypes) {
1494
+ ChildBearingNodeTypes["CASE_EXPRESSION"] = "case-expression";
1495
+ ChildBearingNodeTypes["EXPRESSION_LIST"] = "expression-list";
1496
+ ChildBearingNodeTypes["FUNCTION"] = "function";
1497
+ ChildBearingNodeTypes["INTERVAL"] = "interval";
1498
+ ChildBearingNodeTypes["INTERVAL_QUALIFIER"] = "interval-qualifier";
1499
+ ChildBearingNodeTypes["WHEN_CLAUSE"] = "when-clause";
1500
+ ChildBearingNodeTypes["UNARY_EXPRESSION"] = "unary-expression";
1501
+ ChildBearingNodeTypes["BINARY_EXPRESSION"] = "binary-expression";
1502
+ })(ChildBearingNodeTypes || (ChildBearingNodeTypes = {}));
1503
+ const sqlFunctionParamKeywords = {
1504
+ CAST: ["DATE", "FLOAT", "INTEGER", "REAL", "SMALLINT", "TIME", "TIMESTAMP", "VARCHAR"],
1505
+ EXTRACT: ["YEAR", "MONTH", "DAY", "HOUR", "MINUTE", "SECOND"],
1506
+ TRIM: ["LEADING", "TRAILING", "BOTH"],
1507
+ };
1508
+
1509
+ /* eslint-disable @typescript-eslint/no-magic-numbers */
1510
+ function transposeSdkFunctions(sdkLibrary) {
1511
+ return sdkLibrary.flatMap((category) => category.items).reduce((acc, item) => ({ ...acc, [item.name]: item }), {});
1512
+ }
1513
+ class SqlExprCompletionProvider {
1514
+ constructor(
1515
+ // private _worker: ISqlExprWorkerAccessor,
1516
+ _defaults) {
1517
+ this._defaults = _defaults;
1518
+ this._sdkFunctions = transposeSdkFunctions([]);
1519
+ }
1520
+ async provideCompletionItems(model, position) {
1521
+ const word = model.getWordUntilPosition(position);
1522
+ const range = {
1523
+ startLineNumber: position.lineNumber,
1524
+ endLineNumber: position.lineNumber,
1525
+ startColumn: word.startColumn,
1526
+ endColumn: word.endColumn,
1527
+ };
1528
+ const apiContext = this._defaults.getApiContextForModel(model.uri);
1529
+ // get the fields from the api context
1530
+ const fields = apiContext.profile ? getFieldsFromLayerVariable(apiContext.profile) : [];
1531
+ const sdkLibrary = await this._defaults.getApiLibrary(model.uri.toString());
1532
+ this._sdkFunctions = transposeSdkFunctions(sdkLibrary);
1533
+ const sqlContextCompletion = await sqlExpressionCompletion(range, model, position, this._sdkFunctions);
1534
+ const { sqlKeywordsCompletion, sqlBinOpsCompletion, sqlFunctionCompletion, fieldsCompletion, sqlDataTypeCompletion, } = generateCompletionItems(fields, range, this._sdkFunctions);
1535
+ /*
1536
+ Filters our completion items list so that if we happen to have the same label but
1537
+ different sortText order, we keep the sortText with the lowest ASCII value (i.e.
1538
+ the smallest character in terms of lexicographic ordering).
1539
+ */
1540
+ const filteredItems = new Map();
1541
+ [
1542
+ ...sqlKeywordsCompletion,
1543
+ ...sqlBinOpsCompletion,
1544
+ ...sqlDataTypeCompletion,
1545
+ ...sqlFunctionCompletion,
1546
+ ...fieldsCompletion,
1547
+ ...sqlContextCompletion,
1548
+ ].forEach((item) => {
1549
+ const { sortText } = item;
1550
+ const label = item.label;
1551
+ const itemExists = filteredItems.get(label);
1552
+ if (!itemExists) {
1553
+ filteredItems.set(label, item);
1554
+ }
1555
+ const currentOrder = sortText.charCodeAt(0);
1556
+ const currentLowestOrder = filteredItems.get(label).sortText.charCodeAt(0);
1557
+ if (currentOrder < currentLowestOrder) {
1558
+ filteredItems.set(label, item);
1559
+ }
1560
+ });
1561
+ const completionItems = Array.from(filteredItems.entries()).map(([, value]) => value);
1562
+ return {
1563
+ suggestions: completionItems,
1564
+ };
1565
+ }
1566
+ }
1567
+ // TODO: create DiagnosticsAdapter common class
1568
+ class SqlExprDiagnosticsAdapter {
1569
+ constructor(_languageId, _worker, { defaults, diagnosticService, }) {
1570
+ this._languageId = _languageId;
1571
+ this._worker = _worker;
1572
+ this._disposables = [];
1573
+ this._listener = new Map();
1574
+ this._diagnosticsService = diagnosticService;
1575
+ this._defaults = defaults;
1576
+ const onModelAdd = (model) => {
1577
+ const languageId = model.getLanguageId();
1578
+ if (languageId !== this._languageId) {
1579
+ return;
1580
+ }
1581
+ const debouncedValidate = debounce(() => {
1582
+ this._doValidate(model, languageId).catch((err) => {
1583
+ throw err;
1584
+ });
1585
+ });
1586
+ this._listener.set(model.uri.toString(), model.onDidChangeContent(debouncedValidate));
1587
+ this._doValidate(model, languageId).catch(console.error);
1588
+ };
1589
+ const onModelRemoved = (model) => {
1590
+ const uri = model.uri.toString();
1591
+ editor.setModelMarkers(model, this._languageId, []);
1592
+ const listener = this._listener.get(uri);
1593
+ if (listener) {
1594
+ listener.dispose();
1595
+ this._listener.delete(uri);
1596
+ }
1597
+ };
1598
+ this._disposables.push(editor.onDidCreateModel(onModelAdd));
1599
+ this._disposables.push(editor.onWillDisposeModel((model) => onModelRemoved(model)));
1600
+ this._disposables.push(editor.onDidChangeModelLanguage((event) => {
1601
+ onModelRemoved(event.model);
1602
+ onModelAdd(event.model);
1603
+ }));
1604
+ this._disposables.push(defaults.onDidChange(() => {
1605
+ editor.getModels().forEach((model) => {
1606
+ if (model.getLanguageId() === this._languageId) {
1607
+ onModelRemoved(model);
1608
+ onModelAdd(model);
1609
+ }
1610
+ });
1611
+ }));
1612
+ this._disposables.push(defaults.onModelContextDidChange((key) => {
1613
+ editor.getModels().forEach((model) => {
1614
+ if (model.getLanguageId() === this._languageId && model.uri.toString() === key) {
1615
+ this._doValidate(model, this._languageId).catch(console.error);
1616
+ }
1617
+ });
1618
+ }));
1619
+ this._disposables.push({
1620
+ dispose: () => {
1621
+ this._listener.forEach((value) => value.dispose());
1622
+ this._listener.clear();
1623
+ },
1624
+ });
1625
+ editor.getModels().forEach(onModelAdd);
1626
+ }
1627
+ async _doValidate(model, languageId) {
1628
+ if (!model.isAttachedToEditor()) {
1629
+ return;
1630
+ }
1631
+ try {
1632
+ const workerProxy = await this._worker(model.uri);
1633
+ const context = this._defaults.getApiContextForModel(model.uri);
1634
+ const diagnostics = await workerProxy.doValidation(model.uri.toString(), context);
1635
+ this._diagnosticsService.fireDiagnosticsChange(model.uri, diagnostics);
1636
+ editor.setModelMarkers(model, languageId, diagnostics);
1637
+ }
1638
+ catch (err) {
1639
+ console.error(err);
1640
+ }
1641
+ }
1642
+ }
1643
+ const sqlParenLessFunctions = ["INTERVAL", "CASE"];
1644
+ const dataTypes = sqlFunctionParamKeywords.CAST;
1645
+ /*
1646
+ Helper function that takes in a context string and returns a string list of
1647
+ all the keywords that are used by that identifier. This is used to generate
1648
+ our completion items list for specific contexts.
1649
+ */
1650
+ const getKeywords = (identifier) => {
1651
+ switch (identifier.toLowerCase()) {
1652
+ case "extract":
1653
+ /* Need to revisit TIMEZONE_HOUR and TIMEZONE_MINUTE with the team */
1654
+ return sqlFunctionParamKeywords.EXTRACT;
1655
+ case "position":
1656
+ return ["IN"];
1657
+ case "trim":
1658
+ return sqlFunctionParamKeywords.TRIM;
1659
+ case "cast":
1660
+ return ["AS", ...sqlFunctionParamKeywords.CAST];
1661
+ case "interval":
1662
+ return [...sqlFunctionParamKeywords.EXTRACT];
1663
+ case "case":
1664
+ return ["WHEN", "THEN", "ELSE", "END"];
1665
+ default:
1666
+ return [];
1667
+ }
1668
+ };
1669
+ /*
1670
+ Helper function that takes in an index and node and determines the next
1671
+ delimiter position after the current index argument. This function is invoked
1672
+ only when we have a list of arguments via a function call or expression list.
1673
+ */
1674
+ const getDelimiterPosition = (index, node, model, lastIndex) => {
1675
+ let delimiterPosition;
1676
+ const start = node.range.start;
1677
+ const end = node.range.end;
1678
+ /*
1679
+ We can ensure that the match is never null since a fully generated AST
1680
+ follows from a syntactically correct SQL expression.
1681
+ */
1682
+ if (index === 0) {
1683
+ delimiterPosition = model.findNextMatch("(", {
1684
+ lineNumber: start.line,
1685
+ column: start.column,
1686
+ }, false, true, null, true).range;
1687
+ }
1688
+ else if (index === lastIndex) {
1689
+ delimiterPosition = {
1690
+ startLineNumber: end.line,
1691
+ endLineNumber: end.line,
1692
+ startColumn: end.column,
1693
+ endColumn: end.column,
1694
+ };
1695
+ }
1696
+ else {
1697
+ delimiterPosition = model.findNextMatch(",", {
1698
+ lineNumber: start.line,
1699
+ column: start.column,
1700
+ }, false, true, null, true).range;
1701
+ }
1702
+ return delimiterPosition;
1703
+ };
1704
+ /* Helper function to determine if our word is a SQL parenthesis-less function. */
1705
+ const isSqlParenLessFunction = (word) => {
1706
+ const keyword = word.toUpperCase();
1707
+ return sqlParenLessFunctions.includes(keyword);
1708
+ };
1709
+ /*
1710
+ Helper function to determine if our cursor is within the range of a function.
1711
+ This can be any of the patterns:
1712
+ - function(<here>)
1713
+ - function(<here>
1714
+ <here>
1715
+ <here>)
1716
+ */
1717
+ const getCursorWithinRange = (position, startPos, endPos) => {
1718
+ let inlineWithFunction = false;
1719
+ let wrappedWithinFunction = false;
1720
+ const cursorColumn = position.column;
1721
+ const cursorRow = position.lineNumber;
1722
+ /*
1723
+ Handles the case if our function is inline. An expression following the pattern
1724
+
1725
+ FUNCTION(arg) where our cursor position is in between the '(' and ')' of FUNCTION.
1726
+ i.e. FUNCTION( | arg | ) where '|' represents the possible cursor position.
1727
+ */
1728
+ if (startPos.startLineNumber === endPos.startLineNumber &&
1729
+ startPos.startLineNumber === cursorRow &&
1730
+ cursorColumn > startPos.startColumn &&
1731
+ cursorColumn < endPos.endColumn) {
1732
+ inlineWithFunction = true;
1733
+ }
1734
+ /*
1735
+ Handles the case where our function signature is multiline. That is, if we are
1736
+ anywhere between the '(' and the ')' of some expression that follows the pattern:
1737
+
1738
+ FUNCTION( |
1739
+ | args |
1740
+ | )
1741
+
1742
+ where '|' represents the possible cursor position.
1743
+ */
1744
+ const functionWrap = (cursorRow === startPos.startLineNumber && cursorColumn > startPos.startColumn) ||
1745
+ (cursorRow === endPos.startLineNumber && cursorColumn < endPos.endColumn) ||
1746
+ (cursorRow > startPos.startLineNumber && cursorRow < endPos.endLineNumber);
1747
+ if (startPos.startLineNumber !== endPos.startLineNumber) {
1748
+ if (functionWrap) {
1749
+ wrappedWithinFunction = true;
1750
+ }
1751
+ }
1752
+ return [inlineWithFunction, wrappedWithinFunction];
1753
+ };
1754
+
1755
+ let sqlExprWorker;
1756
+ /**
1757
+ * Returns the sql-expression worker for the model uris.
1758
+ * @param uris The model uris for which to get the worker.
1759
+ * @returns The sql-expression worker.
1760
+ */
1761
+ async function getSqlExprWorker(...uris) {
1762
+ return await new Promise((resolve, reject) => {
1763
+ if (!sqlExprWorker) {
1764
+ reject(new Error("sql expression worker not registered!"));
1765
+ return;
1766
+ }
1767
+ resolve(sqlExprWorker(...uris));
1768
+ });
1769
+ }
1770
+ class SqlExprDiagnosticService {
1771
+ constructor() {
1772
+ this._onDiagnosticsChange = new Emitter();
1773
+ }
1774
+ /**
1775
+ * An event to signal changes to the diagnostics.
1776
+ * The event value is the uri string and the diagnostics.
1777
+ */
1778
+ get onDiagnosticsChange() {
1779
+ return this._onDiagnosticsChange.event;
1780
+ }
1781
+ /**
1782
+ * Fires the diagnostics change event.
1783
+ * @param uri The uri of the model for which the diagnostics changed.
1784
+ * @param diagnostics The diagnostics for the model.
1785
+ */
1786
+ fireDiagnosticsChange(uri, diagnostics) {
1787
+ this._onDiagnosticsChange.fire({ uri, diagnostics });
1788
+ }
1789
+ }
1790
+ const sqlExprDiagnosticService = new SqlExprDiagnosticService();
1791
+ /**
1792
+ *
1793
+ * @returns The Sql Expression Diagnostic Service.
1794
+ */
1795
+ function getSqlExprDiagnosticService() {
1796
+ return sqlExprDiagnosticService;
1797
+ }
1798
+ function setupMode(defaults) {
1799
+ const client = new SqlExprWorkerManager(defaults);
1800
+ const workerAccessor = async (...uris) => await client.getLanguageServiceWorker(...uris);
1801
+ sqlExprWorker = workerAccessor;
1802
+ // Use the sql-expression Monarch Json to define the highlighting
1803
+ languages.setMonarchTokensProvider(sqlExprDefaults.languageId, sqlExprLanguageSyntax);
1804
+ languages.setLanguageConfiguration(sqlExprDefaults.languageId, sqlExprLanguageConfig);
1805
+ const completionProvider = new SqlExprCompletionProvider(sqlExprDefaults);
1806
+ // Creates and registers a Completion Adapter
1807
+ // Registers a completion item provider for our custom language
1808
+ languages.registerCompletionItemProvider(sqlExprDefaults.languageId, completionProvider);
1809
+ // TODO: Creates and registers a Formatter
1810
+ // languages.registerDocumentFormattingEditProvider(
1811
+ // SqlExprDefaults.languageId,
1812
+ // new FormattingProvider(workerAccessor, SqlExprDefaults),
1813
+ // );
1814
+ // eslint-disable-next-line no-new
1815
+ new SqlExprDiagnosticsAdapter(defaults.languageId, workerAccessor, {
1816
+ defaults,
1817
+ diagnosticService: sqlExprDiagnosticService,
1818
+ });
1819
+ }
1820
+
1821
+ export { getSqlExprDiagnosticService, getSqlExprWorker, setupMode };