UNPKG

@kusto/monaco-kusto

Version:

CSL, KQL plugin for the Monaco Editor

729 lines (728 loc) 33.1 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; import * as ls from 'vscode-languageserver-types'; import debounce from 'lodash-es/debounce'; import { createCompletionCacheManager } from './completionCacheManager/completionCacheManager'; import { createCompletionFilteredText, getFocusedItem } from './languageFeatures.utils'; var DiagnosticsAdapter = /** @class */ (function () { function DiagnosticsAdapter(_monacoInstance, _languageId, _worker, defaults, onSchemaChange) { var _this = this; this._monacoInstance = _monacoInstance; this._languageId = _languageId; this._worker = _worker; this.defaults = defaults; this._disposables = []; this._contentListener = Object.create(null); this._configurationListener = Object.create(null); this._schemaListener = Object.create(null); this._cursorListener = Object.create(null); this._debouncedValidations = Object.create(null); var onModelAdd = function (model) { var languageId = model.getLanguageId(); var modelUri = model.uri.toString(); if (languageId !== _this._languageId) { return; } var debouncedValidation = _this.getOrCreateDebouncedValidation(model, languageId); _this._contentListener[modelUri] = model.onDidChangeContent(function (e) { var intervalsToValidate = changeEventToIntervals(e); debouncedValidation(intervalsToValidate); }); _this._configurationListener[modelUri] = _this.defaults.onDidChange(function () { self.setTimeout(function () { return _this._doValidate(model, languageId, []); }, 0); }); _this._schemaListener[modelUri] = onSchemaChange(function () { self.setTimeout(function () { return _this._doValidate(model, languageId, []); }, 0); }); }; var onEditorAdd = function (editor) { var editorId = editor.getId(); if (!_this._cursorListener[editorId]) { editor.onDidDispose(function () { var _a; (_a = _this._cursorListener[editorId]) === null || _a === void 0 ? void 0 : _a.dispose(); delete _this._cursorListener[editorId]; }); _this._cursorListener[editorId] = editor.onDidChangeCursorSelection(function (e) { var model = editor.getModel(); var languageId = model.getLanguageId(); if (languageId !== _this._languageId) { return; } var cursorOffset = model.getOffsetAt(e.selection.getPosition()); var debouncedValidation = _this.getOrCreateDebouncedValidation(model, languageId); debouncedValidation([{ start: cursorOffset, end: cursorOffset }]); }); } }; var onModelRemoved = function (model) { _this._monacoInstance.editor.setModelMarkers(model, _this._languageId, []); var uriStr = model.uri.toString(); var contentListener = _this._contentListener[uriStr]; if (contentListener) { contentListener.dispose(); delete _this._contentListener[uriStr]; } var configurationListener = _this._configurationListener[uriStr]; if (configurationListener) { configurationListener.dispose(); delete _this._configurationListener[uriStr]; } var schemaListener = _this._schemaListener[uriStr]; if (schemaListener) { schemaListener.dispose(); delete _this._schemaListener[uriStr]; } var debouncedValidation = _this._debouncedValidations[uriStr]; if (debouncedValidation) { debouncedValidation.cancel(); delete _this._debouncedValidations[uriStr]; } }; if (this.defaults.languageSettings.enableQuickFixes) { this._disposables.push(monaco.languages.registerCodeActionProvider(this._languageId, { provideCodeActions: function (model, range, context, _token) { return __awaiter(_this, void 0, void 0, function () { var startOffset, endOffset, showQuickFix, actions; return __generator(this, function (_a) { switch (_a.label) { case 0: startOffset = model.getOffsetAt(range.getStartPosition()); endOffset = model.getOffsetAt(range.getEndPosition()); showQuickFix = context.markers.length > 0; return [4 /*yield*/, this.getMonacoCodeActions(model, startOffset, endOffset, showQuickFix)]; case 1: actions = _a.sent(); return [2 /*return*/, { actions: actions, dispose: function () { }, }]; } }); }); }, })); } this._disposables.push(this._monacoInstance.editor.onDidCreateEditor(onEditorAdd)); this._disposables.push(this._monacoInstance.editor.onDidCreateModel(onModelAdd)); this._disposables.push(this._monacoInstance.editor.onWillDisposeModel(onModelRemoved)); this._disposables.push(this._monacoInstance.editor.onDidChangeModelLanguage(function (event) { onModelRemoved(event.model); onModelAdd(event.model); })); this._disposables.push({ dispose: function () { for (var key in _this._contentListener) { _this._contentListener[key].dispose(); } for (var key in _this._cursorListener) { _this._cursorListener[key].dispose(); } for (var key in _this._debouncedValidations) { _this._debouncedValidations[key].cancel(); } }, }); this._monacoInstance.editor.getModels().forEach(onModelAdd); this._monacoInstance.editor.getEditors().forEach(onEditorAdd); } DiagnosticsAdapter.prototype.getMonacoCodeActions = function (model, startOffset, endOffset, enableQuickFix) { return __awaiter(this, void 0, void 0, function () { var actions, worker, resource, codeActions, _loop_1, this_1, i, state_1; var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: actions = []; return [4 /*yield*/, this._worker(model.uri)]; case 1: worker = _b.sent(); resource = model.uri; return [4 /*yield*/, worker.getResultActions(resource.toString(), startOffset, endOffset)]; case 2: codeActions = _b.sent(); _loop_1 = function (i) { var codeAction = codeActions[i]; if (codeAction.kind.includes('Extract Function')) { return "continue"; } var codeActionKind = ((_a = this_1.defaults.languageSettings.quickFixCodeActions) === null || _a === void 0 ? void 0 : _a.find(function (actionKind) { return codeAction.kind.includes(actionKind); })) ? 'quickfix' : 'custom'; if (codeActionKind === 'quickfix' && !enableQuickFix) { return { value: void 0 }; } var changes = codeAction.changes; var edits = changes.map(function (change) { var _a; var startPosition = model.getPositionAt(change.start); var endPosition = model.getPositionAt(change.start + change.deleteLength); return { resource: model.uri, textEdit: { range: { startLineNumber: startPosition.lineNumber, startColumn: startPosition.column, endLineNumber: endPosition.lineNumber, endColumn: endPosition.column, }, text: (_a = change.insertText) !== null && _a !== void 0 ? _a : '', }, }; }); actions.push({ title: codeAction.title, diagnostics: [], kind: codeActionKind, edit: { edits: __spreadArray([], edits, true), }, }); }; this_1 = this; for (i = 0; i < codeActions.length; i++) { state_1 = _loop_1(i); if (typeof state_1 === "object") return [2 /*return*/, state_1.value]; } return [2 /*return*/, actions]; } }); }); }; DiagnosticsAdapter.prototype.getOrCreateDebouncedValidation = function (model, languageId) { var _this = this; var modelUri = model.uri.toString(); if (!this._debouncedValidations[modelUri]) { this._debouncedValidations[modelUri] = debounce(function (intervals) { return _this._doValidate(model, languageId, intervals); }, 500); } return this._debouncedValidations[modelUri]; }; DiagnosticsAdapter.prototype.dispose = function () { this._disposables.forEach(function (d) { return d && d.dispose(); }); this._disposables = []; }; DiagnosticsAdapter.prototype._doValidate = function (model, languageId, intervals) { var _this = this; if (model.isDisposed()) { return; } var resource = model.uri; var versionNumberBefore = model.getVersionId(); this._worker(resource) .then(function (worker) { return worker.doValidation(resource.toString(), intervals); }) .then(function (diagnostics) { var newModel = _this._monacoInstance.editor.getModel(resource); var versionId = newModel.getVersionId(); if (versionId !== versionNumberBefore) { return; } var markers = diagnostics.map(function (d) { return toDiagnostics(resource, d); }); var model = _this._monacoInstance.editor.getModel(resource); var oldDecorations = model .getAllDecorations() .filter(function (decoration) { return decoration.options.className == 'squiggly-error'; }) .map(function (decoration) { return decoration.id; }); if (model && model.getLanguageId() === languageId) { var syntaxErrorAsMarkDown = _this.defaults.languageSettings.syntaxErrorAsMarkDown; if (!syntaxErrorAsMarkDown || !syntaxErrorAsMarkDown.enableSyntaxErrorAsMarkDown) { // Remove previous syntax error decorations and set the new markers (for example, when disabling syntaxErrorAsMarkDown after it was enabled) model.deltaDecorations(oldDecorations, []); _this._monacoInstance.editor.setModelMarkers(model, languageId, markers); } else { // Add custom popup for syntax error: icon, header and message as markdown var header = syntaxErrorAsMarkDown.header ? "**".concat(syntaxErrorAsMarkDown.header, "** \n\n") : ''; var icon = syntaxErrorAsMarkDown.icon ? "![](".concat(syntaxErrorAsMarkDown.icon, ")") : ''; var popupErrorHoverHeaderMessage_1 = "".concat(icon, " ").concat(header); var newDecorations = markers.map(function (marker) { return { range: { startLineNumber: marker.startLineNumber, startColumn: marker.startColumn, endLineNumber: marker.endLineNumber, endColumn: marker.endColumn, }, options: { hoverMessage: { value: popupErrorHoverHeaderMessage_1 + marker.message, }, className: 'squiggly-error', // monaco syntax error style (red underline) zIndex: 100, // This message will be the upper most mesage in the popup overviewRuler: { // The color indication on the right ruler color: 'rgb(255, 18, 18, 0.7)', position: monaco.editor.OverviewRulerLane.Right, }, minimap: { color: 'rgb(255, 18, 18, 0.7)', position: monaco.editor.MinimapPosition.Inline, }, }, }; }); var oldMarkers = monaco.editor.getModelMarkers({ owner: languageId, resource: resource, }); if (oldMarkers && oldMarkers.length > 0) { // In case there were previous markers, remove their decorations (for example, when enabling syntaxErrorAsMarkDown after it was disabled) oldDecorations = []; // Remove previous markers _this._monacoInstance.editor.setModelMarkers(model, languageId, []); } // Remove previous syntax error decorations and set the new decorations model.deltaDecorations(oldDecorations, newDecorations); } } }) .then(undefined, function (err) { console.error(err); }); }; return DiagnosticsAdapter; }()); export { DiagnosticsAdapter }; function changeEventToIntervals(e) { return e.changes.map(function (change) { return ({ start: change.rangeOffset, end: change.rangeOffset + change.text.length, }); }); } function toSeverity(lsSeverity) { switch (lsSeverity) { case ls.DiagnosticSeverity.Error: return monaco.MarkerSeverity.Error; case ls.DiagnosticSeverity.Warning: return monaco.MarkerSeverity.Warning; case ls.DiagnosticSeverity.Information: return monaco.MarkerSeverity.Info; case ls.DiagnosticSeverity.Hint: return monaco.MarkerSeverity.Hint; default: return monaco.MarkerSeverity.Info; } } function toDiagnostics(resource, diag) { var code = typeof diag.code === 'number' ? String(diag.code) : diag.code; return { severity: toSeverity(diag.severity), startLineNumber: diag.range.start.line + 1, startColumn: diag.range.start.character + 1, endLineNumber: diag.range.end.line + 1, endColumn: diag.range.end.character + 1, message: diag.message, code: code, source: diag.source, }; } // --- completion ------ function fromPosition(position) { if (!position) { return void 0; } return { character: position.column - 1, line: position.lineNumber - 1 }; } function fromRange(range) { if (!range) { return void 0; } return { start: fromPosition(range.getStartPosition()), end: fromPosition(range.getEndPosition()) }; } function toRange(range) { if (!range) { return void 0; } return new monaco.Range(range.start.line + 1, range.start.character + 1, range.end.line + 1, range.end.character + 1); } function toCompletionItemKind(kind) { var mItemKind = monaco.languages.CompletionItemKind; switch (kind) { case ls.CompletionItemKind.Text: return mItemKind.Text; case ls.CompletionItemKind.Method: return mItemKind.Method; case ls.CompletionItemKind.Function: return mItemKind.Function; case ls.CompletionItemKind.Constructor: return mItemKind.Constructor; case ls.CompletionItemKind.Field: return mItemKind.Field; case ls.CompletionItemKind.Variable: return mItemKind.Variable; case ls.CompletionItemKind.Class: return mItemKind.Class; case ls.CompletionItemKind.Interface: return mItemKind.Interface; case ls.CompletionItemKind.Module: return mItemKind.Module; case ls.CompletionItemKind.Property: return mItemKind.Property; case ls.CompletionItemKind.Unit: return mItemKind.Unit; case ls.CompletionItemKind.Value: return mItemKind.Value; case ls.CompletionItemKind.Enum: return mItemKind.Enum; case ls.CompletionItemKind.Keyword: return mItemKind.Keyword; case ls.CompletionItemKind.Snippet: return mItemKind.Snippet; case ls.CompletionItemKind.Color: return mItemKind.Color; case ls.CompletionItemKind.File: return mItemKind.File; case ls.CompletionItemKind.Reference: return mItemKind.Reference; } return mItemKind.Property; } function toTextEdit(textEdit) { if (!textEdit) { return void 0; } return { range: toRange(textEdit.range), text: textEdit.newText, }; } var DEFAULT_DOCS_BASE_URL = 'https://learn.microsoft.com/azure/data-explorer/kusto/query'; var CompletionAdapter = /** @class */ (function () { function CompletionAdapter(workerAccessor, languageSettings) { var _this = this; this.languageSettings = languageSettings; var getFromLanguageService = function (resource, position) { return __awaiter(_this, void 0, void 0, function () { var worker; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, workerAccessor(resource)]; case 1: worker = _a.sent(); return [2 /*return*/, worker.doComplete(resource.toString(), position)]; } }); }); }; this.completionCacheManager = createCompletionCacheManager(getFromLanguageService); } Object.defineProperty(CompletionAdapter.prototype, "triggerCharacters", { get: function () { return [' ', '.', '(']; }, enumerable: false, configurable: true }); CompletionAdapter.prototype.provideCompletionItems = function (model, position, context, token) { var _this = this; var _a; var wordInfo = model.getWordUntilPosition(position); var wordRange = new monaco.Range(position.lineNumber, wordInfo.startColumn, position.lineNumber, wordInfo.endColumn); var resource = model.uri; var userInput = (_a = model === null || model === void 0 ? void 0 : model.getWordAtPosition(position)) === null || _a === void 0 ? void 0 : _a.word; var onDidProvideCompletionItems = this.languageSettings.onDidProvideCompletionItems; return this.completionCacheManager .getCompletionItems(userInput, resource, fromPosition(position)) .then(function (info) { return (onDidProvideCompletionItems ? onDidProvideCompletionItems(info) : info); }) .then(function (info) { if (!info) return; var selectedItem = getFocusedItem(info.items, userInput); var items = info.items.map(function (entry, index) { var _a; var item = { label: entry.label, insertText: entry.insertText, sortText: entry.sortText, filterText: createCompletionFilteredText(userInput, entry), // TODO: Is this cast safe? documentation: _this.formatDocLink((_a = entry.documentation) === null || _a === void 0 ? void 0 : _a.value), detail: entry.detail, range: wordRange, kind: toCompletionItemKind(entry.kind), preselect: selectedItem.filterText === entry.filterText, }; if (entry.textEdit) { // TODO: Where is the "range" property coming from? item.range = toRange(entry.textEdit.range); item.insertText = entry.textEdit.newText; } if (entry.insertTextFormat === ls.InsertTextFormat.Snippet) { item.insertTextRules = monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet; } return item; }); return { incomplete: true, suggestions: items, }; }); }; CompletionAdapter.prototype.formatDocLink = function (docString) { // If the docString is empty, we want to return undefined to prevent an empty documentation popup. if (!docString) { return undefined; } var _a = this.languageSettings, _b = _a.documentationBaseUrl, documentationBaseUrl = _b === void 0 ? DEFAULT_DOCS_BASE_URL : _b, documentationSuffix = _a.documentationSuffix; var urisProxy = new Proxy({}, { get: function (_target, prop, _receiver) { // The link comes with a postfix of ".md" that we want to remove var url = prop.toString().replace('.md', ''); // Sometimes we get the link as a full URL. For example in the main doc link of the item if (!url.startsWith('https')) { url = "".concat(documentationBaseUrl, "/").concat(url); } var monacoUri = monaco.Uri.parse(url); if (documentationSuffix) { // We need to override the toString method to add the suffix, otherwise it gets encoded and page doesn't open monacoUri.toString = function () { return url + documentationSuffix; }; } return monacoUri; }, }); return { value: docString, isTrusted: true, uris: urisProxy }; }; return CompletionAdapter; }()); export { CompletionAdapter }; function isMarkupContent(thing) { return thing && typeof thing === 'object' && typeof thing.kind === 'string'; } function toMarkdownString(entry) { if (typeof entry === 'string') { return { value: entry, }; } if (isMarkupContent(entry)) { if (entry.kind === 'plaintext') { return { value: entry.value.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&'), }; } return { value: entry.value, }; } return { value: '```' + entry.value + '\n' + entry.value + '\n```\n' }; } function toMarkedStringArray(contents) { if (!contents) { return void 0; } if (Array.isArray(contents)) { return contents.map(toMarkdownString); } return [toMarkdownString(contents)]; } // --- definition ------ function toLocation(location) { return { uri: monaco.Uri.parse(location.uri), range: toRange(location.range), }; } var DefinitionAdapter = /** @class */ (function () { function DefinitionAdapter(_worker) { this._worker = _worker; } DefinitionAdapter.prototype.provideDefinition = function (model, position, token) { var resource = model.uri; return this._worker(resource) .then(function (worker) { return worker.findDefinition(resource.toString(), fromPosition(position)); }) .then(function (definition) { if (!definition || definition.length == 0) { return; } return [toLocation(definition[0])]; }); }; return DefinitionAdapter; }()); export { DefinitionAdapter }; // --- references ------ var ReferenceAdapter = /** @class */ (function () { function ReferenceAdapter(_worker) { this._worker = _worker; } ReferenceAdapter.prototype.provideReferences = function (model, position, context, token) { var resource = model.uri; return this._worker(resource) .then(function (worker) { return worker.findReferences(resource.toString(), fromPosition(position)); }) .then(function (entries) { if (!entries) { return; } return entries.map(toLocation); }); }; return ReferenceAdapter; }()); export { ReferenceAdapter }; // --- rename ------ function toWorkspaceEdit(edit) { if (!edit || !edit.changes) { return void 0; } var resourceEdits = []; for (var uri in edit.changes) { var _uri = monaco.Uri.parse(uri); for (var _i = 0, _a = edit.changes[uri]; _i < _a.length; _i++) { var e = _a[_i]; resourceEdits.push({ resource: _uri, textEdit: { range: toRange(e.range), text: e.newText, }, versionId: undefined, }); } } return { edits: resourceEdits, }; } var RenameAdapter = /** @class */ (function () { function RenameAdapter(_worker) { this._worker = _worker; } RenameAdapter.prototype.provideRenameEdits = function (model, position, newName, token) { var resource = model.uri; return this._worker(resource) .then(function (worker) { return worker.doRename(resource.toString(), fromPosition(position), newName); }) .then(function (edit) { return toWorkspaceEdit(edit); }); }; return RenameAdapter; }()); export { RenameAdapter }; // --- formatting ----- var DocumentFormatAdapter = /** @class */ (function () { function DocumentFormatAdapter(_worker) { this._worker = _worker; } DocumentFormatAdapter.prototype.provideDocumentFormattingEdits = function (model, options, token) { var resource = model.uri; return this._worker(resource).then(function (worker) { return worker.doDocumentFormat(resource.toString()).then(function (edits) { return edits.map(function (edit) { return toTextEdit(edit); }); }); }); }; return DocumentFormatAdapter; }()); export { DocumentFormatAdapter }; var FormatAdapter = /** @class */ (function () { function FormatAdapter(_worker) { this._worker = _worker; } FormatAdapter.prototype.provideDocumentRangeFormattingEdits = function (model, range, options, token) { var resource = model.uri; return this._worker(resource).then(function (worker) { return worker .doRangeFormat(resource.toString(), fromRange(range)) .then(function (edits) { return edits.map(function (edit) { return toTextEdit(edit); }); }); }); }; return FormatAdapter; }()); export { FormatAdapter }; // --- Folding --- var FoldingAdapter = /** @class */ (function () { function FoldingAdapter(_worker) { this._worker = _worker; } FoldingAdapter.prototype.provideFoldingRanges = function (model, context, token) { var resource = model.uri; return this._worker(resource).then(function (worker) { return worker .doFolding(resource.toString()) .then(function (foldingRanges) { return foldingRanges.map(function (range) { return toFoldingRange(range); }); }); }); }; return FoldingAdapter; }()); export { FoldingAdapter }; function toFoldingRange(range) { return { start: range.startLine + 1, end: range.endLine + 1, kind: monaco.languages.FoldingRangeKind.Region, }; } // --- hover ------ var HoverAdapter = /** @class */ (function () { function HoverAdapter(_worker) { this._worker = _worker; } HoverAdapter.prototype.provideHover = function (model, position, token) { var resource = model.uri; return this._worker(resource) .then(function (worker) { return worker.doHover(resource.toString(), fromPosition(position)); }) .then(function (info) { if (!info) { return; } return { range: toRange(info.range), contents: toMarkedStringArray(info.contents), }; }); }; return HoverAdapter; }()); export { HoverAdapter };