@kusto/monaco-kusto 5.6.6 → 6.0.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 +5663 -8534
  9. package/release/dev/languageFeatures.d.ts +91 -0
  10. package/release/dev/languageService/kustoLanguageService.d.ts +142 -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-24a15f15.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 +2224 -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 +142 -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 +142 -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-1108181b.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/_deps/lodash/lodash.js +0 -17209
  54. package/release/esm/_deps/vscode-languageserver-types/main.js +0 -1908
  55. package/release/esm/_deps/xregexp/xregexp-all.js +0 -4652
  56. package/release/esm/commandFormatter.js +0 -36
  57. package/release/esm/commandHighlighter.js +0 -51
  58. package/release/esm/extendedEditor.js +0 -38
  59. package/release/esm/kustoWorker.js +0 -245
  60. package/release/esm/languageFeatures.js +0 -1032
  61. package/release/esm/languageService/kustoLanguageService.js +0 -1912
  62. package/release/esm/languageService/kustoMonarchLanguageDefinition.js +0 -127
  63. package/release/esm/languageService/schema.js +0 -64
  64. package/release/esm/workerManager.js +0 -102
  65. /package/release/{esm/monaco.d.ts → monaco.d.ts} +0 -0
@@ -1,8 +1,2227 @@
1
+ /*!-----------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * monaco-kusto version: 6.0.0(d333d5bf2aa3d56b17120ed36803a7fb5bcfb87a)
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, end - start + 1, 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]));
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
+ };
667
+ }).filter(resultAction => resultAction.changes.length);
668
+ return Promise.resolve(resultActionsMap);
669
+ }
670
+ flattenCodeActions(codeAction) {
671
+ const applyActions = [];
672
+ if (codeAction instanceof k2.ApplyAction) {
673
+ applyActions.push(codeAction);
674
+ } else if (codeAction instanceof k2.MenuAction) {
675
+ const nestedCodeActions = this.toArray(codeAction.Actions);
676
+ for (let i = 0; i < nestedCodeActions.length; i++) {
677
+ applyActions.push(...this.flattenCodeActions(nestedCodeActions[i]));
678
+ }
679
+ }
680
+ return applyActions;
681
+ }
682
+ toLsDiagnostics(diagnostics, document) {
683
+ return diagnostics.filter(diag => diag.HasLocation).map(diag => {
684
+ const start = document.positionAt(diag.Start);
685
+ const end = document.positionAt(diag.Start + diag.Length);
686
+ const range = ls.Range.create(start, end);
687
+ let severity;
688
+ switch (diag.Severity) {
689
+ case 'Suggestion':
690
+ severity = ls.DiagnosticSeverity.Information;
691
+ break;
692
+ case 'Warning':
693
+ severity = ls.DiagnosticSeverity.Warning;
694
+ break;
695
+ default:
696
+ severity = ls.DiagnosticSeverity.Error;
697
+ }
698
+ return ls.Diagnostic.create(range, diag.Message, severity, diag.Code);
699
+ });
700
+ }
701
+
702
+ /**
703
+ * Colorize one or more kusto blocks (a.k.a commands), or just the entire document.
704
+ * Supports multi-cursor editing (colorizes blocks on multiple changes).
705
+ * @param document The document to colorize
706
+ * @param changeIntervals an array containing 0 or more changed intervals. if the array is empty - just colorize the entire row.
707
+ * if the array contains a single change - just color the kusto blocks that wraps this change. If multiple changes are provided,
708
+ * colorize all blocks that intersect these changes.
709
+ * The code will try to only parse once if this is the same command.
710
+ */
711
+ doColorization(document, changeIntervals) {
712
+ if (!document || !this._languageSettings.useSemanticColorization) {
713
+ return Promise.resolve([]);
714
+ }
715
+
716
+ // V1 intellisense
717
+ if (!this.isIntellisenseV2()) {
718
+ // Handle specific ranges changes (and not the whole doc)
719
+ if (changeIntervals.length > 0) {
720
+ this.parseDocumentV1(document, k.ParseMode.CommandTokensOnly);
721
+ const affectedCommands = this.toArray(this._parser.Results).filter(command =>
722
+ // a command is affected if it intersects at least on of changed ranges.
723
+ command // command can be null. we're filtering all nulls in the array.
724
+ ? changeIntervals.some(({
725
+ start: changeStart,
726
+ end: changeEnd
727
+ }) =>
728
+ // both intervals intersect if either the start or the end of interval A is inside interval B.
729
+ // If we deleted something at the end of a command, the interval will not intersect the current command.
730
+ // so we also want consider affected commands commands the end where the interval begins.
731
+ // hence the + 1.
732
+ command.AbsoluteStart >= changeStart && command.AbsoluteStart <= changeEnd || changeStart >= command.AbsoluteStart && changeStart <= command.AbsoluteEnd + 1) : false);
733
+
734
+ // We're not on any command so don't return any classifications.
735
+ // this can happen if we're at the and of the file and deleting empty rows (for example).
736
+ if (!affectedCommands || affectedCommands.length === 0) {
737
+ return Promise.resolve([{
738
+ classifications: [],
739
+ absoluteStart: changeIntervals[0].start,
740
+ absoluteEnd: changeIntervals[0].end
741
+ }]);
742
+ }
743
+ const results = affectedCommands.map(command => {
744
+ this.parseTextV1(command.Text, k.ParseMode.TokenizeAllText);
745
+ const k2Classifications = this.getClassificationsFromParseResult(command.AbsoluteStart);
746
+ const classifications = toClassifiedRange(k2Classifications);
747
+ return {
748
+ classifications: classifications,
749
+ absoluteStart: command.AbsoluteStart,
750
+ absoluteEnd: command.AbsoluteEnd
751
+ };
752
+ });
753
+ return Promise.resolve(results);
754
+ }
755
+
756
+ // Entire document requested
757
+ this.parseDocumentV1(document, k.ParseMode.TokenizeAllText);
758
+ const classifications = this.getClassificationsFromParseResult();
759
+ return Promise.resolve([{
760
+ classifications: toClassifiedRange(classifications),
761
+ absoluteStart: 0,
762
+ absoluteEnd: document.getText().length
763
+ }]);
764
+ }
765
+
766
+ // V2 intellisense
767
+ const script = this.parseDocumentV2(document);
768
+ if (changeIntervals.length > 0) {
769
+ const blocks = this.toArray(script.Blocks);
770
+ const affectedBlocks = this.getAffectedBlocks(blocks, changeIntervals);
771
+ const result = affectedBlocks.map(block => ({
772
+ classifications: toClassifiedRange(this.toArray(block.Service.GetClassifications(block.Start, block.End).Classifications)),
773
+ absoluteStart: block.Start,
774
+ absoluteEnd: block.End
775
+ }));
776
+ return Promise.resolve(result);
777
+ }
778
+
779
+ // Entire document requested
780
+ const blocks = this.toArray(script.Blocks);
781
+ const classifications = blocks.map(block => {
782
+ return this.toArray(block.Service.GetClassifications(block.Start, block.Length).Classifications);
783
+ }).reduce((prev, curr) => prev.concat(curr), []);
784
+ return Promise.resolve([{
785
+ classifications: toClassifiedRange(classifications),
786
+ absoluteStart: 0,
787
+ absoluteEnd: document.getText().length
788
+ }]);
789
+ }
790
+ getAffectedBlocks(blocks, changeIntervals) {
791
+ return blocks.filter(block =>
792
+ // a command is affected if it intersects at least on of changed ranges.
793
+ block // command can be null. we're filtering all nulls in the array.
794
+ ? changeIntervals.some(({
795
+ start: changeStart,
796
+ end: changeEnd
797
+ }) =>
798
+ // both intervals intersect if either the start or the end of interval A is inside interval B.
799
+ block.Start >= changeStart && block.Start <= changeEnd || changeStart >= block.Start && changeStart <= block.End + 1) : false);
800
+ }
801
+ addClusterToSchema(document, clusterName, databaseNames) {
802
+ let clusterNameOnly = Kusto.Language.KustoFacts.GetHostName(clusterName);
803
+ let cluster = this._kustoJsSchemaV2.GetCluster$1(clusterNameOnly);
804
+ if (cluster) {
805
+ // add databases that are not already in the cluster.
806
+ databaseNames.filter(databaseName => !cluster.GetDatabase(databaseName)).map(databaseName => {
807
+ const symbol = new sym.DatabaseSymbol.$ctor1(databaseName, undefined, false);
808
+ cluster = cluster.AddDatabase(symbol);
809
+ });
810
+ }
811
+ if (!cluster) {
812
+ const databaseSymbols = databaseNames.map(databaseName => {
813
+ const symbol = new sym.DatabaseSymbol.$ctor1(databaseName, undefined, false);
814
+ return symbol;
815
+ });
816
+ const databaseSymbolsList = new (List(sym.DatabaseSymbol).$ctor1)(databaseSymbols);
817
+ cluster = new sym.ClusterSymbol.$ctor1(clusterNameOnly, databaseSymbolsList, false);
818
+ }
819
+ this._kustoJsSchemaV2 = this._kustoJsSchemaV2.AddOrReplaceCluster(cluster);
820
+ this._script = k2.CodeScript.From$1(document.getText(), this._kustoJsSchemaV2);
821
+ return Promise.resolve();
822
+ }
823
+ addDatabaseToSchema(document, clusterName, databaseSchema) {
824
+ let clusterHostName = Kusto.Language.KustoFacts.GetHostName(clusterName);
825
+ let cluster = this._kustoJsSchemaV2.GetCluster$1(clusterHostName);
826
+ if (!cluster) {
827
+ cluster = new sym.ClusterSymbol.$ctor1(clusterHostName, null, false);
828
+ }
829
+ const databaseSymbol = KustoLanguageService.convertToDatabaseSymbol(databaseSchema);
830
+ cluster = cluster.AddOrUpdateDatabase(databaseSymbol);
831
+ this._kustoJsSchemaV2 = this._kustoJsSchemaV2.AddOrReplaceCluster(cluster);
832
+ this._script = k2.CodeScript.From$1(document.getText(), this._kustoJsSchemaV2);
833
+ return Promise.resolve();
834
+ }
835
+ setSchema(schema) {
836
+ this._schema = schema;
837
+ // We support intellisenseV2 only if the clusterType is "Engine", even if the setting is enabled
838
+ if (this._languageSettings.useIntellisenseV2 && schema && schema.clusterType === 'Engine') {
839
+ let kustoJsSchemaV2 = this.convertToKustoJsSchemaV2(schema);
840
+ this._kustoJsSchemaV2 = kustoJsSchemaV2;
841
+ this._script = undefined;
842
+ this._parsePropertiesV2 = undefined;
843
+ }
844
+
845
+ // 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.
846
+ return new Promise((resolve, reject) => {
847
+ const kustoJsSchema = schema ? KustoLanguageService.convertToKustoJsSchema(schema) : undefined;
848
+ this._kustoJsSchema = kustoJsSchema;
849
+ this.createRulesProvider(kustoJsSchema, schema.clusterType);
850
+ resolve(undefined);
851
+ });
852
+ }
853
+ setParameters(scalarParameters, tabularParameters) {
854
+ if (!this._languageSettings.useIntellisenseV2 || this._schema.clusterType !== 'Engine') {
855
+ throw new Error('setParameters requires intellisense V2 and Engine cluster');
856
+ }
857
+ this._schema.globalScalarParameters = scalarParameters;
858
+ this._schema.globalTabularParameters = tabularParameters;
859
+ const scalarSymbols = scalarParameters.map(param => KustoLanguageService.createParameterSymbol(param));
860
+ const tabularSymbols = tabularParameters.map(param => KustoLanguageService.createTabularParameterSymbol(param));
861
+ this._kustoJsSchemaV2 = this._kustoJsSchemaV2.WithParameters(KustoLanguageService.toBridgeList([...scalarSymbols, ...tabularSymbols]));
862
+ return Promise.resolve(undefined);
863
+ }
864
+
865
+ /**
866
+ * A combination of normalizeSchema and setSchema
867
+ * @param schema schema json as received from .show schema as json
868
+ * @param clusterConnectionString cluster connection string
869
+ * @param databaseInContextName name of database in context
870
+ */
871
+ setSchemaFromShowSchema(schema, clusterConnectionString, databaseInContextName, globalScalarParameters, globalTabularParameters) {
872
+ return this.normalizeSchema(schema, clusterConnectionString, databaseInContextName).then(normalized => this.setSchema({
873
+ ...normalized,
874
+ globalScalarParameters,
875
+ globalTabularParameters
876
+ }));
877
+ }
878
+
879
+ /**
880
+ * Converts the result of .show schema as json to a normalized schema used by kusto language service.
881
+ * @param schema result of show schema
882
+ * @param clusterConnectionString cluster connection string`
883
+ * @param databaseInContextName database in context name
884
+ */
885
+ normalizeSchema(schema, clusterConnectionString, databaseInContextName) {
886
+ const databases = Object.keys(schema.Databases).map(key => schema.Databases[key]).map(({
887
+ Name,
888
+ Tables,
889
+ ExternalTables,
890
+ MaterializedViews,
891
+ Functions,
892
+ MinorVersion,
893
+ MajorVersion
894
+ }) => ({
895
+ name: Name,
896
+ minorVersion: MinorVersion,
897
+ majorVersion: MajorVersion,
898
+ tables: [].concat(...[[Tables, 'Table'], [MaterializedViews, 'MaterializedView'], [ExternalTables, 'ExternalTable']].filter(([tableContainer]) => tableContainer).map(([tableContainer, tableEntity]) => Object.values(tableContainer).map(({
899
+ Name,
900
+ OrderedColumns,
901
+ DocString
902
+ }) => ({
903
+ name: Name,
904
+ docstring: DocString,
905
+ entityType: tableEntity,
906
+ columns: OrderedColumns.map(({
907
+ Name,
908
+ Type,
909
+ DocString,
910
+ CslType,
911
+ Examples
912
+ }) => ({
913
+ name: Name,
914
+ type: CslType,
915
+ docstring: DocString,
916
+ examples: Examples
917
+ }))
918
+ })))),
919
+ functions: Object.keys(Functions).map(key => Functions[key]).map(({
920
+ Name,
921
+ Body,
922
+ DocString,
923
+ InputParameters
924
+ }) => ({
925
+ name: Name,
926
+ body: Body,
927
+ docstring: DocString,
928
+ inputParameters: InputParameters.map(inputParam => ({
929
+ name: inputParam.Name,
930
+ type: inputParam.Type,
931
+ cslType: inputParam.CslType,
932
+ cslDefaultValue: inputParam.CslDefaultValue,
933
+ columns: inputParam.Columns ? inputParam.Columns.map(col => ({
934
+ name: col.Name,
935
+ type: col.Type,
936
+ cslType: col.CslType
937
+ })) : inputParam.Columns
938
+ }))
939
+ }))
940
+ }));
941
+ const result = {
942
+ clusterType: 'Engine',
943
+ cluster: {
944
+ connectionString: clusterConnectionString,
945
+ databases: databases
946
+ },
947
+ database: databases.filter(db => db.name === databaseInContextName)[0]
948
+ };
949
+ return Promise.resolve(result);
950
+ }
951
+ getSchema() {
952
+ return Promise.resolve(this._schema);
953
+ }
954
+ getCommandInContext(document, cursorOffset) {
955
+ return this.isIntellisenseV2() ? this.getCommandInContextV2(document, cursorOffset) : this.getCommandInContextV1(document, cursorOffset);
956
+ }
957
+ getCommandAndLocationInContext(document, cursorOffset) {
958
+ // We are going to remove v1 intellisense. no use to keep parity.
959
+ if (!document || !this.isIntellisenseV2()) {
960
+ return Promise.resolve(null);
961
+ }
962
+ const script = this.parseDocumentV2(document);
963
+ const block = this.getCurrentCommandV2(script, cursorOffset);
964
+ if (!block) {
965
+ return Promise.resolve(null);
966
+ }
967
+ const start = document.positionAt(block.Start);
968
+ const end = document.positionAt(block.End);
969
+ const location = ls.Location.create(document.uri, ls.Range.create(start, end));
970
+ const text = block.Text;
971
+ return Promise.resolve({
972
+ text,
973
+ location
974
+ });
975
+ }
976
+ getCommandInContextV1(document, cursorOffset) {
977
+ this.parseDocumentV1(document, k.ParseMode.CommandTokensOnly);
978
+ const command = this.getCurrentCommand(document, cursorOffset);
979
+ if (!command) {
980
+ return Promise.resolve(null);
981
+ }
982
+ return Promise.resolve(command.Text);
983
+ }
984
+ getCommandInContextV2(document, cursorOffset) {
985
+ if (!document) {
986
+ return Promise.resolve(null);
987
+ }
988
+ const script = this.parseDocumentV2(document);
989
+ const block = this.getCurrentCommandV2(script, cursorOffset);
990
+ if (!block) {
991
+ return Promise.resolve(null);
992
+ }
993
+
994
+ // TODO: do we need to do tricks like V1 is doing in this.getCurrentCommand?
995
+ return Promise.resolve(block.Text);
996
+ }
997
+
998
+ /**
999
+ * Return an array of commands in document. each command contains the range and text.
1000
+ */
1001
+ getCommandsInDocument(document) {
1002
+ if (!document) {
1003
+ return Promise.resolve([]);
1004
+ }
1005
+ return this.isIntellisenseV2() ? this.getCommandsInDocumentV2(document) : this.getCommandsInDocumentV1(document);
1006
+ }
1007
+ getCommandsInDocumentV1(document) {
1008
+ this.parseDocumentV1(document, k.ParseMode.CommandTokensOnly);
1009
+ let commands = this.toArray(this._parser.Results);
1010
+ return Promise.resolve(commands.map(({
1011
+ AbsoluteStart,
1012
+ AbsoluteEnd,
1013
+ Text
1014
+ }) => ({
1015
+ absoluteStart: AbsoluteStart,
1016
+ absoluteEnd: AbsoluteEnd,
1017
+ text: Text
1018
+ })));
1019
+ }
1020
+ toPlacementStyle(formatterPlacementStyle) {
1021
+ if (!formatterPlacementStyle) {
1022
+ return undefined;
1023
+ }
1024
+ switch (formatterPlacementStyle) {
1025
+ case 'None':
1026
+ return k2.PlacementStyle.None;
1027
+ case 'NewLine':
1028
+ return k2.PlacementStyle.NewLine;
1029
+ case 'Smart':
1030
+ return k2.PlacementStyle.Smart;
1031
+ default:
1032
+ throw new Error('Unknown PlacementStyle');
1033
+ }
1034
+ }
1035
+ getFormattedCommandsInDocumentV2(document, rangeStart, rangeEnd) {
1036
+ const script = this.parseDocumentV2(document);
1037
+ const commands = this.toArray(script.Blocks).filter(command => {
1038
+ if (!command.Text || command.Text.trim() == '') return false;
1039
+ if (rangeStart == null || rangeEnd == null) return true;
1040
+
1041
+ // calculate command end position without \r\n.
1042
+ let commandEnd = command.End;
1043
+ const commandText = command.Text;
1044
+ for (let i = commandText.length - 1; i >= 0; i--) {
1045
+ if (commandText[i] != '\r' && commandText[i] != '\n') {
1046
+ break;
1047
+ } else {
1048
+ commandEnd--;
1049
+ }
1050
+ }
1051
+ if (command.Start > rangeStart && command.Start < rangeEnd) return true;
1052
+ if (commandEnd > rangeStart && commandEnd < rangeEnd) return true;
1053
+ if (command.Start <= rangeStart && commandEnd >= rangeEnd) return true;
1054
+ });
1055
+ if (commands.length === 0) {
1056
+ return {
1057
+ formattedCommands: []
1058
+ };
1059
+ }
1060
+ const formattedCommands = commands.map(command => {
1061
+ const formatterOptions = this._languageSettings.formatter;
1062
+ 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);
1063
+ if (rangeStart == null || rangeEnd == null || rangeStart === command.Start && rangeEnd === command.End) {
1064
+ const result = command.Service.GetFormattedText(formatter);
1065
+ return result.Text;
1066
+ }
1067
+ return command.Service.GetFormattedText(formatter).Text;
1068
+ });
1069
+ const originalRange = this.createRange(document, commands[0].Start, commands[commands.length - 1].End);
1070
+ return {
1071
+ formattedCommands,
1072
+ originalRange
1073
+ };
1074
+ }
1075
+ getCommandsInDocumentV2(document) {
1076
+ const script = this.parseDocumentV2(document);
1077
+ let commands = this.toArray(script.Blocks).filter(command => command.Text.trim() != '');
1078
+ return Promise.resolve(commands.map(({
1079
+ Start,
1080
+ End,
1081
+ Text
1082
+ }) => ({
1083
+ absoluteStart: Start,
1084
+ absoluteEnd: End,
1085
+ text: Text
1086
+ })));
1087
+ }
1088
+ getClientDirective(text) {
1089
+ let outParam = {
1090
+ v: null
1091
+ };
1092
+ const isClientDirective = k.CslCommandParser.IsClientDirective(text, outParam);
1093
+ return Promise.resolve({
1094
+ isClientDirective,
1095
+ directiveWithoutLeadingComments: outParam.v
1096
+ });
1097
+ }
1098
+ getAdminCommand(text) {
1099
+ let outParam = {
1100
+ v: null
1101
+ };
1102
+ const isAdminCommand = k.CslCommandParser.IsAdminCommand$1(text, outParam);
1103
+ return Promise.resolve({
1104
+ isAdminCommand,
1105
+ adminCommandWithoutLeadingComments: outParam.v
1106
+ });
1107
+ }
1108
+ findDefinition(document, position) {
1109
+ if (!document || !this.isIntellisenseV2()) {
1110
+ return Promise.resolve([]);
1111
+ }
1112
+ const script = this.parseDocumentV2(document);
1113
+ const cursorOffset = document.offsetAt(position);
1114
+ let currentBlock = this.getCurrentCommandV2(script, cursorOffset);
1115
+ if (!currentBlock) {
1116
+ return Promise.resolve([]);
1117
+ }
1118
+ const relatedInfo = currentBlock.Service.GetRelatedElements(document.offsetAt(position));
1119
+ const relatedElements = this.toArray(relatedInfo.Elements);
1120
+ const definition = relatedElements[0];
1121
+ if (!definition) {
1122
+ return Promise.resolve([]);
1123
+ }
1124
+ const start = document.positionAt(definition.Start);
1125
+ const end = document.positionAt(definition.End);
1126
+ const range = ls.Range.create(start, end);
1127
+ const location = ls.Location.create(document.uri, range);
1128
+ return Promise.resolve([location]);
1129
+ }
1130
+ findReferences(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
+ if (!relatedElements || relatedElements.length == 0) {
1143
+ return Promise.resolve([]);
1144
+ }
1145
+ const references = relatedElements.map(relatedElement => {
1146
+ const start = document.positionAt(relatedElement.Start);
1147
+ const end = document.positionAt(relatedElement.End);
1148
+ const range = ls.Range.create(start, end);
1149
+ const location = ls.Location.create(document.uri, range);
1150
+ return location;
1151
+ });
1152
+ return Promise.resolve(references);
1153
+ }
1154
+ getQueryParams(document, cursorOffset) {
1155
+ if (!document || !this.isIntellisenseV2()) {
1156
+ return Promise.resolve([]);
1157
+ }
1158
+ this.parseDocumentV2(document);
1159
+ const parsedAndAnalyzed = this.parseAndAnalyze(document, cursorOffset);
1160
+ const queryParamStatements = this.toArray(parsedAndAnalyzed.Syntax.GetDescendants(Kusto.Language.Syntax.QueryParametersStatement));
1161
+ if (!queryParamStatements || queryParamStatements.length == 0) {
1162
+ return Promise.resolve([]);
1163
+ }
1164
+ const queryParams = [];
1165
+ queryParamStatements.forEach(paramStatement => {
1166
+ paramStatement.WalkElements(el => el.ReferencedSymbol && el.ReferencedSymbol.Type ? queryParams.push({
1167
+ name: el.ReferencedSymbol.Name,
1168
+ type: el.ReferencedSymbol.Type.Name
1169
+ }) : undefined);
1170
+ });
1171
+ return Promise.resolve(queryParams);
1172
+ }
1173
+ getRenderInfo(document, cursorOffset) {
1174
+ const parsedAndAnalyzed = this.parseAndAnalyze(document, cursorOffset);
1175
+ if (!parsedAndAnalyzed) {
1176
+ return Promise.resolve(undefined);
1177
+ }
1178
+ const renderStatements = this.toArray(parsedAndAnalyzed.Syntax.GetDescendants(Kusto.Language.Syntax.RenderOperator));
1179
+ if (!renderStatements || renderStatements.length === 0) {
1180
+ return Promise.resolve(undefined);
1181
+ }
1182
+
1183
+ // assuming a single render statement
1184
+ const renderStatement = renderStatements[0];
1185
+
1186
+ // Start and end relative to block start.
1187
+ const startOffset = renderStatement.TextStart;
1188
+ const endOffset = renderStatement.End;
1189
+ const visualization = renderStatement.ChartType.ValueText;
1190
+ const withClause = renderStatement.WithClause;
1191
+ if (!withClause) {
1192
+ const info = {
1193
+ options: {
1194
+ visualization
1195
+ },
1196
+ location: {
1197
+ startOffset,
1198
+ endOffset
1199
+ }
1200
+ };
1201
+ return Promise.resolve(info);
1202
+ }
1203
+ const properties = this.toArray(withClause.Properties);
1204
+ const props = properties.reduce((prev, property) => {
1205
+ const name = property.Element$1.Name.SimpleName;
1206
+ switch (name) {
1207
+ case 'xcolumn':
1208
+ const value = property.Element$1.Expression.ReferencedSymbol.Name;
1209
+ prev[name] = value;
1210
+ break;
1211
+ case 'ycolumns':
1212
+ case 'anomalycolumns':
1213
+ const nameNodes = this.toArray(property.Element$1.Expression.Names);
1214
+ const values = nameNodes.map(nameNode => nameNode.Element$1.SimpleName);
1215
+ prev[name] = values;
1216
+ break;
1217
+ case 'ymin':
1218
+ case 'ymax':
1219
+ const numericVal = parseFloat(property.Element$1.Expression.ConstantValue);
1220
+ prev[name] = numericVal;
1221
+ break;
1222
+ case 'title':
1223
+ case 'xtitle':
1224
+ case 'ytitle':
1225
+ case 'visualization':
1226
+ case 'series':
1227
+ const strVal = property.Element$1.Expression.ConstantValue;
1228
+ prev[name] = strVal;
1229
+ break;
1230
+ case 'xaxis':
1231
+ case 'yaxis':
1232
+ const scale = property.Element$1.Expression.ConstantValue;
1233
+ prev[name] = scale;
1234
+ break;
1235
+ case 'legend':
1236
+ const legend = property.Element$1.Expression.ConstantValue;
1237
+ prev[name] = legend;
1238
+ break;
1239
+ case 'ysplit':
1240
+ const split = property.Element$1.Expression.ConstantValue;
1241
+ prev[name] = split;
1242
+ break;
1243
+ case 'accumulate':
1244
+ const accumulate = property.Element$1.Expression.ConstantValue;
1245
+ prev[name] = accumulate;
1246
+ break;
1247
+ case 'kind':
1248
+ const val = property.Element$1.Expression.ConstantValue;
1249
+ prev[name] = val;
1250
+ break;
1251
+ default:
1252
+ assertNever(name);
1253
+ }
1254
+ return prev;
1255
+ }, {});
1256
+ const renderOptions = {
1257
+ visualization,
1258
+ ...props
1259
+ };
1260
+ const renderInfo = {
1261
+ options: renderOptions,
1262
+ location: {
1263
+ startOffset,
1264
+ endOffset
1265
+ }
1266
+ };
1267
+ return Promise.resolve(renderInfo);
1268
+ }
1269
+ getReferencedSymbols(document, offset) {
1270
+ const parsedAndAnalyzed = this.parseAndAnalyze(document, offset);
1271
+ if (!parsedAndAnalyzed) {
1272
+ Promise.resolve([]);
1273
+ }
1274
+
1275
+ // We take all referenced symbols in the query
1276
+ const referencedSymbols = this.toArray(parsedAndAnalyzed.Syntax.GetDescendants(Kusto.Language.Syntax.Expression)).filter(expression => expression.ReferencedSymbol !== null).map(x => x.ReferencedSymbol);
1277
+ const result = referencedSymbols.map(sym => ({
1278
+ name: sym.Name,
1279
+ kind: symbolKindToName[sym.Kind] ?? `${sym.Kind}`,
1280
+ display: sym.Display
1281
+ }));
1282
+ return Promise.resolve(result);
1283
+ }
1284
+ getReferencedGlobalParams(document, cursorOffset) {
1285
+ const parsedAndAnalyzed = this.parseAndAnalyze(document, cursorOffset);
1286
+ if (!parsedAndAnalyzed) {
1287
+ Promise.resolve([]);
1288
+ }
1289
+
1290
+ // We take the ambient parameters
1291
+ const ambientParameters = this.toArray(this._kustoJsSchemaV2.Parameters);
1292
+
1293
+ // We take all referenced symbols in the query
1294
+ const referencedSymbols = this.toArray(parsedAndAnalyzed.Syntax.GetDescendants(Kusto.Language.Syntax.Expression)).filter(expression => expression.ReferencedSymbol !== null).map(x => x.ReferencedSymbol);
1295
+
1296
+ // The Intersection between them is the ambient parameters that are used in the query.
1297
+ // Note: Ideally we would use Set here (or at least array.Include), but were' compiling down to es2015.
1298
+ const intersection = referencedSymbols.filter(referencedSymbol => ambientParameters.filter(ambientParameter => ambientParameter === referencedSymbol).length > 0);
1299
+ const result = intersection.map(param => ({
1300
+ name: param.Name,
1301
+ type: param.Type.Name
1302
+ }));
1303
+ return Promise.resolve(result);
1304
+ }
1305
+ getGlobalParams(document) {
1306
+ if (!this.isIntellisenseV2()) {
1307
+ return Promise.resolve([]);
1308
+ }
1309
+ const params = this.toArray(this._kustoJsSchemaV2.Parameters);
1310
+ const result = params.map(param => ({
1311
+ name: param.Name,
1312
+ type: param.Type.Name
1313
+ }));
1314
+ return Promise.resolve(result);
1315
+ }
1316
+ doRename(document, position, newName) {
1317
+ if (!document || !this.isIntellisenseV2()) {
1318
+ return Promise.resolve(undefined);
1319
+ }
1320
+ const script = this.parseDocumentV2(document);
1321
+ const cursorOffset = document.offsetAt(position);
1322
+ let currentBLock = this.getCurrentCommandV2(script, cursorOffset);
1323
+ if (!currentBLock) {
1324
+ return Promise.resolve(undefined);
1325
+ }
1326
+ const relatedInfo = currentBLock.Service.GetRelatedElements(document.offsetAt(position));
1327
+ const relatedElements = this.toArray(relatedInfo.Elements);
1328
+ const declarations = relatedElements.filter(e => e.Kind == k2.RelatedElementKind.Declaration);
1329
+
1330
+ // A declaration must be one of the elements
1331
+ if (!declarations || declarations.length == 0) {
1332
+ return Promise.resolve(undefined);
1333
+ }
1334
+ const edits = relatedElements.map(edit => {
1335
+ const start = document.positionAt(edit.Start);
1336
+ const end = document.positionAt(edit.End);
1337
+ const range = ls.Range.create(start, end);
1338
+ return ls.TextEdit.replace(range, newName);
1339
+ });
1340
+
1341
+ // create a workspace edit
1342
+ const workspaceEdit = {
1343
+ changes: {
1344
+ [document.uri]: edits
1345
+ }
1346
+ };
1347
+ return Promise.resolve(workspaceEdit);
1348
+ }
1349
+ doHover(document, position) {
1350
+ if (!document || !this.isIntellisenseV2()) {
1351
+ return Promise.resolve(undefined);
1352
+ }
1353
+ const script = this.parseDocumentV2(document);
1354
+ const cursorOffset = document.offsetAt(position);
1355
+ let currentBLock = this.getCurrentCommandV2(script, cursorOffset);
1356
+ if (!currentBLock) {
1357
+ return Promise.resolve(undefined);
1358
+ }
1359
+ const isSupported = currentBLock.Service.IsFeatureSupported(k2.CodeServiceFeatures.QuickInfo, cursorOffset);
1360
+ if (!isSupported) {
1361
+ return Promise.resolve(undefined);
1362
+ }
1363
+ const quickInfo = currentBLock.Service.GetQuickInfo(cursorOffset);
1364
+ if (!quickInfo || !quickInfo.Items) {
1365
+ return Promise.resolve(undefined);
1366
+ }
1367
+ let items = this.toArray(quickInfo.Items);
1368
+ if (!items) {
1369
+ return Promise.resolve(undefined);
1370
+ }
1371
+
1372
+ // Errors, Warnings and Suggestions are already shown in getDiagnostics. we don't want them in doHover.
1373
+ items = items.filter(item => item.Kind !== k2.QuickInfoKind.Error && item.Kind !== k2.QuickInfoKind.Suggestion && item.Kind !== k2.QuickInfoKind.Warning);
1374
+ const itemsText = items.map(item => item.Text.replace('\n\n', '\n* * *\n'));
1375
+ // separate items by horizontal line.
1376
+ const text = itemsText.join('\n* * *\n');
1377
+ // Instead of just an empty line between the first line (the signature) and the second line (the description)
1378
+ // add an horizontal line (* * * in markdown) between them.
1379
+ return Promise.resolve({
1380
+ contents: text
1381
+ });
1382
+ }
1383
+
1384
+ //#region dummy schema for manual testing
1385
+ static get dummySchema() {
1386
+ const database = {
1387
+ majorVersion: 0,
1388
+ minorVersion: 0,
1389
+ name: 'Kuskus',
1390
+ tables: [{
1391
+ name: 'KustoLogs',
1392
+ columns: [{
1393
+ name: 'Source',
1394
+ type: 'string'
1395
+ }, {
1396
+ name: 'Timestamp',
1397
+ type: 'datetime'
1398
+ }, {
1399
+ name: 'Directory',
1400
+ type: 'string'
1401
+ }],
1402
+ docstring: 'A dummy description to test that docstring shows as expected when hovering over a table'
1403
+ }],
1404
+ functions: [{
1405
+ name: 'HowBig',
1406
+ inputParameters: [{
1407
+ name: 'T',
1408
+ columns: [{
1409
+ name: 'Timestamp',
1410
+ type: 'System.DateTime',
1411
+ cslType: 'datetime'
1412
+ }]
1413
+ }],
1414
+ docstring: 'A dummy description to test that docstring shows as expected when hovering over a function',
1415
+ 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}"
1416
+ }, {
1417
+ name: 'FindCIDPast24h',
1418
+ inputParameters: [{
1419
+ name: 'clientActivityId',
1420
+ type: 'System.String',
1421
+ cslType: 'string'
1422
+ }],
1423
+ body: '{ KustoLogs | where Timestamp > now(-1d) | where ClientActivityId == clientActivityId} '
1424
+ }]
1425
+ };
1426
+ const languageServiceSchema = {
1427
+ clusterType: 'Engine',
1428
+ cluster: {
1429
+ connectionString: 'https://kuskus.kusto.windows.net;fed=true',
1430
+ databases: [database]
1431
+ },
1432
+ database: database
1433
+ };
1434
+ return languageServiceSchema;
1435
+ }
1436
+ //#endregion
1437
+
1438
+ static convertToEntityDataType(kustoType) {}
1439
+ /**
1440
+ * We do not want to expose Bridge.Net generated schema, so we expose a cleaner javascript schema.
1441
+ * Here it gets converted to the bridge.Net schema
1442
+ * @param schema Language Service schema
1443
+ */
1444
+ static convertToKustoJsSchema(schema) {
1445
+ switch (schema.clusterType) {
1446
+ case 'Engine':
1447
+ const currentDatabaseName = schema.database ? schema.database.name : undefined;
1448
+ const kCluster = new k.KustoIntelliSenseClusterEntity();
1449
+ let kDatabaseInContext = undefined;
1450
+ kCluster.ConnectionString = schema.cluster.connectionString;
1451
+ const databases = [];
1452
+ schema.cluster.databases.forEach(database => {
1453
+ const kDatabase = new k.KustoIntelliSenseDatabaseEntity();
1454
+ kDatabase.Name = database.name;
1455
+ const tables = [];
1456
+ database.tables.forEach(table => {
1457
+ const kTable = new k.KustoIntelliSenseTableEntity();
1458
+ kTable.Name = table.name;
1459
+ const cols = [];
1460
+ table.columns.forEach(column => {
1461
+ const kColumn = new k.KustoIntelliSenseColumnEntity();
1462
+ kColumn.Name = column.name;
1463
+ kColumn.TypeCode = k.EntityDataType[getEntityDataTypeFromCslType(column.type)];
1464
+ cols.push(kColumn);
1465
+ });
1466
+ kTable.Columns = new Bridge.ArrayEnumerable(cols);
1467
+ tables.push(kTable);
1468
+ });
1469
+ const functions = [];
1470
+ database.functions.forEach(fn => {
1471
+ const kFunction = new k.KustoIntelliSenseFunctionEntity();
1472
+ kFunction.Name = fn.name, kFunction.CallName = getCallName(fn), kFunction.Expression = getExpression(fn), functions.push(kFunction);
1473
+ });
1474
+ kDatabase.Tables = new Bridge.ArrayEnumerable(tables);
1475
+ kDatabase.Functions = new Bridge.ArrayEnumerable(functions);
1476
+ databases.push(kDatabase);
1477
+ if (database.name == currentDatabaseName) {
1478
+ kDatabaseInContext = kDatabase;
1479
+ }
1480
+ });
1481
+ kCluster.Databases = new Bridge.ArrayEnumerable(databases);
1482
+ const kSchema = new k.KustoIntelliSenseQuerySchema(kCluster, kDatabaseInContext);
1483
+ return kSchema;
1484
+ case 'ClusterManager':
1485
+ const accounts = schema.accounts.map(account => {
1486
+ const kAccount = new k.KustoIntelliSenseAccountEntity();
1487
+ kAccount.Name = account;
1488
+ return kAccount;
1489
+ });
1490
+ const services = schema.services.map(service => {
1491
+ const kService = new k.KustoIntelliSenseServiceEntity();
1492
+ kService.Name = service;
1493
+ return kService;
1494
+ });
1495
+ const connectionString = schema.connectionString;
1496
+ const result = {
1497
+ accounts,
1498
+ services,
1499
+ connectionString
1500
+ };
1501
+ return result;
1502
+ case 'DataManagement':
1503
+ return undefined;
1504
+ default:
1505
+ return assertNever(schema);
1506
+ }
1507
+ }
1508
+
1509
+ /**
1510
+ * Returns something like '(x: string, y: datetime)'
1511
+ * @param params scalar parameters
1512
+ */
1513
+ static scalarParametersToSignature(params) {
1514
+ const signatureWithoutParens = params.map(param => `${param.name}: ${param.cslType}`).join(', ');
1515
+ return `(${signatureWithoutParens})`;
1516
+ }
1517
+
1518
+ /**
1519
+ * Returns something like '(x: string, T: (y: int))'
1520
+ * @param params input parameters (tabular or scalar)
1521
+ */
1522
+ static inputParameterToSignature(params) {
1523
+ const signatureWithoutParens = params.map(param => {
1524
+ if (param.columns) {
1525
+ const tableSignature = this.scalarParametersToSignature(param.columns);
1526
+ return `${param.name}: ${tableSignature}`;
1527
+ } else {
1528
+ return `${param.name}: ${param.cslType}`;
1529
+ }
1530
+ }).join(', ');
1531
+ return `(${signatureWithoutParens})`;
1532
+ }
1533
+
1534
+ /**
1535
+ * converts a function definition to a let statement.
1536
+ * @param fn function
1537
+ */
1538
+ static toLetStatement(fn) {
1539
+ const signature = this.inputParameterToSignature(fn.inputParameters);
1540
+ return `let ${fn.name} = ${signature} ${fn.body}`;
1541
+ }
1542
+ static createColumnSymbol(col) {
1543
+ return new sym.ColumnSymbol(col.name, sym.ScalarTypes.GetSymbol(getCslTypeNameFromClrType(col.type)), col.docstring, null, null, col.examples ? KustoLanguageService.toBridgeList(col.examples) : null);
1544
+ }
1545
+ static createParameterSymbol(param) {
1546
+ const paramSymbol = Kusto.Language.Symbols.ScalarTypes.GetSymbol(getCslTypeNameFromClrType(param.type));
1547
+ return new sym.ParameterSymbol(param.name, paramSymbol, null);
1548
+ }
1549
+ static createTabularParameterSymbol(param) {
1550
+ const columnSymbols = param.columns.map(col => KustoLanguageService.createColumnSymbol(col));
1551
+ const para = new Kusto.Language.Symbols.TableSymbol.$ctor4(param.name, columnSymbols);
1552
+ return new sym.ParameterSymbol(param.name, para, param.docstring);
1553
+ }
1554
+ static createParameter(param) {
1555
+ if (!param.columns) {
1556
+ const paramSymbol = Kusto.Language.Symbols.ScalarTypes.GetSymbol(getCslTypeNameFromClrType(param.type));
1557
+ const expression = param.cslDefaultValue && typeof param.cslDefaultValue === 'string' ? parsing.QueryParser.ParseLiteral$1(param.cslDefaultValue) : undefined;
1558
+ return new sym.Parameter.$ctor3(param.name, paramSymbol, null, null, null, false, null, 1, 1, expression, null);
1559
+ }
1560
+ if (param.columns.length == 0) {
1561
+ return new sym.Parameter.ctor(param.name, sym.ParameterTypeKind.Tabular, sym.ArgumentKind.Expression, null, null, false, null, 1, 1, null, null);
1562
+ }
1563
+ const argumentType = new sym.TableSymbol.ctor(param.columns.map(col => KustoLanguageService.createColumnSymbol(col)));
1564
+ return new sym.Parameter.$ctor2(param.name, argumentType);
1565
+ }
1566
+ static convertToDatabaseSymbol(db) {
1567
+ const createFunctionSymbol = fn => {
1568
+ const parameters = fn.inputParameters.map(param => KustoLanguageService.createParameter(param));
1569
+
1570
+ // TODO: handle outputColumns (right now it doesn't seem to be implemented for any function).
1571
+ return new sym.FunctionSymbol.$ctor14(fn.name, fn.body, KustoLanguageService.toBridgeList(parameters), fn.docstring);
1572
+ };
1573
+ const createTableSymbol = tbl => {
1574
+ const columnSymbols = tbl.columns.map(col => KustoLanguageService.createColumnSymbol(col));
1575
+ let symbol = new sym.TableSymbol.$ctor4(tbl.name, columnSymbols);
1576
+ symbol.Description = tbl.docstring;
1577
+ switch (tbl.entityType) {
1578
+ case 'MaterializedViewTable':
1579
+ const mvQuery = tbl.mvQuery ?? null;
1580
+ symbol = new sym.MaterializedViewSymbol.$ctor2(tbl.name, symbol.Columns, mvQuery, tbl.docstring);
1581
+ symbol = symbol.WithIsMaterializedView(true);
1582
+ break;
1583
+ case 'ExternalTable':
1584
+ symbol = symbol.WithIsExternal(true);
1585
+ break;
1586
+ }
1587
+ return symbol;
1588
+ };
1589
+ const createDatabaseSymbol = db => {
1590
+ const tableSymbols = db.tables ? db.tables.map(tbl => createTableSymbol(tbl)) : [];
1591
+ const functionSymbols = db.functions ? db.functions.map(fun => createFunctionSymbol(fun)) : [];
1592
+ return new sym.DatabaseSymbol.ctor(db.name, tableSymbols.concat(functionSymbols));
1593
+ };
1594
+ const databaseSymbol = createDatabaseSymbol(db);
1595
+ return databaseSymbol;
1596
+ }
1597
+ convertToKustoJsSchemaV2(schema) {
1598
+ let cached = this._schemaCache[schema.cluster.connectionString];
1599
+
1600
+ // create a cache entry for the cluster if non yet exists.
1601
+ if (!cached) {
1602
+ this._schemaCache[schema.cluster.connectionString] = {};
1603
+ cached = this._schemaCache[schema.cluster.connectionString];
1604
+ }
1605
+
1606
+ // Remove deleted databases from cache
1607
+ const schemaDbLookup = schema.cluster.databases.reduce((prev, curr) => prev[curr.name] = curr, {});
1608
+ Object.keys(cached).map(dbName => {
1609
+ if (!schemaDbLookup[dbName]) {
1610
+ delete cached.dbName;
1611
+ }
1612
+ });
1613
+ let globalState = GlobalState.Default;
1614
+ const currentDatabaseName = schema.database ? schema.database.name : undefined;
1615
+ let databaseInContext = undefined;
1616
+
1617
+ // Update out-of-data databases to cache
1618
+ const databases = schema.cluster.databases.map(db => {
1619
+ const shouldIncludeFunctions = db.name === currentDatabaseName;
1620
+ const cachedDb = cached[db.name];
1621
+ // This is an older version than we have, or we need to parse functions.
1622
+ if (!cachedDb || cachedDb.database.majorVersion < db.majorVersion || shouldIncludeFunctions && !cachedDb.includesFunctions) {
1623
+ // only add functions for the database in context (it's very time consuming)
1624
+
1625
+ const databaseSymbol = KustoLanguageService.convertToDatabaseSymbol(db);
1626
+ cached[db.name] = {
1627
+ database: db,
1628
+ symbol: databaseSymbol,
1629
+ includesFunctions: shouldIncludeFunctions
1630
+ };
1631
+ }
1632
+ const databaseSymbol = cached[db.name].symbol;
1633
+ if (db.name === currentDatabaseName) {
1634
+ databaseInContext = databaseSymbol;
1635
+ }
1636
+ return databaseSymbol;
1637
+ });
1638
+
1639
+ // Replace new URL due to polyfill issue in IE
1640
+ // const hostname = new URL(schema.cluster.connectionString.split(';')[0]).hostname;
1641
+ const hostname = schema.cluster.connectionString.match(/(.*\/\/)?([^\/;]*)/)[2];
1642
+ const clusterName = hostname.split('.')[0];
1643
+ const clusterSymbol = new sym.ClusterSymbol.ctor(clusterName, databases);
1644
+ globalState = globalState.WithCluster(clusterSymbol);
1645
+ if (databaseInContext) {
1646
+ globalState = globalState.WithDatabase(databaseInContext);
1647
+ }
1648
+
1649
+ // Inject global scalar parameters to global scope.
1650
+ const scalarParameters = (schema.globalScalarParameters ?? []).map(param => KustoLanguageService.createParameterSymbol(param));
1651
+
1652
+ // Inject global tabular parameters to global scope.
1653
+ let tabularParameters = (schema.globalTabularParameters ?? []).map(param => KustoLanguageService.createTabularParameterSymbol(param));
1654
+ if (tabularParameters.length || scalarParameters.length) {
1655
+ globalState = globalState.WithParameters(KustoLanguageService.toBridgeList([...scalarParameters, ...tabularParameters]));
1656
+ }
1657
+ return globalState;
1658
+ }
1659
+ getClassificationsFromParseResult(offset = 0) {
1660
+ const classifications = this.toArray(this._parser.Results).map(command => this.toArray(command.Tokens)).reduce((prev, curr) => prev.concat(curr), []).map(cslCommandToken => {
1661
+ const range = new k2.ClassifiedRange(this.tokenKindToClassificationKind(cslCommandToken.TokenKind), cslCommandToken.AbsoluteStart + offset, cslCommandToken.Length);
1662
+ return range;
1663
+ });
1664
+ return classifications;
1665
+ }
1666
+
1667
+ /**
1668
+ * trim trailing newlines from range
1669
+ */
1670
+ static trimTrailingNewlineFromRange(textInRange, rangeStartOffset, document, range) {
1671
+ let currentIndex = textInRange.length - 1;
1672
+ while (textInRange[currentIndex] === '\r' || textInRange[currentIndex] === '\n') {
1673
+ --currentIndex;
1674
+ }
1675
+ const newEndOffset = rangeStartOffset + currentIndex + 1;
1676
+ const newEndPosition = document.positionAt(newEndOffset);
1677
+ const newRange = ls.Range.create(range.start, newEndPosition);
1678
+ return newRange;
1679
+ }
1680
+
1681
+ /**
1682
+ * Maps numbers to strings, such that if a>b numerically, f(a)>f(b) lexicographically.
1683
+ * 1 -> "a", 26 -> "z", 27 -> "za", 28 -> "zb", 52 -> "zz", 53 ->"zza"
1684
+ * @param order - The number to be converted to a sorting-string. order should start at 1.
1685
+ * @returns A string repenting the order.
1686
+ */
1687
+ getSortText(order) {
1688
+ if (order <= 0) {
1689
+ throw new RangeError(`order should be a number >= 1. instead got ${order}`);
1690
+ }
1691
+ let sortText = '';
1692
+ let numCharacters = 26; // "z" - "a" + 1;
1693
+
1694
+ let div = Math.floor(order / numCharacters);
1695
+ for (let i = 0; i < div; ++i) {
1696
+ sortText += 'z';
1697
+ }
1698
+ let reminder = order % numCharacters;
1699
+ if (reminder > 0) {
1700
+ sortText += String.fromCharCode(96 + reminder);
1701
+ }
1702
+ return sortText;
1703
+ }
1704
+
1705
+ /**
1706
+ * ParseTextV1 parses the given text with the given parse mode.
1707
+ * Additionally - it will make sure not to provide rules provider for non-engine clusters
1708
+ * since the only rules provider parse can handle is the engine's. It will try to look for function
1709
+ * definitions to colorize and will throw since they're not there.
1710
+ * @param text
1711
+ * @param parseMode
1712
+ */
1713
+ parseTextV1(text, parseMode) {
1714
+ this._parser.Parse(this._schema.clusterType === 'Engine' ? this._rulesProvider : null, text, parseMode);
1715
+ }
1716
+ parseDocumentV1(document, parseMode) {
1717
+ // already parsed a later version, or better parse mode for this uri
1718
+ if (this._parsePropertiesV1 && !this._parsePropertiesV1.isParseNeeded(document, this._rulesProvider, parseMode)) {
1719
+ return;
1720
+ }
1721
+ this.parseTextV1(document.getText(), parseMode);
1722
+ this._parsePropertiesV1 = new ParseProperties(document.version, document.uri, this._rulesProvider, parseMode);
1723
+ }
1724
+ parseDocumentV2(document) {
1725
+ if (this._parsePropertiesV2 && !this._parsePropertiesV2.isParseNeeded(document, this._rulesProvider)) {
1726
+ return this._script;
1727
+ }
1728
+ if (!this._script) {
1729
+ this._script = k2.CodeScript.From$1(document.getText(), this._kustoJsSchemaV2);
1730
+ } else {
1731
+ this._script = this._script.WithText(document.getText());
1732
+ }
1733
+ this._parsePropertiesV2 = new ParseProperties(document.version, document.uri);
1734
+ return this._script;
1735
+ }
1736
+
1737
+ /**
1738
+ * Return the CslCommand that wraps the caret location, or undefined if caret is outside any command
1739
+ * @param document the document to extract the current command from
1740
+ * @param caretAbsolutePosition absolute caret position
1741
+ */
1742
+ getCurrentCommand(document, caretAbsolutePosition) {
1743
+ let commands = this.toArray(this._parser.Results);
1744
+ let command = commands.filter(command => command.AbsoluteStart <= caretAbsolutePosition && command.AbsoluteEnd >= caretAbsolutePosition)[0];
1745
+
1746
+ // There is an edge case when cursor appears at the end of the command
1747
+ // which is not yet considered to be part of the parsed command (therefore: +1 for the AbsoluteEdit property)
1748
+ if (!command) {
1749
+ command = commands.filter(command => command.AbsoluteStart <= caretAbsolutePosition && command.AbsoluteEnd + 1 >= caretAbsolutePosition)[0];
1750
+
1751
+ // If we have 2 newlines in the end of the text the cursor is _probably_ at the end of the text
1752
+ // which this means that we're not actually standing on any command. Thus return null.
1753
+ if (!command || command.Text.endsWith('\r\n\r\n')) {
1754
+ return null;
1755
+ }
1756
+ }
1757
+ return command;
1758
+ }
1759
+ getCurrentCommandV2(script, offset) {
1760
+ let block = script.GetBlockAtPosition(offset);
1761
+ return block;
1762
+ }
1763
+ getTextToInsert(rule, option) {
1764
+ const beforeApplyInfo = rule.GetBeforeApplyInfo(option.Value);
1765
+ const afterApplyInfo = rule.GetAfterApplyInfo(option.Value);
1766
+
1767
+ // this is the basic text to be inserted,
1768
+ // but we still need to figure out where the cursor will end up after completion is applied.
1769
+ let insertText = beforeApplyInfo.Text || '' + option.Value + afterApplyInfo.Text || '';
1770
+ let insertTextFormat = ls.InsertTextFormat.PlainText;
1771
+ const snippetFinalTabStop = '$0';
1772
+ if (afterApplyInfo.OffsetToken && afterApplyInfo.OffsetPosition) {
1773
+ const tokenOffset = insertText.indexOf(afterApplyInfo.OffsetToken);
1774
+ if (tokenOffset >= 0) {
1775
+ insertText = this.insertToString(insertText, snippetFinalTabStop, tokenOffset - insertText.length + afterApplyInfo.OffsetPosition);
1776
+ insertTextFormat = ls.InsertTextFormat.Snippet;
1777
+ }
1778
+ } else if (afterApplyInfo.OffsetPosition) {
1779
+ // We only handle negative offsets
1780
+ insertText = this.insertToString(insertText, snippetFinalTabStop, afterApplyInfo.OffsetPosition);
1781
+ insertTextFormat = ls.InsertTextFormat.Snippet;
1782
+ }
1783
+ return {
1784
+ insertText,
1785
+ insertTextFormat
1786
+ };
1787
+ }
1788
+
1789
+ /**
1790
+ * create a new string with stringToInsert inserted at offsetFromEnd in originalString.
1791
+ * @param originalString string to insert to
1792
+ * @param stringToInsert string to insert
1793
+ * @param offsetFromEnd a negative number that will represent offset to the left. 0 means simple concat
1794
+ */
1795
+ insertToString(originalString, stringToInsert, offsetFromEnd) {
1796
+ let index = originalString.length + offsetFromEnd;
1797
+ if (offsetFromEnd >= 0 || index < 0) {
1798
+ return originalString; // Cannot insert before or after the string
1799
+ }
1800
+
1801
+ let before = originalString.substring(0, index);
1802
+ let after = originalString.substring(index);
1803
+ return before + stringToInsert + after;
1804
+ }
1805
+ getCommandWithoutLastWord(text) {
1806
+ const lastWordRegex = XRegExp('[\\w_]*$', 's');
1807
+ return text.replace(lastWordRegex, '');
1808
+ }
1809
+ createRulesProvider(schema, clusterType) {
1810
+ let queryParameters = new (List(String))();
1811
+ let availableClusters = new (List(String))();
1812
+ this._parser = new k.CslCommandParser();
1813
+ if (clusterType == 'Engine') {
1814
+ const engineSchema = schema;
1815
+ 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);
1816
+ return;
1817
+ }
1818
+ if (clusterType === 'DataManagement') {
1819
+ this._rulesProvider = new k.DataManagerIntelliSenseRulesProvider(null);
1820
+ return;
1821
+ }
1822
+
1823
+ // This is a cluster manger
1824
+ const {
1825
+ accounts,
1826
+ services,
1827
+ connectionString
1828
+ } = schema;
1829
+ new k.KustoIntelliSenseAccountEntity();
1830
+ new k.KustoIntelliSenseServiceEntity();
1831
+ this._rulesProvider = new k.ClusterManagerIntelliSenseRulesProvider.$ctor1(new Bridge.ArrayEnumerable(accounts), new Bridge.ArrayEnumerable(services), connectionString);
1832
+ }
1833
+ _kustoKindtolsKind = {
1834
+ [k.OptionKind.None]: ls.CompletionItemKind.Interface,
1835
+ [k.OptionKind.Operator]: ls.CompletionItemKind.Method,
1836
+ [k.OptionKind.Command]: ls.CompletionItemKind.Method,
1837
+ [k.OptionKind.Service]: ls.CompletionItemKind.Class,
1838
+ [k.OptionKind.Policy]: ls.CompletionItemKind.Reference,
1839
+ [k.OptionKind.Database]: ls.CompletionItemKind.Class,
1840
+ [k.OptionKind.Table]: ls.CompletionItemKind.Class,
1841
+ [k.OptionKind.DataType]: ls.CompletionItemKind.Class,
1842
+ [k.OptionKind.Literal]: ls.CompletionItemKind.Property,
1843
+ [k.OptionKind.Parameter]: ls.CompletionItemKind.Variable,
1844
+ [k.OptionKind.IngestionMapping]: ls.CompletionItemKind.Variable,
1845
+ [k.OptionKind.ExpressionFunction]: ls.CompletionItemKind.Variable,
1846
+ [k.OptionKind.Option]: ls.CompletionItemKind.Interface,
1847
+ [k.OptionKind.OptionKind]: ls.CompletionItemKind.Interface,
1848
+ [k.OptionKind.OptionRender]: ls.CompletionItemKind.Interface,
1849
+ [k.OptionKind.Column]: ls.CompletionItemKind.Function,
1850
+ [k.OptionKind.ColumnString]: ls.CompletionItemKind.Field,
1851
+ [k.OptionKind.ColumnNumeric]: ls.CompletionItemKind.Field,
1852
+ [k.OptionKind.ColumnDateTime]: ls.CompletionItemKind.Field,
1853
+ [k.OptionKind.ColumnTimespan]: ls.CompletionItemKind.Field,
1854
+ [k.OptionKind.FunctionServerSide]: ls.CompletionItemKind.Field,
1855
+ [k.OptionKind.FunctionAggregation]: ls.CompletionItemKind.Field,
1856
+ [k.OptionKind.FunctionFilter]: ls.CompletionItemKind.Field,
1857
+ [k.OptionKind.FunctionScalar]: ls.CompletionItemKind.Field,
1858
+ [k.OptionKind.ClientDirective]: ls.CompletionItemKind.Enum
1859
+ };
1860
+ _kustoKindToLsKindV2 = {
1861
+ [k2.CompletionKind.AggregateFunction]: ls.CompletionItemKind.Field,
1862
+ [k2.CompletionKind.BuiltInFunction]: ls.CompletionItemKind.Field,
1863
+ [k2.CompletionKind.Cluster]: ls.CompletionItemKind.Class,
1864
+ [k2.CompletionKind.Column]: ls.CompletionItemKind.Function,
1865
+ [k2.CompletionKind.CommandPrefix]: ls.CompletionItemKind.Field,
1866
+ [k2.CompletionKind.Database]: ls.CompletionItemKind.Class,
1867
+ [k2.CompletionKind.DatabaseFunction]: ls.CompletionItemKind.Field,
1868
+ [k2.CompletionKind.Example]: ls.CompletionItemKind.Text,
1869
+ [k2.CompletionKind.Identifier]: ls.CompletionItemKind.Method,
1870
+ [k2.CompletionKind.Keyword]: ls.CompletionItemKind.Method,
1871
+ [k2.CompletionKind.LocalFunction]: ls.CompletionItemKind.Field,
1872
+ [k2.CompletionKind.MaterialiedView]: ls.CompletionItemKind.Class,
1873
+ [k2.CompletionKind.Parameter]: ls.CompletionItemKind.Variable,
1874
+ [k2.CompletionKind.Punctuation]: ls.CompletionItemKind.Interface,
1875
+ [k2.CompletionKind.QueryPrefix]: ls.CompletionItemKind.Function,
1876
+ [k2.CompletionKind.RenderChart]: ls.CompletionItemKind.Method,
1877
+ [k2.CompletionKind.ScalarInfix]: ls.CompletionItemKind.Field,
1878
+ [k2.CompletionKind.ScalarPrefix]: ls.CompletionItemKind.Field,
1879
+ [k2.CompletionKind.ScalarType]: ls.CompletionItemKind.TypeParameter,
1880
+ [k2.CompletionKind.Syntax]: ls.CompletionItemKind.Method,
1881
+ [k2.CompletionKind.Table]: ls.CompletionItemKind.Class,
1882
+ [k2.CompletionKind.TabularPrefix]: ls.CompletionItemKind.Field,
1883
+ // datatable, externaldata
1884
+ [k2.CompletionKind.TabularSuffix]: ls.CompletionItemKind.Field,
1885
+ [k2.CompletionKind.Unknown]: ls.CompletionItemKind.Interface,
1886
+ [k2.CompletionKind.Variable]: ls.CompletionItemKind.Variable,
1887
+ [k2.CompletionKind.Option]: ls.CompletionItemKind.Text,
1888
+ [k2.CompletionKind.Graph]: ls.CompletionItemKind.Class
1889
+ };
1890
+ kustoKindToLsKind(kustoKind) {
1891
+ let res = this._kustoKindtolsKind[kustoKind];
1892
+ return res ? res : ls.CompletionItemKind.Variable;
1893
+ }
1894
+ kustoKindToLsKindV2(kustoKind) {
1895
+ let res = this._kustoKindToLsKindV2[kustoKind];
1896
+ return res ? res : ls.CompletionItemKind.Variable;
1897
+ }
1898
+ createRange(document, start, end) {
1899
+ return ls.Range.create(document.positionAt(start), document.positionAt(end));
1900
+ }
1901
+ toArray(bridgeList) {
1902
+ return Bridge.toArray(bridgeList);
1903
+ }
1904
+ static toBridgeList(array) {
1905
+ // copied from bridge.js from the implementation of Enumerable.prototype.toList
1906
+ return new (System.Collections.Generic.List$1(System.Object).$ctor1)(array);
1907
+ }
1908
+ _tokenKindToClassificationKind = {
1909
+ [TokenKind.TableToken]: k2.ClassificationKind.Table,
1910
+ [TokenKind.TableColumnToken]: k2.ClassificationKind.Column,
1911
+ [TokenKind.OperatorToken]: k2.ClassificationKind.QueryOperator,
1912
+ [TokenKind.SubOperatorToken]: k2.ClassificationKind.Function,
1913
+ [TokenKind.CalculatedColumnToken]: k2.ClassificationKind.Column,
1914
+ [TokenKind.StringLiteralToken]: k2.ClassificationKind.Literal,
1915
+ [TokenKind.FunctionNameToken]: k2.ClassificationKind.Function,
1916
+ [TokenKind.UnknownToken]: k2.ClassificationKind.PlainText,
1917
+ [TokenKind.CommentToken]: k2.ClassificationKind.Comment,
1918
+ [TokenKind.PlainTextToken]: k2.ClassificationKind.PlainText,
1919
+ [TokenKind.DataTypeToken]: k2.ClassificationKind.Type,
1920
+ [TokenKind.ControlCommandToken]: k2.ClassificationKind.PlainText,
1921
+ // TODO ?
1922
+ [TokenKind.CommandPartToken]: k2.ClassificationKind.PlainText,
1923
+ // TODO ?
1924
+ [TokenKind.QueryParametersToken]: k2.ClassificationKind.QueryParameter,
1925
+ [TokenKind.CslCommandToken]: k2.ClassificationKind.Keyword,
1926
+ // TODO ?
1927
+ [TokenKind.LetVariablesToken]: k2.ClassificationKind.Identifier,
1928
+ // TODO ?
1929
+ [TokenKind.PluginToken]: k2.ClassificationKind.Function,
1930
+ [TokenKind.BracketRangeToken]: k2.ClassificationKind.Keyword,
1931
+ // TODO ?
1932
+ [TokenKind.ClientDirectiveToken]: k2.ClassificationKind.Keyword // TODO ?
1933
+ };
1934
+
1935
+ tokenKindToClassificationKind(token) {
1936
+ const conversion = this._tokenKindToClassificationKind[token];
1937
+ return conversion || k2.ClassificationKind.PlainText;
1938
+ }
1939
+ parseAndAnalyze(document, cursorOffset) {
1940
+ if (!document || !this.isIntellisenseV2()) {
1941
+ return undefined;
1942
+ }
1943
+ const script = this.parseDocumentV2(document);
1944
+ let text = script.Text;
1945
+ if (cursorOffset !== undefined) {
1946
+ let currentBlock = this.getCurrentCommandV2(script, cursorOffset);
1947
+ if (!currentBlock) {
1948
+ return undefined;
1949
+ }
1950
+ text = currentBlock.Text;
1951
+ }
1952
+ const parsedAndAnalyzed = Kusto.Language.KustoCode.ParseAndAnalyze(text, this._kustoJsSchemaV2);
1953
+ return parsedAndAnalyzed;
1954
+ }
1955
+ }
1956
+ let languageService = new KustoLanguageService(KustoLanguageService.dummySchema, {
1957
+ includeControlCommands: true,
1958
+ useIntellisenseV2: true,
1959
+ useSemanticColorization: true
1960
+ });
1961
+
1962
+ /**
1963
+ * Obtain an instance of the kusto language service.
1964
+ */
1965
+ function getKustoLanguageService() {
1966
+ return languageService;
1967
+ }
1968
+
1969
+ class KustoWorker {
1970
+ // --- model sync -----------------------
1971
+
1972
+ constructor(ctx, createData) {
1973
+ this._ctx = ctx;
1974
+ this._languageSettings = createData.languageSettings;
1975
+ this._languageService = getKustoLanguageService();
1976
+ this._languageService.configure(this._languageSettings);
1977
+ }
1978
+
1979
+ // --- language service host ---------------
1980
+
1981
+ setSchema(schema) {
1982
+ return this._languageService.setSchema(schema);
1983
+ }
1984
+ addClusterToSchema(uri, clusterName, databasesNames) {
1985
+ const document = this._getTextDocument(uri);
1986
+ if (!document) {
1987
+ console.error(`addClusterToSchema: document is ${document}. uri is ${uri}`);
1988
+ return Promise.resolve();
1989
+ }
1990
+ return this._languageService.addClusterToSchema(document, clusterName, databasesNames);
1991
+ }
1992
+ addDatabaseToSchema(uri, clusterName, databaseSchema) {
1993
+ const document = this._getTextDocument(uri);
1994
+ if (!document) {
1995
+ console.error(`addDatabaseToSchema: document is ${document}. uri is ${uri}`);
1996
+ return Promise.resolve();
1997
+ }
1998
+ return this._languageService.addDatabaseToSchema(document, clusterName, databaseSchema);
1999
+ }
2000
+ setSchemaFromShowSchema(schema, clusterConnectionString, databaseInContextName) {
2001
+ return this._languageService.setSchemaFromShowSchema(schema, clusterConnectionString, databaseInContextName);
2002
+ }
2003
+ normalizeSchema(schema, clusterConnectionString, databaseInContextName) {
2004
+ return this._languageService.normalizeSchema(schema, clusterConnectionString, databaseInContextName);
2005
+ }
2006
+ getSchema() {
2007
+ return this._languageService.getSchema();
2008
+ }
2009
+ getCommandInContext(uri, cursorOffset) {
2010
+ const document = this._getTextDocument(uri);
2011
+ if (!document) {
2012
+ console.error(`getCommandInContext: document is ${document}. uri is ${uri}`);
2013
+ return null;
2014
+ }
2015
+ const commandInContext = this._languageService.getCommandInContext(document, cursorOffset);
2016
+ if (commandInContext === undefined) {
2017
+ return null;
2018
+ }
2019
+ return commandInContext;
2020
+ }
2021
+ getQueryParams(uri, cursorOffset) {
2022
+ const document = this._getTextDocument(uri);
2023
+ if (!document) {
2024
+ console.error(`getQueryParams: document is ${document}. uri is ${uri}`);
2025
+ return null;
2026
+ }
2027
+ const queryParams = this._languageService.getQueryParams(document, cursorOffset);
2028
+ if (queryParams === undefined) {
2029
+ return null;
2030
+ }
2031
+ return queryParams;
2032
+ }
2033
+ getGlobalParams(uri) {
2034
+ const document = this._getTextDocument(uri);
2035
+ if (!document) {
2036
+ console.error(`getGLobalParams: document is ${document}. uri is ${uri}`);
2037
+ return null;
2038
+ }
2039
+ const globalParams = this._languageService.getGlobalParams(document);
2040
+ if (globalParams === undefined) {
2041
+ return null;
2042
+ }
2043
+ return globalParams;
2044
+ }
2045
+ getReferencedSymbols(uri, cursorOffset) {
2046
+ const document = this._getTextDocument(uri);
2047
+ if (!document) {
2048
+ console.error(`getReferencedGlobalParams: document is ${document}. uri is ${uri}`);
2049
+ return null;
2050
+ }
2051
+ const referencedParams = this._languageService.getReferencedSymbols(document, cursorOffset);
2052
+ if (referencedParams === undefined) {
2053
+ return null;
2054
+ }
2055
+ return referencedParams;
2056
+ }
2057
+ getReferencedGlobalParams(uri, cursorOffset) {
2058
+ const document = this._getTextDocument(uri);
2059
+ if (!document) {
2060
+ console.error(`getReferencedGlobalParams: document is ${document}. uri is ${uri}`);
2061
+ return null;
2062
+ }
2063
+ const referencedParams = this._languageService.getReferencedGlobalParams(document, cursorOffset);
2064
+ if (referencedParams === undefined) {
2065
+ return null;
2066
+ }
2067
+ return referencedParams;
2068
+ }
2069
+ getRenderInfo(uri, cursorOffset) {
2070
+ const document = this._getTextDocument(uri);
2071
+ if (!document) {
2072
+ console.error(`getRenderInfo: document is ${document}. uri is ${uri}`);
2073
+ }
2074
+ return this._languageService.getRenderInfo(document, cursorOffset).then(result => {
2075
+ if (!result) {
2076
+ return null;
2077
+ }
2078
+ return result;
2079
+ });
2080
+ }
2081
+
2082
+ /**
2083
+ * Get command in context and the command range.
2084
+ * This method will basically convert generate microsoft language service interface to monaco interface.
2085
+ * @param uri document URI
2086
+ * @param cursorOffset offset from start of document to cursor
2087
+ */
2088
+ getCommandAndLocationInContext(uri, cursorOffset) {
2089
+ const document = this._getTextDocument(uri);
2090
+ if (!document) {
2091
+ console.error(`getCommandAndLocationInContext: document is ${document}. uri is ${uri}`);
2092
+ return Promise.resolve(null);
2093
+ }
2094
+ return this._languageService.getCommandAndLocationInContext(document, cursorOffset).then(result => {
2095
+ if (!result) {
2096
+ return null;
2097
+ }
2098
+
2099
+ // convert to monaco object.
2100
+ const {
2101
+ text,
2102
+ location: {
2103
+ range: {
2104
+ start,
2105
+ end
2106
+ }
2107
+ }
2108
+ } = result;
2109
+ const range = new monaco.Range(start.line + 1, start.character + 1, end.line + 1, end.character + 1);
2110
+ return {
2111
+ range,
2112
+ text
2113
+ };
7
2114
  });
2115
+ }
2116
+ getCommandsInDocument(uri) {
2117
+ const document = this._getTextDocument(uri);
2118
+ if (!document) {
2119
+ console.error(`getCommandInDocument: document is ${document}. uri is ${uri}`);
2120
+ return null;
2121
+ }
2122
+ return this._languageService.getCommandsInDocument(document);
2123
+ }
2124
+ doComplete(uri, position) {
2125
+ let document = this._getTextDocument(uri);
2126
+ if (!document) {
2127
+ return null;
2128
+ }
2129
+ let completions = this._languageService.doComplete(document, position);
2130
+ return completions;
2131
+ }
2132
+ doValidation(uri, intervals, includeWarnings, includeSuggestions) {
2133
+ const document = this._getTextDocument(uri);
2134
+ const diagnostics = this._languageService.doValidation(document, intervals, includeWarnings, includeSuggestions);
2135
+ return diagnostics;
2136
+ }
2137
+ getResultActions(uri, start, end) {
2138
+ const document = this._getTextDocument(uri);
2139
+ return this._languageService.getResultActions(document, start, end);
2140
+ }
2141
+ doRangeFormat(uri, range) {
2142
+ const document = this._getTextDocument(uri);
2143
+ const formatted = this._languageService.doRangeFormat(document, range);
2144
+ return formatted;
2145
+ }
2146
+ doFolding(uri) {
2147
+ const document = this._getTextDocument(uri);
2148
+ const folding = this._languageService.doFolding(document);
2149
+ return folding;
2150
+ }
2151
+ doDocumentFormat(uri) {
2152
+ const document = this._getTextDocument(uri);
2153
+ const formatted = this._languageService.doDocumentFormat(document);
2154
+ return formatted;
2155
+ }
2156
+ doCurrentCommandFormat(uri, caretPosition) {
2157
+ const document = this._getTextDocument(uri);
2158
+ const formatted = this._languageService.doCurrentCommandFormat(document, caretPosition);
2159
+ return formatted;
2160
+ }
2161
+
2162
+ // Colorize document. if offsets provided, will only colorize commands at these offsets. otherwise - will color the entire document.
2163
+ doColorization(uri, colorizationIntervals) {
2164
+ const document = this._getTextDocument(uri);
2165
+ const colorizationInfo = this._languageService.doColorization(document, colorizationIntervals);
2166
+ return colorizationInfo;
2167
+ }
2168
+ getClientDirective(text) {
2169
+ return this._languageService.getClientDirective(text);
2170
+ }
2171
+ getAdminCommand(text) {
2172
+ return this._languageService.getAdminCommand(text);
2173
+ }
2174
+ findDefinition(uri, position) {
2175
+ const document = this._getTextDocument(uri);
2176
+ const definition = this._languageService.findDefinition(document, position);
2177
+ return definition;
2178
+ }
2179
+ findReferences(uri, position) {
2180
+ let document = this._getTextDocument(uri);
2181
+ const references = this._languageService.findReferences(document, position);
2182
+ return references;
2183
+ }
2184
+ doRename(uri, position, newName) {
2185
+ const document = this._getTextDocument(uri);
2186
+ const workspaceEdit = this._languageService.doRename(document, position, newName);
2187
+ return workspaceEdit;
2188
+ }
2189
+ doHover(uri, position) {
2190
+ let document = this._getTextDocument(uri);
2191
+ let hover = this._languageService.doHover(document, position);
2192
+ return hover;
2193
+ }
2194
+ setParameters(scalarParameters, tabularParameters) {
2195
+ return this._languageService.setParameters(scalarParameters, tabularParameters);
2196
+ }
2197
+ getClusterReferences(uri, cursorOffset) {
2198
+ let document = this._getTextDocument(uri);
2199
+ if (!document) {
2200
+ return Promise.resolve(null);
2201
+ }
2202
+ return this._languageService.getClusterReferences(document, cursorOffset);
2203
+ }
2204
+ getDatabaseReferences(uri, cursorOffset) {
2205
+ let document = this._getTextDocument(uri);
2206
+ if (!document) {
2207
+ return Promise.resolve(null);
2208
+ }
2209
+ return this._languageService.getDatabaseReferences(document, cursorOffset);
2210
+ }
2211
+ _getTextDocument(uri) {
2212
+ let models = this._ctx.getMirrorModels();
2213
+ for (let model of models) {
2214
+ if (model.uri.toString() === uri) {
2215
+ return ls.TextDocument.create(uri, this._languageId, model.version, model.getValue());
2216
+ }
2217
+ }
2218
+ return null;
2219
+ }
2220
+ }
2221
+
2222
+ self.onmessage = () => {
2223
+ // ignore the first message
2224
+ worker.initialize((ctx, createData) => {
2225
+ return new KustoWorker(ctx, createData);
2226
+ });
8
2227
  };