UNPKG

@arcgis/coding-components

Version:
374 lines (373 loc) • 10.4 kB
/* COPYRIGHT Esri - https://js.arcgis.com/5.1/LICENSE.txt */ import { Emitter as T, languages as h } 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 E } from "./monaco-importer.js"; import { a as d } from "./sql-expr-defaults.js"; import { debounce as S } from "@arcgis/toolkit/function"; import { rethrowError as g } from "@arcgis/toolkit/log"; import { g as f } from "./sql-expr-service-accessors.js"; let p = Promise.withResolvers(); class _ { constructor(e) { this._defaults = e, this._worker = null, this._client = null, this._configChangeListener = this._defaults.onDidChange(() => this.stopWorker()); } dispose() { this._configChangeListener.dispose(), this.stopWorker(); } stopWorker() { this._worker && (this._worker.dispose(), this._worker = null, p = Promise.withResolvers()), this._client = null; } /** * Wait for the worker to be ready. * @returns A promise that resolves when the worker is ready. */ static async waitForWorker() { return await p.promise; } async _getClientProxy() { const e = await E(); 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 Expression workers." ); this._worker = e.createWebWorker({ worker: i("SqlExprWorker", 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 N = [ "AND", "AS", "BOTH", "CASE", "CAST", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "DATE", "DAY", "DOY", "DOW", "ELSE", "END", "ESCAPE", "FALSE", "FLOAT", "FOR", "FROM", "HOUR", "IN", "INT", "INTEGER", "INTERVAL", "LEADING", "LIKE", "MINUTE", "MONTH", "POSITION", "REAL", "SECOND", "SMALLINT", "THEN", "TIME", "TIMESTAMP", "TIMEZONE_HOUR", "TIMEZONE_MINUTE", "TO", "TRAILING", "TRIM", "TRUE", "VARCHAR", "WEEK", "WHEN", "WITH", "YEAR", "ZONE" ], A = [ // Logical "AND", "BETWEEN", "LIKE", "NOT", "OR", // Predicates "IS", "NULL" ], L = [ // Conversion "CAST", // Datetime "EXTRACT", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", // Mathematical "ABS", "ACOS", "ASIN", "ATAN", "CEILING", "COS", "FLOOR", "LOG", "LOG10", "MOD", "NULLIF", "POWER", "ROUND", "SIGN", "SIN", "TAN", "TRUNCATE", // String "CHAR_LENGTH", "COALESCE", "CONCAT", "LOWER", "POSITION", "SUBSTRING", "TRIM", "UPPER" ], R = { comments: { lineComment: "--", blockComment: ["/*", "*/"] }, brackets: [ ["{", "}"], ["[", "]"], ["(", ")"] ], autoClosingPairs: [ { open: "{", close: "}" }, { open: "[", close: "]" }, { open: "(", close: ")" }, { open: '"', close: '"' }, { open: "'", close: "'" } ], surroundingPairs: [ { open: "{", close: "}" }, { open: "[", close: "]" }, { open: "(", close: ")" }, { open: '"', close: '"' }, { open: "'", close: "'" } ] }, x = { defaultToken: "", tokenPostfix: ".arcgis", ignoreCase: !0, brackets: [ { open: "[", close: "]", token: "delimiter.square" }, { open: "(", close: ")", token: "delimiter.parenthesis" } ], keywords: N, operators: A, builtinFunctions: L, 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"], // Not supporting nested comments, as nested comments seem to not be standard? // i.e. http://stackoverflow.com/questions/728172/are-there-multiline-comment-delimiters-in-sql-that-are-vendor-agnostic // [/\/\*/, { token: 'comment.quote', next: '@push' }], // nested comment not allowed :-( [/\*\//, { 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: [[/"/, { token: "identifier.quote", next: "@quotedIdentifier" }]], quotedIdentifier: [ [/[^"]+/, "identifier"], [/""/, "identifier"], [/"/, { 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" }] ] } }; class M { constructor(e, t) { this._worker = e, this._defaults = t; } async provideCompletionItems(e, t) { try { const i = await this._worker(e.uri), n = e.getWordUntilPosition(t), s = { startLineNumber: t.lineNumber, endLineNumber: t.lineNumber, startColumn: n.startColumn, endColumn: n.endColumn }, c = this._defaults.getApiContextForModel(e.uri); return await i.doComplete(e.uri.toString(), s, t, c); } catch (i) { return g("SqlExprCompletionProvider")(i), { suggestions: [] }; } } } class b { constructor(e, t, { defaults: i, diagnosticService: n }) { this._languageId = e, this._worker = t, this._disposables = [], this._modelListeners = /* @__PURE__ */ new Map(), this._diagnosticsService = n, this._defaults = i, E().then((s) => { const c = (o) => { const r = o.getLanguageId(); if (r !== this._languageId) return; const l = S(() => { this._doValidate(o, r).catch((I) => { throw I; }); }), w = o.onDidChangeContent(l), C = o.onDidChangeAttached(l); this._modelListeners.set(o.uri.toString(), [w, C]), this._doValidate(o, r).catch(g("SqlExprDiagnosticsAdapter")); }, u = (o) => { const r = o.uri.toString(); s.setModelMarkers(o, this._languageId, []); const l = this._modelListeners.get(r); if (l) { for (; l.length; ) l.pop()?.dispose(); this._modelListeners.delete(r); } }; this._disposables.push(s.onDidCreateModel(c)), this._disposables.push(s.onWillDisposeModel((o) => u(o))), this._disposables.push( s.onDidChangeModelLanguage((o) => { u(o.model), c(o.model); }) ), this._disposables.push( i.onDidChange(() => { s.getModels().forEach((o) => { o.getLanguageId() === this._languageId && (u(o), c(o)); }); }) ), this._disposables.push( i.onModelContextDidChange((o) => { s.getModels().forEach((r) => { r.getLanguageId() === this._languageId && r.uri.toString() === o && this._doValidate(r, this._languageId).catch(g("SqlExprDiagnosticsAdapter")); }); }) ), this._disposables.push({ dispose: () => { this._modelListeners.forEach((o) => o.forEach((r) => r.dispose())), this._modelListeners.clear(); } }), s.getModels().forEach(c); }); } async _doValidate(e, t) { const i = await E(); if (e.isAttachedToEditor()) try { const n = await this._worker(e.uri), s = this._defaults.getApiContextForModel(e.uri), c = await n.doValidation(e.uri.toString(), s); this._diagnosticsService.fireDiagnosticsChange(e.uri, c), i.setModelMarkers(e, t, c); } catch (n) { g("SqlExprDiagnosticsAdapter")(n); } } } let m; async function Y(...a) { return await _.waitForWorker(), await new Promise((e, t) => { if (!m) { t(new Error("sql expression worker not registered!")); return; } e(m(...a)); }); } class D { constructor() { this._onDiagnosticsChange = new T(); } /** * An event to signal changes to the diagnostics. * The event value is the uri string and the diagnostics. */ get onDiagnosticsChange() { return this._onDiagnosticsChange.event; } /** * Fires the diagnostics change event. * @param uri The uri of the model for which the diagnostics changed. * @param diagnostics The diagnostics for the model. */ fireDiagnosticsChange(e, t) { this._onDiagnosticsChange.fire({ uri: e, diagnostics: t }); } } const k = new D(); async function Z() { return await f(), k; } function X(a) { const e = new _(a), t = async (...n) => await e.getLanguageServiceWorker(...n); m = t, h.setMonarchTokensProvider(d.languageId, x), h.setLanguageConfiguration(d.languageId, R); const i = new M(t, d); h.registerCompletionItemProvider(d.languageId, i), new b(a.languageId, t, { defaults: a, diagnosticService: k }); } export { Z as getSqlExprDiagnosticService, Y as getSqlExprWorker, X as setupMode };