@arcgis/coding-components
Version:
ArcGIS Coding Components
302 lines (301 loc) • 10.2 kB
JavaScript
/* 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
};