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