UNPKG

@arcgis/coding-components

Version:
302 lines (301 loc) • 10.2 kB
/* COPYRIGHT Esri - https://js.arcgis.com/5.1/LICENSE.txt */ import { Emitter as C, languages as u } from "monaco-editor"; import "monaco-editor/esm/vs/editor/standalone/browser/standaloneServices.js"; import "monaco-editor/esm/vs/editor/standalone/common/standaloneTheme.js"; import "monaco-editor/esm/vs/platform/hover/browser/hover.js"; import "monaco-editor/esm/vs/editor/common/languages/supports/tokenization.js"; import "monaco-editor/esm/vs/base/browser/dom.js"; import "monaco-editor/esm/vs/editor/browser/editorDom.js"; import "monaco-editor/esm/vs/editor/browser/controller/mouseTarget.js"; import { i as g } from "./monaco-importer.js"; import { debounce as x } from "@arcgis/toolkit/function"; import { rethrowError as l } from "@arcgis/toolkit/log"; let p = Promise.withResolvers(); class k { constructor(e) { this._defaults = e, this._worker = null, this._client = null, this._modelLifecycleListeners = [], this._isDisposed = !1, this._configChangeListener = this._defaults.onDidChange(() => this.stopWorker()), this._modelContextChangeListener = this._defaults.onModelContextDidChange(() => { this._stopWorkerIfUnused(); }), this._registerModelLifecycleListeners(); } dispose() { for (this._isDisposed = !0, this._configChangeListener.dispose(), this._modelContextChangeListener.dispose(); this._modelLifecycleListeners.length; ) this._modelLifecycleListeners.pop()?.dispose(); this.stopWorker(); } stopWorker() { this._worker && (this._worker.dispose(), this._worker = null, p = Promise.withResolvers()), this._client = null; } static async waitForWorker() { return await p.promise; } // TODO: should also do this for arcade and other languages, as it ensures that workers are stopped when not in use. async _registerModelLifecycleListeners() { const e = await g(); this._isDisposed || this._modelLifecycleListeners.push( e.onWillDisposeModel(() => { this._stopWorkerIfUnused(); }), e.onDidChangeModelLanguage(() => { this._stopWorkerIfUnused(); }) ); } async _stopWorkerIfUnused() { if (!this._worker) return; (await g()).getModels().some((i) => i.getLanguageId() === this._defaults.languageId) || this.stopWorker(); } async _getClientProxy() { const e = await g(); if (!this._client) { const { languageId: t } = this._defaults, i = window.MonacoEnvironment?.getWorker; if (!i) throw new Error( "MonacoEnvironment.getWorker is not configured. Call setupMonacoEnvironment(...) before creating SQL Layer workers." ); this._worker = e.createWebWorker({ worker: i("SqlLayerWorker", t), host: this._defaults.workerHost }), p.resolve(this._worker), this._client = this._worker.getProxy(); } return await this._client; } async getLanguageServiceWorker(...e) { const t = await this._getClientProxy(); return await this._worker?.withSyncedResources(e), t; } } const b = (o) => { const e = _(o.start), t = _(o.end); return { startRegex: new RegExp(e), contentRegex: new RegExp(`[^${t}]+`), escapedRegex: new RegExp(`${t}${t}`), endRegex: new RegExp(t) }; }, M = (o) => { const e = o.specialChars.identifierQuote; return { comments: { lineComment: "--", blockComment: ["/*", "*/"] }, brackets: [ ["{", "}"], ["[", "]"], ["(", ")"] ], autoClosingPairs: [ { open: "{", close: "}" }, { open: "[", close: "]" }, { open: "(", close: ")" }, { open: '"', close: '"' }, { open: "'", close: "'" }, { open: e.start, close: e.end } ], surroundingPairs: [ { open: "{", close: "}" }, { open: "[", close: "]" }, { open: "(", close: ")" }, { open: '"', close: '"' }, { open: "'", close: "'" }, { open: e.start, close: e.end } ] }; }, S = (o) => { const e = b(o.specialChars.identifierQuote); return { defaultToken: "", tokenPostfix: ".arcgis", ignoreCase: !0, brackets: [ { open: "[", close: "]", token: "delimiter.square" }, { open: "(", close: ")", token: "delimiter.parenthesis" } ], keywords: o.keywords, operators: o.operators, builtinFunctions: o.functions, builtinVariables: [], tokenizer: { root: [ { include: "@comments" }, { include: "@whitespace" }, { include: "@numbers" }, { include: "@strings" }, { include: "@complexIdentifiers" }, { include: "@scopes" }, [/[;,.]/, "delimiter"], [/[()]/, "@brackets"], [ /[\w@#$]+/, { cases: { "@operators": "operator", "@builtinVariables": "predefined", "@builtinFunctions": "predefined", "@keywords": "keyword", "@default": "identifier" } } ], [/[<>=!%&+\-*/|~^]/, "operator"] ], whitespace: [[/\s+/, "white"]], comments: [ [/--+.*/, "comment"], [/\/\*/, { token: "comment.quote", next: "@comment" }] ], comment: [ [/[^*/]+/, "comment"], [/\*\//, { token: "comment.quote", next: "@pop" }], [/./, "comment"] ], numbers: [ [/0[xX][0-9a-fA-F]*/, "number"], [/[$][+-]*\d*(\.\d*)?/, "number"], [/((\d+(\.\d*)?)|(\.\d+))([eE][\-+]?\d+)?/, "number"] ], strings: [ [/N'/, { token: "string", next: "@string" }], [/'/, { token: "string", next: "@string" }] ], string: [ [/[^']+/, "string"], [/''/, "string"], [/'/, { token: "string", next: "@pop" }] ], complexIdentifiers: [[e.startRegex, { token: "identifier.quote", next: "@quotedIdentifier" }]], quotedIdentifier: [ [e.contentRegex, "identifier"], [e.escapedRegex, "identifier"], [e.endRegex, { token: "identifier.quote", next: "@pop" }] ], scopes: [ [/(BEGIN|CASE)\b/i, { token: "keyword.block" }], [/END\b/i, { token: "keyword.block" }], [/WHEN\b/i, { token: "keyword.choice" }], [/THEN\b/i, { token: "keyword.choice" }] ] } }; }, _ = (o) => o.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); class I { constructor(e, t) { this._worker = e, this._defaults = t; } async provideCompletionItems(e, t) { try { const i = await this._worker(e.uri), a = this._defaults.getApiContextForModel(e.uri), r = e.getWordUntilPosition(t), c = { startLineNumber: t.lineNumber, endLineNumber: t.lineNumber, startColumn: r.startColumn, endColumn: r.endColumn }; return await i.doComplete(e.uri.toString(), c, t, a); } catch (i) { return l("SqlLayerCompletionProvider")(i), { suggestions: [] }; } } } class D { constructor(e, t, { defaults: i, diagnosticService: a }) { this._languageId = e, this._worker = t, this._disposables = [], this._modelListeners = /* @__PURE__ */ new Map(), this._defaults = i, this._diagnosticsService = a, g().then((r) => { const c = (s) => { const n = s.getLanguageId(); if (n !== this._languageId) return; const d = x(() => { this._doValidate(s, n).catch((y) => { throw y; }); }), f = s.onDidChangeContent(d), L = s.onDidChangeAttached(d); this._modelListeners.set(s.uri.toString(), [f, L]), this._doValidate(s, n).catch(l("SqlLayerDiagnosticsAdapter")); }, h = (s) => { const n = s.uri.toString(); r.setModelMarkers(s, this._languageId, []); const d = this._modelListeners.get(n); if (d) { for (; d.length; ) d.pop()?.dispose(); this._modelListeners.delete(n); } }; this._disposables.push(r.onDidCreateModel(c)), this._disposables.push(r.onWillDisposeModel((s) => h(s))), this._disposables.push( r.onDidChangeModelLanguage((s) => { h(s.model), c(s.model); }) ), this._disposables.push( i.onDidChange(() => { r.getModels().forEach((s) => { s.getLanguageId() === this._languageId && (h(s), c(s)); }); }) ), this._disposables.push( i.onModelContextDidChange((s) => { r.getModels().forEach((n) => { n.getLanguageId() === this._languageId && n.uri.toString() === s && this._doValidate(n, this._languageId).catch(l("SqlLayerDiagnosticsAdapter")); }); }) ), this._disposables.push({ dispose: () => { this._modelListeners.forEach((s) => s.forEach((n) => n.dispose())), this._modelListeners.clear(); } }), r.getModels().forEach(c); }); } async _doValidate(e, t) { const i = await g(); if (e.isAttachedToEditor()) try { const a = await this._worker(e.uri), r = this._defaults.getApiContextForModel(e.uri), c = await a.doValidation(e.uri.toString(), r); this._diagnosticsService.fireDiagnosticsChange(e.uri, c), i.setModelMarkers(e, t, c); } catch (a) { l("SqlLayerDiagnosticsAdapter")(a); } } } let w; async function U(...o) { return await k.waitForWorker(), await new Promise((e, t) => { if (!w) { t(new Error("SQL layer worker not registered!")); return; } e(w(...o)); }); } class v { constructor() { this._onDiagnosticsChange = new C(); } get onDiagnosticsChange() { return this._onDiagnosticsChange.event; } fireDiagnosticsChange(e, t) { this._onDiagnosticsChange.fire({ uri: e, diagnostics: t }); } } const m = new v(); function T() { return m; } async function H(o) { const e = new k(o), t = async (...a) => await e.getLanguageServiceWorker(...a); w = t; const i = await o.getDialectSpec(); u.setMonarchTokensProvider(o.languageId, S(i)), u.setLanguageConfiguration(o.languageId, M(i)), u.registerCompletionItemProvider( o.languageId, new I(t, o) ), new D(o.languageId, t, { defaults: o, diagnosticService: m }); } export { T as getSqlLayerDiagnosticService, U as getSqlLayerWorker, H as setupMode };