@kusto/monaco-kusto 5.6.6 → 6.1.0

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 (65) hide show
  1. package/package.json +26 -16
  2. package/release/dev/commandFormatter.d.ts +6 -0
  3. package/release/dev/commandHighlighter.d.ts +20 -0
  4. package/release/dev/extendedEditor.d.ts +7 -0
  5. package/release/dev/kustoMode.d.ts +8 -0
  6. package/release/dev/kustoMode.js +1730 -3278
  7. package/release/dev/kustoWorker.d.ts +92 -0
  8. package/release/dev/kustoWorker.js +5687 -8534
  9. package/release/dev/languageFeatures.d.ts +91 -0
  10. package/release/dev/languageService/kustoLanguageService.d.ts +143 -0
  11. package/release/dev/languageService/kustoMonarchLanguageDefinition.d.ts +1 -0
  12. package/release/dev/languageService/renderInfo.d.ts +31 -0
  13. package/release/dev/languageService/schema.d.ts +144 -0
  14. package/release/dev/languageService/settings.d.ts +30 -0
  15. package/release/dev/main-ac12725e.js +1958 -0
  16. package/release/dev/monaco.contribution.d.ts +15 -0
  17. package/release/dev/monaco.contribution.js +462 -309
  18. package/release/dev/workerManager.d.ts +23 -0
  19. package/release/esm/commandFormatter.d.ts +6 -0
  20. package/release/esm/commandHighlighter.d.ts +20 -0
  21. package/release/esm/extendedEditor.d.ts +7 -0
  22. package/release/esm/kusto.worker.js +2246 -5
  23. package/release/esm/kustoMode.d.ts +8 -0
  24. package/release/esm/kustoMode.js +1120 -88
  25. package/release/esm/kustoWorker.d.ts +92 -0
  26. package/release/esm/languageFeatures.d.ts +91 -0
  27. package/release/esm/languageService/kustoLanguageService.d.ts +143 -0
  28. package/release/esm/languageService/kustoMonarchLanguageDefinition.d.ts +1 -0
  29. package/release/esm/languageService/renderInfo.d.ts +31 -0
  30. package/release/esm/languageService/schema.d.ts +144 -0
  31. package/release/esm/languageService/settings.d.ts +30 -0
  32. package/release/esm/monaco.contribution.d.ts +1 -0
  33. package/release/esm/monaco.contribution.js +424 -181
  34. package/release/esm/workerManager.d.ts +23 -0
  35. package/release/min/commandFormatter.d.ts +6 -0
  36. package/release/min/commandHighlighter.d.ts +20 -0
  37. package/release/min/extendedEditor.d.ts +7 -0
  38. package/release/min/kustoMode.d.ts +8 -0
  39. package/release/min/kustoMode.js +2 -11
  40. package/release/min/kustoWorker.d.ts +92 -0
  41. package/release/min/kustoWorker.js +10 -53
  42. package/release/min/languageFeatures.d.ts +91 -0
  43. package/release/min/languageService/kustoLanguageService.d.ts +143 -0
  44. package/release/min/languageService/kustoMonarchLanguageDefinition.d.ts +1 -0
  45. package/release/min/languageService/renderInfo.d.ts +31 -0
  46. package/release/min/languageService/schema.d.ts +144 -0
  47. package/release/min/languageService/settings.d.ts +30 -0
  48. package/release/min/main-5b1cc297.js +7 -0
  49. package/release/min/monaco.contribution.d.ts +1 -0
  50. package/release/min/monaco.contribution.js +2 -2
  51. package/release/min/monaco.d.ts +1 -319
  52. package/release/min/workerManager.d.ts +23 -0
  53. package/release/{esm/monaco.d.ts → monaco.d.ts} +1 -1
  54. package/release/esm/_deps/lodash/lodash.js +0 -17209
  55. package/release/esm/_deps/vscode-languageserver-types/main.js +0 -1908
  56. package/release/esm/_deps/xregexp/xregexp-all.js +0 -4652
  57. package/release/esm/commandFormatter.js +0 -36
  58. package/release/esm/commandHighlighter.js +0 -51
  59. package/release/esm/extendedEditor.js +0 -38
  60. package/release/esm/kustoWorker.js +0 -245
  61. package/release/esm/languageFeatures.js +0 -1032
  62. package/release/esm/languageService/kustoLanguageService.js +0 -1912
  63. package/release/esm/languageService/kustoMonarchLanguageDefinition.js +0 -127
  64. package/release/esm/languageService/schema.js +0 -64
  65. package/release/esm/workerManager.js +0 -102
@@ -1,8 +1,2249 @@
1
+ /*!-----------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * monaco-kusto version: 6.1.0(b14a2ec944d355ac340af8280e7c1a4ea98f6c8c)
4
+ * Released under the MIT license
5
+ * https://https://github.com/Azure/monaco-kusto/blob/master/README.md
6
+ *-----------------------------------------------------------------------------*/
7
+
1
8
  import * as worker from 'monaco-editor-core/esm/vs/editor/editor.worker';
2
- import { KustoWorker } from './kustoWorker';
3
- self.onmessage = function () {
4
- // ignore the first message
5
- worker.initialize(function (ctx, createData) {
6
- return new KustoWorker(ctx, createData);
9
+ import * as ls from 'vscode-languageserver-types';
10
+ import XRegExp from 'xregexp';
11
+
12
+ // Definition of schema object in the context of language services. This model is exposed to consumers of this library.
13
+
14
+ // an input parameter either be a scalar in which case it has a name, type and cslType, or it can be columnar, in which case
15
+ // it will have a name, and a list of scalar types which are the column types.
16
+
17
+ /**
18
+ * Schema types:
19
+ * Engine – The main schema type. Contains clusters, databases, tables, table columns and functions.
20
+ * Cluster Manager – Internal only. A schema for clusters that manages other clusters.
21
+ * Data Management – Internal only. A schema for ingestion point operations.
22
+ */
23
+
24
+ const dotnetTypeToKustoType = {
25
+ 'System.SByte': 'bool',
26
+ 'System.Byte': 'uint8',
27
+ 'System.Int16': 'int16',
28
+ 'System.UInt16': 'uint16',
29
+ 'System.Int32': 'int',
30
+ 'System.UInt32': 'uint',
31
+ 'System.Int64': 'long',
32
+ 'System.UInt64': 'ulong',
33
+ 'System.String': 'string',
34
+ 'System.Single': 'float',
35
+ 'System.Double': 'real',
36
+ 'System.DateTime': 'datetime',
37
+ 'System.TimeSpan': 'timespan',
38
+ 'System.Guid': 'guid',
39
+ 'System.Boolean': 'bool',
40
+ 'Newtonsoft.Json.Linq.JArray': 'dynamic',
41
+ 'Newtonsoft.Json.Linq.JObject': 'dynamic',
42
+ 'Newtonsoft.Json.Linq.JToken': 'dynamic',
43
+ 'System.Object': 'dynamic',
44
+ 'System.Data.SqlTypes.SqlDecimal': 'decimal'
45
+ };
46
+ const getCslTypeNameFromClrType = clrType => dotnetTypeToKustoType[clrType] || clrType;
47
+ const kustoTypeToEntityDataType = {
48
+ object: 'Object',
49
+ bool: 'Boolean',
50
+ uint8: 'Byte',
51
+ int16: 'Int16',
52
+ uint16: 'UInt16',
53
+ int: 'Int32',
54
+ uint: 'UInt32',
55
+ long: 'Int64',
56
+ ulong: 'UInt64',
57
+ float: 'Single',
58
+ real: 'Double',
59
+ decimal: 'Decimal',
60
+ datetime: 'DateTime',
61
+ string: 'String',
62
+ dynamic: 'Dynamic',
63
+ timespan: 'TimeSpan'
64
+ };
65
+ const getEntityDataTypeFromCslType = cslType => kustoTypeToEntityDataType[cslType] || cslType;
66
+ const getCallName = fn => `${fn.name}(${fn.inputParameters.map(p => `{${p.name}}`).join(',')})`;
67
+ const getExpression = fn => `let ${fn.name} = ${getInputParametersAsCslString(fn.inputParameters)} ${fn.body}`;
68
+ const getInputParametersAsCslString = inputParameters => `(${inputParameters.map(inputParameter => getInputParameterAsCslString(inputParameter)).join(',')})`;
69
+ const getInputParameterAsCslString = inputParameter => {
70
+ // If this is a tabular parameter
71
+ if (inputParameter.columns && inputParameter.columns.length > 0) {
72
+ const attributesAsString = inputParameter.columns.map(col => `${col.name}:${col.cslType || getCslTypeNameFromClrType(col.type)}`).join(',');
73
+ return `${inputParameter.name}:${attributesAsString === '' ? '*' : attributesAsString}`;
74
+ } else {
75
+ return `${inputParameter.name}:${inputParameter.cslType || getCslTypeNameFromClrType(inputParameter.type)}`;
76
+ }
77
+ };
78
+
79
+ // polyfill string endsWith
80
+ if (!String.prototype.endsWith) {
81
+ String.prototype.endsWith = function (search, this_len) {
82
+ if (this_len === undefined || this_len > this.length) {
83
+ this_len = this.length;
84
+ }
85
+ return this.substring(this_len - search.length, this_len) === search;
86
+ };
87
+ }
88
+
89
+ // If we're running in a web worker - which doesn't share global context with the main thread -
90
+ // we need to manually load dependencies that are not explicit- meaning our non-module dependencies
91
+ // generated by Bridge.Net
92
+ if (typeof document == 'undefined') {
93
+ // monaco will run the worker from vs/base/worker so the relative path needs to be from there (hence going up 2 dirs)
94
+ importScripts('../../language/kusto/bridge.min.js');
95
+ importScripts('../../language/kusto/kusto.javascript.client.min.js');
96
+ importScripts('../../language/kusto/Kusto.Language.Bridge.min.js');
97
+ }
98
+ var k = Kusto.Data.IntelliSense;
99
+ var parsing = Kusto.Language.Parsing;
100
+ Kusto.Language.Syntax;
101
+ var k2 = Kusto.Language.Editor;
102
+ var sym = Kusto.Language.Symbols;
103
+ var GlobalState = Kusto.Language.GlobalState;
104
+ let List = System.Collections.Generic.List$1;
105
+ function assertNever(x) {
106
+ throw new Error('Unexpected object: ' + x);
107
+ }
108
+ class ParseProperties {
109
+ constructor(version, uri, rulesProvider, parseMode) {
110
+ this.version = version;
111
+ this.uri = uri;
112
+ this.rulesProvider = rulesProvider;
113
+ this.parseMode = parseMode;
114
+ }
115
+ isParseNeeded(document, rulesProvider, parseMode) {
116
+ if (document.uri === this.uri && (!rulesProvider || rulesProvider === this.rulesProvider) && document.version <= this.version && parseMode && parseMode <= this.parseMode) {
117
+ return false;
118
+ }
119
+ return true;
120
+ }
121
+ }
122
+ let TokenKind;
123
+
124
+ /**
125
+ * A plain old javascript object that is roughly equivalent to the @kusto/language-service-next object, but without
126
+ * all the Bridge.Net properties and methods. this object is being sent from web worker to main thread and turns out
127
+ * that when posting the message we lose all properties (and functions), thus we use a POJO instead.
128
+ * This issue started happening once upgrading to 0.20.0 from 0.15.5.
129
+ */
130
+ (function (TokenKind) {
131
+ TokenKind[TokenKind["TableToken"] = 2] = "TableToken";
132
+ TokenKind[TokenKind["TableColumnToken"] = 4] = "TableColumnToken";
133
+ TokenKind[TokenKind["OperatorToken"] = 8] = "OperatorToken";
134
+ TokenKind[TokenKind["SubOperatorToken"] = 16] = "SubOperatorToken";
135
+ TokenKind[TokenKind["CalculatedColumnToken"] = 32] = "CalculatedColumnToken";
136
+ TokenKind[TokenKind["StringLiteralToken"] = 64] = "StringLiteralToken";
137
+ TokenKind[TokenKind["FunctionNameToken"] = 128] = "FunctionNameToken";
138
+ TokenKind[TokenKind["UnknownToken"] = 256] = "UnknownToken";
139
+ TokenKind[TokenKind["CommentToken"] = 512] = "CommentToken";
140
+ TokenKind[TokenKind["PlainTextToken"] = 1024] = "PlainTextToken";
141
+ TokenKind[TokenKind["DataTypeToken"] = 2048] = "DataTypeToken";
142
+ TokenKind[TokenKind["ControlCommandToken"] = 4096] = "ControlCommandToken";
143
+ TokenKind[TokenKind["CommandPartToken"] = 8192] = "CommandPartToken";
144
+ TokenKind[TokenKind["QueryParametersToken"] = 16384] = "QueryParametersToken";
145
+ TokenKind[TokenKind["CslCommandToken"] = 32768] = "CslCommandToken";
146
+ TokenKind[TokenKind["LetVariablesToken"] = 65536] = "LetVariablesToken";
147
+ TokenKind[TokenKind["PluginToken"] = 131072] = "PluginToken";
148
+ TokenKind[TokenKind["BracketRangeToken"] = 262144] = "BracketRangeToken";
149
+ TokenKind[TokenKind["ClientDirectiveToken"] = 524288] = "ClientDirectiveToken";
150
+ })(TokenKind || (TokenKind = {}));
151
+ /**
152
+ * convert the bridge.net object to a plain javascript object that only contains data.
153
+ * @param k2Classifications @kusto/language-service-next bridge.net object
154
+ */
155
+ function toClassifiedRange(k2Classifications) {
156
+ return k2Classifications.map(classification => ({
157
+ start: classification.Start,
158
+ end: classification.End,
159
+ length: classification.Length,
160
+ kind: classification.Kind
161
+ }));
162
+ }
163
+
164
+ /**
165
+ * colorization data for specific line range.
166
+ */
167
+
168
+ const symbolKindToName = {
169
+ [sym.SymbolKind.Cluster]: 'Cluster',
170
+ [sym.SymbolKind.Column]: 'Column',
171
+ [sym.SymbolKind.Command]: 'Command',
172
+ [sym.SymbolKind.Database]: 'Database',
173
+ [sym.SymbolKind.EntityGroup]: 'EntityGroup',
174
+ [sym.SymbolKind.EntityGroupElement]: 'EntityGroupElement',
175
+ [sym.SymbolKind.Error]: 'Error',
176
+ [sym.SymbolKind.Function]: 'Function',
177
+ [sym.SymbolKind.Graph]: 'Graph',
178
+ [sym.SymbolKind.Group]: 'Group',
179
+ [sym.SymbolKind.MaterializedView]: 'MaterializedView',
180
+ [sym.SymbolKind.None]: 'None',
181
+ [sym.SymbolKind.Operator]: 'Operator',
182
+ [sym.SymbolKind.Option]: 'Option',
183
+ [sym.SymbolKind.Parameter]: 'Parameter',
184
+ [sym.SymbolKind.Pattern]: 'Pattern',
185
+ [sym.SymbolKind.QueryOperatorParameter]: 'QueryOperatorParameter',
186
+ [sym.SymbolKind.Scalar]: 'Scalar',
187
+ [sym.SymbolKind.Table]: 'Table',
188
+ [sym.SymbolKind.Tuple]: 'Tuple',
189
+ [sym.SymbolKind.Variable]: 'Variable',
190
+ [sym.SymbolKind.Void]: 'Void'
191
+ };
192
+ /**
193
+ * Kusto Language service translates the kusto object model (transpiled from C# by Bridge.Net)
194
+ * to the vscode language server types, which are used by vscode language extensions.
195
+ * This should make things easier in the future to provide a vscode extension based on this translation layer.
196
+ *
197
+ * Further translations, if needed, to support specific editors (Atom, sublime, Etc)
198
+ * should be done on top of this API, since it is (at least meant to be) a standard that is supported by multiple editors.
199
+ *
200
+ * Note1: Currently monaco isn't using this object model so further translation will be necessary on calling modules.
201
+ *
202
+ * Note2: This file is responsible for interacting with the kusto object model and exposing Microsoft language service types.
203
+ * An exception to that rule is tokenization (and syntax highlighting which depends on it) -
204
+ * since it's not currently part of the Microsoft language service protocol. Thus tokenize() _does_ 'leak' kusto types to the callers.
205
+ */
206
+ class KustoLanguageService {
207
+ /**
208
+ * Taken from:
209
+ * https://msazure.visualstudio.com/One/_git/Azure-Kusto-Service?path=/Src/Tools/Kusto.Explorer.Control/QueryEditors/KustoScriptEditor/KustoScriptEditorControl2.xaml.cs&version=GBdev&line=2075&lineEnd=2075&lineStartColumn=9&lineEndColumn=77&lineStyle=plain&_a=contents
210
+ */
211
+ _toOptionKind = {
212
+ [k2.CompletionKind.AggregateFunction]: k.OptionKind.FunctionAggregation,
213
+ [k2.CompletionKind.BuiltInFunction]: k.OptionKind.FunctionScalar,
214
+ [k2.CompletionKind.Cluster]: k.OptionKind.Database,
215
+ [k2.CompletionKind.Column]: k.OptionKind.Column,
216
+ [k2.CompletionKind.CommandPrefix]: k.OptionKind.Command,
217
+ [k2.CompletionKind.Database]: k.OptionKind.Database,
218
+ [k2.CompletionKind.DatabaseFunction]: k.OptionKind.FunctionServerSide,
219
+ [k2.CompletionKind.Example]: k.OptionKind.Literal,
220
+ [k2.CompletionKind.Identifier]: k.OptionKind.None,
221
+ [k2.CompletionKind.Keyword]: k.OptionKind.Option,
222
+ [k2.CompletionKind.LocalFunction]: k.OptionKind.FunctionLocal,
223
+ [k2.CompletionKind.MaterialiedView]: k.OptionKind.MaterializedView,
224
+ [k2.CompletionKind.Parameter]: k.OptionKind.Parameter,
225
+ [k2.CompletionKind.Punctuation]: k.OptionKind.None,
226
+ [k2.CompletionKind.QueryPrefix]: k.OptionKind.Operator,
227
+ [k2.CompletionKind.RenderChart]: k.OptionKind.OptionRender,
228
+ [k2.CompletionKind.ScalarInfix]: k.OptionKind.None,
229
+ [k2.CompletionKind.ScalarPrefix]: k.OptionKind.Literal,
230
+ [k2.CompletionKind.ScalarType]: k.OptionKind.DataType,
231
+ [k2.CompletionKind.Syntax]: k.OptionKind.None,
232
+ [k2.CompletionKind.Table]: k.OptionKind.Table,
233
+ [k2.CompletionKind.TabularPrefix]: k.OptionKind.None,
234
+ [k2.CompletionKind.TabularSuffix]: k.OptionKind.None,
235
+ [k2.CompletionKind.Unknown]: k.OptionKind.None,
236
+ [k2.CompletionKind.Variable]: k.OptionKind.Parameter,
237
+ [k2.CompletionKind.Option]: k.OptionKind.Option,
238
+ [k2.CompletionKind.Graph]: k.OptionKind.Graph
239
+ };
240
+ constructor(schema, languageSettings) {
241
+ this._schemaCache = {};
242
+ this._kustoJsSchema = KustoLanguageService.convertToKustoJsSchema(schema);
243
+ this.__kustoJsSchemaV2 = this.convertToKustoJsSchemaV2(schema);
244
+ this._schema = schema;
245
+ this._clustersSetInGlobalState = new Set();
246
+ this._nonEmptyDatabaseSetInGlobalState = new Set(); // used to remove clusters that are already in the global state
247
+
248
+ this.configure(languageSettings);
249
+ this._newlineAppendPipePolicy = new Kusto.Data.IntelliSense.ApplyPolicy();
250
+ this._newlineAppendPipePolicy.Text = '\n| ';
251
+ }
252
+ createDatabaseUniqueName(clusterName, databaseName) {
253
+ return `${clusterName}_${databaseName}`;
254
+ }
255
+
256
+ /**
257
+ * A setter for _kustoJsSchemaV2. After a schema (global state) is set, create 2 sets of cluster and database names.
258
+ */
259
+ set _kustoJsSchemaV2(globalState) {
260
+ this.__kustoJsSchemaV2 = globalState;
261
+ this._clustersSetInGlobalState.clear();
262
+ this._nonEmptyDatabaseSetInGlobalState.clear();
263
+
264
+ // create 2 Sets with cluster names and database names based on the updated Global State.
265
+ for (let i = 0; i < globalState.Clusters.Count; i++) {
266
+ const clusterSymbol = this._kustoJsSchemaV2.Clusters.getItem(i);
267
+ this._clustersSetInGlobalState.add(clusterSymbol.Name);
268
+ for (let i2 = 0; i2 < clusterSymbol.Databases.Count; i2++) {
269
+ const databaseSymbol = clusterSymbol.Databases.getItem(i2);
270
+ if (databaseSymbol.Tables.Count > 0) {
271
+ // only include database with tables
272
+ this._nonEmptyDatabaseSetInGlobalState.add(this.createDatabaseUniqueName(clusterSymbol.Name, databaseSymbol.Name));
273
+ }
274
+ }
275
+ }
276
+ }
277
+
278
+ /**
279
+ * A getter for __kustoJsSchemaV2
280
+ */
281
+ get _kustoJsSchemaV2() {
282
+ return this.__kustoJsSchemaV2;
283
+ }
284
+ configure(languageSettings) {
285
+ this._languageSettings = languageSettings;
286
+
287
+ // Since we're still reverting to V1 intellisense for control commands, we need to update the rules provider
288
+ // (which is a notion of V1 intellisense).
289
+ this.createRulesProvider(this._kustoJsSchema, this._schema.clusterType);
290
+ }
291
+ doComplete(document, position) {
292
+ return this.isIntellisenseV2() ? this.doCompleteV2(document, position) : this.doCompleteV1(document, position);
293
+ }
294
+ disabledCompletionItemsV2 = {
295
+ // render charts
296
+ ladderchart: k2.CompletionKind.RenderChart,
297
+ pivotchart: k2.CompletionKind.RenderChart,
298
+ timeline: k2.CompletionKind.RenderChart,
299
+ timepivot: k2.CompletionKind.RenderChart,
300
+ '3Dchart': k2.CompletionKind.RenderChart,
301
+ list: k2.CompletionKind.RenderChart
302
+ };
303
+
304
+ /**
305
+ * important: Only use during development to test Global State.
306
+ * Prints clusters, databases and tables that are currently in the GlobalState.
307
+ */
308
+ debugGlobalState(globals) {
309
+ // iterate over clusters
310
+ console.log(`globals.Clusters.Count: ${globals.Clusters.Count}`);
311
+ for (let i = 0; i < globals.Clusters.Count; i++) {
312
+ const cluster = globals.Clusters.getItem(i);
313
+ console.log(`cluster: ${cluster.Name}`);
314
+
315
+ // iterate over databases
316
+ console.log(`cluster.Databases.Count: ${cluster.Databases.Count}`);
317
+ for (let i2 = 0; i2 < cluster.Databases.Count; i2++) {
318
+ const database = cluster.Databases.getItem(i2);
319
+ console.log(`cluster.database: [${cluster.Name}].[${database.Name}]`);
320
+
321
+ // iterate over tables
322
+ console.log(`cluster.Databases.Tables.Count: ${database.Tables.Count}`);
323
+ for (let i3 = 0; i3 < database.Tables.Count; i3++) {
324
+ const table = database.Tables.getItem(i3);
325
+ console.log(`cluster.database.table: [${cluster.Name}].[${database.Name}].[${table.Name}]`);
326
+ }
327
+ }
328
+ }
329
+ }
330
+
331
+ /**
332
+ * Prepending the doc of the actual topic at the top
333
+ */
334
+ formatHelpTopic(helpTopic) {
335
+ return `**${helpTopic.Name} [(view online)](${helpTopic.Url})**\n\n${helpTopic.LongDescription}`;
336
+ }
337
+ doCompleteV2(document, position) {
338
+ if (!document) {
339
+ return Promise.resolve(ls.CompletionList.create([]));
340
+ }
341
+ const script = this.parseDocumentV2(document);
342
+
343
+ // print cluster/database/tables from CodeScript.Globals
344
+ // this.debugGlobalState(script.Globals);
345
+
346
+ // get current command
347
+ const cursorOffset = document.offsetAt(position);
348
+ let currentCommand = script.GetBlockAtPosition(cursorOffset);
349
+
350
+ // get completion items
351
+ const completionItems = currentCommand.Service.GetCompletionItems(cursorOffset);
352
+ let disabledItems = this.disabledCompletionItemsV2;
353
+ if (this._languageSettings.disabledCompletionItems) {
354
+ this._languageSettings.disabledCompletionItems.map(item => {
355
+ // logic will treat unknown as a '*' wildcard, meaning that if the key is in the object
356
+ // the completion item will be suppressed.
357
+ disabledItems[item] = k2.CompletionKind.Unknown;
358
+ });
359
+ }
360
+ const itemsAsArray = this.toArray(completionItems.Items);
361
+ let items = itemsAsArray.filter(item => !(item && item.MatchText && disabledItems[item.MatchText] !== undefined && (disabledItems[item.MatchText] === k2.CompletionKind.Unknown || disabledItems[item.MatchText] === item.Kind))).map((kItem, i) => {
362
+ const v1CompletionOption = new k.CompletionOption(this._toOptionKind[kItem.Kind] || k.OptionKind.None, kItem.DisplayText);
363
+ const helpTopic = this.getTopic(v1CompletionOption);
364
+ // If we have AfterText it means that the cursor should no be placed at end of suggested text.
365
+ // In that case we switch to snippet format and represent the point where the cursor should be as
366
+ // as '\$0'
367
+ const {
368
+ textToInsert,
369
+ format
370
+ } = kItem.AfterText && kItem.AfterText.length > 0 ? {
371
+ // Need to escape dollar sign since it is used as a placeholder in snippet.
372
+ // Usually dollar sign is not a valid character in a function name, but grafana uses macros that start with dollars.
373
+ textToInsert: `${kItem.EditText.replace('$', '\\$')}$0${kItem.AfterText}`,
374
+ format: ls.InsertTextFormat.Snippet
375
+ } : {
376
+ textToInsert: kItem.EditText,
377
+ format: ls.InsertTextFormat.PlainText
378
+ };
379
+ const lsItem = ls.CompletionItem.create(kItem.DisplayText);
380
+
381
+ // Adding to columns a prefix to their sortText so they will appear first in the list
382
+ const sortTextPrefix = lsItem.kind === ls.CompletionItemKind.Field ? 0 : itemsAsArray.length;
383
+ const startPosition = document.positionAt(completionItems.EditStart);
384
+ const endPosition = document.positionAt(completionItems.EditStart + completionItems.EditLength);
385
+ lsItem.textEdit = ls.TextEdit.replace(ls.Range.create(startPosition, endPosition), textToInsert);
386
+ lsItem.sortText = this.getSortText(sortTextPrefix + i + 1);
387
+ // Changing the first letter to be lower case, to ignore case-sensitive matching
388
+ lsItem.filterText = lsItem.label.charAt(0).toLowerCase() + lsItem.label.slice(1);
389
+ lsItem.kind = this.kustoKindToLsKindV2(kItem.Kind);
390
+ lsItem.insertTextFormat = format;
391
+ lsItem.detail = helpTopic ? helpTopic.ShortDescription : undefined;
392
+ lsItem.documentation = helpTopic ? {
393
+ value: this.formatHelpTopic(helpTopic),
394
+ kind: ls.MarkupKind.Markdown
395
+ } : undefined;
396
+ return lsItem;
397
+ });
398
+ return Promise.resolve(ls.CompletionList.create(items));
399
+ }
400
+ isIntellisenseV2 = () => this._languageSettings.useIntellisenseV2 && this._schema && this._schema.clusterType === 'Engine';
401
+
402
+ /**
403
+ * when trying to get a topic we need the function name (abs, toLower, ETC).
404
+ * The problem is that the 'Value' string also contains the arguments (e.g abs(number)), which means that we are
405
+ * not able to correlate the option with its documentation.
406
+ * This piece of code tries to strip this hwne getting topic.
407
+ * @param completionOption the Completion option
408
+ */
409
+ getTopic(completionOption) {
410
+ if (completionOption.Kind == k.OptionKind.FunctionScalar || completionOption.Kind == k.OptionKind.FunctionAggregation) {
411
+ // from a value like 'abs(number)' remove the '(number)' so that only 'abs' will remain
412
+ const indexOfParen = completionOption.Value.indexOf('(');
413
+ if (indexOfParen >= 0) {
414
+ completionOption = new k.CompletionOption(completionOption.Kind, completionOption.Value.substring(0, indexOfParen));
415
+ }
416
+ }
417
+ return k.CslDocumentation.Instance.GetTopic(completionOption);
418
+ }
419
+ disabledCompletionItemsV1 = {
420
+ capacity: k.OptionKind.Policy,
421
+ callout: k.OptionKind.Policy,
422
+ encoding: k.OptionKind.Policy,
423
+ batching: k.OptionKind.Policy,
424
+ querythrottling: k.OptionKind.Policy,
425
+ merge: k.OptionKind.Policy,
426
+ querylimit: k.OptionKind.Policy,
427
+ rowstore: k.OptionKind.Policy,
428
+ streamingingestion: k.OptionKind.Policy,
429
+ restricted_view_access: k.OptionKind.Policy,
430
+ sharding: k.OptionKind.Policy,
431
+ 'restricted-viewers': k.OptionKind.Policy,
432
+ attach: k.OptionKind.Command,
433
+ purge: k.OptionKind.Command
434
+ };
435
+ doCompleteV1(document, position) {
436
+ // TODO: fix typing in CslCommandParser to allow rulesProvider to be query only.
437
+ let caretAbsolutePosition = document.offsetAt(position);
438
+
439
+ // find out what's the current command to only parse this one.
440
+ this.parseDocumentV1(document, k.ParseMode.CommandTokensOnly);
441
+ let currentCommand = this.getCurrentCommand(document, caretAbsolutePosition);
442
+ let commandTextUntilCursor = '';
443
+ if (currentCommand) {
444
+ currentCommand.AbsoluteStart;
445
+ this.parseTextV1(currentCommand.Text, k.ParseMode.TokenizeAllText);
446
+ const caretRelativePosition = caretAbsolutePosition - currentCommand.AbsoluteStart;
447
+ commandTextUntilCursor = currentCommand.Text.substring(currentCommand.CslExpressionStartPosition, caretRelativePosition);
448
+ }
449
+ let commandTextWithoutLastWord = this.getCommandWithoutLastWord(commandTextUntilCursor);
450
+ let context = this._rulesProvider.AnalyzeCommand$1(commandTextUntilCursor, currentCommand).Context;
451
+ let result = {
452
+ v: null
453
+ };
454
+ this._rulesProvider.TryMatchAnyRule(commandTextWithoutLastWord, result);
455
+ let rule = result.v;
456
+ if (rule) {
457
+ const completionOptions = this.toArray(rule.GetCompletionOptions(context));
458
+
459
+ // TODO once AppendPipePolicy becomes a public static member of ApplyPolicy in our c# code, and bridge.Net transplies this,
460
+ // remove the 'as any' part..
461
+ // Also = DefaultApplyPolicy is internal in c# code, so not exposed in d.ts, so we cast it to any.
462
+ if (this._languageSettings.newlineAfterPipe && rule.DefaultAfterApplyPolicy === Kusto.Data.IntelliSense.ApplyPolicy.AppendPipePolicy) {
463
+ rule.DefaultAfterApplyPolicy = this._newlineAppendPipePolicy;
464
+ }
465
+ let options = completionOptions.filter(option => !(option && option.Value && this.disabledCompletionItemsV1[option.Value] === option.Kind)).map((option, ordinal) => {
466
+ const {
467
+ insertText,
468
+ insertTextFormat
469
+ } = this.getTextToInsert(rule, option);
470
+ const helpTopic = k.CslDocumentation.Instance.GetTopic(option);
471
+ const item = ls.CompletionItem.create(option.Value);
472
+ item.kind = this.kustoKindToLsKind(option.Kind);
473
+ item.insertText = insertText;
474
+ item.insertTextFormat = insertTextFormat;
475
+ item.sortText = this.getSortText(ordinal + 1);
476
+ item.detail = helpTopic ? helpTopic.ShortDescription : undefined;
477
+ item.documentation = helpTopic ? {
478
+ value: helpTopic.LongDescription,
479
+ kind: ls.MarkupKind.Markdown
480
+ } : undefined;
481
+ return item;
482
+ });
483
+ return Promise.resolve(ls.CompletionList.create(options));
484
+ }
485
+ return Promise.resolve(ls.CompletionList.create([]));
486
+ }
487
+ doRangeFormat(document, range) {
488
+ if (!document) {
489
+ return Promise.resolve([]);
490
+ }
491
+ const rangeStartOffset = document.offsetAt(range.start);
492
+ const rangeEndOffset = document.offsetAt(range.end);
493
+ const commands = this.getFormattedCommandsInDocumentV2(document, rangeStartOffset, rangeEndOffset);
494
+ if (!commands.originalRange || commands.formattedCommands.length === 0) {
495
+ return Promise.resolve([]);
496
+ }
497
+ return Promise.resolve([ls.TextEdit.replace(commands.originalRange, commands.formattedCommands.join(''))]);
498
+ }
499
+ doDocumentFormat(document) {
500
+ if (!document) {
501
+ return Promise.resolve([]);
502
+ }
503
+ const startPos = document.positionAt(0);
504
+ const endPos = document.positionAt(document.getText().length);
505
+ const fullDocRange = ls.Range.create(startPos, endPos);
506
+ const formattedDoc = this.getFormattedCommandsInDocumentV2(document).formattedCommands.join('');
507
+ return Promise.resolve([ls.TextEdit.replace(fullDocRange, formattedDoc)]);
508
+ }
509
+
510
+ // Method is not triggered, instead doRangeFormat is invoked with the range of the caret's line.
511
+ doCurrentCommandFormat(document, caretPosition) {
512
+ const offset = document.offsetAt(caretPosition);
513
+ const range = this.createRange(document, offset - 1, offset + 1);
514
+ return this.doRangeFormat(document, range);
515
+ }
516
+ doFolding(document) {
517
+ if (!document) {
518
+ return Promise.resolve([]);
519
+ }
520
+ return this.getCommandsInDocument(document).then(commands => {
521
+ return commands.map(command => {
522
+ // don't count the last empty line as part of the folded range (consider linux, mac, pc newlines)
523
+ if (command.text.endsWith('\r\n')) {
524
+ command.absoluteEnd -= 2;
525
+ } else if (command.text.endsWith('\r') || command.text.endsWith('\n')) {
526
+ --command.absoluteEnd;
527
+ }
528
+ const startPosition = document.positionAt(command.absoluteStart);
529
+ const endPosition = document.positionAt(command.absoluteEnd);
530
+ return {
531
+ startLine: startPosition.line,
532
+ startCharacter: startPosition.character,
533
+ endLine: endPosition.line,
534
+ endCharacter: endPosition.character
535
+ };
536
+ });
537
+ });
538
+ }
539
+ getClusterReferences(document, cursorOffset) {
540
+ const script = this.parseDocumentV2(document);
541
+ const currentBlock = this.getCurrentCommandV2(script, cursorOffset);
542
+ let clusterReferences = currentBlock?.Service?.GetClusterReferences();
543
+ if (!clusterReferences) {
544
+ return Promise.resolve([]);
545
+ }
546
+ let newClustersReferences = [];
547
+ let newClustersReferencesSet = new Set(); // used to remove duplicates
548
+
549
+ // Keep only unique clusters that aren't already exist in the Global State
550
+ for (let i = 0; i < clusterReferences.Count; i++) {
551
+ const clusterReference = clusterReferences.getItem(i);
552
+ const clusterHostName = Kusto.Language.KustoFacts.GetHostName(clusterReference.Cluster);
553
+
554
+ // ignore duplicates
555
+ if (newClustersReferencesSet.has(clusterHostName)) {
556
+ continue;
557
+ }
558
+ newClustersReferencesSet.add(clusterHostName);
559
+
560
+ // ignore references that are already in the GlobalState.
561
+ if (!this._clustersSetInGlobalState.has(clusterHostName)) {
562
+ newClustersReferences.push({
563
+ clusterName: clusterHostName
564
+ });
565
+ }
566
+ }
567
+ return Promise.resolve(newClustersReferences);
568
+ }
569
+ getDatabaseReferences(document, cursorOffset) {
570
+ const script = this.parseDocumentV2(document);
571
+ const currentBlock = this.getCurrentCommandV2(script, cursorOffset);
572
+ let databasesReferences = currentBlock?.Service?.GetDatabaseReferences();
573
+ if (!databasesReferences) {
574
+ return Promise.resolve([]);
575
+ }
576
+ let newDatabasesReferences = [];
577
+ let newDatabasesReferencesSet = new Set();
578
+ for (let i1 = 0; i1 < databasesReferences.Count; i1++) {
579
+ const databaseReference = databasesReferences.getItem(i1);
580
+ const clusterHostName = Kusto.Language.KustoFacts.GetHostName(databaseReference.Cluster);
581
+
582
+ // ignore duplicates
583
+ const databaseReferenceUniqueId = this.createDatabaseUniqueName(clusterHostName, databaseReference.Database);
584
+ if (newDatabasesReferencesSet.has(databaseReferenceUniqueId)) {
585
+ continue;
586
+ }
587
+ newDatabasesReferencesSet.add(databaseReferenceUniqueId);
588
+
589
+ // ignore references that are already in the GlobalState.
590
+ let foundInGlobalState = this._nonEmptyDatabaseSetInGlobalState.has(databaseReferenceUniqueId);
591
+ if (!foundInGlobalState) {
592
+ newDatabasesReferences.push({
593
+ databaseName: databaseReference.Database,
594
+ clusterName: databaseReference.Cluster
595
+ });
596
+ }
597
+ }
598
+ return Promise.resolve(newDatabasesReferences);
599
+ }
600
+ doValidation(document, changeIntervals, includeWarnings, includeSuggestions) {
601
+ // didn't implement validation for v1.
602
+ if (!document || !this.isIntellisenseV2()) {
603
+ return Promise.resolve([]);
604
+ }
605
+ const script = this.parseDocumentV2(document);
606
+ let blocks = this.toArray(script.Blocks);
607
+ if (changeIntervals.length > 0) {
608
+ blocks = this.getAffectedBlocks(blocks, changeIntervals);
609
+ }
610
+ const diagnostics = blocks.map(block => {
611
+ // GetDiagnostics returns the errors in the block
612
+ let diagnostics = this.toArray(block.Service.GetDiagnostics());
613
+ const enableWarnings = includeWarnings ?? this._languageSettings.enableQueryWarnings;
614
+ const enableSuggestions = includeSuggestions ?? this._languageSettings.enableQuerySuggestions;
615
+ if (enableWarnings || enableSuggestions) {
616
+ // Concat Warnings and suggestions to the diagnostics
617
+ const warningAndSuggestionDiagnostics = block.Service.GetAnalyzerDiagnostics(true);
618
+ const filterredDiagnostics = this.toArray(warningAndSuggestionDiagnostics).filter(d => {
619
+ const allowSeverity = enableWarnings && d.Severity === 'Warning' || enableSuggestions && d.Severity === 'Suggestion';
620
+ const allowCode = !this._languageSettings.disabledDiagnoticCodes?.includes(d.Code);
621
+ return allowSeverity && allowCode;
622
+ });
623
+ diagnostics = diagnostics.concat(filterredDiagnostics);
624
+ }
625
+ return diagnostics;
626
+ }).reduce((prev, curr) => prev.concat(curr), []);
627
+ const lsDiagnostics = this.toLsDiagnostics(diagnostics, document);
628
+ return Promise.resolve(lsDiagnostics);
629
+ }
630
+ getApplyCodeActions(document, start, end) {
631
+ const script = this.parseDocumentV2(document);
632
+ let block = this.getAffectedBlocks(this.toArray(script.Blocks), [{
633
+ start,
634
+ end
635
+ }])[0];
636
+ const codeActionInfo = block.Service.GetCodeActions(start, start, 0, null, true, null, new Kusto.Language.Utils.CancellationToken());
637
+ const codeActions = this.toArray(codeActionInfo.Actions);
638
+ // Some code actions are of type "MenuAction". We want to flat them out, to show them seperately.
639
+ let flatCodeActions = [];
640
+ for (let i = 0; i < codeActions.length; i++) {
641
+ flatCodeActions.push(...this.flattenCodeActions(codeActions[i], null));
642
+ }
643
+ return flatCodeActions;
644
+ }
645
+ getResultActions(document, start, end) {
646
+ const script = this.parseDocumentV2(document);
647
+ let block = this.getAffectedBlocks(this.toArray(script.Blocks), [{
648
+ start,
649
+ end
650
+ }])[0];
651
+ const applyCodeActions = this.getApplyCodeActions(document, start, end);
652
+ const resultActionsMap = applyCodeActions.map(applyCodeAction => {
653
+ let changes = [];
654
+ const codeActionResults = this.toArray(block.Service.ApplyCodeAction(applyCodeAction, start).Actions);
655
+ const changeTextAction = codeActionResults.find(c => c instanceof Kusto.Language.Editor.ChangeTextAction);
656
+ if (changeTextAction) {
657
+ changes = this.toArray(changeTextAction.Changes).map(change => ({
658
+ start: change.Start + block.Start,
659
+ deleteLength: change.DeleteLength,
660
+ insertText: change.InsertText
661
+ }));
662
+ }
663
+ return {
664
+ title: applyCodeAction.Title,
665
+ changes,
666
+ kind: applyCodeAction.Kind
667
+ };
668
+ }).filter(resultAction => resultAction.changes.length);
669
+ return Promise.resolve(resultActionsMap);
670
+ }
671
+ transformCodeActionTitle(currentActionTitle, parentActionTitle) {
672
+ let title = currentActionTitle;
673
+ switch (title) {
674
+ case 'Apply':
675
+ title = 'Apply once';
676
+ break;
677
+ case 'Fix All':
678
+ title = 'Apply to all';
679
+ break;
680
+ case 'Extract Value':
681
+ title = 'Extract value';
682
+ break;
683
+ }
684
+ if (parentActionTitle) {
685
+ // We want to lower case the first character since it's going to be in brackets
686
+ const parentActionTitleLowerCased = parentActionTitle.charAt(0).toUpperCase() + parentActionTitle.slice(1);
687
+ title = `${title} (${parentActionTitleLowerCased})`;
688
+ }
689
+ return title;
690
+ }
691
+ flattenCodeActions(codeAction, parentTitle) {
692
+ const applyActions = [];
693
+ if (codeAction instanceof k2.ApplyAction) {
694
+ codeAction.Title = this.transformCodeActionTitle(codeAction.Title, parentTitle);
695
+ applyActions.push(codeAction);
696
+ } else if (codeAction instanceof k2.MenuAction) {
697
+ const nestedCodeActions = this.toArray(codeAction.Actions);
698
+ for (let i = 0; i < nestedCodeActions.length; i++) {
699
+ applyActions.push(...this.flattenCodeActions(nestedCodeActions[i], codeAction.Title));
700
+ }
701
+ }
702
+ return applyActions;
703
+ }
704
+ toLsDiagnostics(diagnostics, document) {
705
+ return diagnostics.filter(diag => diag.HasLocation).map(diag => {
706
+ const start = document.positionAt(diag.Start);
707
+ const end = document.positionAt(diag.Start + diag.Length);
708
+ const range = ls.Range.create(start, end);
709
+ let severity;
710
+ switch (diag.Severity) {
711
+ case 'Suggestion':
712
+ severity = ls.DiagnosticSeverity.Information;
713
+ break;
714
+ case 'Warning':
715
+ severity = ls.DiagnosticSeverity.Warning;
716
+ break;
717
+ default:
718
+ severity = ls.DiagnosticSeverity.Error;
719
+ }
720
+ return ls.Diagnostic.create(range, diag.Message, severity, diag.Code);
721
+ });
722
+ }
723
+
724
+ /**
725
+ * Colorize one or more kusto blocks (a.k.a commands), or just the entire document.
726
+ * Supports multi-cursor editing (colorizes blocks on multiple changes).
727
+ * @param document The document to colorize
728
+ * @param changeIntervals an array containing 0 or more changed intervals. if the array is empty - just colorize the entire row.
729
+ * if the array contains a single change - just color the kusto blocks that wraps this change. If multiple changes are provided,
730
+ * colorize all blocks that intersect these changes.
731
+ * The code will try to only parse once if this is the same command.
732
+ */
733
+ doColorization(document, changeIntervals) {
734
+ if (!document || !this._languageSettings.useSemanticColorization) {
735
+ return Promise.resolve([]);
736
+ }
737
+
738
+ // V1 intellisense
739
+ if (!this.isIntellisenseV2()) {
740
+ // Handle specific ranges changes (and not the whole doc)
741
+ if (changeIntervals.length > 0) {
742
+ this.parseDocumentV1(document, k.ParseMode.CommandTokensOnly);
743
+ const affectedCommands = this.toArray(this._parser.Results).filter(command =>
744
+ // a command is affected if it intersects at least on of changed ranges.
745
+ command // command can be null. we're filtering all nulls in the array.
746
+ ? changeIntervals.some(({
747
+ start: changeStart,
748
+ end: changeEnd
749
+ }) =>
750
+ // both intervals intersect if either the start or the end of interval A is inside interval B.
751
+ // If we deleted something at the end of a command, the interval will not intersect the current command.
752
+ // so we also want consider affected commands commands the end where the interval begins.
753
+ // hence the + 1.
754
+ command.AbsoluteStart >= changeStart && command.AbsoluteStart <= changeEnd || changeStart >= command.AbsoluteStart && changeStart <= command.AbsoluteEnd + 1) : false);
755
+
756
+ // We're not on any command so don't return any classifications.
757
+ // this can happen if we're at the and of the file and deleting empty rows (for example).
758
+ if (!affectedCommands || affectedCommands.length === 0) {
759
+ return Promise.resolve([{
760
+ classifications: [],
761
+ absoluteStart: changeIntervals[0].start,
762
+ absoluteEnd: changeIntervals[0].end
763
+ }]);
764
+ }
765
+ const results = affectedCommands.map(command => {
766
+ this.parseTextV1(command.Text, k.ParseMode.TokenizeAllText);
767
+ const k2Classifications = this.getClassificationsFromParseResult(command.AbsoluteStart);
768
+ const classifications = toClassifiedRange(k2Classifications);
769
+ return {
770
+ classifications: classifications,
771
+ absoluteStart: command.AbsoluteStart,
772
+ absoluteEnd: command.AbsoluteEnd
773
+ };
774
+ });
775
+ return Promise.resolve(results);
776
+ }
777
+
778
+ // Entire document requested
779
+ this.parseDocumentV1(document, k.ParseMode.TokenizeAllText);
780
+ const classifications = this.getClassificationsFromParseResult();
781
+ return Promise.resolve([{
782
+ classifications: toClassifiedRange(classifications),
783
+ absoluteStart: 0,
784
+ absoluteEnd: document.getText().length
785
+ }]);
786
+ }
787
+
788
+ // V2 intellisense
789
+ const script = this.parseDocumentV2(document);
790
+ if (changeIntervals.length > 0) {
791
+ const blocks = this.toArray(script.Blocks);
792
+ const affectedBlocks = this.getAffectedBlocks(blocks, changeIntervals);
793
+ const result = affectedBlocks.map(block => ({
794
+ classifications: toClassifiedRange(this.toArray(block.Service.GetClassifications(block.Start, block.End).Classifications)),
795
+ absoluteStart: block.Start,
796
+ absoluteEnd: block.End
797
+ }));
798
+ return Promise.resolve(result);
799
+ }
800
+
801
+ // Entire document requested
802
+ const blocks = this.toArray(script.Blocks);
803
+ const classifications = blocks.map(block => {
804
+ return this.toArray(block.Service.GetClassifications(block.Start, block.Length).Classifications);
805
+ }).reduce((prev, curr) => prev.concat(curr), []);
806
+ return Promise.resolve([{
807
+ classifications: toClassifiedRange(classifications),
808
+ absoluteStart: 0,
809
+ absoluteEnd: document.getText().length
810
+ }]);
811
+ }
812
+ getAffectedBlocks(blocks, changeIntervals) {
813
+ return blocks.filter(block =>
814
+ // a command is affected if it intersects at least on of changed ranges.
815
+ block // command can be null. we're filtering all nulls in the array.
816
+ ? changeIntervals.some(({
817
+ start: changeStart,
818
+ end: changeEnd
819
+ }) =>
820
+ // both intervals intersect if either the start or the end of interval A is inside interval B.
821
+ block.Start >= changeStart && block.Start <= changeEnd || changeStart >= block.Start && changeStart <= block.End + 1) : false);
822
+ }
823
+ addClusterToSchema(document, clusterName, databaseNames) {
824
+ let clusterNameOnly = Kusto.Language.KustoFacts.GetHostName(clusterName);
825
+ let cluster = this._kustoJsSchemaV2.GetCluster$1(clusterNameOnly);
826
+ if (cluster) {
827
+ // add databases that are not already in the cluster.
828
+ databaseNames.filter(databaseName => !cluster.GetDatabase(databaseName)).map(databaseName => {
829
+ const symbol = new sym.DatabaseSymbol.$ctor1(databaseName, undefined, false);
830
+ cluster = cluster.AddDatabase(symbol);
831
+ });
832
+ }
833
+ if (!cluster) {
834
+ const databaseSymbols = databaseNames.map(databaseName => {
835
+ const symbol = new sym.DatabaseSymbol.$ctor1(databaseName, undefined, false);
836
+ return symbol;
837
+ });
838
+ const databaseSymbolsList = new (List(sym.DatabaseSymbol).$ctor1)(databaseSymbols);
839
+ cluster = new sym.ClusterSymbol.$ctor1(clusterNameOnly, databaseSymbolsList, false);
840
+ }
841
+ this._kustoJsSchemaV2 = this._kustoJsSchemaV2.AddOrReplaceCluster(cluster);
842
+ this._script = k2.CodeScript.From$1(document.getText(), this._kustoJsSchemaV2);
843
+ return Promise.resolve();
844
+ }
845
+ addDatabaseToSchema(document, clusterName, databaseSchema) {
846
+ let clusterHostName = Kusto.Language.KustoFacts.GetHostName(clusterName);
847
+ let cluster = this._kustoJsSchemaV2.GetCluster$1(clusterHostName);
848
+ if (!cluster) {
849
+ cluster = new sym.ClusterSymbol.$ctor1(clusterHostName, null, false);
850
+ }
851
+ const databaseSymbol = KustoLanguageService.convertToDatabaseSymbol(databaseSchema);
852
+ cluster = cluster.AddOrUpdateDatabase(databaseSymbol);
853
+ this._kustoJsSchemaV2 = this._kustoJsSchemaV2.AddOrReplaceCluster(cluster);
854
+ this._script = k2.CodeScript.From$1(document.getText(), this._kustoJsSchemaV2);
855
+ return Promise.resolve();
856
+ }
857
+ setSchema(schema) {
858
+ this._schema = schema;
859
+ // We support intellisenseV2 only if the clusterType is "Engine", even if the setting is enabled
860
+ if (this._languageSettings.useIntellisenseV2 && schema && schema.clusterType === 'Engine') {
861
+ let kustoJsSchemaV2 = this.convertToKustoJsSchemaV2(schema);
862
+ this._kustoJsSchemaV2 = kustoJsSchemaV2;
863
+ this._script = undefined;
864
+ this._parsePropertiesV2 = undefined;
865
+ }
866
+
867
+ // since V2 doesn't support control commands, we're initializing V1 intellisense for both cases and we'll going to use V1 intellisense for control commands.
868
+ return new Promise((resolve, reject) => {
869
+ const kustoJsSchema = schema ? KustoLanguageService.convertToKustoJsSchema(schema) : undefined;
870
+ this._kustoJsSchema = kustoJsSchema;
871
+ this.createRulesProvider(kustoJsSchema, schema.clusterType);
872
+ resolve(undefined);
873
+ });
874
+ }
875
+ setParameters(scalarParameters, tabularParameters) {
876
+ if (!this._languageSettings.useIntellisenseV2 || this._schema.clusterType !== 'Engine') {
877
+ throw new Error('setParameters requires intellisense V2 and Engine cluster');
878
+ }
879
+ this._schema.globalScalarParameters = scalarParameters;
880
+ this._schema.globalTabularParameters = tabularParameters;
881
+ const scalarSymbols = scalarParameters.map(param => KustoLanguageService.createParameterSymbol(param));
882
+ const tabularSymbols = tabularParameters.map(param => KustoLanguageService.createTabularParameterSymbol(param));
883
+ this._kustoJsSchemaV2 = this._kustoJsSchemaV2.WithParameters(KustoLanguageService.toBridgeList([...scalarSymbols, ...tabularSymbols]));
884
+ return Promise.resolve(undefined);
885
+ }
886
+
887
+ /**
888
+ * A combination of normalizeSchema and setSchema
889
+ * @param schema schema json as received from .show schema as json
890
+ * @param clusterConnectionString cluster connection string
891
+ * @param databaseInContextName name of database in context
892
+ */
893
+ setSchemaFromShowSchema(schema, clusterConnectionString, databaseInContextName, globalScalarParameters, globalTabularParameters) {
894
+ return this.normalizeSchema(schema, clusterConnectionString, databaseInContextName).then(normalized => this.setSchema({
895
+ ...normalized,
896
+ globalScalarParameters,
897
+ globalTabularParameters
898
+ }));
899
+ }
900
+
901
+ /**
902
+ * Converts the result of .show schema as json to a normalized schema used by kusto language service.
903
+ * @param schema result of show schema
904
+ * @param clusterConnectionString cluster connection string`
905
+ * @param databaseInContextName database in context name
906
+ */
907
+ normalizeSchema(schema, clusterConnectionString, databaseInContextName) {
908
+ const databases = Object.keys(schema.Databases).map(key => schema.Databases[key]).map(({
909
+ Name,
910
+ Tables,
911
+ ExternalTables,
912
+ MaterializedViews,
913
+ Functions,
914
+ MinorVersion,
915
+ MajorVersion
916
+ }) => ({
917
+ name: Name,
918
+ minorVersion: MinorVersion,
919
+ majorVersion: MajorVersion,
920
+ tables: [].concat(...[[Tables, 'Table'], [MaterializedViews, 'MaterializedView'], [ExternalTables, 'ExternalTable']].filter(([tableContainer]) => tableContainer).map(([tableContainer, tableEntity]) => Object.values(tableContainer).map(({
921
+ Name,
922
+ OrderedColumns,
923
+ DocString
924
+ }) => ({
925
+ name: Name,
926
+ docstring: DocString,
927
+ entityType: tableEntity,
928
+ columns: OrderedColumns.map(({
929
+ Name,
930
+ Type,
931
+ DocString,
932
+ CslType,
933
+ Examples
934
+ }) => ({
935
+ name: Name,
936
+ type: CslType,
937
+ docstring: DocString,
938
+ examples: Examples
939
+ }))
940
+ })))),
941
+ functions: Object.keys(Functions).map(key => Functions[key]).map(({
942
+ Name,
943
+ Body,
944
+ DocString,
945
+ InputParameters
946
+ }) => ({
947
+ name: Name,
948
+ body: Body,
949
+ docstring: DocString,
950
+ inputParameters: InputParameters.map(inputParam => ({
951
+ name: inputParam.Name,
952
+ type: inputParam.Type,
953
+ cslType: inputParam.CslType,
954
+ cslDefaultValue: inputParam.CslDefaultValue,
955
+ columns: inputParam.Columns ? inputParam.Columns.map(col => ({
956
+ name: col.Name,
957
+ type: col.Type,
958
+ cslType: col.CslType
959
+ })) : inputParam.Columns
960
+ }))
961
+ }))
962
+ }));
963
+ const result = {
964
+ clusterType: 'Engine',
965
+ cluster: {
966
+ connectionString: clusterConnectionString,
967
+ databases: databases
968
+ },
969
+ database: databases.filter(db => db.name === databaseInContextName)[0]
970
+ };
971
+ return Promise.resolve(result);
972
+ }
973
+ getSchema() {
974
+ return Promise.resolve(this._schema);
975
+ }
976
+ getCommandInContext(document, cursorOffset) {
977
+ return this.isIntellisenseV2() ? this.getCommandInContextV2(document, cursorOffset) : this.getCommandInContextV1(document, cursorOffset);
978
+ }
979
+ getCommandAndLocationInContext(document, cursorOffset) {
980
+ // We are going to remove v1 intellisense. no use to keep parity.
981
+ if (!document || !this.isIntellisenseV2()) {
982
+ return Promise.resolve(null);
983
+ }
984
+ const script = this.parseDocumentV2(document);
985
+ const block = this.getCurrentCommandV2(script, cursorOffset);
986
+ if (!block) {
987
+ return Promise.resolve(null);
988
+ }
989
+ const start = document.positionAt(block.Start);
990
+ const end = document.positionAt(block.End);
991
+ const location = ls.Location.create(document.uri, ls.Range.create(start, end));
992
+ const text = block.Text;
993
+ return Promise.resolve({
994
+ text,
995
+ location
996
+ });
997
+ }
998
+ getCommandInContextV1(document, cursorOffset) {
999
+ this.parseDocumentV1(document, k.ParseMode.CommandTokensOnly);
1000
+ const command = this.getCurrentCommand(document, cursorOffset);
1001
+ if (!command) {
1002
+ return Promise.resolve(null);
1003
+ }
1004
+ return Promise.resolve(command.Text);
1005
+ }
1006
+ getCommandInContextV2(document, cursorOffset) {
1007
+ if (!document) {
1008
+ return Promise.resolve(null);
1009
+ }
1010
+ const script = this.parseDocumentV2(document);
1011
+ const block = this.getCurrentCommandV2(script, cursorOffset);
1012
+ if (!block) {
1013
+ return Promise.resolve(null);
1014
+ }
1015
+
1016
+ // TODO: do we need to do tricks like V1 is doing in this.getCurrentCommand?
1017
+ return Promise.resolve(block.Text);
1018
+ }
1019
+
1020
+ /**
1021
+ * Return an array of commands in document. each command contains the range and text.
1022
+ */
1023
+ getCommandsInDocument(document) {
1024
+ if (!document) {
1025
+ return Promise.resolve([]);
1026
+ }
1027
+ return this.isIntellisenseV2() ? this.getCommandsInDocumentV2(document) : this.getCommandsInDocumentV1(document);
1028
+ }
1029
+ getCommandsInDocumentV1(document) {
1030
+ this.parseDocumentV1(document, k.ParseMode.CommandTokensOnly);
1031
+ let commands = this.toArray(this._parser.Results);
1032
+ return Promise.resolve(commands.map(({
1033
+ AbsoluteStart,
1034
+ AbsoluteEnd,
1035
+ Text
1036
+ }) => ({
1037
+ absoluteStart: AbsoluteStart,
1038
+ absoluteEnd: AbsoluteEnd,
1039
+ text: Text
1040
+ })));
1041
+ }
1042
+ toPlacementStyle(formatterPlacementStyle) {
1043
+ if (!formatterPlacementStyle) {
1044
+ return undefined;
1045
+ }
1046
+ switch (formatterPlacementStyle) {
1047
+ case 'None':
1048
+ return k2.PlacementStyle.None;
1049
+ case 'NewLine':
1050
+ return k2.PlacementStyle.NewLine;
1051
+ case 'Smart':
1052
+ return k2.PlacementStyle.Smart;
1053
+ default:
1054
+ throw new Error('Unknown PlacementStyle');
1055
+ }
1056
+ }
1057
+ getFormattedCommandsInDocumentV2(document, rangeStart, rangeEnd) {
1058
+ const script = this.parseDocumentV2(document);
1059
+ const commands = this.toArray(script.Blocks).filter(command => {
1060
+ if (!command.Text || command.Text.trim() == '') return false;
1061
+ if (rangeStart == null || rangeEnd == null) return true;
1062
+
1063
+ // calculate command end position without \r\n.
1064
+ let commandEnd = command.End;
1065
+ const commandText = command.Text;
1066
+ for (let i = commandText.length - 1; i >= 0; i--) {
1067
+ if (commandText[i] != '\r' && commandText[i] != '\n') {
1068
+ break;
1069
+ } else {
1070
+ commandEnd--;
1071
+ }
1072
+ }
1073
+ if (command.Start > rangeStart && command.Start < rangeEnd) return true;
1074
+ if (commandEnd > rangeStart && commandEnd < rangeEnd) return true;
1075
+ if (command.Start <= rangeStart && commandEnd >= rangeEnd) return true;
1076
+ });
1077
+ if (commands.length === 0) {
1078
+ return {
1079
+ formattedCommands: []
1080
+ };
1081
+ }
1082
+ const formattedCommands = commands.map(command => {
1083
+ const formatterOptions = this._languageSettings.formatter;
1084
+ const formatter = Kusto.Language.Editor.FormattingOptions.Default.WithIndentationSize(formatterOptions?.indentationSize ?? 4).WithInsertMissingTokens(false).WithPipeOperatorStyle(this.toPlacementStyle(formatterOptions?.pipeOperatorStyle) ?? k2.PlacementStyle.Smart).WithSemicolonStyle(Kusto.Language.Editor.PlacementStyle.None).WithBrackettingStyle(k2.BrackettingStyle.Diagonal);
1085
+ if (rangeStart == null || rangeEnd == null || rangeStart === command.Start && rangeEnd === command.End) {
1086
+ const result = command.Service.GetFormattedText(formatter);
1087
+ return result.Text;
1088
+ }
1089
+ return command.Service.GetFormattedText(formatter).Text;
1090
+ });
1091
+ const originalRange = this.createRange(document, commands[0].Start, commands[commands.length - 1].End);
1092
+ return {
1093
+ formattedCommands,
1094
+ originalRange
1095
+ };
1096
+ }
1097
+ getCommandsInDocumentV2(document) {
1098
+ const script = this.parseDocumentV2(document);
1099
+ let commands = this.toArray(script.Blocks).filter(command => command.Text.trim() != '');
1100
+ return Promise.resolve(commands.map(({
1101
+ Start,
1102
+ End,
1103
+ Text
1104
+ }) => ({
1105
+ absoluteStart: Start,
1106
+ absoluteEnd: End,
1107
+ text: Text
1108
+ })));
1109
+ }
1110
+ getClientDirective(text) {
1111
+ let outParam = {
1112
+ v: null
1113
+ };
1114
+ const isClientDirective = k.CslCommandParser.IsClientDirective(text, outParam);
1115
+ return Promise.resolve({
1116
+ isClientDirective,
1117
+ directiveWithoutLeadingComments: outParam.v
1118
+ });
1119
+ }
1120
+ getAdminCommand(text) {
1121
+ let outParam = {
1122
+ v: null
1123
+ };
1124
+ const isAdminCommand = k.CslCommandParser.IsAdminCommand$1(text, outParam);
1125
+ return Promise.resolve({
1126
+ isAdminCommand,
1127
+ adminCommandWithoutLeadingComments: outParam.v
1128
+ });
1129
+ }
1130
+ findDefinition(document, position) {
1131
+ if (!document || !this.isIntellisenseV2()) {
1132
+ return Promise.resolve([]);
1133
+ }
1134
+ const script = this.parseDocumentV2(document);
1135
+ const cursorOffset = document.offsetAt(position);
1136
+ let currentBlock = this.getCurrentCommandV2(script, cursorOffset);
1137
+ if (!currentBlock) {
1138
+ return Promise.resolve([]);
1139
+ }
1140
+ const relatedInfo = currentBlock.Service.GetRelatedElements(document.offsetAt(position));
1141
+ const relatedElements = this.toArray(relatedInfo.Elements);
1142
+ const definition = relatedElements[0];
1143
+ if (!definition) {
1144
+ return Promise.resolve([]);
1145
+ }
1146
+ const start = document.positionAt(definition.Start);
1147
+ const end = document.positionAt(definition.End);
1148
+ const range = ls.Range.create(start, end);
1149
+ const location = ls.Location.create(document.uri, range);
1150
+ return Promise.resolve([location]);
1151
+ }
1152
+ findReferences(document, position) {
1153
+ if (!document || !this.isIntellisenseV2()) {
1154
+ return Promise.resolve([]);
1155
+ }
1156
+ const script = this.parseDocumentV2(document);
1157
+ const cursorOffset = document.offsetAt(position);
1158
+ let currentBlock = this.getCurrentCommandV2(script, cursorOffset);
1159
+ if (!currentBlock) {
1160
+ return Promise.resolve([]);
1161
+ }
1162
+ const relatedInfo = currentBlock.Service.GetRelatedElements(document.offsetAt(position));
1163
+ const relatedElements = this.toArray(relatedInfo.Elements);
1164
+ if (!relatedElements || relatedElements.length == 0) {
1165
+ return Promise.resolve([]);
1166
+ }
1167
+ const references = relatedElements.map(relatedElement => {
1168
+ const start = document.positionAt(relatedElement.Start);
1169
+ const end = document.positionAt(relatedElement.End);
1170
+ const range = ls.Range.create(start, end);
1171
+ const location = ls.Location.create(document.uri, range);
1172
+ return location;
1173
+ });
1174
+ return Promise.resolve(references);
1175
+ }
1176
+ getQueryParams(document, cursorOffset) {
1177
+ if (!document || !this.isIntellisenseV2()) {
1178
+ return Promise.resolve([]);
1179
+ }
1180
+ this.parseDocumentV2(document);
1181
+ const parsedAndAnalyzed = this.parseAndAnalyze(document, cursorOffset);
1182
+ const queryParamStatements = this.toArray(parsedAndAnalyzed.Syntax.GetDescendants(Kusto.Language.Syntax.QueryParametersStatement));
1183
+ if (!queryParamStatements || queryParamStatements.length == 0) {
1184
+ return Promise.resolve([]);
1185
+ }
1186
+ const queryParams = [];
1187
+ queryParamStatements.forEach(paramStatement => {
1188
+ paramStatement.WalkElements(el => el.ReferencedSymbol && el.ReferencedSymbol.Type ? queryParams.push({
1189
+ name: el.ReferencedSymbol.Name,
1190
+ type: el.ReferencedSymbol.Type.Name
1191
+ }) : undefined);
1192
+ });
1193
+ return Promise.resolve(queryParams);
1194
+ }
1195
+ getRenderInfo(document, cursorOffset) {
1196
+ const parsedAndAnalyzed = this.parseAndAnalyze(document, cursorOffset);
1197
+ if (!parsedAndAnalyzed) {
1198
+ return Promise.resolve(undefined);
1199
+ }
1200
+ const renderStatements = this.toArray(parsedAndAnalyzed.Syntax.GetDescendants(Kusto.Language.Syntax.RenderOperator));
1201
+ if (!renderStatements || renderStatements.length === 0) {
1202
+ return Promise.resolve(undefined);
1203
+ }
1204
+
1205
+ // assuming a single render statement
1206
+ const renderStatement = renderStatements[0];
1207
+
1208
+ // Start and end relative to block start.
1209
+ const startOffset = renderStatement.TextStart;
1210
+ const endOffset = renderStatement.End;
1211
+ const visualization = renderStatement.ChartType.ValueText;
1212
+ const withClause = renderStatement.WithClause;
1213
+ if (!withClause) {
1214
+ const info = {
1215
+ options: {
1216
+ visualization
1217
+ },
1218
+ location: {
1219
+ startOffset,
1220
+ endOffset
1221
+ }
1222
+ };
1223
+ return Promise.resolve(info);
1224
+ }
1225
+ const properties = this.toArray(withClause.Properties);
1226
+ const props = properties.reduce((prev, property) => {
1227
+ const name = property.Element$1.Name.SimpleName;
1228
+ switch (name) {
1229
+ case 'xcolumn':
1230
+ const value = property.Element$1.Expression.ReferencedSymbol.Name;
1231
+ prev[name] = value;
1232
+ break;
1233
+ case 'ycolumns':
1234
+ case 'anomalycolumns':
1235
+ const nameNodes = this.toArray(property.Element$1.Expression.Names);
1236
+ const values = nameNodes.map(nameNode => nameNode.Element$1.SimpleName);
1237
+ prev[name] = values;
1238
+ break;
1239
+ case 'ymin':
1240
+ case 'ymax':
1241
+ const numericVal = parseFloat(property.Element$1.Expression.ConstantValue);
1242
+ prev[name] = numericVal;
1243
+ break;
1244
+ case 'title':
1245
+ case 'xtitle':
1246
+ case 'ytitle':
1247
+ case 'visualization':
1248
+ case 'series':
1249
+ const strVal = property.Element$1.Expression.ConstantValue;
1250
+ prev[name] = strVal;
1251
+ break;
1252
+ case 'xaxis':
1253
+ case 'yaxis':
1254
+ const scale = property.Element$1.Expression.ConstantValue;
1255
+ prev[name] = scale;
1256
+ break;
1257
+ case 'legend':
1258
+ const legend = property.Element$1.Expression.ConstantValue;
1259
+ prev[name] = legend;
1260
+ break;
1261
+ case 'ysplit':
1262
+ const split = property.Element$1.Expression.ConstantValue;
1263
+ prev[name] = split;
1264
+ break;
1265
+ case 'accumulate':
1266
+ const accumulate = property.Element$1.Expression.ConstantValue;
1267
+ prev[name] = accumulate;
1268
+ break;
1269
+ case 'kind':
1270
+ const val = property.Element$1.Expression.ConstantValue;
1271
+ prev[name] = val;
1272
+ break;
1273
+ default:
1274
+ assertNever(name);
1275
+ }
1276
+ return prev;
1277
+ }, {});
1278
+ const renderOptions = {
1279
+ visualization,
1280
+ ...props
1281
+ };
1282
+ const renderInfo = {
1283
+ options: renderOptions,
1284
+ location: {
1285
+ startOffset,
1286
+ endOffset
1287
+ }
1288
+ };
1289
+ return Promise.resolve(renderInfo);
1290
+ }
1291
+ getReferencedSymbols(document, offset) {
1292
+ const parsedAndAnalyzed = this.parseAndAnalyze(document, offset);
1293
+ if (!parsedAndAnalyzed) {
1294
+ Promise.resolve([]);
1295
+ }
1296
+
1297
+ // We take all referenced symbols in the query
1298
+ const referencedSymbols = this.toArray(parsedAndAnalyzed.Syntax.GetDescendants(Kusto.Language.Syntax.Expression)).filter(expression => expression.ReferencedSymbol !== null).map(x => x.ReferencedSymbol);
1299
+ const result = referencedSymbols.map(sym => ({
1300
+ name: sym.Name,
1301
+ kind: symbolKindToName[sym.Kind] ?? `${sym.Kind}`,
1302
+ display: sym.Display
1303
+ }));
1304
+ return Promise.resolve(result);
1305
+ }
1306
+ getReferencedGlobalParams(document, cursorOffset) {
1307
+ const parsedAndAnalyzed = this.parseAndAnalyze(document, cursorOffset);
1308
+ if (!parsedAndAnalyzed) {
1309
+ Promise.resolve([]);
1310
+ }
1311
+
1312
+ // We take the ambient parameters
1313
+ const ambientParameters = this.toArray(this._kustoJsSchemaV2.Parameters);
1314
+
1315
+ // We take all referenced symbols in the query
1316
+ const referencedSymbols = this.toArray(parsedAndAnalyzed.Syntax.GetDescendants(Kusto.Language.Syntax.Expression)).filter(expression => expression.ReferencedSymbol !== null).map(x => x.ReferencedSymbol);
1317
+
1318
+ // The Intersection between them is the ambient parameters that are used in the query.
1319
+ // Note: Ideally we would use Set here (or at least array.Include), but were' compiling down to es2015.
1320
+ const intersection = referencedSymbols.filter(referencedSymbol => ambientParameters.filter(ambientParameter => ambientParameter === referencedSymbol).length > 0);
1321
+ const result = intersection.map(param => ({
1322
+ name: param.Name,
1323
+ type: param.Type.Name
1324
+ }));
1325
+ return Promise.resolve(result);
1326
+ }
1327
+ getGlobalParams(document) {
1328
+ if (!this.isIntellisenseV2()) {
1329
+ return Promise.resolve([]);
1330
+ }
1331
+ const params = this.toArray(this._kustoJsSchemaV2.Parameters);
1332
+ const result = params.map(param => ({
1333
+ name: param.Name,
1334
+ type: param.Type.Name
1335
+ }));
1336
+ return Promise.resolve(result);
1337
+ }
1338
+ doRename(document, position, newName) {
1339
+ if (!document || !this.isIntellisenseV2()) {
1340
+ return Promise.resolve(undefined);
1341
+ }
1342
+ const script = this.parseDocumentV2(document);
1343
+ const cursorOffset = document.offsetAt(position);
1344
+ let currentBLock = this.getCurrentCommandV2(script, cursorOffset);
1345
+ if (!currentBLock) {
1346
+ return Promise.resolve(undefined);
1347
+ }
1348
+ const relatedInfo = currentBLock.Service.GetRelatedElements(document.offsetAt(position));
1349
+ const relatedElements = this.toArray(relatedInfo.Elements);
1350
+ const declarations = relatedElements.filter(e => e.Kind == k2.RelatedElementKind.Declaration);
1351
+
1352
+ // A declaration must be one of the elements
1353
+ if (!declarations || declarations.length == 0) {
1354
+ return Promise.resolve(undefined);
1355
+ }
1356
+ const edits = relatedElements.map(edit => {
1357
+ const start = document.positionAt(edit.Start);
1358
+ const end = document.positionAt(edit.End);
1359
+ const range = ls.Range.create(start, end);
1360
+ return ls.TextEdit.replace(range, newName);
1361
+ });
1362
+
1363
+ // create a workspace edit
1364
+ const workspaceEdit = {
1365
+ changes: {
1366
+ [document.uri]: edits
1367
+ }
1368
+ };
1369
+ return Promise.resolve(workspaceEdit);
1370
+ }
1371
+ doHover(document, position) {
1372
+ if (!document || !this.isIntellisenseV2()) {
1373
+ return Promise.resolve(undefined);
1374
+ }
1375
+ const script = this.parseDocumentV2(document);
1376
+ const cursorOffset = document.offsetAt(position);
1377
+ let currentBLock = this.getCurrentCommandV2(script, cursorOffset);
1378
+ if (!currentBLock) {
1379
+ return Promise.resolve(undefined);
1380
+ }
1381
+ const isSupported = currentBLock.Service.IsFeatureSupported(k2.CodeServiceFeatures.QuickInfo, cursorOffset);
1382
+ if (!isSupported) {
1383
+ return Promise.resolve(undefined);
1384
+ }
1385
+ const quickInfo = currentBLock.Service.GetQuickInfo(cursorOffset);
1386
+ if (!quickInfo || !quickInfo.Items) {
1387
+ return Promise.resolve(undefined);
1388
+ }
1389
+ let items = this.toArray(quickInfo.Items);
1390
+ if (!items) {
1391
+ return Promise.resolve(undefined);
1392
+ }
1393
+
1394
+ // Errors, Warnings and Suggestions are already shown in getDiagnostics. we don't want them in doHover.
1395
+ items = items.filter(item => item.Kind !== k2.QuickInfoKind.Error && item.Kind !== k2.QuickInfoKind.Suggestion && item.Kind !== k2.QuickInfoKind.Warning);
1396
+ const itemsText = items.map(item => item.Text.replace('\n\n', '\n* * *\n'));
1397
+ // separate items by horizontal line.
1398
+ const text = itemsText.join('\n* * *\n');
1399
+ // Instead of just an empty line between the first line (the signature) and the second line (the description)
1400
+ // add an horizontal line (* * * in markdown) between them.
1401
+ return Promise.resolve({
1402
+ contents: text
1403
+ });
1404
+ }
1405
+
1406
+ //#region dummy schema for manual testing
1407
+ static get dummySchema() {
1408
+ const database = {
1409
+ majorVersion: 0,
1410
+ minorVersion: 0,
1411
+ name: 'Kuskus',
1412
+ tables: [{
1413
+ name: 'KustoLogs',
1414
+ columns: [{
1415
+ name: 'Source',
1416
+ type: 'string'
1417
+ }, {
1418
+ name: 'Timestamp',
1419
+ type: 'datetime'
1420
+ }, {
1421
+ name: 'Directory',
1422
+ type: 'string'
1423
+ }],
1424
+ docstring: 'A dummy description to test that docstring shows as expected when hovering over a table'
1425
+ }],
1426
+ functions: [{
1427
+ name: 'HowBig',
1428
+ inputParameters: [{
1429
+ name: 'T',
1430
+ columns: [{
1431
+ name: 'Timestamp',
1432
+ type: 'System.DateTime',
1433
+ cslType: 'datetime'
1434
+ }]
1435
+ }],
1436
+ docstring: 'A dummy description to test that docstring shows as expected when hovering over a function',
1437
+ body: "{\r\n union \r\n (T | count | project V='Volume', Metric = strcat(Count/1e9, ' Billion records')),\r\n (T | summarize FirstRecord=min(Timestamp)| project V='Volume', Metric = strcat(toint((now()-FirstRecord)/1d), ' Days of data (from: ', format_datetime(FirstRecord, 'yyyy-MM-dd'),')')),\r\n (T | where Timestamp > ago(1h) | count | project V='Velocity', Metric = strcat(Count/1e6, ' Million records / hour')),\r\n (T | summarize Latency=now()-max(Timestamp) | project V='Velocity', Metric = strcat(Latency / 1sec, ' seconds latency')),\r\n (T | take 1 | project V='Variety', Metric=tostring(pack_all()))\r\n | order by V \r\n}"
1438
+ }, {
1439
+ name: 'FindCIDPast24h',
1440
+ inputParameters: [{
1441
+ name: 'clientActivityId',
1442
+ type: 'System.String',
1443
+ cslType: 'string'
1444
+ }],
1445
+ body: '{ KustoLogs | where Timestamp > now(-1d) | where ClientActivityId == clientActivityId} '
1446
+ }]
1447
+ };
1448
+ const languageServiceSchema = {
1449
+ clusterType: 'Engine',
1450
+ cluster: {
1451
+ connectionString: 'https://kuskus.kusto.windows.net;fed=true',
1452
+ databases: [database]
1453
+ },
1454
+ database: database
1455
+ };
1456
+ return languageServiceSchema;
1457
+ }
1458
+ //#endregion
1459
+
1460
+ static convertToEntityDataType(kustoType) {}
1461
+ /**
1462
+ * We do not want to expose Bridge.Net generated schema, so we expose a cleaner javascript schema.
1463
+ * Here it gets converted to the bridge.Net schema
1464
+ * @param schema Language Service schema
1465
+ */
1466
+ static convertToKustoJsSchema(schema) {
1467
+ switch (schema.clusterType) {
1468
+ case 'Engine':
1469
+ const currentDatabaseName = schema.database ? schema.database.name : undefined;
1470
+ const kCluster = new k.KustoIntelliSenseClusterEntity();
1471
+ let kDatabaseInContext = undefined;
1472
+ kCluster.ConnectionString = schema.cluster.connectionString;
1473
+ const databases = [];
1474
+ schema.cluster.databases.forEach(database => {
1475
+ const kDatabase = new k.KustoIntelliSenseDatabaseEntity();
1476
+ kDatabase.Name = database.name;
1477
+ const tables = [];
1478
+ database.tables.forEach(table => {
1479
+ const kTable = new k.KustoIntelliSenseTableEntity();
1480
+ kTable.Name = table.name;
1481
+ const cols = [];
1482
+ table.columns.forEach(column => {
1483
+ const kColumn = new k.KustoIntelliSenseColumnEntity();
1484
+ kColumn.Name = column.name;
1485
+ kColumn.TypeCode = k.EntityDataType[getEntityDataTypeFromCslType(column.type)];
1486
+ cols.push(kColumn);
1487
+ });
1488
+ kTable.Columns = new Bridge.ArrayEnumerable(cols);
1489
+ tables.push(kTable);
1490
+ });
1491
+ const functions = [];
1492
+ database.functions.forEach(fn => {
1493
+ const kFunction = new k.KustoIntelliSenseFunctionEntity();
1494
+ kFunction.Name = fn.name, kFunction.CallName = getCallName(fn), kFunction.Expression = getExpression(fn), functions.push(kFunction);
1495
+ });
1496
+ kDatabase.Tables = new Bridge.ArrayEnumerable(tables);
1497
+ kDatabase.Functions = new Bridge.ArrayEnumerable(functions);
1498
+ databases.push(kDatabase);
1499
+ if (database.name == currentDatabaseName) {
1500
+ kDatabaseInContext = kDatabase;
1501
+ }
1502
+ });
1503
+ kCluster.Databases = new Bridge.ArrayEnumerable(databases);
1504
+ const kSchema = new k.KustoIntelliSenseQuerySchema(kCluster, kDatabaseInContext);
1505
+ return kSchema;
1506
+ case 'ClusterManager':
1507
+ const accounts = schema.accounts.map(account => {
1508
+ const kAccount = new k.KustoIntelliSenseAccountEntity();
1509
+ kAccount.Name = account;
1510
+ return kAccount;
1511
+ });
1512
+ const services = schema.services.map(service => {
1513
+ const kService = new k.KustoIntelliSenseServiceEntity();
1514
+ kService.Name = service;
1515
+ return kService;
1516
+ });
1517
+ const connectionString = schema.connectionString;
1518
+ const result = {
1519
+ accounts,
1520
+ services,
1521
+ connectionString
1522
+ };
1523
+ return result;
1524
+ case 'DataManagement':
1525
+ return undefined;
1526
+ default:
1527
+ return assertNever(schema);
1528
+ }
1529
+ }
1530
+
1531
+ /**
1532
+ * Returns something like '(x: string, y: datetime)'
1533
+ * @param params scalar parameters
1534
+ */
1535
+ static scalarParametersToSignature(params) {
1536
+ const signatureWithoutParens = params.map(param => `${param.name}: ${param.cslType}`).join(', ');
1537
+ return `(${signatureWithoutParens})`;
1538
+ }
1539
+
1540
+ /**
1541
+ * Returns something like '(x: string, T: (y: int))'
1542
+ * @param params input parameters (tabular or scalar)
1543
+ */
1544
+ static inputParameterToSignature(params) {
1545
+ const signatureWithoutParens = params.map(param => {
1546
+ if (param.columns) {
1547
+ const tableSignature = this.scalarParametersToSignature(param.columns);
1548
+ return `${param.name}: ${tableSignature}`;
1549
+ } else {
1550
+ return `${param.name}: ${param.cslType}`;
1551
+ }
1552
+ }).join(', ');
1553
+ return `(${signatureWithoutParens})`;
1554
+ }
1555
+
1556
+ /**
1557
+ * converts a function definition to a let statement.
1558
+ * @param fn function
1559
+ */
1560
+ static toLetStatement(fn) {
1561
+ const signature = this.inputParameterToSignature(fn.inputParameters);
1562
+ return `let ${fn.name} = ${signature} ${fn.body}`;
1563
+ }
1564
+ static createColumnSymbol(col) {
1565
+ return new sym.ColumnSymbol(col.name, sym.ScalarTypes.GetSymbol(getCslTypeNameFromClrType(col.type)), col.docstring, null, null, col.examples ? KustoLanguageService.toBridgeList(col.examples) : null);
1566
+ }
1567
+ static createParameterSymbol(param) {
1568
+ const paramSymbol = Kusto.Language.Symbols.ScalarTypes.GetSymbol(getCslTypeNameFromClrType(param.type));
1569
+ return new sym.ParameterSymbol(param.name, paramSymbol, null);
1570
+ }
1571
+ static createTabularParameterSymbol(param) {
1572
+ const columnSymbols = param.columns.map(col => KustoLanguageService.createColumnSymbol(col));
1573
+ const para = new Kusto.Language.Symbols.TableSymbol.$ctor4(param.name, columnSymbols);
1574
+ return new sym.ParameterSymbol(param.name, para, param.docstring);
1575
+ }
1576
+ static createParameter(param) {
1577
+ if (!param.columns) {
1578
+ const paramSymbol = Kusto.Language.Symbols.ScalarTypes.GetSymbol(getCslTypeNameFromClrType(param.type));
1579
+ const expression = param.cslDefaultValue && typeof param.cslDefaultValue === 'string' ? parsing.QueryParser.ParseLiteral$1(param.cslDefaultValue) : undefined;
1580
+ return new sym.Parameter.$ctor3(param.name, paramSymbol, null, null, null, false, null, 1, 1, expression, null);
1581
+ }
1582
+ if (param.columns.length == 0) {
1583
+ return new sym.Parameter.ctor(param.name, sym.ParameterTypeKind.Tabular, sym.ArgumentKind.Expression, null, null, false, null, 1, 1, null, null);
1584
+ }
1585
+ const argumentType = new sym.TableSymbol.ctor(param.columns.map(col => KustoLanguageService.createColumnSymbol(col)));
1586
+ return new sym.Parameter.$ctor2(param.name, argumentType);
1587
+ }
1588
+ static convertToDatabaseSymbol(db) {
1589
+ const createFunctionSymbol = fn => {
1590
+ const parameters = fn.inputParameters.map(param => KustoLanguageService.createParameter(param));
1591
+
1592
+ // TODO: handle outputColumns (right now it doesn't seem to be implemented for any function).
1593
+ return new sym.FunctionSymbol.$ctor14(fn.name, fn.body, KustoLanguageService.toBridgeList(parameters), fn.docstring);
1594
+ };
1595
+ const createTableSymbol = tbl => {
1596
+ const columnSymbols = tbl.columns.map(col => KustoLanguageService.createColumnSymbol(col));
1597
+ let symbol = new sym.TableSymbol.$ctor4(tbl.name, columnSymbols);
1598
+ symbol.Description = tbl.docstring;
1599
+ switch (tbl.entityType) {
1600
+ case 'MaterializedViewTable':
1601
+ const mvQuery = tbl.mvQuery ?? null;
1602
+ symbol = new sym.MaterializedViewSymbol.$ctor2(tbl.name, symbol.Columns, mvQuery, tbl.docstring);
1603
+ symbol = symbol.WithIsMaterializedView(true);
1604
+ break;
1605
+ case 'ExternalTable':
1606
+ symbol = symbol.WithIsExternal(true);
1607
+ break;
1608
+ }
1609
+ return symbol;
1610
+ };
1611
+ const createDatabaseSymbol = db => {
1612
+ const tableSymbols = db.tables ? db.tables.map(tbl => createTableSymbol(tbl)) : [];
1613
+ const functionSymbols = db.functions ? db.functions.map(fun => createFunctionSymbol(fun)) : [];
1614
+ return new sym.DatabaseSymbol.ctor(db.name, tableSymbols.concat(functionSymbols));
1615
+ };
1616
+ const databaseSymbol = createDatabaseSymbol(db);
1617
+ return databaseSymbol;
1618
+ }
1619
+ convertToKustoJsSchemaV2(schema) {
1620
+ let cached = this._schemaCache[schema.cluster.connectionString];
1621
+
1622
+ // create a cache entry for the cluster if non yet exists.
1623
+ if (!cached) {
1624
+ this._schemaCache[schema.cluster.connectionString] = {};
1625
+ cached = this._schemaCache[schema.cluster.connectionString];
1626
+ }
1627
+
1628
+ // Remove deleted databases from cache
1629
+ const schemaDbLookup = schema.cluster.databases.reduce((prev, curr) => prev[curr.name] = curr, {});
1630
+ Object.keys(cached).map(dbName => {
1631
+ if (!schemaDbLookup[dbName]) {
1632
+ delete cached.dbName;
1633
+ }
1634
+ });
1635
+ let globalState = GlobalState.Default;
1636
+ const currentDatabaseName = schema.database ? schema.database.name : undefined;
1637
+ let databaseInContext = undefined;
1638
+
1639
+ // Update out-of-data databases to cache
1640
+ const databases = schema.cluster.databases.map(db => {
1641
+ const shouldIncludeFunctions = db.name === currentDatabaseName;
1642
+ const cachedDb = cached[db.name];
1643
+ // This is an older version than we have, or we need to parse functions.
1644
+ if (!cachedDb || cachedDb.database.majorVersion < db.majorVersion || shouldIncludeFunctions && !cachedDb.includesFunctions) {
1645
+ // only add functions for the database in context (it's very time consuming)
1646
+
1647
+ const databaseSymbol = KustoLanguageService.convertToDatabaseSymbol(db);
1648
+ cached[db.name] = {
1649
+ database: db,
1650
+ symbol: databaseSymbol,
1651
+ includesFunctions: shouldIncludeFunctions
1652
+ };
1653
+ }
1654
+ const databaseSymbol = cached[db.name].symbol;
1655
+ if (db.name === currentDatabaseName) {
1656
+ databaseInContext = databaseSymbol;
1657
+ }
1658
+ return databaseSymbol;
1659
+ });
1660
+
1661
+ // Replace new URL due to polyfill issue in IE
1662
+ // const hostname = new URL(schema.cluster.connectionString.split(';')[0]).hostname;
1663
+ const hostname = schema.cluster.connectionString.match(/(.*\/\/)?([^\/;]*)/)[2];
1664
+ const clusterName = hostname.split('.')[0];
1665
+ const clusterSymbol = new sym.ClusterSymbol.ctor(clusterName, databases);
1666
+ globalState = globalState.WithCluster(clusterSymbol);
1667
+ if (databaseInContext) {
1668
+ globalState = globalState.WithDatabase(databaseInContext);
1669
+ }
1670
+
1671
+ // Inject global scalar parameters to global scope.
1672
+ const scalarParameters = (schema.globalScalarParameters ?? []).map(param => KustoLanguageService.createParameterSymbol(param));
1673
+
1674
+ // Inject global tabular parameters to global scope.
1675
+ let tabularParameters = (schema.globalTabularParameters ?? []).map(param => KustoLanguageService.createTabularParameterSymbol(param));
1676
+ if (tabularParameters.length || scalarParameters.length) {
1677
+ globalState = globalState.WithParameters(KustoLanguageService.toBridgeList([...scalarParameters, ...tabularParameters]));
1678
+ }
1679
+ return globalState;
1680
+ }
1681
+ getClassificationsFromParseResult(offset = 0) {
1682
+ const classifications = this.toArray(this._parser.Results).map(command => this.toArray(command.Tokens)).reduce((prev, curr) => prev.concat(curr), []).map(cslCommandToken => {
1683
+ const range = new k2.ClassifiedRange(this.tokenKindToClassificationKind(cslCommandToken.TokenKind), cslCommandToken.AbsoluteStart + offset, cslCommandToken.Length);
1684
+ return range;
1685
+ });
1686
+ return classifications;
1687
+ }
1688
+
1689
+ /**
1690
+ * trim trailing newlines from range
1691
+ */
1692
+ static trimTrailingNewlineFromRange(textInRange, rangeStartOffset, document, range) {
1693
+ let currentIndex = textInRange.length - 1;
1694
+ while (textInRange[currentIndex] === '\r' || textInRange[currentIndex] === '\n') {
1695
+ --currentIndex;
1696
+ }
1697
+ const newEndOffset = rangeStartOffset + currentIndex + 1;
1698
+ const newEndPosition = document.positionAt(newEndOffset);
1699
+ const newRange = ls.Range.create(range.start, newEndPosition);
1700
+ return newRange;
1701
+ }
1702
+
1703
+ /**
1704
+ * Maps numbers to strings, such that if a>b numerically, f(a)>f(b) lexicographically.
1705
+ * 1 -> "a", 26 -> "z", 27 -> "za", 28 -> "zb", 52 -> "zz", 53 ->"zza"
1706
+ * @param order - The number to be converted to a sorting-string. order should start at 1.
1707
+ * @returns A string repenting the order.
1708
+ */
1709
+ getSortText(order) {
1710
+ if (order <= 0) {
1711
+ throw new RangeError(`order should be a number >= 1. instead got ${order}`);
1712
+ }
1713
+ let sortText = '';
1714
+ let numCharacters = 26; // "z" - "a" + 1;
1715
+
1716
+ let div = Math.floor(order / numCharacters);
1717
+ for (let i = 0; i < div; ++i) {
1718
+ sortText += 'z';
1719
+ }
1720
+ let reminder = order % numCharacters;
1721
+ if (reminder > 0) {
1722
+ sortText += String.fromCharCode(96 + reminder);
1723
+ }
1724
+ return sortText;
1725
+ }
1726
+
1727
+ /**
1728
+ * ParseTextV1 parses the given text with the given parse mode.
1729
+ * Additionally - it will make sure not to provide rules provider for non-engine clusters
1730
+ * since the only rules provider parse can handle is the engine's. It will try to look for function
1731
+ * definitions to colorize and will throw since they're not there.
1732
+ * @param text
1733
+ * @param parseMode
1734
+ */
1735
+ parseTextV1(text, parseMode) {
1736
+ this._parser.Parse(this._schema.clusterType === 'Engine' ? this._rulesProvider : null, text, parseMode);
1737
+ }
1738
+ parseDocumentV1(document, parseMode) {
1739
+ // already parsed a later version, or better parse mode for this uri
1740
+ if (this._parsePropertiesV1 && !this._parsePropertiesV1.isParseNeeded(document, this._rulesProvider, parseMode)) {
1741
+ return;
1742
+ }
1743
+ this.parseTextV1(document.getText(), parseMode);
1744
+ this._parsePropertiesV1 = new ParseProperties(document.version, document.uri, this._rulesProvider, parseMode);
1745
+ }
1746
+ parseDocumentV2(document) {
1747
+ if (this._parsePropertiesV2 && !this._parsePropertiesV2.isParseNeeded(document, this._rulesProvider)) {
1748
+ return this._script;
1749
+ }
1750
+ if (!this._script) {
1751
+ this._script = k2.CodeScript.From$1(document.getText(), this._kustoJsSchemaV2);
1752
+ } else {
1753
+ this._script = this._script.WithText(document.getText());
1754
+ }
1755
+ this._parsePropertiesV2 = new ParseProperties(document.version, document.uri);
1756
+ return this._script;
1757
+ }
1758
+
1759
+ /**
1760
+ * Return the CslCommand that wraps the caret location, or undefined if caret is outside any command
1761
+ * @param document the document to extract the current command from
1762
+ * @param caretAbsolutePosition absolute caret position
1763
+ */
1764
+ getCurrentCommand(document, caretAbsolutePosition) {
1765
+ let commands = this.toArray(this._parser.Results);
1766
+ let command = commands.filter(command => command.AbsoluteStart <= caretAbsolutePosition && command.AbsoluteEnd >= caretAbsolutePosition)[0];
1767
+
1768
+ // There is an edge case when cursor appears at the end of the command
1769
+ // which is not yet considered to be part of the parsed command (therefore: +1 for the AbsoluteEdit property)
1770
+ if (!command) {
1771
+ command = commands.filter(command => command.AbsoluteStart <= caretAbsolutePosition && command.AbsoluteEnd + 1 >= caretAbsolutePosition)[0];
1772
+
1773
+ // If we have 2 newlines in the end of the text the cursor is _probably_ at the end of the text
1774
+ // which this means that we're not actually standing on any command. Thus return null.
1775
+ if (!command || command.Text.endsWith('\r\n\r\n')) {
1776
+ return null;
1777
+ }
1778
+ }
1779
+ return command;
1780
+ }
1781
+ getCurrentCommandV2(script, offset) {
1782
+ let block = script.GetBlockAtPosition(offset);
1783
+ return block;
1784
+ }
1785
+ getTextToInsert(rule, option) {
1786
+ const beforeApplyInfo = rule.GetBeforeApplyInfo(option.Value);
1787
+ const afterApplyInfo = rule.GetAfterApplyInfo(option.Value);
1788
+
1789
+ // this is the basic text to be inserted,
1790
+ // but we still need to figure out where the cursor will end up after completion is applied.
1791
+ let insertText = beforeApplyInfo.Text || '' + option.Value + afterApplyInfo.Text || '';
1792
+ let insertTextFormat = ls.InsertTextFormat.PlainText;
1793
+ const snippetFinalTabStop = '$0';
1794
+ if (afterApplyInfo.OffsetToken && afterApplyInfo.OffsetPosition) {
1795
+ const tokenOffset = insertText.indexOf(afterApplyInfo.OffsetToken);
1796
+ if (tokenOffset >= 0) {
1797
+ insertText = this.insertToString(insertText, snippetFinalTabStop, tokenOffset - insertText.length + afterApplyInfo.OffsetPosition);
1798
+ insertTextFormat = ls.InsertTextFormat.Snippet;
1799
+ }
1800
+ } else if (afterApplyInfo.OffsetPosition) {
1801
+ // We only handle negative offsets
1802
+ insertText = this.insertToString(insertText, snippetFinalTabStop, afterApplyInfo.OffsetPosition);
1803
+ insertTextFormat = ls.InsertTextFormat.Snippet;
1804
+ }
1805
+ return {
1806
+ insertText,
1807
+ insertTextFormat
1808
+ };
1809
+ }
1810
+
1811
+ /**
1812
+ * create a new string with stringToInsert inserted at offsetFromEnd in originalString.
1813
+ * @param originalString string to insert to
1814
+ * @param stringToInsert string to insert
1815
+ * @param offsetFromEnd a negative number that will represent offset to the left. 0 means simple concat
1816
+ */
1817
+ insertToString(originalString, stringToInsert, offsetFromEnd) {
1818
+ let index = originalString.length + offsetFromEnd;
1819
+ if (offsetFromEnd >= 0 || index < 0) {
1820
+ return originalString; // Cannot insert before or after the string
1821
+ }
1822
+
1823
+ let before = originalString.substring(0, index);
1824
+ let after = originalString.substring(index);
1825
+ return before + stringToInsert + after;
1826
+ }
1827
+ getCommandWithoutLastWord(text) {
1828
+ const lastWordRegex = XRegExp('[\\w_]*$', 's');
1829
+ return text.replace(lastWordRegex, '');
1830
+ }
1831
+ createRulesProvider(schema, clusterType) {
1832
+ let queryParameters = new (List(String))();
1833
+ let availableClusters = new (List(String))();
1834
+ this._parser = new k.CslCommandParser();
1835
+ if (clusterType == 'Engine') {
1836
+ const engineSchema = schema;
1837
+ this._rulesProvider = this._languageSettings && this._languageSettings.includeControlCommands ? new k.CslIntelliSenseRulesProvider.$ctor1(engineSchema.Cluster, engineSchema, queryParameters, availableClusters, null, true, true) : new k.CslQueryIntelliSenseRulesProvider.$ctor1(engineSchema.Cluster, engineSchema, queryParameters, availableClusters, null, null, null);
1838
+ return;
1839
+ }
1840
+ if (clusterType === 'DataManagement') {
1841
+ this._rulesProvider = new k.DataManagerIntelliSenseRulesProvider(null);
1842
+ return;
1843
+ }
1844
+
1845
+ // This is a cluster manger
1846
+ const {
1847
+ accounts,
1848
+ services,
1849
+ connectionString
1850
+ } = schema;
1851
+ new k.KustoIntelliSenseAccountEntity();
1852
+ new k.KustoIntelliSenseServiceEntity();
1853
+ this._rulesProvider = new k.ClusterManagerIntelliSenseRulesProvider.$ctor1(new Bridge.ArrayEnumerable(accounts), new Bridge.ArrayEnumerable(services), connectionString);
1854
+ }
1855
+ _kustoKindtolsKind = {
1856
+ [k.OptionKind.None]: ls.CompletionItemKind.Interface,
1857
+ [k.OptionKind.Operator]: ls.CompletionItemKind.Method,
1858
+ [k.OptionKind.Command]: ls.CompletionItemKind.Method,
1859
+ [k.OptionKind.Service]: ls.CompletionItemKind.Class,
1860
+ [k.OptionKind.Policy]: ls.CompletionItemKind.Reference,
1861
+ [k.OptionKind.Database]: ls.CompletionItemKind.Class,
1862
+ [k.OptionKind.Table]: ls.CompletionItemKind.Class,
1863
+ [k.OptionKind.DataType]: ls.CompletionItemKind.Class,
1864
+ [k.OptionKind.Literal]: ls.CompletionItemKind.Property,
1865
+ [k.OptionKind.Parameter]: ls.CompletionItemKind.Variable,
1866
+ [k.OptionKind.IngestionMapping]: ls.CompletionItemKind.Variable,
1867
+ [k.OptionKind.ExpressionFunction]: ls.CompletionItemKind.Variable,
1868
+ [k.OptionKind.Option]: ls.CompletionItemKind.Interface,
1869
+ [k.OptionKind.OptionKind]: ls.CompletionItemKind.Interface,
1870
+ [k.OptionKind.OptionRender]: ls.CompletionItemKind.Interface,
1871
+ [k.OptionKind.Column]: ls.CompletionItemKind.Function,
1872
+ [k.OptionKind.ColumnString]: ls.CompletionItemKind.Field,
1873
+ [k.OptionKind.ColumnNumeric]: ls.CompletionItemKind.Field,
1874
+ [k.OptionKind.ColumnDateTime]: ls.CompletionItemKind.Field,
1875
+ [k.OptionKind.ColumnTimespan]: ls.CompletionItemKind.Field,
1876
+ [k.OptionKind.FunctionServerSide]: ls.CompletionItemKind.Field,
1877
+ [k.OptionKind.FunctionAggregation]: ls.CompletionItemKind.Field,
1878
+ [k.OptionKind.FunctionFilter]: ls.CompletionItemKind.Field,
1879
+ [k.OptionKind.FunctionScalar]: ls.CompletionItemKind.Field,
1880
+ [k.OptionKind.ClientDirective]: ls.CompletionItemKind.Enum
1881
+ };
1882
+ _kustoKindToLsKindV2 = {
1883
+ [k2.CompletionKind.AggregateFunction]: ls.CompletionItemKind.Field,
1884
+ [k2.CompletionKind.BuiltInFunction]: ls.CompletionItemKind.Field,
1885
+ [k2.CompletionKind.Cluster]: ls.CompletionItemKind.Class,
1886
+ [k2.CompletionKind.Column]: ls.CompletionItemKind.Function,
1887
+ [k2.CompletionKind.CommandPrefix]: ls.CompletionItemKind.Field,
1888
+ [k2.CompletionKind.Database]: ls.CompletionItemKind.Class,
1889
+ [k2.CompletionKind.DatabaseFunction]: ls.CompletionItemKind.Field,
1890
+ [k2.CompletionKind.Example]: ls.CompletionItemKind.Text,
1891
+ [k2.CompletionKind.Identifier]: ls.CompletionItemKind.Method,
1892
+ [k2.CompletionKind.Keyword]: ls.CompletionItemKind.Method,
1893
+ [k2.CompletionKind.LocalFunction]: ls.CompletionItemKind.Field,
1894
+ [k2.CompletionKind.MaterialiedView]: ls.CompletionItemKind.Class,
1895
+ [k2.CompletionKind.Parameter]: ls.CompletionItemKind.Variable,
1896
+ [k2.CompletionKind.Punctuation]: ls.CompletionItemKind.Interface,
1897
+ [k2.CompletionKind.QueryPrefix]: ls.CompletionItemKind.Function,
1898
+ [k2.CompletionKind.RenderChart]: ls.CompletionItemKind.Method,
1899
+ [k2.CompletionKind.ScalarInfix]: ls.CompletionItemKind.Field,
1900
+ [k2.CompletionKind.ScalarPrefix]: ls.CompletionItemKind.Field,
1901
+ [k2.CompletionKind.ScalarType]: ls.CompletionItemKind.TypeParameter,
1902
+ [k2.CompletionKind.Syntax]: ls.CompletionItemKind.Method,
1903
+ [k2.CompletionKind.Table]: ls.CompletionItemKind.Class,
1904
+ [k2.CompletionKind.TabularPrefix]: ls.CompletionItemKind.Field,
1905
+ // datatable, externaldata
1906
+ [k2.CompletionKind.TabularSuffix]: ls.CompletionItemKind.Field,
1907
+ [k2.CompletionKind.Unknown]: ls.CompletionItemKind.Interface,
1908
+ [k2.CompletionKind.Variable]: ls.CompletionItemKind.Variable,
1909
+ [k2.CompletionKind.Option]: ls.CompletionItemKind.Text,
1910
+ [k2.CompletionKind.Graph]: ls.CompletionItemKind.Class
1911
+ };
1912
+ kustoKindToLsKind(kustoKind) {
1913
+ let res = this._kustoKindtolsKind[kustoKind];
1914
+ return res ? res : ls.CompletionItemKind.Variable;
1915
+ }
1916
+ kustoKindToLsKindV2(kustoKind) {
1917
+ let res = this._kustoKindToLsKindV2[kustoKind];
1918
+ return res ? res : ls.CompletionItemKind.Variable;
1919
+ }
1920
+ createRange(document, start, end) {
1921
+ return ls.Range.create(document.positionAt(start), document.positionAt(end));
1922
+ }
1923
+ toArray(bridgeList) {
1924
+ return Bridge.toArray(bridgeList);
1925
+ }
1926
+ static toBridgeList(array) {
1927
+ // copied from bridge.js from the implementation of Enumerable.prototype.toList
1928
+ return new (System.Collections.Generic.List$1(System.Object).$ctor1)(array);
1929
+ }
1930
+ _tokenKindToClassificationKind = {
1931
+ [TokenKind.TableToken]: k2.ClassificationKind.Table,
1932
+ [TokenKind.TableColumnToken]: k2.ClassificationKind.Column,
1933
+ [TokenKind.OperatorToken]: k2.ClassificationKind.QueryOperator,
1934
+ [TokenKind.SubOperatorToken]: k2.ClassificationKind.Function,
1935
+ [TokenKind.CalculatedColumnToken]: k2.ClassificationKind.Column,
1936
+ [TokenKind.StringLiteralToken]: k2.ClassificationKind.Literal,
1937
+ [TokenKind.FunctionNameToken]: k2.ClassificationKind.Function,
1938
+ [TokenKind.UnknownToken]: k2.ClassificationKind.PlainText,
1939
+ [TokenKind.CommentToken]: k2.ClassificationKind.Comment,
1940
+ [TokenKind.PlainTextToken]: k2.ClassificationKind.PlainText,
1941
+ [TokenKind.DataTypeToken]: k2.ClassificationKind.Type,
1942
+ [TokenKind.ControlCommandToken]: k2.ClassificationKind.PlainText,
1943
+ // TODO ?
1944
+ [TokenKind.CommandPartToken]: k2.ClassificationKind.PlainText,
1945
+ // TODO ?
1946
+ [TokenKind.QueryParametersToken]: k2.ClassificationKind.QueryParameter,
1947
+ [TokenKind.CslCommandToken]: k2.ClassificationKind.Keyword,
1948
+ // TODO ?
1949
+ [TokenKind.LetVariablesToken]: k2.ClassificationKind.Identifier,
1950
+ // TODO ?
1951
+ [TokenKind.PluginToken]: k2.ClassificationKind.Function,
1952
+ [TokenKind.BracketRangeToken]: k2.ClassificationKind.Keyword,
1953
+ // TODO ?
1954
+ [TokenKind.ClientDirectiveToken]: k2.ClassificationKind.Keyword // TODO ?
1955
+ };
1956
+
1957
+ tokenKindToClassificationKind(token) {
1958
+ const conversion = this._tokenKindToClassificationKind[token];
1959
+ return conversion || k2.ClassificationKind.PlainText;
1960
+ }
1961
+ parseAndAnalyze(document, cursorOffset) {
1962
+ if (!document || !this.isIntellisenseV2()) {
1963
+ return undefined;
1964
+ }
1965
+ const script = this.parseDocumentV2(document);
1966
+ let text = script.Text;
1967
+ if (cursorOffset !== undefined) {
1968
+ let currentBlock = this.getCurrentCommandV2(script, cursorOffset);
1969
+ if (!currentBlock) {
1970
+ return undefined;
1971
+ }
1972
+ text = currentBlock.Text;
1973
+ }
1974
+ const parsedAndAnalyzed = Kusto.Language.KustoCode.ParseAndAnalyze(text, this._kustoJsSchemaV2);
1975
+ return parsedAndAnalyzed;
1976
+ }
1977
+ }
1978
+ let languageService = new KustoLanguageService(KustoLanguageService.dummySchema, {
1979
+ includeControlCommands: true,
1980
+ useIntellisenseV2: true,
1981
+ useSemanticColorization: true
1982
+ });
1983
+
1984
+ /**
1985
+ * Obtain an instance of the kusto language service.
1986
+ */
1987
+ function getKustoLanguageService() {
1988
+ return languageService;
1989
+ }
1990
+
1991
+ class KustoWorker {
1992
+ // --- model sync -----------------------
1993
+
1994
+ constructor(ctx, createData) {
1995
+ this._ctx = ctx;
1996
+ this._languageSettings = createData.languageSettings;
1997
+ this._languageService = getKustoLanguageService();
1998
+ this._languageService.configure(this._languageSettings);
1999
+ }
2000
+
2001
+ // --- language service host ---------------
2002
+
2003
+ setSchema(schema) {
2004
+ return this._languageService.setSchema(schema);
2005
+ }
2006
+ addClusterToSchema(uri, clusterName, databasesNames) {
2007
+ const document = this._getTextDocument(uri);
2008
+ if (!document) {
2009
+ console.error(`addClusterToSchema: document is ${document}. uri is ${uri}`);
2010
+ return Promise.resolve();
2011
+ }
2012
+ return this._languageService.addClusterToSchema(document, clusterName, databasesNames);
2013
+ }
2014
+ addDatabaseToSchema(uri, clusterName, databaseSchema) {
2015
+ const document = this._getTextDocument(uri);
2016
+ if (!document) {
2017
+ console.error(`addDatabaseToSchema: document is ${document}. uri is ${uri}`);
2018
+ return Promise.resolve();
2019
+ }
2020
+ return this._languageService.addDatabaseToSchema(document, clusterName, databaseSchema);
2021
+ }
2022
+ setSchemaFromShowSchema(schema, clusterConnectionString, databaseInContextName) {
2023
+ return this._languageService.setSchemaFromShowSchema(schema, clusterConnectionString, databaseInContextName);
2024
+ }
2025
+ normalizeSchema(schema, clusterConnectionString, databaseInContextName) {
2026
+ return this._languageService.normalizeSchema(schema, clusterConnectionString, databaseInContextName);
2027
+ }
2028
+ getSchema() {
2029
+ return this._languageService.getSchema();
2030
+ }
2031
+ getCommandInContext(uri, cursorOffset) {
2032
+ const document = this._getTextDocument(uri);
2033
+ if (!document) {
2034
+ console.error(`getCommandInContext: document is ${document}. uri is ${uri}`);
2035
+ return null;
2036
+ }
2037
+ const commandInContext = this._languageService.getCommandInContext(document, cursorOffset);
2038
+ if (commandInContext === undefined) {
2039
+ return null;
2040
+ }
2041
+ return commandInContext;
2042
+ }
2043
+ getQueryParams(uri, cursorOffset) {
2044
+ const document = this._getTextDocument(uri);
2045
+ if (!document) {
2046
+ console.error(`getQueryParams: document is ${document}. uri is ${uri}`);
2047
+ return null;
2048
+ }
2049
+ const queryParams = this._languageService.getQueryParams(document, cursorOffset);
2050
+ if (queryParams === undefined) {
2051
+ return null;
2052
+ }
2053
+ return queryParams;
2054
+ }
2055
+ getGlobalParams(uri) {
2056
+ const document = this._getTextDocument(uri);
2057
+ if (!document) {
2058
+ console.error(`getGLobalParams: document is ${document}. uri is ${uri}`);
2059
+ return null;
2060
+ }
2061
+ const globalParams = this._languageService.getGlobalParams(document);
2062
+ if (globalParams === undefined) {
2063
+ return null;
2064
+ }
2065
+ return globalParams;
2066
+ }
2067
+ getReferencedSymbols(uri, cursorOffset) {
2068
+ const document = this._getTextDocument(uri);
2069
+ if (!document) {
2070
+ console.error(`getReferencedGlobalParams: document is ${document}. uri is ${uri}`);
2071
+ return null;
2072
+ }
2073
+ const referencedParams = this._languageService.getReferencedSymbols(document, cursorOffset);
2074
+ if (referencedParams === undefined) {
2075
+ return null;
2076
+ }
2077
+ return referencedParams;
2078
+ }
2079
+ getReferencedGlobalParams(uri, cursorOffset) {
2080
+ const document = this._getTextDocument(uri);
2081
+ if (!document) {
2082
+ console.error(`getReferencedGlobalParams: document is ${document}. uri is ${uri}`);
2083
+ return null;
2084
+ }
2085
+ const referencedParams = this._languageService.getReferencedGlobalParams(document, cursorOffset);
2086
+ if (referencedParams === undefined) {
2087
+ return null;
2088
+ }
2089
+ return referencedParams;
2090
+ }
2091
+ getRenderInfo(uri, cursorOffset) {
2092
+ const document = this._getTextDocument(uri);
2093
+ if (!document) {
2094
+ console.error(`getRenderInfo: document is ${document}. uri is ${uri}`);
2095
+ }
2096
+ return this._languageService.getRenderInfo(document, cursorOffset).then(result => {
2097
+ if (!result) {
2098
+ return null;
2099
+ }
2100
+ return result;
2101
+ });
2102
+ }
2103
+
2104
+ /**
2105
+ * Get command in context and the command range.
2106
+ * This method will basically convert generate microsoft language service interface to monaco interface.
2107
+ * @param uri document URI
2108
+ * @param cursorOffset offset from start of document to cursor
2109
+ */
2110
+ getCommandAndLocationInContext(uri, cursorOffset) {
2111
+ const document = this._getTextDocument(uri);
2112
+ if (!document) {
2113
+ console.error(`getCommandAndLocationInContext: document is ${document}. uri is ${uri}`);
2114
+ return Promise.resolve(null);
2115
+ }
2116
+ return this._languageService.getCommandAndLocationInContext(document, cursorOffset).then(result => {
2117
+ if (!result) {
2118
+ return null;
2119
+ }
2120
+
2121
+ // convert to monaco object.
2122
+ const {
2123
+ text,
2124
+ location: {
2125
+ range: {
2126
+ start,
2127
+ end
2128
+ }
2129
+ }
2130
+ } = result;
2131
+ const range = new monaco.Range(start.line + 1, start.character + 1, end.line + 1, end.character + 1);
2132
+ return {
2133
+ range,
2134
+ text
2135
+ };
7
2136
  });
2137
+ }
2138
+ getCommandsInDocument(uri) {
2139
+ const document = this._getTextDocument(uri);
2140
+ if (!document) {
2141
+ console.error(`getCommandInDocument: document is ${document}. uri is ${uri}`);
2142
+ return null;
2143
+ }
2144
+ return this._languageService.getCommandsInDocument(document);
2145
+ }
2146
+ doComplete(uri, position) {
2147
+ let document = this._getTextDocument(uri);
2148
+ if (!document) {
2149
+ return null;
2150
+ }
2151
+ let completions = this._languageService.doComplete(document, position);
2152
+ return completions;
2153
+ }
2154
+ doValidation(uri, intervals, includeWarnings, includeSuggestions) {
2155
+ const document = this._getTextDocument(uri);
2156
+ const diagnostics = this._languageService.doValidation(document, intervals, includeWarnings, includeSuggestions);
2157
+ return diagnostics;
2158
+ }
2159
+ getResultActions(uri, start, end) {
2160
+ const document = this._getTextDocument(uri);
2161
+ return this._languageService.getResultActions(document, start, end);
2162
+ }
2163
+ doRangeFormat(uri, range) {
2164
+ const document = this._getTextDocument(uri);
2165
+ const formatted = this._languageService.doRangeFormat(document, range);
2166
+ return formatted;
2167
+ }
2168
+ doFolding(uri) {
2169
+ const document = this._getTextDocument(uri);
2170
+ const folding = this._languageService.doFolding(document);
2171
+ return folding;
2172
+ }
2173
+ doDocumentFormat(uri) {
2174
+ const document = this._getTextDocument(uri);
2175
+ const formatted = this._languageService.doDocumentFormat(document);
2176
+ return formatted;
2177
+ }
2178
+ doCurrentCommandFormat(uri, caretPosition) {
2179
+ const document = this._getTextDocument(uri);
2180
+ const formatted = this._languageService.doCurrentCommandFormat(document, caretPosition);
2181
+ return formatted;
2182
+ }
2183
+
2184
+ // Colorize document. if offsets provided, will only colorize commands at these offsets. otherwise - will color the entire document.
2185
+ doColorization(uri, colorizationIntervals) {
2186
+ const document = this._getTextDocument(uri);
2187
+ const colorizationInfo = this._languageService.doColorization(document, colorizationIntervals);
2188
+ return colorizationInfo;
2189
+ }
2190
+ getClientDirective(text) {
2191
+ return this._languageService.getClientDirective(text);
2192
+ }
2193
+ getAdminCommand(text) {
2194
+ return this._languageService.getAdminCommand(text);
2195
+ }
2196
+ findDefinition(uri, position) {
2197
+ const document = this._getTextDocument(uri);
2198
+ const definition = this._languageService.findDefinition(document, position);
2199
+ return definition;
2200
+ }
2201
+ findReferences(uri, position) {
2202
+ let document = this._getTextDocument(uri);
2203
+ const references = this._languageService.findReferences(document, position);
2204
+ return references;
2205
+ }
2206
+ doRename(uri, position, newName) {
2207
+ const document = this._getTextDocument(uri);
2208
+ const workspaceEdit = this._languageService.doRename(document, position, newName);
2209
+ return workspaceEdit;
2210
+ }
2211
+ doHover(uri, position) {
2212
+ let document = this._getTextDocument(uri);
2213
+ let hover = this._languageService.doHover(document, position);
2214
+ return hover;
2215
+ }
2216
+ setParameters(scalarParameters, tabularParameters) {
2217
+ return this._languageService.setParameters(scalarParameters, tabularParameters);
2218
+ }
2219
+ getClusterReferences(uri, cursorOffset) {
2220
+ let document = this._getTextDocument(uri);
2221
+ if (!document) {
2222
+ return Promise.resolve(null);
2223
+ }
2224
+ return this._languageService.getClusterReferences(document, cursorOffset);
2225
+ }
2226
+ getDatabaseReferences(uri, cursorOffset) {
2227
+ let document = this._getTextDocument(uri);
2228
+ if (!document) {
2229
+ return Promise.resolve(null);
2230
+ }
2231
+ return this._languageService.getDatabaseReferences(document, cursorOffset);
2232
+ }
2233
+ _getTextDocument(uri) {
2234
+ let models = this._ctx.getMirrorModels();
2235
+ for (let model of models) {
2236
+ if (model.uri.toString() === uri) {
2237
+ return ls.TextDocument.create(uri, this._languageId, model.version, model.getValue());
2238
+ }
2239
+ }
2240
+ return null;
2241
+ }
2242
+ }
2243
+
2244
+ self.onmessage = () => {
2245
+ // ignore the first message
2246
+ worker.initialize((ctx, createData) => {
2247
+ return new KustoWorker(ctx, createData);
2248
+ });
8
2249
  };