@arcgis/coding-components 4.31.0-next.33 → 4.31.0-next.34

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 (121) 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 +38 -39
  3. package/dist/arcgis-coding-components/index.esm.js +2 -2
  4. package/dist/arcgis-coding-components/{p-75433d50.js → p-102debc5.js} +2 -2
  5. package/dist/arcgis-coding-components/{p-993bc0d7.js → p-1bce90df.js} +1 -1
  6. package/dist/arcgis-coding-components/{p-b320932c.js → p-226d94e2.js} +2 -2
  7. package/dist/arcgis-coding-components/{p-1e8ecfad.js → p-2680fa02.js} +2 -2
  8. package/dist/arcgis-coding-components/{p-e9a06305.js → p-27169bfc.js} +2 -2
  9. package/dist/arcgis-coding-components/{p-41da41b7.js → p-2a576903.js} +2 -2
  10. package/dist/arcgis-coding-components/{p-ffffcb8d.entry.js → p-3c3209ac.entry.js} +2 -2
  11. package/dist/arcgis-coding-components/p-6de7e7ae.js +6 -0
  12. package/dist/arcgis-coding-components/{p-31373916.js → p-77245004.js} +2 -2
  13. package/dist/arcgis-coding-components/p-82fb561d.js +6 -0
  14. package/dist/arcgis-coding-components/{p-08b429c3.js → p-8423b230.js} +2 -2
  15. package/dist/arcgis-coding-components/{p-5712f14a.js → p-85a8c1ea.js} +2 -2
  16. package/dist/arcgis-coding-components/{p-8a69d580.js → p-8b57879b.js} +2 -2
  17. package/dist/arcgis-coding-components/{p-9f2c7bf5.js → p-94d2e235.js} +1 -1
  18. package/dist/arcgis-coding-components/{p-81abf375.js → p-abdf566d.js} +9 -9
  19. package/dist/arcgis-coding-components/{p-355201f9.js → p-ad4575ad.js} +1 -1
  20. package/dist/arcgis-coding-components/{p-0f7c3b46.entry.js → p-b6c88155.entry.js} +2 -2
  21. package/dist/arcgis-coding-components/{p-26bd361b.entry.js → p-c11b620f.entry.js} +2 -2
  22. package/dist/cjs/{app-globals-4fe8cc70.js → app-globals-7c96ec13.js} +1 -1
  23. package/dist/cjs/{arcade-defaults-a591369b.js → arcade-defaults-41afd487.js} +4 -4
  24. package/dist/cjs/{arcade-mode-8e6464d3.js → arcade-mode-c7fb5c66.js} +266 -6
  25. package/dist/cjs/arcgis-arcade-editor_6.cjs.entry.js +5 -5
  26. package/dist/cjs/arcgis-coding-components.cjs.js +3 -3
  27. package/dist/cjs/arcgis-sql-expression-editor.cjs.entry.js +4 -4
  28. package/dist/cjs/arcgis-sql-expression-fields.cjs.entry.js +4 -4
  29. package/dist/cjs/{css-67e59b62.js → css-894931ae.js} +1 -1
  30. package/dist/cjs/{cssMode-ac51091d.js → cssMode-6a5028d4.js} +2 -2
  31. package/dist/cjs/{html-3127941b.js → html-dda4542d.js} +2 -2
  32. package/dist/cjs/{htmlMode-c686ddee.js → htmlMode-983bb1b6.js} +2 -2
  33. package/dist/cjs/{index-8e823a40.js → index-7dad49d8.js} +1 -1
  34. package/dist/cjs/index.cjs.js +4 -4
  35. package/dist/cjs/{javascript-37aa76f5.js → javascript-6ba81d8b.js} +3 -3
  36. package/dist/cjs/{jsonMode-9d875fc8.js → jsonMode-caca2bdb.js} +2 -2
  37. package/dist/cjs/{language-defaults-base-82944173.js → language-defaults-base-a5648a80.js} +9 -29
  38. package/dist/cjs/loader.cjs.js +3 -3
  39. package/dist/cjs/{sql-expr-defaults-d74ef4f2.js → sql-expr-defaults-8860e52d.js} +4 -17
  40. package/dist/cjs/sql-expr-mode-09647860.js +467 -0
  41. package/dist/cjs/{tsMode-916f8dc0.js → tsMode-ec3475be.js} +2 -2
  42. package/dist/cjs/{typescript-05142a58.js → typescript-b7cc5417.js} +2 -2
  43. package/dist/components/arcade-defaults.js +1 -1
  44. package/dist/components/arcade-mode.js +265 -4
  45. package/dist/components/arcade-results.js +1 -1
  46. package/dist/components/arcade-suggestions.js +1 -1
  47. package/dist/components/arcade-variables.js +1 -1
  48. package/dist/components/arcgis-arcade-editor.js +1 -1
  49. package/dist/components/arcgis-arcade-results.js +1 -1
  50. package/dist/components/arcgis-arcade-suggestions.js +1 -1
  51. package/dist/components/arcgis-arcade-variables.js +1 -1
  52. package/dist/components/arcgis-assets.d.ts +1 -1
  53. package/dist/components/arcgis-assets.js +1 -1
  54. package/dist/components/arcgis-code-editor.js +1 -1
  55. package/dist/components/arcgis-language-api-panel.js +1 -1
  56. package/dist/components/arcgis-sql-expression-editor.js +1 -1
  57. package/dist/components/arcgis-sql-expression-fields.js +1 -1
  58. package/dist/components/chunk-EOMOY2EF.js +1 -1
  59. package/dist/components/code-editor.js +1 -1
  60. package/dist/components/fields.js +2 -20
  61. package/dist/components/index.js +1 -1
  62. package/dist/components/index2.js +1 -1
  63. package/dist/components/language-api-panel.js +1 -1
  64. package/dist/components/language-defaults-base.js +1 -1
  65. package/dist/components/markdown.js +1 -1
  66. package/dist/components/sql-expr-defaults.js +2 -14
  67. package/dist/components/sql-expr-mode.js +161 -868
  68. package/dist/components/sql-expression-fields.js +1 -1
  69. package/dist/components/useT9n.js +1 -1
  70. package/dist/components/utilities.js +1 -1
  71. package/dist/esm/{app-globals-5a151155.js → app-globals-424a420a.js} +1 -1
  72. package/dist/esm/{arcade-defaults-5c8f894d.js → arcade-defaults-412fa0f2.js} +4 -4
  73. package/dist/esm/{arcade-mode-c4a6c6e6.js → arcade-mode-f9a5bed5.js} +263 -3
  74. package/dist/esm/arcgis-arcade-editor_6.entry.js +5 -5
  75. package/dist/esm/arcgis-coding-components.js +4 -4
  76. package/dist/esm/arcgis-sql-expression-editor.entry.js +4 -4
  77. package/dist/esm/arcgis-sql-expression-fields.entry.js +4 -4
  78. package/dist/esm/{css-6709b3e0.js → css-71097c66.js} +1 -1
  79. package/dist/esm/{cssMode-76e8796d.js → cssMode-72309bd9.js} +2 -2
  80. package/dist/esm/{html-53d10c85.js → html-8592a408.js} +2 -2
  81. package/dist/esm/{htmlMode-e9f070f1.js → htmlMode-0943cb15.js} +2 -2
  82. package/dist/esm/{index-b16f119e.js → index-f65aa2e7.js} +1 -1
  83. package/dist/esm/index.js +4 -4
  84. package/dist/esm/{javascript-785bbfd8.js → javascript-a33a7f7a.js} +3 -3
  85. package/dist/esm/{jsonMode-cd6fe200.js → jsonMode-1435c17d.js} +2 -2
  86. package/dist/esm/{language-defaults-base-e68e4d35.js → language-defaults-base-42358eac.js} +10 -28
  87. package/dist/esm/loader.js +4 -4
  88. package/dist/esm/{sql-expr-defaults-bdfdf7b4.js → sql-expr-defaults-b496e04d.js} +5 -17
  89. package/dist/esm/sql-expr-mode-626418cf.js +463 -0
  90. package/dist/esm/{tsMode-31b5e806.js → tsMode-50c3b01e.js} +2 -2
  91. package/dist/esm/{typescript-b2aa1d45.js → typescript-20e50254.js} +2 -2
  92. package/dist/loader/cdn.js +1 -1
  93. package/dist/loader/index.cjs.js +1 -1
  94. package/dist/loader/index.es2017.js +1 -1
  95. package/dist/loader/index.js +1 -1
  96. package/dist/types/data/arcgis-web-compoments/actions-runner-1/_work/arcgis-web-components/arcgis-web-components/packages/coding-packages/coding-components/.stencil/stencil.config.d.ts +3 -0
  97. package/dist/types/utils/sql-expr-monaco/sql-expr-completion.d.ts +56 -6
  98. package/dist/types/utils/sql-expr-monaco/sql-expr-constants.d.ts +22 -6
  99. package/dist/types/utils/sql-expr-monaco/sql-expr-language-features.d.ts +3 -18
  100. package/dist/types/utils/sql-expr-monaco/sql-expr-parser-utils.d.ts +30 -0
  101. package/dist/types/utils/sql-expr-monaco/sql-expr-validation-utils.d.ts +0 -2
  102. package/dist/types/utils/sql-expr-monaco/sql-expr.worker.d.ts +4 -4
  103. package/dist/types/utils/sql-expr-monaco/types.d.ts +25 -17
  104. package/package.json +11 -11
  105. package/dist/arcgis-coding-components/p-11d77b20.js +0 -6
  106. package/dist/arcgis-coding-components/p-86eb1d4c.js +0 -6
  107. package/dist/arcgis-coding-components/p-f544962a.js +0 -6
  108. package/dist/cjs/arcade-language-features-58ffb6a0.js +0 -274
  109. package/dist/cjs/sql-expr-mode-98437b6d.js +0 -1171
  110. package/dist/components/arcade-language-features.js +0 -271
  111. package/dist/esm/arcade-language-features-664e727d.js +0 -269
  112. package/dist/esm/sql-expr-mode-e58e1f11.js +0 -1167
  113. package/dist/types/data/actions-runner-1/_work/arcgis-web-components/arcgis-web-components/packages/coding-packages/coding-components/.stencil/stencil.config.d.ts +0 -3
  114. /package/dist/types/data/{actions-runner-1 → arcgis-web-compoments/actions-runner-1}/_work/arcgis-web-components/arcgis-web-components/packages/coding-packages/coding-components/.stencil/stories/internal/arcade-editor/arcade-editor.stories.d.ts +0 -0
  115. /package/dist/types/data/{actions-runner-1 → arcgis-web-compoments/actions-runner-1}/_work/arcgis-web-components/arcgis-web-components/packages/coding-packages/coding-components/.stencil/stories/internal/arcade-editor/editorContext.d.ts +0 -0
  116. /package/dist/types/data/{actions-runner-1 → arcgis-web-compoments/actions-runner-1}/_work/arcgis-web-components/arcgis-web-components/packages/coding-packages/coding-components/.stencil/stories/internal/code-editor/code-editor.stories.d.ts +0 -0
  117. /package/dist/types/data/{actions-runner-1 → arcgis-web-compoments/actions-runner-1}/_work/arcgis-web-components/arcgis-web-components/packages/coding-packages/coding-components/.stencil/stories/internal/sql-expression-editor/sql-expression-editor.stories.d.ts +0 -0
  118. /package/dist/types/data/{actions-runner-1 → arcgis-web-compoments/actions-runner-1}/_work/arcgis-web-components/arcgis-web-components/packages/coding-packages/coding-components/.stencil/stories/reference/stories/arcade-editor.stories.d.ts +0 -0
  119. /package/dist/types/data/{actions-runner-1 → arcgis-web-compoments/actions-runner-1}/_work/arcgis-web-components/arcgis-web-components/packages/coding-packages/coding-components/.stencil/support/sass-inline-url-importer.d.ts +0 -0
  120. /package/dist/types/data/{actions-runner-1 → arcgis-web-compoments/actions-runner-1}/_work/arcgis-web-components/arcgis-web-components/packages/coding-packages/coding-components/.stencil/support/sass-json-importer.d.ts +0 -0
  121. /package/dist/types/data/{actions-runner-1 → arcgis-web-compoments/actions-runner-1}/_work/arcgis-web-components/arcgis-web-components/packages/coding-packages/coding-components/.stencil/support/stencil-monaco-plugins.d.ts +0 -0
@@ -1,14 +1,10 @@
1
1
  /*!
2
2
  * All material copyright Esri, All Rights Reserved, unless otherwise specified.
3
3
  * See https://js.arcgis.com/4.31/esri/copyright.txt for details.
4
- * v4.31.0-next.33
4
+ * v4.31.0-next.34
5
5
  */
6
- import * as monaco from 'monaco-editor';
7
6
  import { editor, languages, Emitter } from 'monaco-editor';
8
- import { a as getFieldsFromLayerVariable, s as sqlExprDefaults } from './sql-expr-defaults.js';
9
- import { t as toCompletionItemKind } from './arcade-language-features.js';
10
- import { InsertTextFormat } from 'vscode-languageserver-types';
11
- import { h as importCoreSql, j as newLayersSupportFieldsIndex } from './fields.js';
7
+ import { s as sqlExprDefaults } from './sql-expr-defaults.js';
12
8
  import { t as debounce } from './index2.js';
13
9
 
14
10
  /**
@@ -57,6 +53,142 @@ class SqlExprWorkerManager {
57
53
  }
58
54
  }
59
55
 
56
+ // list of properties that sit on a SQLNode and can have children
57
+ var ChildBearingProperties;
58
+ (function (ChildBearingProperties) {
59
+ ChildBearingProperties["ARGS"] = "args";
60
+ ChildBearingProperties["CLAUSES"] = "clauses";
61
+ ChildBearingProperties["ELSE"] = "else";
62
+ ChildBearingProperties["END"] = "end";
63
+ ChildBearingProperties["EXPR"] = "expr";
64
+ ChildBearingProperties["LEFT"] = "left";
65
+ ChildBearingProperties["OPERAND"] = "operand";
66
+ ChildBearingProperties["QUALIFIER"] = "qualifier";
67
+ ChildBearingProperties["RIGHT"] = "right";
68
+ ChildBearingProperties["START"] = "start";
69
+ ChildBearingProperties["VALUE"] = "value";
70
+ })(ChildBearingProperties || (ChildBearingProperties = {}));
71
+ // list of node types that can have children
72
+ var ChildBearingNodeTypes;
73
+ (function (ChildBearingNodeTypes) {
74
+ ChildBearingNodeTypes["CASE_EXPRESSION"] = "case-expression";
75
+ ChildBearingNodeTypes["EXPRESSION_LIST"] = "expression-list";
76
+ ChildBearingNodeTypes["FUNCTION"] = "function";
77
+ ChildBearingNodeTypes["INTERVAL"] = "interval";
78
+ ChildBearingNodeTypes["INTERVAL_QUALIFIER"] = "interval-qualifier";
79
+ ChildBearingNodeTypes["WHEN_CLAUSE"] = "when-clause";
80
+ ChildBearingNodeTypes["UNARY_EXPRESSION"] = "unary-expression";
81
+ ChildBearingNodeTypes["BINARY_EXPRESSION"] = "binary-expression";
82
+ })(ChildBearingNodeTypes || (ChildBearingNodeTypes = {}));
83
+ // use object to ensure the list matches the types from the JS api
84
+ const castDataTypesDict = {
85
+ date: "DATE",
86
+ float: "FLOAT",
87
+ integer: "INTEGER",
88
+ real: "REAL",
89
+ smallint: "SMALLINT",
90
+ time: "TIME",
91
+ timestamp: "TIMESTAMP",
92
+ varchar: "VARCHAR",
93
+ };
94
+ // convert the object to a list of strings for easier use, but keep type information
95
+ Object.values(castDataTypesDict);
96
+ const sqlExprKeywords = [
97
+ "AND",
98
+ "AS",
99
+ "BETWEEN",
100
+ "BOTH",
101
+ "CASE",
102
+ "CAST",
103
+ "CURRENT_DATE",
104
+ "CURRENT_TIME",
105
+ "CURRENT_TIMESTAMP",
106
+ "DATE",
107
+ "DAY",
108
+ "ELSE",
109
+ "END",
110
+ "ESCAPE",
111
+ "FALSE",
112
+ "FLOAT",
113
+ "FOR",
114
+ "FROM",
115
+ "HOUR",
116
+ "IN",
117
+ "INTEGER",
118
+ "INTERVAL",
119
+ "IS",
120
+ "LEADING",
121
+ "LIKE",
122
+ "MINUTE",
123
+ "MONTH",
124
+ "NOT",
125
+ "NULL",
126
+ "OR",
127
+ "POSITION",
128
+ "REAL",
129
+ "SECOND",
130
+ "SMALLINT",
131
+ "SUBSTRING",
132
+ "THEN",
133
+ "TIME",
134
+ "TIMESTAMP",
135
+ "TIMEZONE_HOUR",
136
+ "TIMEZONE_MINUTE",
137
+ "TO",
138
+ "TRAILING",
139
+ "TRIM",
140
+ "TRUE",
141
+ "VARCHAR",
142
+ "WHEN",
143
+ "WITH",
144
+ "YEAR",
145
+ "ZONE",
146
+ ];
147
+ const sqlExprOperators = [
148
+ // Logical
149
+ "AND",
150
+ "BETWEEN",
151
+ "IN",
152
+ "LIKE",
153
+ "NOT",
154
+ "OR",
155
+ // Predicates
156
+ "IS",
157
+ "NULL",
158
+ ];
159
+ const sqlExprFunctions = [
160
+ // Conversion
161
+ "CAST",
162
+ // Datetime
163
+ "EXTRACT",
164
+ "CURRENT_DATE",
165
+ "CURRENT_TIME",
166
+ "CURRENT_TIMESTAMP",
167
+ // Mathematical
168
+ "ABS",
169
+ "ACOS",
170
+ "ASIN",
171
+ "ATAN",
172
+ "CEILING",
173
+ "COS",
174
+ "FLOOR",
175
+ "LOG",
176
+ "LOG10",
177
+ "POWER",
178
+ "ROUND",
179
+ "SIGN",
180
+ "SIN",
181
+ "TAN",
182
+ // String
183
+ "CHAR_LENGTH",
184
+ "COALESCE",
185
+ "CONCAT",
186
+ "LOWER",
187
+ "POSITION",
188
+ "TRIM",
189
+ "UPPER",
190
+ ];
191
+
60
192
  const sqlExprLanguageConfig = {
61
193
  comments: {
62
194
  lineComment: "--",
@@ -91,104 +223,9 @@ const sqlExprLanguageSyntax = {
91
223
  { open: "[", close: "]", token: "delimiter.square" },
92
224
  { open: "(", close: ")", token: "delimiter.parenthesis" },
93
225
  ],
94
- keywords: [
95
- // This list is narrowed to the scope of esri's sql flavor
96
- "AND",
97
- "AS",
98
- "BETWEEN",
99
- "BOTH",
100
- "CASE",
101
- "CAST",
102
- "CURRENT_DATE",
103
- "CURRENT_TIME",
104
- "CURRENT_TIMESTAMP",
105
- "DATE",
106
- "DAY",
107
- "ELSE",
108
- "END",
109
- "ESCAPE",
110
- "EXTRACT",
111
- "FALSE",
112
- "FLOAT",
113
- "FOR",
114
- "FROM",
115
- "HOUR",
116
- "IN",
117
- "INTEGER",
118
- "INTERVAL",
119
- "IS",
120
- "LEADING",
121
- "LIKE",
122
- "MINUTE",
123
- "MONTH",
124
- "NOT",
125
- "NULL",
126
- "OR",
127
- "POSITION",
128
- "REAL",
129
- "SECOND",
130
- "SMALLINT",
131
- "SUBSTRING",
132
- "THEN",
133
- "TIME",
134
- "TIMESTAMP",
135
- "TIMEZONE_HOUR",
136
- "TIMEZONE_MINUTE",
137
- "TO",
138
- "TRAILING",
139
- "TRIM",
140
- "TRUE",
141
- "VARCHAR",
142
- "WHEN",
143
- "WITH",
144
- "YEAR",
145
- "ZONE",
146
- ],
147
- operators: [
148
- // Logical
149
- "AND",
150
- "BETWEEN",
151
- "IN",
152
- "LIKE",
153
- "NOT",
154
- "OR",
155
- // Predicates
156
- "IS",
157
- "NULL",
158
- ],
159
- builtinFunctions: [
160
- // Conversion
161
- "CAST",
162
- // Datetime
163
- "DATE",
164
- "CURRENT_DATE",
165
- "CURRENT_TIME",
166
- "CURRENT_TIMESTAMP",
167
- "DAY",
168
- "MONTH",
169
- "YEAR",
170
- // Mathematical
171
- "ABS",
172
- "ACOS",
173
- "ASIN",
174
- "ATAN",
175
- "CEILING",
176
- "COS",
177
- "FLOOR",
178
- "LOG",
179
- "LOG10",
180
- "POWER",
181
- "ROUND",
182
- "SIGN",
183
- "SIN",
184
- "TAN",
185
- // String
186
- "CHAR_LENGTH",
187
- "CONCAT",
188
- "LOWER",
189
- "TRIM",
190
- "UPPER",
191
- ],
226
+ keywords: sqlExprKeywords,
227
+ operators: sqlExprOperators,
228
+ builtinFunctions: sqlExprFunctions,
192
229
  builtinVariables: [],
193
230
  tokenizer: {
194
231
  root: [
@@ -256,661 +293,28 @@ const sqlExprLanguageSyntax = {
256
293
  },
257
294
  };
258
295
 
259
- async function sqlExpressionCompletion(range, model, position, sdkFunctions) {
260
- try {
261
- /*
262
- Handles the case where we have a valid SQL expression in the editor and is
263
- able to generate an AST from it and traverse it to provide completion items.
264
- */
265
- const editorValue = model.getValue();
266
- const sql = await importCoreSql();
267
- const fieldIndex = await newLayersSupportFieldsIndex([]);
268
- const { parseTree } = await sql.parseWhereClause(editorValue, fieldIndex);
269
- const completionItems = getCompletionItems(model, parseTree, range, position, sdkFunctions);
270
- return completionItems;
271
- }
272
- catch {
273
- /*
274
- Handles the case where we have an incomplete SQL expression (i.e. if users are
275
- still typing or has erroneous syntax). We fall back to a more lexical approach
276
- in this way to "fail gracefully" in order to still provide completion items.
277
- */
278
- const errorRange = {
279
- startColumn: 1,
280
- endColumn: position.column,
281
- startLineNumber: 1,
282
- endLineNumber: position.lineNumber,
283
- };
284
- /* Matching a '(', ')', ' ', or ',' */
285
- const delimiter = /(?:\(|\)|\s|,)/u;
286
- /*
287
- Filter callback function to filter out all non-function words, non-sql keywords, empty
288
- strings and single quotes ('). Preserve all '(', ')', ',' and 'END' tokens
289
- within our array.
290
- */
291
- const filterToken = (token) => {
292
- const tokenIsAFunction = sdkFunctions[token] !== undefined;
293
- const isParenLessFunction = isSqlParenLessFunction(token);
294
- const excludedTokens = token !== "" && token !== "'";
295
- const includedTokens = ["(", ")", ",", "END"].includes(token);
296
- /*
297
- Returns the tokens if they are either a standardized function, parenthesis-less function
298
- or is one of the tokens that we specified to keep. Filters all empty string and
299
- single quotations.
300
- */
301
- return excludedTokens && (tokenIsAFunction || isParenLessFunction || includedTokens);
302
- };
303
- const textValueUpToCursor = model.getValueInRange(errorRange).toUpperCase();
304
- // TODO: test this...
305
- const tokens = textValueUpToCursor
306
- .split(delimiter)
307
- .map((token) => token.trim())
308
- .filter((token) => filterToken(token));
309
- /* Keeps track of our current context item */
310
- const contextStack = [];
311
- for (let i = 0; i < tokens.length; i++) {
312
- const token = tokens[i];
313
- const nonEmptyContextStack = contextStack.length > 0;
314
- /*
315
- As long as we have not hit the last token in our list yet, we can do a
316
- lookahead and see if the next token makes a valid sub-expression.
317
- */
318
- if (i < tokens.length - 1) {
319
- const nextToken = tokens[i + 1];
320
- const invalidFunctionCall = sdkFunctions[token] && nextToken !== "(";
321
- if (invalidFunctionCall) {
322
- continue;
323
- }
324
- }
325
- if (token === "(" || token === ",") {
326
- if (nonEmptyContextStack) {
327
- const currentContext = contextStack[contextStack.length - 1];
328
- /*
329
- This marks the end of our parenthesis-less function's scope, hence
330
- we cleanse the context and let the next token update the context.
331
- */
332
- if (isSqlParenLessFunction(currentContext)) {
333
- contextStack.pop();
334
- }
335
- }
336
- continue;
337
- }
338
- else if (token === ")" || token === "END") {
339
- /*
340
- If we have a non empty context stack, that means we are within another
341
- function's scope right now (another context). We will return to the outer
342
- scope to provide us the necessary completion items.
343
- */
344
- if (nonEmptyContextStack) {
345
- contextStack.pop();
346
- }
347
- continue;
348
- }
349
- /*
350
- If we end off with a standardized function without a '(', we should not
351
- update the context. We should only trigger completion if they have a '('
352
- to indicate that it's a function call.
353
- */
354
- if (i === tokens.length - 1 && sdkFunctions[token]) {
355
- continue;
356
- }
357
- /*
358
- Very specific case where we have something like
359
-
360
- | TRIM(INTERVAL INTERVAL, ... |
361
-
362
- and we do not want the completion items for "INTERVAL" to persist even after
363
- the context ends.
364
-
365
- In essence, we only update the context once.
366
- */
367
- if (i > 0) {
368
- const prevTokenIsParenLess = isSqlParenLessFunction(tokens[i - 1]);
369
- const curTokenIsParenLess = isSqlParenLessFunction(token);
370
- if (!(curTokenIsParenLess && prevTokenIsParenLess)) {
371
- contextStack.push(token);
372
- }
373
- }
374
- else if (i === 0) {
375
- contextStack.push(token);
376
- }
377
- }
378
- let completionItems = [];
379
- [...new Set(contextStack)].forEach((context, index) => {
380
- const autoCompletionItem = generateCompletionList(context, range, index);
381
- completionItems = [...completionItems, ...autoCompletionItem];
382
- });
383
- return completionItems;
384
- }
385
- }
386
- /*
387
- Helper function that takes in our model/position information and traverse the
388
- completed AST to generate an array of Monaco's completion items.
389
-
390
- This function is invoked only if we have a valid SQL expression that can be parsed to
391
- generate an AST. In other words, this is only invoked for our "success" case.
392
- */
393
- const getCompletionItems = (model, node, range, position, sdkFunctions) => {
394
- /*
395
- Using an object here so that we can pass by reference to the function to
396
- handle updating the state instead of pass by value if we use a primitive
397
- type like string. This also allows us to extend the context and add more
398
- data and attributes if we need to in the future.
399
- */
400
- const context = { contextString: "NONE", additionalContext: "NONE" };
401
- traverseAST({ model, node, position, context, sdkFunctions });
402
- const { contextString, additionalContext } = context;
403
- const completionItems = generateCompletionList(contextString, range, 0);
404
- const additionalItems = generateCompletionList(additionalContext, range, 1);
405
- return [...completionItems, ...additionalItems];
406
- };
407
- /*
408
- This function traverses the AST and finds which node and context we are currently
409
- in to help provide the necessary completion items. This follows from having a complete
410
- and valid SQL expression. The main purpose of this function is to provide the correct
411
- completion items when users trigger IntelliSense via CTRL + Space.
412
-
413
- This is a recursive function.
414
- */
415
- // eslint-disable-next-line complexity
416
- const traverseAST = (
417
- /*
418
- Keeping the node type as generic 'any' here as __esri.SQLNode does not have updated types
419
- with the addition of the range property. Documentation is outdated and may or may not
420
- contain mismatching property names (ListNode uses "expr" as property for all expressions
421
- but representation is via "value") and some missing cases (e.g. case "parameter" is not
422
- identified).
423
- */
424
- { model, node, position, context, sdkFunctions, delimiterPos, }) => {
425
- /* Our current cursor position within the editor */
426
- // const cursorRow: number = position.lineNumber;
427
- // const cursorColumn: number = position.column;
428
- switch (node.type) {
429
- case "interval": {
430
- /*
431
- Here, we are using an interval node as a standalone and not within any
432
- function call or expression list.
433
- */
434
- if (!delimiterPos) {
435
- updateContext(context, "INTERVAL", false);
436
- return;
437
- }
438
- /*
439
- We can ensure that the match is never null since a fully generated AST
440
- follows from a syntactically correct SQL expression.
441
- */
442
- const nextDelimPos = model.findNextMatch(
443
- // @ts-ignore
444
- /\)|,/u, {
445
- lineNumber: node.location.end.line,
446
- column: node.location.end.column,
447
- }, true, true, null, true).range;
448
- const [inlineFunc, funcWrapped] = getCursorWithinRange(position, delimiterPos, nextDelimPos);
449
- if (inlineFunc ?? funcWrapped) {
450
- updateContext(context, "INTERVAL", false);
451
- }
452
- return;
453
- }
454
- case "case-expression": {
455
- /* Only update context if we are within the range */
456
- // Representing the range for the "CASE" keyword for starting the case expression
457
- const startPos = {
458
- startColumn: node.location.start.column,
459
- endColumn: node.location.start.column + "CASE".length,
460
- startLineNumber: node.location.start.line,
461
- endLineNumber: node.location.start.line,
462
- };
463
- // Representing the range for the "END" keyword for ending the case expression
464
- const endPos = {
465
- startColumn: node.location.end.column - "END".length,
466
- endColumn: node.location.end.column,
467
- startLineNumber: node.location.end.line,
468
- endLineNumber: node.location.end.line,
469
- };
470
- const [inline, wrapped] = getCursorWithinRange(position, startPos, endPos);
471
- if (inline ?? wrapped) {
472
- updateContext(context, "CASE", false);
473
- }
474
- return;
475
- }
476
- case "expression-list": {
477
- const exprList = node.value;
478
- let delimiter;
479
- exprList.forEach((expr, index) => {
480
- /* The position of our opening delimiter '(' */
481
- if (index === 0) {
482
- const match = model.findMatches("(", {
483
- startLineNumber: 1,
484
- endLineNumber: node.location.start.line,
485
- startColumn: 1,
486
- endColumn: node.location.start.column,
487
- }, false, true, null, true);
488
- delimiter = match[match.length - 1].range;
489
- }
490
- traverseAST({ model, node: expr, position, context, sdkFunctions, delimiterPos: delimiter });
491
- /*
492
- Finds and updates our next delimiter position (i.e. the delimiter that immediately
493
- follows this current node)
494
- */
495
- delimiter = getDelimiterPosition(index + 1, expr, model, exprList.length);
496
- });
497
- return;
498
- }
499
- case "unary-expression":
500
- traverseAST({ model, node: node.expr, position, context, sdkFunctions });
501
- return;
502
- case "binary-expression":
503
- traverseAST({ model, node: node.left, position, context, sdkFunctions });
504
- traverseAST({ model, node: node.right, position, context, sdkFunctions });
505
- return;
506
- case "null":
507
- case "boolean":
508
- case "string":
509
- case "timestamp":
510
- case "date":
511
- case "time":
512
- case "number":
513
- case "current-time":
514
- return;
515
- case "column-reference": {
516
- const columnName = node.column;
517
- const isParenLessFunction = isSqlParenLessFunction(columnName);
518
- /*
519
- In this case, we are using a standalone column reference, not a part of any
520
- expression list or function param. Update the context accordingly if it is
521
- any of the parenthesis-less functions.
522
- */
523
- if (!delimiterPos) {
524
- if (isParenLessFunction) {
525
- updateContext(context, columnName, false);
526
- }
527
- return;
528
- }
529
- /*
530
- The delimiter right before we start our expression. This delimiter is useful for us
531
- as it gives positional information for when the expression starts if it's used as a
532
- parameter in a function.
533
-
534
- For example, if we have something like Function(a, b), then the range of b would have
535
- a start column value of 13 (offset of 12) whereas "," would be 11 (offset of 10).
536
-
537
- This will be useful in the case where we want to provide completion items before our
538
- keyword appears (if any).
539
- */
540
- const delimiterCharacter = model.getValueInRange(delimiterPos);
541
- const nextDelimiterPos = model.findNextMatch(
542
- /*
543
- Function call only accepts string even though docs noted that RegExp is allowed.
544
- "@param searchString — The string used to search. If it is a regular expression,
545
- set isRegex to true"
546
- */
547
- // @ts-ignore
548
- /\)|,/u, {
549
- column: node.location.start.column,
550
- lineNumber: node.location.start.line,
551
- }, true, true, null, true).range;
552
- const [inlineWithFunction, wrappedWithinFunction] = getCursorWithinRange(position, delimiterPos, nextDelimiterPos);
553
- if (inlineWithFunction ?? wrappedWithinFunction) {
554
- /*
555
- We updated our additional context here if this is our first argument within a function call.
556
- This is so we don't override the previous context with our current context, which may
557
- strip away certain completion items. For example, if we have the SQL expression
558
- CAST(INTERVAL), we still want the completion items for CAST as well as INTERVAL.
559
- */
560
- if (isParenLessFunction) {
561
- updateContext(context, columnName, delimiterCharacter === "(");
562
- }
563
- }
564
- return;
565
- }
566
- case "data-type":
567
- return;
568
- case "function": {
569
- const functionName = node.name;
570
- const argsList = node.args.value;
571
- const startRange = {
572
- startLineNumber: node.location.start.line,
573
- endLineNumber: node.location.start.line,
574
- startColumn: node.location.start.column,
575
- endColumn: node.location.start.column + functionName.length,
576
- };
577
- const endRange = {
578
- startLineNumber: node.location.end.line,
579
- endLineNumber: node.location.end.line,
580
- startColumn: node.location.end.column,
581
- endColumn: node.location.end.column,
582
- };
583
- const [inlineWithFunc, wrappedWithinFunc] = getCursorWithinRange(position, startRange, endRange);
584
- if (inlineWithFunc ?? wrappedWithinFunc) {
585
- updateContext(context, functionName, false);
586
- }
587
- let delimiterPosition;
588
- argsList.forEach((arg, index) => {
589
- const nodeType = arg.type;
590
- let completionNodeType = false;
591
- switch (nodeType) {
592
- case "function":
593
- case "column-reference":
594
- case "interval":
595
- case "case-expression":
596
- completionNodeType = true;
597
- break;
598
- }
599
- /*
600
- We can ensure that the match is never undefined since a fully generated AST
601
- follows from a syntactically correct SQL expression.
602
- */
603
- if (index === 0) {
604
- delimiterPosition = model.findNextMatch("(", {
605
- column: node.location.start.column,
606
- lineNumber: node.location.start.line,
607
- }, false, true, null, true).range;
608
- }
609
- /* TRIM(abc) === TRIM(BOTH ' ' FROM abc). In this case, our next delimiter is the end */
610
- const trimFuncDefault = arg.type === "string" && arg.value === "BOTH";
611
- /*
612
- If the current node is not a completion node type, and is the default trim function,
613
- then we will just use the current node as a reference to get the next delimiter position.
614
- */
615
- if (!completionNodeType && trimFuncDefault) {
616
- return;
617
- }
618
- traverseAST({ model, node: arg, position, context, sdkFunctions, delimiterPos: delimiterPosition });
619
- delimiterPosition = getDelimiterPosition(index + 1, arg, model, argsList.length);
620
- });
621
- return;
622
- }
623
- /* Currently no support for parameter type (@) */
624
- case "parameter":
625
- case "when-clause":
626
- case "interval-period":
627
- case "interval-qualifier":
628
- return;
629
- default:
630
- throw new Error("Invalid syntax.");
631
- }
632
- };
633
- /*
634
- Helper function that generates our list of completion items after traversing
635
- our AST and updating the context.
636
-
637
- The sorting number is used as a way of grouping and organizing our completion lists.
638
-
639
- - All default completion items have a sort value of 'z' (ASCII 122) + their corresponding
640
- grouping value (e.g. 'za', 'zb', etc...)
641
- - All completion items found by cursor matching have a value between 'a' - 'y' (ASCII 97-121)
642
- - The higher the sorting number, the higher it will appear on our completion list
643
- */
644
- const generateCompletionList = (functionName, range, sortingNumber) => {
645
- const identifiers = getKeywords(functionName);
646
- const completionItems = identifiers?.map((identifier) => {
647
- const dataKind = dataTypes.includes(identifier.toUpperCase())
648
- ? monaco.languages.CompletionItemKind.TypeParameter
649
- : monaco.languages.CompletionItemKind.Keyword;
650
- // ASCII value of 'y'
651
- const asciiY = 121;
652
- return {
653
- label: identifier,
654
- kind: dataKind,
655
- insertText: identifier,
656
- range,
657
- sortText: String.fromCharCode(asciiY - sortingNumber),
658
- };
659
- });
660
- return completionItems ?? [];
661
- };
662
- /*
663
- Function to help us update our current context depending on where our cursor
664
- is within the editor. This will allow us to gather the relevant completion items
665
- depending on the value of the context (i.e. the function that our cursor is within).
666
-
667
- This is invoked only during our traversal of the AST (i.e. "success" case).
668
- */
669
- const updateContext = (oldContext, newContext, updateAdditionalContext) => {
670
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions
671
- updateAdditionalContext ? (oldContext.additionalContext = newContext) : (oldContext.contextString = newContext);
672
- };
673
- /** source */
674
- const sqlBinOps = [
675
- "AND",
676
- "OR",
677
- "IS",
678
- "ISNOT",
679
- "IN",
680
- "NOT IN",
681
- "BETWEEN",
682
- "NOTBETWEEN",
683
- "LIKE",
684
- "NOT LIKE",
685
- ];
686
- /*
687
- The current state of sorting completion items.
688
-
689
- All fields that are provided by default (keywords, standardized functions, constants, etc.)
690
- all have a sort value of "z". As we parse our SQL Expression and traverse an AST (if we can),
691
- we will have new completion items depending on our cursor context. These completion items
692
- should appear first at the top of our list, with the most recent context having the highest
693
- appearance on our list. In this manner, we add new completion items with a smaller
694
- lexicographic value than the previous to promote the correct ordering. (I.e. add in new completion
695
- items with a sort value of "y" next, then "x", then "w", all the way until "a". It is unlikely
696
- that we will need more than 26 difference context but if we do require more, we can make use of
697
- double-lettered sort values).
698
- */
699
- const sortTextValues = {
700
- identifiers: "a",
701
- fields: "aa",
702
- constants: "ab",
703
- literals: "ab",
704
- functions: "ac",
705
- snippets: "b",
706
- keywords: "c",
707
- dataType: "d",
708
- };
709
- function generateCompletionItems(fields, range, sdkFunctions) {
710
- const fieldsCompletion = fields.map((field) => ({
711
- label: field.completion?.label ?? field.name,
712
- insertText: field.completion?.insertText ?? field.name,
713
- sortText: `z${sortTextValues.fields}`,
714
- detail: field.completion?.detail,
715
- range,
716
- kind: monaco.languages.CompletionItemKind.Variable,
717
- documentation: field.completion?.documentation,
718
- }));
719
- // {
720
- // const funcName = func.toLowerCase();
721
- // const apiContext = apiFunctions[funcName as keyof typeof apiFunctions] as ApiContextType;
722
- // const { name, snippet, parameters, returnValue, description, examples } = apiContext;
723
- // const completionItemData = {
724
- // kind: monaco.languages.CompletionItemKind.Function,
725
- // range,
726
- // sortText: `z${sortTextValues.functions}`,
727
- // detail: generateTypeDescription(name, parameters, returnValue),
728
- // documentation: generateCompletionDocumentation(description, examples),
729
- // };
730
- // /*
731
- // We take 2 different forms of the same function over our REST API.
732
- // e.g. CURRENT_TIME and CURRENT_TIME() are both accepted.
733
- // */
734
- // if (Array.isArray(snippet) && snippet.length) {
735
- // return [
736
- // { ...completionItemData, label: name, insertText: snippet[0]! },
737
- // snippet[1] && { ...completionItemData, label: name.slice(0, -2), insertText: snippet[1] },
738
- // ];
739
- // }
740
- // return [{ ...completionItemData, label: name, insertText: snippet }];
741
- // }
742
- // );
743
- const sqlBinOpsCompletion = sqlBinOps.map((binOp) => {
744
- const formatOperator = (binOp) => {
745
- if (binOp === "ISNOT") {
746
- return "IS NOT";
747
- }
748
- if (binOp === "NOTBETWEEN") {
749
- return "NOT BETWEEN";
750
- }
751
- return binOp;
752
- };
753
- return {
754
- label: formatOperator(binOp),
755
- kind: monaco.languages.CompletionItemKind.Keyword,
756
- insertText: formatOperator(binOp),
757
- range,
758
- sortText: `z${sortTextValues.keywords}`,
759
- };
760
- });
761
- const sqlKeywordsCompletion = sqlParenLessFunctions.map((keyword) => {
762
- if (keyword === "CASE") {
763
- return {
764
- label: keyword,
765
- kind: monaco.languages.CompletionItemKind.Snippet,
766
- insertText: `CASE
767
- WHEN condition1 THEN result1
768
- ELSE result
769
- END`,
770
- range,
771
- sortText: `z${sortTextValues.snippets}`,
772
- };
773
- }
774
- return {
775
- label: keyword,
776
- kind: monaco.languages.CompletionItemKind.Keyword,
777
- insertText: keyword,
778
- range,
779
- sortText: `z${sortTextValues.keywords}`,
780
- };
781
- });
782
- const sqlDataTypeCompletion = dataTypes.map((dataType) => ({
783
- label: dataType,
784
- kind: monaco.languages.CompletionItemKind.TypeParameter,
785
- insertText: dataType,
786
- range,
787
- sortText: `z${sortTextValues.dataType}`,
788
- }));
789
- const sqlFunctionCompletion = Object.values(sdkFunctions).map((apiItem) => {
790
- const entry = apiItem.completion;
791
- const item = {
792
- label: entry.label,
793
- insertText: entry.insertText || entry.label,
794
- sortText: `z${sortTextValues.functions}`,
795
- filterText: entry.filterText,
796
- detail: entry.detail,
797
- range,
798
- kind: toCompletionItemKind(entry.kind),
799
- };
800
- if (entry.insertTextFormat === InsertTextFormat.Snippet) {
801
- item.insertTextRules = languages.CompletionItemInsertTextRule.InsertAsSnippet;
802
- }
803
- if (entry.documentation) {
804
- if (typeof entry.documentation === "string") {
805
- item.documentation = entry.documentation;
806
- }
807
- else {
808
- item.documentation = {
809
- supportThemeIcons: false,
810
- value: entry.documentation.value,
811
- supportHtml: true,
812
- };
813
- }
814
- }
815
- return item;
816
- });
817
- return {
818
- fieldsCompletion,
819
- sqlFunctionCompletion,
820
- sqlBinOpsCompletion,
821
- sqlKeywordsCompletion,
822
- sqlDataTypeCompletion,
823
- };
824
- }
825
-
826
- // list of properties that sit on a SQLNode and can have children
827
- var ChildBearingProperties;
828
- (function (ChildBearingProperties) {
829
- ChildBearingProperties["ARGS"] = "args";
830
- ChildBearingProperties["CLAUSES"] = "clauses";
831
- ChildBearingProperties["ELSE"] = "else";
832
- ChildBearingProperties["END"] = "end";
833
- ChildBearingProperties["EXPR"] = "expr";
834
- ChildBearingProperties["LEFT"] = "left";
835
- ChildBearingProperties["OPERAND"] = "operand";
836
- ChildBearingProperties["QUALIFIER"] = "qualifier";
837
- ChildBearingProperties["RIGHT"] = "right";
838
- ChildBearingProperties["START"] = "start";
839
- ChildBearingProperties["VALUE"] = "value";
840
- })(ChildBearingProperties || (ChildBearingProperties = {}));
841
- // list of node types that can have children
842
- var ChildBearingNodeTypes;
843
- (function (ChildBearingNodeTypes) {
844
- ChildBearingNodeTypes["CASE_EXPRESSION"] = "case-expression";
845
- ChildBearingNodeTypes["EXPRESSION_LIST"] = "expression-list";
846
- ChildBearingNodeTypes["FUNCTION"] = "function";
847
- ChildBearingNodeTypes["INTERVAL"] = "interval";
848
- ChildBearingNodeTypes["INTERVAL_QUALIFIER"] = "interval-qualifier";
849
- ChildBearingNodeTypes["WHEN_CLAUSE"] = "when-clause";
850
- ChildBearingNodeTypes["UNARY_EXPRESSION"] = "unary-expression";
851
- ChildBearingNodeTypes["BINARY_EXPRESSION"] = "binary-expression";
852
- })(ChildBearingNodeTypes || (ChildBearingNodeTypes = {}));
853
- const sqlFunctionParamKeywords = {
854
- CAST: ["DATE", "FLOAT", "INTEGER", "REAL", "SMALLINT", "TIME", "TIMESTAMP", "VARCHAR"],
855
- EXTRACT: ["YEAR", "MONTH", "DAY", "HOUR", "MINUTE", "SECOND"],
856
- TRIM: ["LEADING", "TRAILING", "BOTH"],
857
- };
858
-
859
- function transposeSdkFunctions(sdkLibrary) {
860
- return sdkLibrary.flatMap((category) => category.items).reduce((acc, item) => ({ ...acc, [item.name]: item }), {});
861
- }
862
296
  class SqlExprCompletionProvider {
863
- constructor(
864
- // private _worker: ISqlExprWorkerAccessor,
865
- _defaults) {
297
+ constructor(_worker, _defaults) {
298
+ this._worker = _worker;
866
299
  this._defaults = _defaults;
867
- this._sdkFunctions = transposeSdkFunctions([]);
868
300
  }
869
301
  async provideCompletionItems(model, position) {
870
- const word = model.getWordUntilPosition(position);
871
- const range = {
872
- startLineNumber: position.lineNumber,
873
- endLineNumber: position.lineNumber,
874
- startColumn: word.startColumn,
875
- endColumn: word.endColumn,
876
- };
877
- const apiContext = this._defaults.getApiContextForModel(model.uri);
878
- // get the fields from the api context
879
- const fields = apiContext.profile ? getFieldsFromLayerVariable(apiContext.profile) : [];
880
- const sdkLibrary = await this._defaults.getApiLibrary(model.uri.toString());
881
- this._sdkFunctions = transposeSdkFunctions(sdkLibrary);
882
- const sqlContextCompletion = await sqlExpressionCompletion(range, model, position, this._sdkFunctions);
883
- const { sqlKeywordsCompletion, sqlBinOpsCompletion, sqlFunctionCompletion, fieldsCompletion, sqlDataTypeCompletion, } = generateCompletionItems(fields, range, this._sdkFunctions);
884
- /*
885
- Filters our completion items list so that if we happen to have the same label but
886
- different sortText order, we keep the sortText with the lowest ASCII value (i.e.
887
- the smallest character in terms of lexicographic ordering).
888
- */
889
- const filteredItems = new Map();
890
- [
891
- ...sqlKeywordsCompletion,
892
- ...sqlBinOpsCompletion,
893
- ...sqlDataTypeCompletion,
894
- ...sqlFunctionCompletion,
895
- ...fieldsCompletion,
896
- ...sqlContextCompletion,
897
- ].forEach((item) => {
898
- const { sortText } = item;
899
- const label = item.label;
900
- const itemExists = filteredItems.get(label);
901
- if (!itemExists) {
902
- filteredItems.set(label, item);
903
- }
904
- const currentOrder = sortText.charCodeAt(0);
905
- const currentLowestOrder = filteredItems.get(label).sortText.charCodeAt(0);
906
- if (currentOrder < currentLowestOrder) {
907
- filteredItems.set(label, item);
908
- }
909
- });
910
- const completionItems = Array.from(filteredItems.entries()).map(([, value]) => value);
911
- return {
912
- suggestions: completionItems,
913
- };
302
+ try {
303
+ const worker = await this._worker(model.uri);
304
+ const word = model.getWordUntilPosition(position);
305
+ const range = {
306
+ startLineNumber: position.lineNumber,
307
+ endLineNumber: position.lineNumber,
308
+ startColumn: word.startColumn,
309
+ endColumn: word.endColumn,
310
+ };
311
+ const apiContext = this._defaults.getApiContextForModel(model.uri);
312
+ return await worker.doComplete(model.uri.toString(), range, position, apiContext);
313
+ }
314
+ catch (err) {
315
+ console.error(err);
316
+ return { suggestions: [] };
317
+ }
914
318
  }
915
319
  }
916
320
  // TODO: create DiagnosticsAdapter common class
@@ -989,117 +393,6 @@ class SqlExprDiagnosticsAdapter {
989
393
  }
990
394
  }
991
395
  }
992
- const sqlParenLessFunctions = ["INTERVAL", "CASE"];
993
- const dataTypes = sqlFunctionParamKeywords.CAST;
994
- /*
995
- Helper function that takes in a context string and returns a string list of
996
- all the keywords that are used by that identifier. This is used to generate
997
- our completion items list for specific contexts.
998
- */
999
- const getKeywords = (identifier) => {
1000
- switch (identifier.toLowerCase()) {
1001
- case "extract":
1002
- /* Need to revisit TIMEZONE_HOUR and TIMEZONE_MINUTE with the team */
1003
- return sqlFunctionParamKeywords.EXTRACT;
1004
- case "position":
1005
- return ["IN"];
1006
- case "trim":
1007
- return sqlFunctionParamKeywords.TRIM;
1008
- case "cast":
1009
- return ["AS", ...sqlFunctionParamKeywords.CAST];
1010
- case "interval":
1011
- return [...sqlFunctionParamKeywords.EXTRACT];
1012
- case "case":
1013
- return ["WHEN", "THEN", "ELSE", "END"];
1014
- default:
1015
- return [];
1016
- }
1017
- };
1018
- /*
1019
- Helper function that takes in an index and node and determines the next
1020
- delimiter position after the current index argument. This function is invoked
1021
- only when we have a list of arguments via a function call or expression list.
1022
- */
1023
- const getDelimiterPosition = (index, node, model, lastIndex) => {
1024
- let delimiterPosition;
1025
- const start = node.location.start;
1026
- const end = node.location.end;
1027
- /*
1028
- We can ensure that the match is never null since a fully generated AST
1029
- follows from a syntactically correct SQL expression.
1030
- */
1031
- if (index === 0) {
1032
- delimiterPosition = model.findNextMatch("(", {
1033
- lineNumber: start.line,
1034
- column: start.column,
1035
- }, false, true, null, true).range;
1036
- }
1037
- else if (index === lastIndex) {
1038
- delimiterPosition = {
1039
- startLineNumber: end.line,
1040
- endLineNumber: end.line,
1041
- startColumn: end.column,
1042
- endColumn: end.column,
1043
- };
1044
- }
1045
- else {
1046
- delimiterPosition = model.findNextMatch(",", {
1047
- lineNumber: start.line,
1048
- column: start.column,
1049
- }, false, true, null, true).range;
1050
- }
1051
- return delimiterPosition;
1052
- };
1053
- /* Helper function to determine if our word is a SQL parenthesis-less function. */
1054
- const isSqlParenLessFunction = (word) => {
1055
- const keyword = word.toUpperCase();
1056
- return sqlParenLessFunctions.includes(keyword);
1057
- };
1058
- /*
1059
- Helper function to determine if our cursor is within the range of a function.
1060
- This can be any of the patterns:
1061
- - function(<here>)
1062
- - function(<here>
1063
- <here>
1064
- <here>)
1065
- */
1066
- const getCursorWithinRange = (position, startPos, endPos) => {
1067
- let inlineWithFunction = false;
1068
- let wrappedWithinFunction = false;
1069
- const cursorColumn = position.column;
1070
- const cursorRow = position.lineNumber;
1071
- /*
1072
- Handles the case if our function is inline. An expression following the pattern
1073
-
1074
- FUNCTION(arg) where our cursor position is in between the '(' and ')' of FUNCTION.
1075
- i.e. FUNCTION( | arg | ) where '|' represents the possible cursor position.
1076
- */
1077
- if (startPos.startLineNumber === endPos.startLineNumber &&
1078
- startPos.startLineNumber === cursorRow &&
1079
- cursorColumn > startPos.startColumn &&
1080
- cursorColumn < endPos.endColumn) {
1081
- inlineWithFunction = true;
1082
- }
1083
- /*
1084
- Handles the case where our function signature is multiline. That is, if we are
1085
- anywhere between the '(' and the ')' of some expression that follows the pattern:
1086
-
1087
- FUNCTION( |
1088
- | args |
1089
- | )
1090
-
1091
- where '|' represents the possible cursor position.
1092
- */
1093
- const functionWrap = (cursorRow === startPos.startLineNumber && cursorColumn > startPos.startColumn) ||
1094
- (cursorRow === endPos.startLineNumber && cursorColumn < endPos.endColumn) ||
1095
- (cursorRow > startPos.startLineNumber && cursorRow < endPos.endLineNumber);
1096
- if (startPos.startLineNumber !== endPos.startLineNumber) {
1097
- if (functionWrap) {
1098
- wrappedWithinFunction = true;
1099
- }
1100
- }
1101
- return [inlineWithFunction, wrappedWithinFunction];
1102
- };
1103
396
 
1104
397
  let sqlExprWorker;
1105
398
  /**
@@ -1151,10 +444,10 @@ function setupMode(defaults) {
1151
444
  // Use the sql-expression Monarch Json to define the highlighting
1152
445
  languages.setMonarchTokensProvider(sqlExprDefaults.languageId, sqlExprLanguageSyntax);
1153
446
  languages.setLanguageConfiguration(sqlExprDefaults.languageId, sqlExprLanguageConfig);
1154
- const completionProvider = new SqlExprCompletionProvider(sqlExprDefaults);
447
+ const completionProvider = new SqlExprCompletionProvider(workerAccessor, sqlExprDefaults);
448
+ languages.registerCompletionItemProvider(sqlExprDefaults.languageId, completionProvider);
1155
449
  // Creates and registers a Completion Adapter
1156
450
  // Registers a completion item provider for our custom language
1157
- languages.registerCompletionItemProvider(sqlExprDefaults.languageId, completionProvider);
1158
451
  // TODO: Creates and registers a Formatter
1159
452
  // languages.registerDocumentFormattingEditProvider(
1160
453
  // SqlExprDefaults.languageId,