diff --git a/data.json b/data.json new file mode 100644 index 0000000..9ba6454 --- /dev/null +++ b/data.json @@ -0,0 +1,14 @@ +{ + "micropubEndpoint": "https://blog.giersig.eu/micropub", + "mediaEndpoint": "https://blog.giersig.eu/media", + "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZSI6Imh0dHBzOi8vYmxvZy5naWVyc2lnLmV1Iiwic2NvcGUiOiJjcmVhdGUgdXBkYXRlIG1lZGlhIiwiaWF0IjoxNzczNTA1OTI2LCJleHAiOjE3ODEyODE5MjZ9.AaxtwgiQYbU7iWQt9GCResGK-DVfa0r7q2CHr_ibdFM", + "defaultSyndicateTo": [], + "autoDiscover": false, + "siteUrl": "https://blog.giersig.eu", + "authorizationEndpoint": "https://blog.giersig.eu/auth", + "tokenEndpoint": "https://blog.giersig.eu/auth/token", + "me": "https://blog.giersig.eu", + "writeUrlToFrontmatter": true, + "mapGardenTags": true, + "defaultVisibility": "public" +} \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 0000000..8b930a2 --- /dev/null +++ b/main.js @@ -0,0 +1,907 @@ +/* +THIS IS A GENERATED/BUNDLED FILE BY ESBUILD +if you want to view the source, please visit the github repository of this plugin +*/ + +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// src/main.ts +var main_exports = {}; +__export(main_exports, { + default: () => MicropubPlugin +}); +module.exports = __toCommonJS(main_exports); +var import_obsidian5 = require("obsidian"); + +// src/types.ts +var DEFAULT_SETTINGS = { + micropubEndpoint: "", + mediaEndpoint: "", + accessToken: "", + defaultSyndicateTo: [], + autoDiscover: false, + siteUrl: "", + authorizationEndpoint: "", + tokenEndpoint: "", + me: "", + writeUrlToFrontmatter: true, + mapGardenTags: true, + defaultVisibility: "public" +}; + +// src/SettingsTab.ts +var import_obsidian3 = require("obsidian"); + +// src/MicropubClient.ts +var import_obsidian = require("obsidian"); +var MicropubClient = class { + constructor(getEndpoint, getMediaEndpoint, getToken) { + this.getEndpoint = getEndpoint; + this.getMediaEndpoint = getMediaEndpoint; + this.getToken = getToken; + } + // ── Config discovery ───────────────────────────────────────────────────── + /** Fetch Micropub server config (syndication targets, media endpoint, etc.) */ + async fetchConfig() { + const url = `${this.getEndpoint()}?q=config`; + const resp = await (0, import_obsidian.requestUrl)({ + url, + method: "GET", + headers: this.authHeaders() + }); + return resp.json; + } + /** + * Discover micropub + token endpoint URLs from a site's home page + * by reading and tags. + */ + async discoverEndpoints(siteUrl) { + const resp = await (0, import_obsidian.requestUrl)({ url: siteUrl, method: "GET" }); + const html = resp.text; + const micropub = this.extractLinkRel(html, "micropub"); + const tokenEndpoint = this.extractLinkRel(html, "token_endpoint"); + let mediaEndpoint; + if (micropub) { + try { + const cfg = await this.fetchConfigFrom(micropub); + mediaEndpoint = cfg["media-endpoint"]; + } catch (e) { + } + } + return { micropubEndpoint: micropub, tokenEndpoint, mediaEndpoint }; + } + // ── Post publishing ────────────────────────────────────────────────────── + /** + * Create a new post via Micropub. + * Sends a JSON body with h-entry properties. + * Returns the Location header URL on success. + */ + async createPost(properties) { + var _a, _b, _c; + const body = { + type: ["h-entry"], + properties + }; + try { + const resp = await (0, import_obsidian.requestUrl)({ + url: this.getEndpoint(), + method: "POST", + headers: { + ...this.authHeaders(), + "Content-Type": "application/json" + }, + body: JSON.stringify(body), + throw: false + }); + if (resp.status === 201 || resp.status === 202) { + const location = ((_a = resp.headers) == null ? void 0 : _a["location"]) || ((_b = resp.headers) == null ? void 0 : _b["Location"]) || ((_c = resp.json) == null ? void 0 : _c.url); + return { success: true, url: location }; + } + const detail = this.extractError(resp.text); + return { success: false, error: `HTTP ${resp.status}: ${detail}` }; + } catch (err) { + return { success: false, error: String(err) }; + } + } + /** + * Update an existing post. + * @param postUrl The canonical URL of the post to update + * @param replace Properties to replace (will overwrite existing values) + */ + async updatePost(postUrl, replace) { + const body = { action: "update", url: postUrl, replace }; + try { + const resp = await (0, import_obsidian.requestUrl)({ + url: this.getEndpoint(), + method: "POST", + headers: { + ...this.authHeaders(), + "Content-Type": "application/json" + }, + body: JSON.stringify(body), + throw: false + }); + if (resp.status >= 200 && resp.status < 300) { + return { success: true, url: postUrl }; + } + return { + success: false, + error: `HTTP ${resp.status}: ${this.extractError(resp.text)}` + }; + } catch (err) { + return { success: false, error: String(err) }; + } + } + // ── Media upload ───────────────────────────────────────────────────────── + /** + * Upload a binary file to the media endpoint. + * @returns The URL of the uploaded media, or throws on failure. + */ + async uploadMedia(fileBuffer, fileName, mimeType) { + var _a, _b, _c; + const endpoint = this.getMediaEndpoint() || `${this.getEndpoint()}/media`; + const boundary = `----MicropubBoundary${Date.now()}`; + const header = `--${boundary}\r +Content-Disposition: form-data; name="file"; filename="${fileName}"\r +Content-Type: ${mimeType}\r +\r +`; + const footer = `\r +--${boundary}--\r +`; + const headerBuf = new TextEncoder().encode(header); + const footerBuf = new TextEncoder().encode(footer); + const fileBuf = new Uint8Array(fileBuffer); + const combined = new Uint8Array( + headerBuf.length + fileBuf.length + footerBuf.length + ); + combined.set(headerBuf, 0); + combined.set(fileBuf, headerBuf.length); + combined.set(footerBuf, headerBuf.length + fileBuf.length); + const resp = await (0, import_obsidian.requestUrl)({ + url: endpoint, + method: "POST", + headers: { + ...this.authHeaders(), + "Content-Type": `multipart/form-data; boundary=${boundary}` + }, + body: combined.buffer, + throw: false + }); + if (resp.status === 201 || resp.status === 202) { + const location = ((_a = resp.headers) == null ? void 0 : _a["location"]) || ((_b = resp.headers) == null ? void 0 : _b["Location"]) || ((_c = resp.json) == null ? void 0 : _c.url); + if (location) return location; + } + throw new Error( + `Media upload failed (HTTP ${resp.status}): ${this.extractError(resp.text)}` + ); + } + // ── Helpers ────────────────────────────────────────────────────────────── + authHeaders() { + return { Authorization: `Bearer ${this.getToken()}` }; + } + extractLinkRel(html, rel) { + var _a; + const re = new RegExp( + `]+rel=["']${rel}["'][^>]+href=["']([^"']+)["']|]+href=["']([^"']+)["'][^>]+rel=["']${rel}["']`, + "i" + ); + const m = html.match(re); + return (_a = m == null ? void 0 : m[1]) != null ? _a : m == null ? void 0 : m[2]; + } + async fetchConfigFrom(endpoint) { + const resp = await (0, import_obsidian.requestUrl)({ + url: `${endpoint}?q=config`, + method: "GET", + headers: this.authHeaders() + }); + return resp.json; + } + extractError(text) { + var _a, _b; + try { + const obj = JSON.parse(text); + return (_b = (_a = obj.error_description) != null ? _a : obj.error) != null ? _b : text.slice(0, 200); + } catch (e) { + return text.slice(0, 200); + } + } +}; + +// src/IndieAuth.ts +var crypto = __toESM(require("crypto")); +var import_obsidian2 = require("obsidian"); +var CLIENT_ID = "https://svemagie.github.io/obsidian-micropub/"; +var REDIRECT_URI = "https://svemagie.github.io/obsidian-micropub/callback"; +var SCOPE = "create update media"; +var AUTH_TIMEOUT_MS = 5 * 60 * 1e3; +var pendingCallback = null; +function handleProtocolCallback(params) { + if (!pendingCallback) return; + const { resolve, state: expectedState } = pendingCallback; + pendingCallback = null; + resolve(params); +} +var IndieAuth = class _IndieAuth { + // ── Public API ──────────────────────────────────────────────────────────── + /** + * Discover IndieAuth + Micropub endpoint URLs from the site's home page + * by reading tags in the HTML . + */ + static async discoverEndpoints(siteUrl) { + const resp = await (0, import_obsidian2.requestUrl)({ url: siteUrl, method: "GET" }); + const html = resp.text; + const authorizationEndpoint = _IndieAuth.extractLinkRel(html, "authorization_endpoint"); + const tokenEndpoint = _IndieAuth.extractLinkRel(html, "token_endpoint"); + const micropubEndpoint = _IndieAuth.extractLinkRel(html, "micropub"); + if (!authorizationEndpoint) { + throw new Error( + `No found at ${siteUrl}. Make sure Indiekit is running and SITE_URL is set correctly.` + ); + } + if (!tokenEndpoint) { + throw new Error(`No found at ${siteUrl}.`); + } + return { authorizationEndpoint, tokenEndpoint, micropubEndpoint }; + } + /** + * Run the full IndieAuth PKCE sign-in flow. + * + * Opens the browser at the user's IndieAuth login page. After login the + * browser is redirected to the GitHub Pages callback, which triggers + * the obsidian://micropub-auth protocol, which resolves the Promise here. + * + * Requires handleProtocolCallback() to be wired up in main.ts via + * this.registerObsidianProtocolHandler("micropub-auth", handleProtocolCallback) + */ + static async signIn(siteUrl) { + var _a, _b, _c, _d, _e, _f; + const { authorizationEndpoint, tokenEndpoint, micropubEndpoint } = await _IndieAuth.discoverEndpoints(siteUrl); + const state = _IndieAuth.base64url(crypto.randomBytes(16)); + const codeVerifier = _IndieAuth.base64url(crypto.randomBytes(64)); + const codeChallenge = _IndieAuth.base64url( + crypto.createHash("sha256").update(codeVerifier).digest() + ); + const callbackPromise = new Promise( + (resolve, reject) => { + const timeout = setTimeout(() => { + pendingCallback = null; + reject(new Error("Sign-in timed out (5 min). Please try again.")); + }, AUTH_TIMEOUT_MS); + pendingCallback = { + state, + resolve: (params) => { + clearTimeout(timeout); + resolve(params); + } + }; + } + ); + const authUrl = new URL(authorizationEndpoint); + authUrl.searchParams.set("response_type", "code"); + authUrl.searchParams.set("client_id", CLIENT_ID); + authUrl.searchParams.set("redirect_uri", REDIRECT_URI); + authUrl.searchParams.set("state", state); + authUrl.searchParams.set("code_challenge", codeChallenge); + authUrl.searchParams.set("code_challenge_method", "S256"); + authUrl.searchParams.set("scope", SCOPE); + authUrl.searchParams.set("me", siteUrl); + window.open(authUrl.toString()); + const callbackParams = await callbackPromise; + if (callbackParams.state !== state) { + throw new Error("State mismatch \u2014 possible CSRF attack. Please try again."); + } + const code = callbackParams.code; + if (!code) { + throw new Error( + (_b = (_a = callbackParams.error_description) != null ? _a : callbackParams.error) != null ? _b : "No authorization code received." + ); + } + const tokenResp = await (0, import_obsidian2.requestUrl)({ + url: tokenEndpoint, + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + Accept: "application/json" + }, + body: new URLSearchParams({ + grant_type: "authorization_code", + code, + client_id: CLIENT_ID, + redirect_uri: REDIRECT_URI, + code_verifier: codeVerifier + }).toString(), + throw: false + }); + const data = tokenResp.json; + if (!data.access_token) { + throw new Error( + (_d = (_c = data.error_description) != null ? _c : data.error) != null ? _d : `Token exchange failed (HTTP ${tokenResp.status})` + ); + } + return { + accessToken: data.access_token, + scope: (_e = data.scope) != null ? _e : SCOPE, + me: (_f = data.me) != null ? _f : siteUrl, + authorizationEndpoint, + tokenEndpoint, + micropubEndpoint + }; + } + // ── Helpers ─────────────────────────────────────────────────────────────── + static base64url(buf) { + return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); + } + static extractLinkRel(html, rel) { + var _a; + const re = new RegExp( + `]+rel=["'][^"']*\\b${rel}\\b[^"']*["'][^>]+href=["']([^"']+)["']|]+href=["']([^"']+)["'][^>]+rel=["'][^"']*\\b${rel}\\b[^"']*["']`, + "i" + ); + const m = html.match(re); + return (_a = m == null ? void 0 : m[1]) != null ? _a : m == null ? void 0 : m[2]; + } +}; + +// src/SettingsTab.ts +var MicropubSettingsTab = class extends import_obsidian3.PluginSettingTab { + constructor(app, plugin) { + super(app, plugin); + this.plugin = plugin; + } + display() { + const { containerEl } = this; + containerEl.empty(); + containerEl.createEl("h2", { text: "Micropub Publisher" }); + containerEl.createEl("h3", { text: "Account" }); + if (this.plugin.settings.me && this.plugin.settings.accessToken) { + this.renderSignedIn(containerEl); + } else { + this.renderSignedOut(containerEl); + } + containerEl.createEl("h3", { text: "Endpoints" }); + containerEl.createEl("p", { + text: "These are filled automatically when you sign in. Only edit them manually if your server uses non-standard paths.", + cls: "setting-item-description" + }); + new import_obsidian3.Setting(containerEl).setName("Micropub endpoint").setDesc("e.g. https://blog.giersig.eu/micropub").addText( + (text) => text.setPlaceholder("https://example.com/micropub").setValue(this.plugin.settings.micropubEndpoint).onChange(async (value) => { + this.plugin.settings.micropubEndpoint = value.trim(); + await this.plugin.saveSettings(); + }) + ); + new import_obsidian3.Setting(containerEl).setName("Media endpoint").setDesc("For image uploads. Auto-discovered if blank.").addText( + (text) => text.setPlaceholder("https://example.com/micropub/media").setValue(this.plugin.settings.mediaEndpoint).onChange(async (value) => { + this.plugin.settings.mediaEndpoint = value.trim(); + await this.plugin.saveSettings(); + }) + ); + containerEl.createEl("h3", { text: "Publish Behaviour" }); + new import_obsidian3.Setting(containerEl).setName("Default visibility").setDesc("Applies when the note has no explicit visibility property.").addDropdown( + (drop) => drop.addOption("public", "Public").addOption("unlisted", "Unlisted").addOption("private", "Private").setValue(this.plugin.settings.defaultVisibility).onChange(async (value) => { + this.plugin.settings.defaultVisibility = value; + await this.plugin.saveSettings(); + }) + ); + new import_obsidian3.Setting(containerEl).setName("Write URL back to note").setDesc( + "After publishing, store the post URL as `mp-url` in frontmatter. Subsequent publishes will update the existing post instead of creating a new one." + ).addToggle( + (toggle) => toggle.setValue(this.plugin.settings.writeUrlToFrontmatter).onChange(async (value) => { + this.plugin.settings.writeUrlToFrontmatter = value; + await this.plugin.saveSettings(); + }) + ); + containerEl.createEl("h3", { text: "Digital Garden" }); + new import_obsidian3.Setting(containerEl).setName("Map #garden/* tags to gardenStage").setDesc( + "Obsidian tags like #garden/plant become a `garden-stage: plant` Micropub property. The blog renders these as growth stage badges at /garden/." + ).addToggle( + (toggle) => toggle.setValue(this.plugin.settings.mapGardenTags).onChange(async (value) => { + this.plugin.settings.mapGardenTags = value; + await this.plugin.saveSettings(); + }) + ); + containerEl.createEl("p", { + text: "Stages: plant \u{1F331} \xB7 cultivate \u{1F33F} \xB7 question \u2753 \xB7 repot \u{1FAB4} \xB7 revitalize \u2728 \xB7 revisit \u{1F504}", + cls: "setting-item-description" + }); + } + // ── Signed-out state ───────────────────────────────────────────────────── + renderSignedOut(containerEl) { + new import_obsidian3.Setting(containerEl).setName("Site URL").setDesc( + "Your site's home page. Clicking Sign in opens your blog's login page in the browser \u2014 the same flow iA Writer uses." + ).addText( + (text) => text.setPlaceholder("https://blog.giersig.eu").setValue(this.plugin.settings.siteUrl).onChange(async (value) => { + this.plugin.settings.siteUrl = value.trim(); + await this.plugin.saveSettings(); + }) + ).addButton((btn) => { + btn.setButtonText("Sign in").setCta().onClick(async () => { + const siteUrl = this.plugin.settings.siteUrl.trim(); + if (!siteUrl) { + new import_obsidian3.Notice("Enter your site URL first."); + return; + } + btn.setDisabled(true); + btn.setButtonText("Opening browser\u2026"); + try { + const result = await IndieAuth.signIn(siteUrl); + this.plugin.settings.accessToken = result.accessToken; + this.plugin.settings.me = result.me; + this.plugin.settings.authorizationEndpoint = result.authorizationEndpoint; + this.plugin.settings.tokenEndpoint = result.tokenEndpoint; + if (result.micropubEndpoint) { + this.plugin.settings.micropubEndpoint = result.micropubEndpoint; + } + if (result.mediaEndpoint) { + this.plugin.settings.mediaEndpoint = result.mediaEndpoint; + } + await this.plugin.saveSettings(); + if (!this.plugin.settings.mediaEndpoint) { + try { + const client = new MicropubClient( + () => this.plugin.settings.micropubEndpoint, + () => this.plugin.settings.mediaEndpoint, + () => this.plugin.settings.accessToken + ); + const cfg = await client.fetchConfig(); + if (cfg["media-endpoint"]) { + this.plugin.settings.mediaEndpoint = cfg["media-endpoint"]; + await this.plugin.saveSettings(); + } + } catch (e) { + } + } + new import_obsidian3.Notice(`\u2705 Signed in as ${result.me}`); + this.display(); + } catch (err) { + new import_obsidian3.Notice(`Sign-in failed: ${String(err)}`, 8e3); + btn.setDisabled(false); + btn.setButtonText("Sign in"); + } + }); + }); + const details = containerEl.createEl("details"); + details.createEl("summary", { + text: "Or paste a token manually", + cls: "setting-item-description" + }); + details.style.marginTop = "8px"; + details.style.marginBottom = "8px"; + new import_obsidian3.Setting(details).setName("Access token").setDesc("Bearer token from your Indiekit admin panel.").addText((text) => { + text.setPlaceholder("your-bearer-token").setValue(this.plugin.settings.accessToken).onChange(async (value) => { + this.plugin.settings.accessToken = value.trim(); + await this.plugin.saveSettings(); + }); + text.inputEl.type = "password"; + }).addButton( + (btn) => btn.setButtonText("Verify").onClick(async () => { + if (!this.plugin.settings.micropubEndpoint || !this.plugin.settings.accessToken) { + new import_obsidian3.Notice("Set the Micropub endpoint and token first."); + return; + } + btn.setDisabled(true); + try { + const client = new MicropubClient( + () => this.plugin.settings.micropubEndpoint, + () => this.plugin.settings.mediaEndpoint, + () => this.plugin.settings.accessToken + ); + await client.fetchConfig(); + new import_obsidian3.Notice("\u2705 Token is valid!"); + } catch (err) { + new import_obsidian3.Notice(`Token check failed: ${String(err)}`); + } finally { + btn.setDisabled(false); + } + }) + ); + } + // ── Signed-in state ────────────────────────────────────────────────────── + renderSignedIn(containerEl) { + const me = this.plugin.settings.me; + const banner = containerEl.createDiv({ + cls: "micropub-auth-banner" + }); + banner.style.cssText = "display:flex;align-items:center;gap:12px;padding:12px 16px;border:1px solid var(--background-modifier-border);border-radius:8px;margin-bottom:16px;background:var(--background-secondary);"; + const icon = banner.createDiv(); + icon.style.cssText = "width:40px;height:40px;border-radius:50%;background:var(--interactive-accent);display:flex;align-items:center;justify-content:center;font-size:1.2rem;flex-shrink:0;"; + icon.textContent = "\u{1F310}"; + const info = banner.createDiv(); + info.createEl("div", { + text: "Signed in", + attr: { style: "font-size:.75rem;color:var(--text-muted);margin-bottom:2px" } + }); + info.createEl("div", { + text: me, + attr: { style: "font-weight:500;word-break:break-all" } + }); + new import_obsidian3.Setting(containerEl).setName("Site URL").addText( + (text) => text.setValue(this.plugin.settings.siteUrl).setDisabled(true) + ).addButton( + (btn) => btn.setButtonText("Sign out").setWarning().onClick(async () => { + this.plugin.settings.accessToken = ""; + this.plugin.settings.me = ""; + this.plugin.settings.authorizationEndpoint = ""; + this.plugin.settings.tokenEndpoint = ""; + await this.plugin.saveSettings(); + this.display(); + }) + ); + } +}; + +// src/Publisher.ts +var import_obsidian4 = require("obsidian"); +var GARDEN_TAG_PREFIX = "garden/"; +var Publisher = class { + constructor(app, settings) { + this.app = app; + this.settings = settings; + this.client = new MicropubClient( + () => settings.micropubEndpoint, + () => settings.mediaEndpoint, + () => settings.accessToken + ); + } + /** Publish the given file. Returns PublishResult. */ + async publish(file) { + var _a, _b; + const raw = await this.app.vault.read(file); + const { frontmatter, body } = this.parseFrontmatter(raw); + const existingUrl = (_b = (_a = frontmatter["mp-url"]) != null ? _a : frontmatter["url"]) != null ? _b : void 0; + const { content: processedBody, uploadedUrls } = await this.processImages(body); + const properties = this.buildProperties(frontmatter, processedBody, uploadedUrls); + let result; + if (existingUrl) { + const replace = {}; + for (const [k, v] of Object.entries(properties)) { + replace[k] = Array.isArray(v) ? v : [v]; + } + result = await this.client.updatePost(existingUrl, replace); + } else { + result = await this.client.createPost(properties); + } + if (result.success && result.url && this.settings.writeUrlToFrontmatter) { + await this.writeUrlToNote(file, raw, result.url); + } + return result; + } + // ── Property builder ───────────────────────────────────────────────────── + buildProperties(fm, body, uploadedUrls) { + var _a, _b, _c, _d, _e, _f, _g, _h, _i; + const props = {}; + const trimmedBody = body.trim(); + const bookmarkOf = (_a = fm["bookmarkOf"]) != null ? _a : fm["bookmark-of"]; + const likeOf = (_b = fm["likeOf"]) != null ? _b : fm["like-of"]; + const inReplyTo = (_c = fm["inReplyTo"]) != null ? _c : fm["in-reply-to"]; + const repostOf = (_d = fm["repostOf"]) != null ? _d : fm["repost-of"]; + if (bookmarkOf) props["bookmark-of"] = [String(bookmarkOf)]; + if (likeOf) props["like-of"] = [String(likeOf)]; + if (inReplyTo) props["in-reply-to"] = [String(inReplyTo)]; + if (repostOf) props["repost-of"] = [String(repostOf)]; + const isInteractionWithoutBody = (likeOf || repostOf) && !trimmedBody; + if (!isInteractionWithoutBody) { + props["content"] = trimmedBody ? [{ html: trimmedBody }] : [{ html: "" }]; + } + if (fm["title"]) { + props["name"] = [String(fm["title"])]; + } + if ((_e = fm["summary"]) != null ? _e : fm["excerpt"]) { + props["summary"] = [String((_f = fm["summary"]) != null ? _f : fm["excerpt"])]; + } + if (fm["date"]) { + props["published"] = [new Date(String(fm["date"])).toISOString()]; + } + const rawTags = this.resolveArray((_g = fm["tags"]) != null ? _g : fm["category"]); + const gardenStage = this.extractGardenStage(rawTags); + const normalTags = rawTags.filter( + (t) => !t.startsWith(GARDEN_TAG_PREFIX) && t !== "garden" + ); + if (normalTags.length > 0) { + props["category"] = normalTags; + } + if (this.settings.mapGardenTags && gardenStage) { + props["garden-stage"] = [gardenStage]; + } + const syndicateTo = this.resolveArray( + (_h = fm["mp-syndicate-to"]) != null ? _h : fm["mpSyndicateTo"] + ); + const allSyndicateTo = [ + .../* @__PURE__ */ new Set([...this.settings.defaultSyndicateTo, ...syndicateTo]) + ]; + if (allSyndicateTo.length > 0) { + props["mp-syndicate-to"] = allSyndicateTo; + } + const visibility = (_i = fm["visibility"]) != null ? _i : this.settings.defaultVisibility; + if (visibility && visibility !== "public") { + props["visibility"] = [visibility]; + } + if (fm["ai"] && typeof fm["ai"] === "object") { + props["ai"] = [fm["ai"]]; + } + const fmPhotos = this.resolvePhotoArray(fm["photo"]); + if (fmPhotos.length > 0) { + props["photo"] = fmPhotos; + } else if (uploadedUrls.length > 0) { + props["photo"] = uploadedUrls.map((url) => ({ value: url })); + } + for (const [k, v] of Object.entries(fm)) { + if (k.startsWith("mp-") && k !== "mp-url" && k !== "mp-syndicate-to") { + props[k] = this.resolveArray(v); + } + } + return props; + } + /** + * Normalise the `photo` frontmatter field into Micropub photo objects. + * Handles three formats: + * - string URL: "https://..." + * - array of strings: ["https://..."] + * - array of objects: [{url: "https://...", alt: "..."}] + */ + resolvePhotoArray(value) { + if (!value) return []; + const items = Array.isArray(value) ? value : [value]; + return items.map((item) => { + var _a, _b; + if (typeof item === "string") return { value: item }; + if (typeof item === "object" && item !== null) { + const obj = item; + const url = String((_b = (_a = obj["url"]) != null ? _a : obj["value"]) != null ? _b : ""); + if (!url) return null; + return obj["alt"] ? { value: url, alt: String(obj["alt"]) } : { value: url }; + } + return null; + }).filter((x) => x !== null); + } + // ── Garden tag extraction ──────────────────────────────────────────────── + /** + * Find the first #garden/ tag and return the stage name. + * Supports both "garden/plant" (Obsidian array) and "#garden/plant" (inline). + */ + extractGardenStage(tags) { + for (const tag of tags) { + const clean = tag.replace(/^#/, ""); + if (clean.startsWith(GARDEN_TAG_PREFIX)) { + const stage = clean.slice(GARDEN_TAG_PREFIX.length); + const valid = [ + "plant", + "cultivate", + "question", + "repot", + "revitalize", + "revisit" + ]; + if (valid.includes(stage)) return stage; + } + } + return void 0; + } + // ── Image processing ───────────────────────────────────────────────────── + /** + * Find all `![[local-image.png]]` or `![alt](relative/path.jpg)` in the body, + * upload them to the media endpoint, and replace the references with remote URLs. + */ + async processImages(body) { + const uploadedUrls = []; + const wikiPattern = /!\[\[([^\]]+\.(png|jpg|jpeg|gif|webp|svg))\]\]/gi; + const mdPattern = /!\[([^\]]*)\]\(([^)]+\.(png|jpg|jpeg|gif|webp|svg))\)/gi; + let content = body; + const wikiMatches = [...body.matchAll(wikiPattern)]; + for (const match of wikiMatches) { + const filename = match[1]; + try { + const remoteUrl = await this.uploadLocalFile(filename); + if (remoteUrl) { + uploadedUrls.push(remoteUrl); + content = content.replace(match[0], `![${filename}](${remoteUrl})`); + } + } catch (err) { + console.warn(`[micropub] Failed to upload ${filename}:`, err); + } + } + const mdMatches = [...content.matchAll(mdPattern)]; + for (const match of mdMatches) { + const alt = match[1]; + const path = match[2]; + if (path.startsWith("http")) continue; + try { + const remoteUrl = await this.uploadLocalFile(path); + if (remoteUrl) { + uploadedUrls.push(remoteUrl); + content = content.replace(match[0], `![${alt}](${remoteUrl})`); + } + } catch (err) { + console.warn(`[micropub] Failed to upload ${path}:`, err); + } + } + return { content, uploadedUrls }; + } + async uploadLocalFile(path) { + const file = this.app.vault.getFiles().find( + (f) => f.name === path || f.path === path + ); + if (!file) return void 0; + const buffer = await this.app.vault.readBinary(file); + const mimeType = this.guessMimeType(file.extension); + return this.client.uploadMedia(buffer, file.name, mimeType); + } + // ── Frontmatter helpers ────────────────────────────────────────────────── + parseFrontmatter(raw) { + var _a; + const fmMatch = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/); + if (!fmMatch) return { frontmatter: {}, body: raw }; + let frontmatter = {}; + try { + frontmatter = (_a = (0, import_obsidian4.parseYaml)(fmMatch[1])) != null ? _a : {}; + } catch (e) { + } + return { frontmatter, body: fmMatch[2] }; + } + async writeUrlToNote(file, originalContent, url) { + const fmMatch = originalContent.match( + /^(---\r?\n[\s\S]*?\r?\n---\r?\n)([\s\S]*)$/ + ); + if (!fmMatch) { + const newFm = `--- +mp-url: "${url}" +--- +`; + await this.app.vault.modify(file, newFm + originalContent); + return; + } + const fmBlock = fmMatch[1]; + const body = fmMatch[2]; + if (fmBlock.includes("mp-url:")) { + const updated = fmBlock.replace( + /mp-url:.*(\r?\n)/, + `mp-url: "${url}"$1` + ); + await this.app.vault.modify(file, updated + body); + } else { + const updated = fmBlock.replace( + /(\r?\n---\r?\n)$/, + ` +mp-url: "${url}"$1` + ); + await this.app.vault.modify(file, updated + body); + } + } + resolveArray(value) { + if (!value) return []; + if (Array.isArray(value)) return value.map(String); + return [String(value)]; + } + guessMimeType(ext) { + var _a; + const map = { + png: "image/png", + jpg: "image/jpeg", + jpeg: "image/jpeg", + gif: "image/gif", + webp: "image/webp", + svg: "image/svg+xml" + }; + return (_a = map[ext.toLowerCase()]) != null ? _a : "application/octet-stream"; + } +}; + +// src/main.ts +var MicropubPlugin = class extends import_obsidian5.Plugin { + async onload() { + await this.loadSettings(); + this.addCommand({ + id: "publish-to-micropub", + name: "Publish to Micropub", + checkCallback: (checking) => { + const file = this.app.workspace.getActiveFile(); + if (!file || file.extension !== "md") return false; + if (checking) return true; + this.publishActiveNote(file); + return true; + } + }); + this.addCommand({ + id: "publish-to-micropub-update", + name: "Update existing Micropub post", + checkCallback: (checking) => { + const file = this.app.workspace.getActiveFile(); + if (!file || file.extension !== "md") return false; + if (checking) return true; + this.publishActiveNote(file); + return true; + } + }); + this.registerObsidianProtocolHandler("micropub-auth", (params) => { + handleProtocolCallback(params); + }); + this.addSettingTab(new MicropubSettingsTab(this.app, this)); + this.addRibbonIcon("send", "Publish to Micropub", () => { + const file = this.app.workspace.getActiveFile(); + if (!file || file.extension !== "md") { + new import_obsidian5.Notice("Open a Markdown note to publish."); + return; + } + this.publishActiveNote(file); + }); + } + onunload() { + } + // ── Publish flow ────────────────────────────────────────────────────────── + async publishActiveNote(file) { + if (!this.settings.micropubEndpoint) { + new import_obsidian5.Notice( + "\u26A0\uFE0F Micropub endpoint not configured. Open plugin settings to add it." + ); + return; + } + if (!this.settings.accessToken) { + new import_obsidian5.Notice( + "\u26A0\uFE0F Access token not configured. Open plugin settings to add it." + ); + return; + } + const notice = new import_obsidian5.Notice( + "Publishing\u2026", + 0 + /* persist until dismissed */ + ); + try { + const publisher = new Publisher(this.app, this.settings); + const result = await publisher.publish(file); + notice.hide(); + if (result.success) { + const urlDisplay = result.url ? ` +${result.url}` : ""; + new import_obsidian5.Notice(`\u2705 Published!${urlDisplay}`, 8e3); + } else { + new import_obsidian5.Notice(`\u274C Publish failed: ${result.error}`, 1e4); + console.error("[micropub] Publish failed:", result.error); + } + } catch (err) { + notice.hide(); + const msg = err instanceof Error ? err.message : String(err); + new import_obsidian5.Notice(`\u274C Error: ${msg}`, 1e4); + console.error("[micropub] Unexpected error:", err); + } + } + // ── Settings persistence ────────────────────────────────────────────────── + async loadSettings() { + this.settings = Object.assign( + {}, + DEFAULT_SETTINGS, + await this.loadData() + ); + } + async saveSettings() { + await this.saveData(this.settings); + } +}; +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsic3JjL21haW4udHMiLCAic3JjL3R5cGVzLnRzIiwgInNyYy9TZXR0aW5nc1RhYi50cyIsICJzcmMvTWljcm9wdWJDbGllbnQudHMiLCAic3JjL0luZGllQXV0aC50cyIsICJzcmMvUHVibGlzaGVyLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyIvKipcbiAqIG1haW4udHMgXHUyMDE0IG9ic2lkaWFuLW1pY3JvcHViIHBsdWdpbiBlbnRyeSBwb2ludFxuICpcbiAqIFB1Ymxpc2hlcyB0aGUgYWN0aXZlIG5vdGUgdG8gYW55IE1pY3JvcHViLWNvbXBhdGlibGUgZW5kcG9pbnQuXG4gKiBEZXNpZ25lZCB0byB3b3JrIHdpdGggSW5kaWVraXQgKGh0dHBzOi8vZ2V0aW5kaWVraXQuY29tKSBidXQgY29tcGF0aWJsZVxuICogd2l0aCBhbnkgc2VydmVyIHRoYXQgaW1wbGVtZW50cyB0aGUgTWljcm9wdWIgc3BlYyAoVzNDKS5cbiAqXG4gKiBLZXkgZmVhdHVyZXMgdnMuIHRoZSBvcmlnaW5hbCBvYnNpZGlhbi1taWNyb2Jsb2c6XG4gKiAgIC0gQ29uZmlndXJhYmxlIGVuZHBvaW50IFVSTCAobm90IGhhcmRjb2RlZCB0byBNaWNyby5ibG9nKVxuICogICAtIEF1dG8tZGlzY292ZXJ5IG9mIG1pY3JvcHViL21lZGlhIGVuZHBvaW50cyBmcm9tIDxsaW5rIHJlbD4gaGVhZGVyc1xuICogICAtICNnYXJkZW4vKiB0YWcgXHUyMTkyIGdhcmRlblN0YWdlIHByb3BlcnR5IG1hcHBpbmcgZm9yIERpZ2l0YWwgR2FyZGVuXG4gKiAgIC0gV3JpdGVzIHJldHVybmVkIHBvc3QgVVJMIGJhY2sgdG8gbm90ZSBmcm9udG1hdHRlciBmb3IgZnV0dXJlIHVwZGF0ZXNcbiAqICAgLSBTdXBwb3J0cyBjcmVhdGUgKyB1cGRhdGUgZmxvd3NcbiAqXG4gKiBCYXNlZCBvbjogaHR0cHM6Ly9naXRodWIuY29tL3N2ZW1hZ2llL29ic2lkaWFuLW1pY3JvYmxvZyAoTUlUKVxuICovXG5cbmltcG9ydCB7IE5vdGljZSwgUGx1Z2luLCBURmlsZSB9IGZyb20gXCJvYnNpZGlhblwiO1xuaW1wb3J0IHsgREVGQVVMVF9TRVRUSU5HUywgdHlwZSBNaWNyb3B1YlNldHRpbmdzIH0gZnJvbSBcIi4vdHlwZXNcIjtcbmltcG9ydCB7IE1pY3JvcHViU2V0dGluZ3NUYWIgfSBmcm9tIFwiLi9TZXR0aW5nc1RhYlwiO1xuaW1wb3J0IHsgUHVibGlzaGVyIH0gZnJvbSBcIi4vUHVibGlzaGVyXCI7XG5pbXBvcnQgeyBoYW5kbGVQcm90b2NvbENhbGxiYWNrIH0gZnJvbSBcIi4vSW5kaWVBdXRoXCI7XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIE1pY3JvcHViUGx1Z2luIGV4dGVuZHMgUGx1Z2luIHtcbiAgc2V0dGluZ3MhOiBNaWNyb3B1YlNldHRpbmdzO1xuXG4gIGFzeW5jIG9ubG9hZCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhd2FpdCB0aGlzLmxvYWRTZXR0aW5ncygpO1xuXG4gICAgLy8gXHUyNTAwXHUyNTAwIENvbW1hbmRzIFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFxuXG4gICAgdGhpcy5hZGRDb21tYW5kKHtcbiAgICAgIGlkOiBcInB1Ymxpc2gtdG8tbWljcm9wdWJcIixcbiAgICAgIG5hbWU6IFwiUHVibGlzaCB0byBNaWNyb3B1YlwiLFxuICAgICAgY2hlY2tDYWxsYmFjazogKGNoZWNraW5nOiBib29sZWFuKSA9PiB7XG4gICAgICAgIGNvbnN0IGZpbGUgPSB0aGlzLmFwcC53b3Jrc3BhY2UuZ2V0QWN0aXZlRmlsZSgpO1xuICAgICAgICBpZiAoIWZpbGUgfHwgZmlsZS5leHRlbnNpb24gIT09IFwibWRcIikgcmV0dXJuIGZhbHNlO1xuICAgICAgICBpZiAoY2hlY2tpbmcpIHJldHVybiB0cnVlO1xuXG4gICAgICAgIHRoaXMucHVibGlzaEFjdGl2ZU5vdGUoZmlsZSk7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgfSxcbiAgICB9KTtcblxuICAgIHRoaXMuYWRkQ29tbWFuZCh7XG4gICAgICBpZDogXCJwdWJsaXNoLXRvLW1pY3JvcHViLXVwZGF0ZVwiLFxuICAgICAgbmFtZTogXCJVcGRhdGUgZXhpc3RpbmcgTWljcm9wdWIgcG9zdFwiLFxuICAgICAgY2hlY2tDYWxsYmFjazogKGNoZWNraW5nOiBib29sZWFuKSA9PiB7XG4gICAgICAgIGNvbnN0IGZpbGUgPSB0aGlzLmFwcC53b3Jrc3BhY2UuZ2V0QWN0aXZlRmlsZSgpO1xuICAgICAgICBpZiAoIWZpbGUgfHwgZmlsZS5leHRlbnNpb24gIT09IFwibWRcIikgcmV0dXJuIGZhbHNlO1xuICAgICAgICBpZiAoY2hlY2tpbmcpIHJldHVybiB0cnVlO1xuXG4gICAgICAgIC8vIFVwZGF0ZSB1c2VzIHRoZSBzYW1lIHB1Ymxpc2ggZmxvdyBcdTIwMTQgUHVibGlzaGVyIGRldGVjdHMgbXAtdXJsIGFuZCByb3V0ZXMgdG8gdXBkYXRlXG4gICAgICAgIHRoaXMucHVibGlzaEFjdGl2ZU5vdGUoZmlsZSk7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgfSxcbiAgICB9KTtcblxuICAgIC8vIFx1MjUwMFx1MjUwMCBJbmRpZUF1dGggcHJvdG9jb2wgaGFuZGxlciBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcbiAgICAvLyBSZWNlaXZlcyBvYnNpZGlhbjovL21pY3JvcHViLWF1dGg/Y29kZT0uLi4mc3RhdGU9Li4uIGFmdGVyIHRoZSB1c2VyXG4gICAgLy8gYXBwcm92ZXMgb24gdGhlaXIgSW5kaWVBdXRoIGxvZ2luIHBhZ2UuIFRoZSBHaXRIdWIgUGFnZXMgY2FsbGJhY2sgcGFnZVxuICAgIC8vIGF0IHN2ZW1hZ2llLmdpdGh1Yi5pby9vYnNpZGlhbi1taWNyb3B1Yi9jYWxsYmFjayByZWRpcmVjdHMgaGVyZS5cbiAgICB0aGlzLnJlZ2lzdGVyT2JzaWRpYW5Qcm90b2NvbEhhbmRsZXIoXCJtaWNyb3B1Yi1hdXRoXCIsIChwYXJhbXMpID0+IHtcbiAgICAgIGhhbmRsZVByb3RvY29sQ2FsbGJhY2socGFyYW1zIGFzIFJlY29yZDxzdHJpbmcsIHN0cmluZz4pO1xuICAgIH0pO1xuXG4gICAgLy8gXHUyNTAwXHUyNTAwIFNldHRpbmdzIHRhYiBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcblxuICAgIHRoaXMuYWRkU2V0dGluZ1RhYihuZXcgTWljcm9wdWJTZXR0aW5nc1RhYih0aGlzLmFwcCwgdGhpcykpO1xuXG4gICAgLy8gXHUyNTAwXHUyNTAwIFJpYmJvbiBpY29uIFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFxuXG4gICAgdGhpcy5hZGRSaWJib25JY29uKFwic2VuZFwiLCBcIlB1Ymxpc2ggdG8gTWljcm9wdWJcIiwgKCkgPT4ge1xuICAgICAgY29uc3QgZmlsZSA9IHRoaXMuYXBwLndvcmtzcGFjZS5nZXRBY3RpdmVGaWxlKCk7XG4gICAgICBpZiAoIWZpbGUgfHwgZmlsZS5leHRlbnNpb24gIT09IFwibWRcIikge1xuICAgICAgICBuZXcgTm90aWNlKFwiT3BlbiBhIE1hcmtkb3duIG5vdGUgdG8gcHVibGlzaC5cIik7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHRoaXMucHVibGlzaEFjdGl2ZU5vdGUoZmlsZSk7XG4gICAgfSk7XG4gIH1cblxuICBvbnVubG9hZCgpOiB2b2lkIHtcbiAgICAvLyBOb3RoaW5nIHRvIGNsZWFuIHVwXG4gIH1cblxuICAvLyBcdTI1MDBcdTI1MDAgUHVibGlzaCBmbG93IFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFxuXG4gIHByaXZhdGUgYXN5bmMgcHVibGlzaEFjdGl2ZU5vdGUoZmlsZTogVEZpbGUpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAoIXRoaXMuc2V0dGluZ3MubWljcm9wdWJFbmRwb2ludCkge1xuICAgICAgbmV3IE5vdGljZShcbiAgICAgICAgXCJcdTI2QTBcdUZFMEYgTWljcm9wdWIgZW5kcG9pbnQgbm90IGNvbmZpZ3VyZWQuIE9wZW4gcGx1Z2luIHNldHRpbmdzIHRvIGFkZCBpdC5cIixcbiAgICAgICk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKCF0aGlzLnNldHRpbmdzLmFjY2Vzc1Rva2VuKSB7XG4gICAgICBuZXcgTm90aWNlKFxuICAgICAgICBcIlx1MjZBMFx1RkUwRiBBY2Nlc3MgdG9rZW4gbm90IGNvbmZpZ3VyZWQuIE9wZW4gcGx1Z2luIHNldHRpbmdzIHRvIGFkZCBpdC5cIixcbiAgICAgICk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3Qgbm90aWNlID0gbmV3IE5vdGljZShcIlB1Ymxpc2hpbmdcdTIwMjZcIiwgMCAvKiBwZXJzaXN0IHVudGlsIGRpc21pc3NlZCAqLyk7XG5cbiAgICB0cnkge1xuICAgICAgY29uc3QgcHVibGlzaGVyID0gbmV3IFB1Ymxpc2hlcih0aGlzLmFwcCwgdGhpcy5zZXR0aW5ncyk7XG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBwdWJsaXNoZXIucHVibGlzaChmaWxlKTtcblxuICAgICAgbm90aWNlLmhpZGUoKTtcblxuICAgICAgaWYgKHJlc3VsdC5zdWNjZXNzKSB7XG4gICAgICAgIGNvbnN0IHVybERpc3BsYXkgPSByZXN1bHQudXJsXG4gICAgICAgICAgPyBgXFxuJHtyZXN1bHQudXJsfWBcbiAgICAgICAgICA6IFwiXCI7XG4gICAgICAgIG5ldyBOb3RpY2UoYFx1MjcwNSBQdWJsaXNoZWQhJHt1cmxEaXNwbGF5fWAsIDgwMDApO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbmV3IE5vdGljZShgXHUyNzRDIFB1Ymxpc2ggZmFpbGVkOiAke3Jlc3VsdC5lcnJvcn1gLCAxMDAwMCk7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoXCJbbWljcm9wdWJdIFB1Ymxpc2ggZmFpbGVkOlwiLCByZXN1bHQuZXJyb3IpO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycjogdW5rbm93bikge1xuICAgICAgbm90aWNlLmhpZGUoKTtcbiAgICAgIGNvbnN0IG1zZyA9IGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiBTdHJpbmcoZXJyKTtcbiAgICAgIG5ldyBOb3RpY2UoYFx1Mjc0QyBFcnJvcjogJHttc2d9YCwgMTAwMDApO1xuICAgICAgY29uc29sZS5lcnJvcihcIlttaWNyb3B1Yl0gVW5leHBlY3RlZCBlcnJvcjpcIiwgZXJyKTtcbiAgICB9XG4gIH1cblxuICAvLyBcdTI1MDBcdTI1MDAgU2V0dGluZ3MgcGVyc2lzdGVuY2UgXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXG5cbiAgYXN5bmMgbG9hZFNldHRpbmdzKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuc2V0dGluZ3MgPSBPYmplY3QuYXNzaWduKFxuICAgICAge30sXG4gICAgICBERUZBVUxUX1NFVFRJTkdTLFxuICAgICAgYXdhaXQgdGhpcy5sb2FkRGF0YSgpLFxuICAgICkgYXMgTWljcm9wdWJTZXR0aW5ncztcbiAgfVxuXG4gIGFzeW5jIHNhdmVTZXR0aW5ncygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhd2FpdCB0aGlzLnNhdmVEYXRhKHRoaXMuc2V0dGluZ3MpO1xuICB9XG59XG4iLCAiLyoqXG4gKiB0eXBlcy50cyBcdTIwMTQgc2hhcmVkIGludGVyZmFjZXMgZm9yIG9ic2lkaWFuLW1pY3JvcHViXG4gKi9cblxuLyoqIFBsdWdpbiBzZXR0aW5ncyBzdG9yZWQgaW4gZGF0YS5qc29uICovXG5leHBvcnQgaW50ZXJmYWNlIE1pY3JvcHViU2V0dGluZ3Mge1xuICAvKiogRnVsbCBVUkwgb2YgdGhlIE1pY3JvcHViIGVuZHBvaW50LCBlLmcuIGh0dHBzOi8vZXhhbXBsZS5jb20vbWljcm9wdWIgKi9cbiAgbWljcm9wdWJFbmRwb2ludDogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBGdWxsIFVSTCBvZiB0aGUgbWVkaWEgZW5kcG9pbnQgZm9yIGltYWdlIHVwbG9hZHMuXG4gICAqIElmIGVtcHR5LCBkaXNjb3ZlcmVkIGF1dG9tYXRpY2FsbHkgZnJvbSB0aGUgTWljcm9wdWIgY29uZmlnIHF1ZXJ5LFxuICAgKiBvciBkZXJpdmVkIGZyb20gdGhlIG1pY3JvcHViRW5kcG9pbnQgKHNvbWUgc2VydmVycyB1c2UgL21pY3JvcHViL21lZGlhKS5cbiAgICovXG4gIG1lZGlhRW5kcG9pbnQ6IHN0cmluZztcblxuICAvKipcbiAgICogQmVhcmVyIHRva2VuIGZvciBBdXRob3JpemF0aW9uOiBCZWFyZXIgPHRva2VuPi5cbiAgICogT2J0YWluIGZyb20geW91ciBJbmRpZUF1dGggdG9rZW4gZW5kcG9pbnQgb3Igc2VydmVyIGFkbWluIHBhbmVsLlxuICAgKi9cbiAgYWNjZXNzVG9rZW46IHN0cmluZztcblxuICAvKipcbiAgICogVGhlIHN5bmRpY2F0aW9uIHRhcmdldHMgdG8gcHJlLXRpY2sgaW4gdGhlIHB1Ymxpc2ggZGlhbG9nLlxuICAgKiBWYWx1ZXMgYXJlIHVpZCBzdHJpbmdzIHJldHVybmVkIGJ5IHRoZSBNaWNyb3B1YiBjb25maWcgP3E9Y29uZmlnLlxuICAgKi9cbiAgZGVmYXVsdFN5bmRpY2F0ZVRvOiBzdHJpbmdbXTtcblxuICAvKipcbiAgICogV2hlbiB0cnVlLCBwZXJmb3JtIGEgZGlzY292ZXJ5IGZldGNoIGFnYWluc3QgdGhlIHNpdGUgVVJMIHRvIGF1dG8tZGV0ZWN0XG4gICAqIHRoZSBtaWNyb3B1YiBhbmQgdG9rZW4gZW5kcG9pbnRzIGZyb20gPGxpbmsgcmVsPVwibWljcm9wdWJcIj4gaGVhZGVycy5cbiAgICovXG4gIGF1dG9EaXNjb3ZlcjogYm9vbGVhbjtcblxuICAvKiogWW91ciBzaXRlJ3MgaG9tZXBhZ2UgVVJMIFx1MjAxNCB1c2VkIGZvciBlbmRwb2ludCBkaXNjb3ZlcnkgYW5kIEluZGllQXV0aC4gKi9cbiAgc2l0ZVVybDogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBUaGUgYXV0aG9yaXphdGlvbl9lbmRwb2ludCBkaXNjb3ZlcmVkIGZyb20gdGhlIHNpdGUuXG4gICAqIFBvcHVsYXRlZCBhdXRvbWF0aWNhbGx5IGJ5IHRoZSBJbmRpZUF1dGggc2lnbi1pbiBmbG93LlxuICAgKi9cbiAgYXV0aG9yaXphdGlvbkVuZHBvaW50OiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFRoZSB0b2tlbl9lbmRwb2ludCBkaXNjb3ZlcmVkIGZyb20gdGhlIHNpdGUuXG4gICAqIFBvcHVsYXRlZCBhdXRvbWF0aWNhbGx5IGJ5IHRoZSBJbmRpZUF1dGggc2lnbi1pbiBmbG93LlxuICAgKi9cbiAgdG9rZW5FbmRwb2ludDogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBUaGUgY2Fub25pY2FsIFwibWVcIiBVUkwgcmV0dXJuZWQgYnkgdGhlIHRva2VuIGVuZHBvaW50IGFmdGVyIHNpZ24taW4uXG4gICAqIFVzZWQgdG8gc2hvdyB3aG8gaXMgY3VycmVudGx5IGxvZ2dlZCBpbi5cbiAgICovXG4gIG1lOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFdoZW4gdHJ1ZSwgYWZ0ZXIgYSBzdWNjZXNzZnVsIHB1Ymxpc2ggdGhlIHBvc3QgVVJMIHJldHVybmVkIGJ5IHRoZSBzZXJ2ZXJcbiAgICogaXMgd3JpdHRlbiBiYWNrIHRvIHRoZSBub3RlJ3MgZnJvbnRtYXR0ZXIgYXMgYG1wLXVybGAuXG4gICAqL1xuICB3cml0ZVVybFRvRnJvbnRtYXR0ZXI6IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIE1hcCBPYnNpZGlhbiAjZ2FyZGVuLyogdGFncyB0byBhIGBnYXJkZW5TdGFnZWAgTWljcm9wdWIgcHJvcGVydHkuXG4gICAqIFdoZW4gZW5hYmxlZCwgYSB0YWcgbGlrZSAjZ2FyZGVuL3BsYW50IGJlY29tZXMgeyBcImdhcmRlbi1zdGFnZVwiOiBcInBsYW50XCIgfVxuICAgKiBpbiB0aGUgTWljcm9wdWIgcmVxdWVzdCAoYW5kIGdhcmRlblN0YWdlOiBwbGFudCBpbiB0aGUgc2VydmVyJ3MgZnJvbnQgbWF0dGVyKS5cbiAgICovXG4gIG1hcEdhcmRlblRhZ3M6IGJvb2xlYW47XG5cbiAgLyoqIFZpc2liaWxpdHkgZGVmYXVsdCBmb3IgbmV3IHBvc3RzOiBcInB1YmxpY1wiIHwgXCJ1bmxpc3RlZFwiIHwgXCJwcml2YXRlXCIgKi9cbiAgZGVmYXVsdFZpc2liaWxpdHk6IFwicHVibGljXCIgfCBcInVubGlzdGVkXCIgfCBcInByaXZhdGVcIjtcbn1cblxuZXhwb3J0IGNvbnN0IERFRkFVTFRfU0VUVElOR1M6IE1pY3JvcHViU2V0dGluZ3MgPSB7XG4gIG1pY3JvcHViRW5kcG9pbnQ6IFwiXCIsXG4gIG1lZGlhRW5kcG9pbnQ6IFwiXCIsXG4gIGFjY2Vzc1Rva2VuOiBcIlwiLFxuICBkZWZhdWx0U3luZGljYXRlVG86IFtdLFxuICBhdXRvRGlzY292ZXI6IGZhbHNlLFxuICBzaXRlVXJsOiBcIlwiLFxuICBhdXRob3JpemF0aW9uRW5kcG9pbnQ6IFwiXCIsXG4gIHRva2VuRW5kcG9pbnQ6IFwiXCIsXG4gIG1lOiBcIlwiLFxuICB3cml0ZVVybFRvRnJvbnRtYXR0ZXI6IHRydWUsXG4gIG1hcEdhcmRlblRhZ3M6IHRydWUsXG4gIGRlZmF1bHRWaXNpYmlsaXR5OiBcInB1YmxpY1wiLFxufTtcblxuLyoqIEEgc3luZGljYXRpb24gdGFyZ2V0IGFzIHJldHVybmVkIGJ5IE1pY3JvcHViIGNvbmZpZyBxdWVyeSAqL1xuZXhwb3J0IGludGVyZmFjZSBTeW5kaWNhdGlvblRhcmdldCB7XG4gIHVpZDogc3RyaW5nO1xuICBuYW1lOiBzdHJpbmc7XG59XG5cbi8qKiBNaWNyb3B1YiBjb25maWcgcmVzcG9uc2UgKD9xPWNvbmZpZykgKi9cbmV4cG9ydCBpbnRlcmZhY2UgTWljcm9wdWJDb25maWcge1xuICBcIm1lZGlhLWVuZHBvaW50XCI/OiBzdHJpbmc7XG4gIFwic3luZGljYXRlLXRvXCI/OiBTeW5kaWNhdGlvblRhcmdldFtdO1xuICBcInBvc3QtdHlwZXNcIj86IEFycmF5PHsgdHlwZTogc3RyaW5nOyBuYW1lOiBzdHJpbmcgfT47XG59XG5cbi8qKlxuICogR2FyZGVuIHN0YWdlcyBcdTIwMTQgbWF0Y2hlcyBPYnNpZGlhbiAjZ2FyZGVuLyogdGFncyBhbmQgYmxvZyBnYXJkZW5TdGFnZSB2YWx1ZXMuXG4gKiBUaGUgTWljcm9wdWIgcHJvcGVydHkgbmFtZSBpcyBcImdhcmRlbi1zdGFnZVwiIChoeXBoZW5hdGVkLCBNaWNyb3B1YiBjb252ZW50aW9uKS5cbiAqL1xuZXhwb3J0IHR5cGUgR2FyZGVuU3RhZ2UgPVxuICB8IFwicGxhbnRcIlxuICB8IFwiY3VsdGl2YXRlXCJcbiAgfCBcInF1ZXN0aW9uXCJcbiAgfCBcInJlcG90XCJcbiAgfCBcInJldml0YWxpemVcIlxuICB8IFwicmV2aXNpdFwiO1xuXG5leHBvcnQgY29uc3QgR0FSREVOX1NUQUdFX0xBQkVMUzogUmVjb3JkPEdhcmRlblN0YWdlLCBzdHJpbmc+ID0ge1xuICBwbGFudDogICAgICBcIlx1RDgzQ1x1REYzMSBTZWVkbGluZ1wiLFxuICBjdWx0aXZhdGU6ICBcIlx1RDgzQ1x1REYzRiBHcm93aW5nXCIsXG4gIHF1ZXN0aW9uOiAgIFwiXHUyNzUzIE9wZW4gUXVlc3Rpb25cIixcbiAgcmVwb3Q6ICAgICAgXCJcdUQ4M0VcdURFQjQgUmVwb3R0aW5nXCIsXG4gIHJldml0YWxpemU6IFwiXHUyNzI4IFJldml0YWxpemluZ1wiLFxuICByZXZpc2l0OiAgICBcIlx1RDgzRFx1REQwNCBSZXZpc2l0XCIsXG59O1xuXG4vKiogUmVzdWx0IHJldHVybmVkIGJ5IFB1Ymxpc2hlci5wdWJsaXNoKCkgKi9cbmV4cG9ydCBpbnRlcmZhY2UgUHVibGlzaFJlc3VsdCB7XG4gIHN1Y2Nlc3M6IGJvb2xlYW47XG4gIC8qKiBVUkwgb2YgdGhlIHB1Ymxpc2hlZCBwb3N0IChmcm9tIExvY2F0aW9uIHJlc3BvbnNlIGhlYWRlcikgKi9cbiAgdXJsPzogc3RyaW5nO1xuICBlcnJvcj86IHN0cmluZztcbn1cbiIsICIvKipcbiAqIFNldHRpbmdzVGFiLnRzIFx1MjAxNCBPYnNpZGlhbiBzZXR0aW5ncyBVSSBmb3Igb2JzaWRpYW4tbWljcm9wdWJcbiAqXG4gKiBBdXRoZW50aWNhdGlvbiBzZWN0aW9uIHdvcmtzIGxpa2UgaUEgV3JpdGVyOlxuICogICAxLiBVc2VyIGVudGVycyB0aGVpciBzaXRlIFVSTFxuICogICAyLiBDbGlja3MgXCJTaWduIGluXCIgXHUyMDE0IGJyb3dzZXIgb3BlbnMgYXQgdGhlaXIgSW5kaWVBdXRoIGxvZ2luIHBhZ2VcbiAqICAgMy4gVGhleSBsb2cgaW4gd2l0aCB0aGVpciBibG9nIHBhc3N3b3JkXG4gKiAgIDQuIEJyb3dzZXIgcmVkaXJlY3RzIGJhY2s7IHBsdWdpbiByZWNlaXZlcyB0aGUgdG9rZW4gYXV0b21hdGljYWxseVxuICogICA1LiBTZXR0aW5ncyBzaG93IFwiU2lnbmVkIGluIGFzIDxtZT5cIiArIGEgU2lnbiBPdXQgYnV0dG9uXG4gKlxuICogQWR2YW5jZWQgdXNlcnMgY2FuIHN0aWxsIHBhc3RlIGEgdG9rZW4gbWFudWFsbHkgaWYgdGhleSBwcmVmZXIuXG4gKi9cblxuaW1wb3J0IHsgQXBwLCBOb3RpY2UsIFBsdWdpblNldHRpbmdUYWIsIFNldHRpbmcgfSBmcm9tIFwib2JzaWRpYW5cIjtcbmltcG9ydCB0eXBlIE1pY3JvcHViUGx1Z2luIGZyb20gXCIuL21haW5cIjtcbmltcG9ydCB7IE1pY3JvcHViQ2xpZW50IH0gZnJvbSBcIi4vTWljcm9wdWJDbGllbnRcIjtcbmltcG9ydCB7IEluZGllQXV0aCB9IGZyb20gXCIuL0luZGllQXV0aFwiO1xuXG5leHBvcnQgY2xhc3MgTWljcm9wdWJTZXR0aW5nc1RhYiBleHRlbmRzIFBsdWdpblNldHRpbmdUYWIge1xuICBjb25zdHJ1Y3RvcihcbiAgICBhcHA6IEFwcCxcbiAgICBwcml2YXRlIHJlYWRvbmx5IHBsdWdpbjogTWljcm9wdWJQbHVnaW4sXG4gICkge1xuICAgIHN1cGVyKGFwcCwgcGx1Z2luKTtcbiAgfVxuXG4gIGRpc3BsYXkoKTogdm9pZCB7XG4gICAgY29uc3QgeyBjb250YWluZXJFbCB9ID0gdGhpcztcbiAgICBjb250YWluZXJFbC5lbXB0eSgpO1xuXG4gICAgY29udGFpbmVyRWwuY3JlYXRlRWwoXCJoMlwiLCB7IHRleHQ6IFwiTWljcm9wdWIgUHVibGlzaGVyXCIgfSk7XG5cbiAgICAvLyBcdTI1MDBcdTI1MDAgU2l0ZSBVUkwgKyBTaWduIEluIFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFxuICAgIGNvbnRhaW5lckVsLmNyZWF0ZUVsKFwiaDNcIiwgeyB0ZXh0OiBcIkFjY291bnRcIiB9KTtcblxuICAgIC8vIFNob3cgY3VycmVudCBzaWduLWluIHN0YXR1c1xuICAgIGlmICh0aGlzLnBsdWdpbi5zZXR0aW5ncy5tZSAmJiB0aGlzLnBsdWdpbi5zZXR0aW5ncy5hY2Nlc3NUb2tlbikge1xuICAgICAgdGhpcy5yZW5kZXJTaWduZWRJbihjb250YWluZXJFbCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMucmVuZGVyU2lnbmVkT3V0KGNvbnRhaW5lckVsKTtcbiAgICB9XG5cbiAgICAvLyBcdTI1MDBcdTI1MDAgRW5kcG9pbnRzIChjb2xsYXBzZWQgLyBhZHZhbmNlZCkgXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXG4gICAgY29udGFpbmVyRWwuY3JlYXRlRWwoXCJoM1wiLCB7IHRleHQ6IFwiRW5kcG9pbnRzXCIgfSk7XG5cbiAgICBjb250YWluZXJFbC5jcmVhdGVFbChcInBcIiwge1xuICAgICAgdGV4dDogXCJUaGVzZSBhcmUgZmlsbGVkIGF1dG9tYXRpY2FsbHkgd2hlbiB5b3Ugc2lnbiBpbi4gT25seSBlZGl0IHRoZW0gbWFudWFsbHkgaWYgeW91ciBzZXJ2ZXIgdXNlcyBub24tc3RhbmRhcmQgcGF0aHMuXCIsXG4gICAgICBjbHM6IFwic2V0dGluZy1pdGVtLWRlc2NyaXB0aW9uXCIsXG4gICAgfSk7XG5cbiAgICBuZXcgU2V0dGluZyhjb250YWluZXJFbClcbiAgICAgIC5zZXROYW1lKFwiTWljcm9wdWIgZW5kcG9pbnRcIilcbiAgICAgIC5zZXREZXNjKFwiZS5nLiBodHRwczovL2Jsb2cuZ2llcnNpZy5ldS9taWNyb3B1YlwiKVxuICAgICAgLmFkZFRleHQoKHRleHQpID0+XG4gICAgICAgIHRleHRcbiAgICAgICAgICAuc2V0UGxhY2Vob2xkZXIoXCJodHRwczovL2V4YW1wbGUuY29tL21pY3JvcHViXCIpXG4gICAgICAgICAgLnNldFZhbHVlKHRoaXMucGx1Z2luLnNldHRpbmdzLm1pY3JvcHViRW5kcG9pbnQpXG4gICAgICAgICAgLm9uQ2hhbmdlKGFzeW5jICh2YWx1ZSkgPT4ge1xuICAgICAgICAgICAgdGhpcy5wbHVnaW4uc2V0dGluZ3MubWljcm9wdWJFbmRwb2ludCA9IHZhbHVlLnRyaW0oKTtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMucGx1Z2luLnNhdmVTZXR0aW5ncygpO1xuICAgICAgICAgIH0pLFxuICAgICAgKTtcblxuICAgIG5ldyBTZXR0aW5nKGNvbnRhaW5lckVsKVxuICAgICAgLnNldE5hbWUoXCJNZWRpYSBlbmRwb2ludFwiKVxuICAgICAgLnNldERlc2MoXCJGb3IgaW1hZ2UgdXBsb2Fkcy4gQXV0by1kaXNjb3ZlcmVkIGlmIGJsYW5rLlwiKVxuICAgICAgLmFkZFRleHQoKHRleHQpID0+XG4gICAgICAgIHRleHRcbiAgICAgICAgICAuc2V0UGxhY2Vob2xkZXIoXCJodHRwczovL2V4YW1wbGUuY29tL21pY3JvcHViL21lZGlhXCIpXG4gICAgICAgICAgLnNldFZhbHVlKHRoaXMucGx1Z2luLnNldHRpbmdzLm1lZGlhRW5kcG9pbnQpXG4gICAgICAgICAgLm9uQ2hhbmdlKGFzeW5jICh2YWx1ZSkgPT4ge1xuICAgICAgICAgICAgdGhpcy5wbHVnaW4uc2V0dGluZ3MubWVkaWFFbmRwb2ludCA9IHZhbHVlLnRyaW0oKTtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMucGx1Z2luLnNhdmVTZXR0aW5ncygpO1xuICAgICAgICAgIH0pLFxuICAgICAgKTtcblxuICAgIC8vIFx1MjUwMFx1MjUwMCBQdWJsaXNoIGJlaGF2aW91ciBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcbiAgICBjb250YWluZXJFbC5jcmVhdGVFbChcImgzXCIsIHsgdGV4dDogXCJQdWJsaXNoIEJlaGF2aW91clwiIH0pO1xuXG4gICAgbmV3IFNldHRpbmcoY29udGFpbmVyRWwpXG4gICAgICAuc2V0TmFtZShcIkRlZmF1bHQgdmlzaWJpbGl0eVwiKVxuICAgICAgLnNldERlc2MoXCJBcHBsaWVzIHdoZW4gdGhlIG5vdGUgaGFzIG5vIGV4cGxpY2l0IHZpc2liaWxpdHkgcHJvcGVydHkuXCIpXG4gICAgICAuYWRkRHJvcGRvd24oKGRyb3ApID0+XG4gICAgICAgIGRyb3BcbiAgICAgICAgICAuYWRkT3B0aW9uKFwicHVibGljXCIsIFwiUHVibGljXCIpXG4gICAgICAgICAgLmFkZE9wdGlvbihcInVubGlzdGVkXCIsIFwiVW5saXN0ZWRcIilcbiAgICAgICAgICAuYWRkT3B0aW9uKFwicHJpdmF0ZVwiLCBcIlByaXZhdGVcIilcbiAgICAgICAgICAuc2V0VmFsdWUodGhpcy5wbHVnaW4uc2V0dGluZ3MuZGVmYXVsdFZpc2liaWxpdHkpXG4gICAgICAgICAgLm9uQ2hhbmdlKGFzeW5jICh2YWx1ZSkgPT4ge1xuICAgICAgICAgICAgdGhpcy5wbHVnaW4uc2V0dGluZ3MuZGVmYXVsdFZpc2liaWxpdHkgPSB2YWx1ZSBhc1xuICAgICAgICAgICAgICB8IFwicHVibGljXCJcbiAgICAgICAgICAgICAgfCBcInVubGlzdGVkXCJcbiAgICAgICAgICAgICAgfCBcInByaXZhdGVcIjtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMucGx1Z2luLnNhdmVTZXR0aW5ncygpO1xuICAgICAgICAgIH0pLFxuICAgICAgKTtcblxuICAgIG5ldyBTZXR0aW5nKGNvbnRhaW5lckVsKVxuICAgICAgLnNldE5hbWUoXCJXcml0ZSBVUkwgYmFjayB0byBub3RlXCIpXG4gICAgICAuc2V0RGVzYyhcbiAgICAgICAgXCJBZnRlciBwdWJsaXNoaW5nLCBzdG9yZSB0aGUgcG9zdCBVUkwgYXMgYG1wLXVybGAgaW4gZnJvbnRtYXR0ZXIuIFwiICtcbiAgICAgICAgXCJTdWJzZXF1ZW50IHB1Ymxpc2hlcyB3aWxsIHVwZGF0ZSB0aGUgZXhpc3RpbmcgcG9zdCBpbnN0ZWFkIG9mIGNyZWF0aW5nIGEgbmV3IG9uZS5cIixcbiAgICAgIClcbiAgICAgIC5hZGRUb2dnbGUoKHRvZ2dsZSkgPT5cbiAgICAgICAgdG9nZ2xlXG4gICAgICAgICAgLnNldFZhbHVlKHRoaXMucGx1Z2luLnNldHRpbmdzLndyaXRlVXJsVG9Gcm9udG1hdHRlcilcbiAgICAgICAgICAub25DaGFuZ2UoYXN5bmMgKHZhbHVlKSA9PiB7XG4gICAgICAgICAgICB0aGlzLnBsdWdpbi5zZXR0aW5ncy53cml0ZVVybFRvRnJvbnRtYXR0ZXIgPSB2YWx1ZTtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMucGx1Z2luLnNhdmVTZXR0aW5ncygpO1xuICAgICAgICAgIH0pLFxuICAgICAgKTtcblxuICAgIC8vIFx1MjUwMFx1MjUwMCBEaWdpdGFsIEdhcmRlbiBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcbiAgICBjb250YWluZXJFbC5jcmVhdGVFbChcImgzXCIsIHsgdGV4dDogXCJEaWdpdGFsIEdhcmRlblwiIH0pO1xuXG4gICAgbmV3IFNldHRpbmcoY29udGFpbmVyRWwpXG4gICAgICAuc2V0TmFtZShcIk1hcCAjZ2FyZGVuLyogdGFncyB0byBnYXJkZW5TdGFnZVwiKVxuICAgICAgLnNldERlc2MoXG4gICAgICAgIFwiT2JzaWRpYW4gdGFncyBsaWtlICNnYXJkZW4vcGxhbnQgYmVjb21lIGEgYGdhcmRlbi1zdGFnZTogcGxhbnRgIE1pY3JvcHViIFwiICtcbiAgICAgICAgXCJwcm9wZXJ0eS4gVGhlIGJsb2cgcmVuZGVycyB0aGVzZSBhcyBncm93dGggc3RhZ2UgYmFkZ2VzIGF0IC9nYXJkZW4vLlwiLFxuICAgICAgKVxuICAgICAgLmFkZFRvZ2dsZSgodG9nZ2xlKSA9PlxuICAgICAgICB0b2dnbGVcbiAgICAgICAgICAuc2V0VmFsdWUodGhpcy5wbHVnaW4uc2V0dGluZ3MubWFwR2FyZGVuVGFncylcbiAgICAgICAgICAub25DaGFuZ2UoYXN5bmMgKHZhbHVlKSA9PiB7XG4gICAgICAgICAgICB0aGlzLnBsdWdpbi5zZXR0aW5ncy5tYXBHYXJkZW5UYWdzID0gdmFsdWU7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLnBsdWdpbi5zYXZlU2V0dGluZ3MoKTtcbiAgICAgICAgICB9KSxcbiAgICAgICk7XG5cbiAgICBjb250YWluZXJFbC5jcmVhdGVFbChcInBcIiwge1xuICAgICAgdGV4dDogXCJTdGFnZXM6IHBsYW50IFx1RDgzQ1x1REYzMSBcdTAwQjcgY3VsdGl2YXRlIFx1RDgzQ1x1REYzRiBcdTAwQjcgcXVlc3Rpb24gXHUyNzUzIFx1MDBCNyByZXBvdCBcdUQ4M0VcdURFQjQgXHUwMEI3IHJldml0YWxpemUgXHUyNzI4IFx1MDBCNyByZXZpc2l0IFx1RDgzRFx1REQwNFwiLFxuICAgICAgY2xzOiBcInNldHRpbmctaXRlbS1kZXNjcmlwdGlvblwiLFxuICAgIH0pO1xuICB9XG5cbiAgLy8gXHUyNTAwXHUyNTAwIFNpZ25lZC1vdXQgc3RhdGUgXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXG5cbiAgcHJpdmF0ZSByZW5kZXJTaWduZWRPdXQoY29udGFpbmVyRWw6IEhUTUxFbGVtZW50KTogdm9pZCB7XG4gICAgLy8gU2l0ZSBVUkwgaW5wdXQgKyBTaWduIEluIGJ1dHRvbiBvbiB0aGUgc2FtZSByb3dcbiAgICBuZXcgU2V0dGluZyhjb250YWluZXJFbClcbiAgICAgIC5zZXROYW1lKFwiU2l0ZSBVUkxcIilcbiAgICAgIC5zZXREZXNjKFxuICAgICAgICBcIllvdXIgc2l0ZSdzIGhvbWUgcGFnZS4gQ2xpY2tpbmcgU2lnbiBpbiBvcGVucyB5b3VyIGJsb2cncyBsb2dpbiBwYWdlIFwiICtcbiAgICAgICAgXCJpbiB0aGUgYnJvd3NlciBcdTIwMTQgdGhlIHNhbWUgZmxvdyBpQSBXcml0ZXIgdXNlcy5cIixcbiAgICAgIClcbiAgICAgIC5hZGRUZXh0KCh0ZXh0KSA9PlxuICAgICAgICB0ZXh0XG4gICAgICAgICAgLnNldFBsYWNlaG9sZGVyKFwiaHR0cHM6Ly9ibG9nLmdpZXJzaWcuZXVcIilcbiAgICAgICAgICAuc2V0VmFsdWUodGhpcy5wbHVnaW4uc2V0dGluZ3Muc2l0ZVVybClcbiAgICAgICAgICAub25DaGFuZ2UoYXN5bmMgKHZhbHVlKSA9PiB7XG4gICAgICAgICAgICB0aGlzLnBsdWdpbi5zZXR0aW5ncy5zaXRlVXJsID0gdmFsdWUudHJpbSgpO1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5wbHVnaW4uc2F2ZVNldHRpbmdzKCk7XG4gICAgICAgICAgfSksXG4gICAgICApXG4gICAgICAuYWRkQnV0dG9uKChidG4pID0+IHtcbiAgICAgICAgYnRuXG4gICAgICAgICAgLnNldEJ1dHRvblRleHQoXCJTaWduIGluXCIpXG4gICAgICAgICAgLnNldEN0YSgpXG4gICAgICAgICAgLm9uQ2xpY2soYXN5bmMgKCkgPT4ge1xuICAgICAgICAgICAgY29uc3Qgc2l0ZVVybCA9IHRoaXMucGx1Z2luLnNldHRpbmdzLnNpdGVVcmwudHJpbSgpO1xuICAgICAgICAgICAgaWYgKCFzaXRlVXJsKSB7XG4gICAgICAgICAgICAgIG5ldyBOb3RpY2UoXCJFbnRlciB5b3VyIHNpdGUgVVJMIGZpcnN0LlwiKTtcbiAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBidG4uc2V0RGlzYWJsZWQodHJ1ZSk7XG4gICAgICAgICAgICBidG4uc2V0QnV0dG9uVGV4dChcIk9wZW5pbmcgYnJvd3Nlclx1MjAyNlwiKTtcblxuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgSW5kaWVBdXRoLnNpZ25JbihzaXRlVXJsKTtcblxuICAgICAgICAgICAgICAvLyBTYXZlIGV2ZXJ5dGhpbmcgcmV0dXJuZWQgYnkgdGhlIGF1dGggZmxvd1xuICAgICAgICAgICAgICB0aGlzLnBsdWdpbi5zZXR0aW5ncy5hY2Nlc3NUb2tlbiAgICAgID0gcmVzdWx0LmFjY2Vzc1Rva2VuO1xuICAgICAgICAgICAgICB0aGlzLnBsdWdpbi5zZXR0aW5ncy5tZSAgICAgICAgICAgICAgID0gcmVzdWx0Lm1lO1xuICAgICAgICAgICAgICB0aGlzLnBsdWdpbi5zZXR0aW5ncy5hdXRob3JpemF0aW9uRW5kcG9pbnQgPSByZXN1bHQuYXV0aG9yaXphdGlvbkVuZHBvaW50O1xuICAgICAgICAgICAgICB0aGlzLnBsdWdpbi5zZXR0aW5ncy50b2tlbkVuZHBvaW50ICAgID0gcmVzdWx0LnRva2VuRW5kcG9pbnQ7XG4gICAgICAgICAgICAgIGlmIChyZXN1bHQubWljcm9wdWJFbmRwb2ludCkge1xuICAgICAgICAgICAgICAgIHRoaXMucGx1Z2luLnNldHRpbmdzLm1pY3JvcHViRW5kcG9pbnQgPSByZXN1bHQubWljcm9wdWJFbmRwb2ludDtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBpZiAocmVzdWx0Lm1lZGlhRW5kcG9pbnQpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnBsdWdpbi5zZXR0aW5ncy5tZWRpYUVuZHBvaW50ID0gcmVzdWx0Lm1lZGlhRW5kcG9pbnQ7XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICBhd2FpdCB0aGlzLnBsdWdpbi5zYXZlU2V0dGluZ3MoKTtcblxuICAgICAgICAgICAgICAvLyBUcnkgdG8gZmV0Y2ggdGhlIE1pY3JvcHViIGNvbmZpZyB0byBwaWNrIHVwIG1lZGlhIGVuZHBvaW50XG4gICAgICAgICAgICAgIGlmICghdGhpcy5wbHVnaW4uc2V0dGluZ3MubWVkaWFFbmRwb2ludCkge1xuICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICBjb25zdCBjbGllbnQgPSBuZXcgTWljcm9wdWJDbGllbnQoXG4gICAgICAgICAgICAgICAgICAgICgpID0+IHRoaXMucGx1Z2luLnNldHRpbmdzLm1pY3JvcHViRW5kcG9pbnQsXG4gICAgICAgICAgICAgICAgICAgICgpID0+IHRoaXMucGx1Z2luLnNldHRpbmdzLm1lZGlhRW5kcG9pbnQsXG4gICAgICAgICAgICAgICAgICAgICgpID0+IHRoaXMucGx1Z2luLnNldHRpbmdzLmFjY2Vzc1Rva2VuLFxuICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICAgIGNvbnN0IGNmZyA9IGF3YWl0IGNsaWVudC5mZXRjaENvbmZpZygpO1xuICAgICAgICAgICAgICAgICAgaWYgKGNmZ1tcIm1lZGlhLWVuZHBvaW50XCJdKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMucGx1Z2luLnNldHRpbmdzLm1lZGlhRW5kcG9pbnQgPSBjZmdbXCJtZWRpYS1lbmRwb2ludFwiXTtcbiAgICAgICAgICAgICAgICAgICAgYXdhaXQgdGhpcy5wbHVnaW4uc2F2ZVNldHRpbmdzKCk7XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgICAgICAgICAvLyBOb24tZmF0YWxcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICBuZXcgTm90aWNlKGBcdTI3MDUgU2lnbmVkIGluIGFzICR7cmVzdWx0Lm1lfWApO1xuICAgICAgICAgICAgICB0aGlzLmRpc3BsYXkoKTsgLy8gUmVmcmVzaCB0byBzaG93IHNpZ25lZC1pbiBzdGF0ZVxuICAgICAgICAgICAgfSBjYXRjaCAoZXJyOiB1bmtub3duKSB7XG4gICAgICAgICAgICAgIG5ldyBOb3RpY2UoYFNpZ24taW4gZmFpbGVkOiAke1N0cmluZyhlcnIpfWAsIDgwMDApO1xuICAgICAgICAgICAgICBidG4uc2V0RGlzYWJsZWQoZmFsc2UpO1xuICAgICAgICAgICAgICBidG4uc2V0QnV0dG9uVGV4dChcIlNpZ24gaW5cIik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSk7XG4gICAgICB9KTtcblxuICAgIC8vIERpdmlkZXIgKyBtYW51YWwgdG9rZW4gZmFsbGJhY2sgKGNvbGxhcHNlZCBieSBkZWZhdWx0KVxuICAgIGNvbnN0IGRldGFpbHMgPSBjb250YWluZXJFbC5jcmVhdGVFbChcImRldGFpbHNcIik7XG4gICAgZGV0YWlscy5jcmVhdGVFbChcInN1bW1hcnlcIiwge1xuICAgICAgdGV4dDogXCJPciBwYXN0ZSBhIHRva2VuIG1hbnVhbGx5XCIsXG4gICAgICBjbHM6IFwic2V0dGluZy1pdGVtLWRlc2NyaXB0aW9uXCIsXG4gICAgfSk7XG4gICAgZGV0YWlscy5zdHlsZS5tYXJnaW5Ub3AgPSBcIjhweFwiO1xuICAgIGRldGFpbHMuc3R5bGUubWFyZ2luQm90dG9tID0gXCI4cHhcIjtcblxuICAgIG5ldyBTZXR0aW5nKGRldGFpbHMpXG4gICAgICAuc2V0TmFtZShcIkFjY2VzcyB0b2tlblwiKVxuICAgICAgLnNldERlc2MoXCJCZWFyZXIgdG9rZW4gZnJvbSB5b3VyIEluZGlla2l0IGFkbWluIHBhbmVsLlwiKVxuICAgICAgLmFkZFRleHQoKHRleHQpID0+IHtcbiAgICAgICAgdGV4dFxuICAgICAgICAgIC5zZXRQbGFjZWhvbGRlcihcInlvdXItYmVhcmVyLXRva2VuXCIpXG4gICAgICAgICAgLnNldFZhbHVlKHRoaXMucGx1Z2luLnNldHRpbmdzLmFjY2Vzc1Rva2VuKVxuICAgICAgICAgIC5vbkNoYW5nZShhc3luYyAodmFsdWUpID0+IHtcbiAgICAgICAgICAgIHRoaXMucGx1Z2luLnNldHRpbmdzLmFjY2Vzc1Rva2VuID0gdmFsdWUudHJpbSgpO1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5wbHVnaW4uc2F2ZVNldHRpbmdzKCk7XG4gICAgICAgICAgfSk7XG4gICAgICAgIHRleHQuaW5wdXRFbC50eXBlID0gXCJwYXNzd29yZFwiO1xuICAgICAgfSlcbiAgICAgIC5hZGRCdXR0b24oKGJ0bikgPT5cbiAgICAgICAgYnRuLnNldEJ1dHRvblRleHQoXCJWZXJpZnlcIikub25DbGljayhhc3luYyAoKSA9PiB7XG4gICAgICAgICAgaWYgKFxuICAgICAgICAgICAgIXRoaXMucGx1Z2luLnNldHRpbmdzLm1pY3JvcHViRW5kcG9pbnQgfHxcbiAgICAgICAgICAgICF0aGlzLnBsdWdpbi5zZXR0aW5ncy5hY2Nlc3NUb2tlblxuICAgICAgICAgICkge1xuICAgICAgICAgICAgbmV3IE5vdGljZShcIlNldCB0aGUgTWljcm9wdWIgZW5kcG9pbnQgYW5kIHRva2VuIGZpcnN0LlwiKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG4gICAgICAgICAgYnRuLnNldERpc2FibGVkKHRydWUpO1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCBjbGllbnQgPSBuZXcgTWljcm9wdWJDbGllbnQoXG4gICAgICAgICAgICAgICgpID0+IHRoaXMucGx1Z2luLnNldHRpbmdzLm1pY3JvcHViRW5kcG9pbnQsXG4gICAgICAgICAgICAgICgpID0+IHRoaXMucGx1Z2luLnNldHRpbmdzLm1lZGlhRW5kcG9pbnQsXG4gICAgICAgICAgICAgICgpID0+IHRoaXMucGx1Z2luLnNldHRpbmdzLmFjY2Vzc1Rva2VuLFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIGF3YWl0IGNsaWVudC5mZXRjaENvbmZpZygpO1xuICAgICAgICAgICAgbmV3IE5vdGljZShcIlx1MjcwNSBUb2tlbiBpcyB2YWxpZCFcIik7XG4gICAgICAgICAgfSBjYXRjaCAoZXJyOiB1bmtub3duKSB7XG4gICAgICAgICAgICBuZXcgTm90aWNlKGBUb2tlbiBjaGVjayBmYWlsZWQ6ICR7U3RyaW5nKGVycil9YCk7XG4gICAgICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAgICAgIGJ0bi5zZXREaXNhYmxlZChmYWxzZSk7XG4gICAgICAgICAgfVxuICAgICAgICB9KSxcbiAgICAgICk7XG4gIH1cblxuICAvLyBcdTI1MDBcdTI1MDAgU2lnbmVkLWluIHN0YXRlIFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFxuXG4gIHByaXZhdGUgcmVuZGVyU2lnbmVkSW4oY29udGFpbmVyRWw6IEhUTUxFbGVtZW50KTogdm9pZCB7XG4gICAgY29uc3QgbWUgPSB0aGlzLnBsdWdpbi5zZXR0aW5ncy5tZTtcblxuICAgIC8vIEF2YXRhciArIFwiU2lnbmVkIGluIGFzXCIgYmFubmVyXG4gICAgY29uc3QgYmFubmVyID0gY29udGFpbmVyRWwuY3JlYXRlRGl2KHtcbiAgICAgIGNsczogXCJtaWNyb3B1Yi1hdXRoLWJhbm5lclwiLFxuICAgIH0pO1xuICAgIGJhbm5lci5zdHlsZS5jc3NUZXh0ID1cbiAgICAgIFwiZGlzcGxheTpmbGV4O2FsaWduLWl0ZW1zOmNlbnRlcjtnYXA6MTJweDtwYWRkaW5nOjEycHggMTZweDtcIiArXG4gICAgICBcImJvcmRlcjoxcHggc29saWQgdmFyKC0tYmFja2dyb3VuZC1tb2RpZmllci1ib3JkZXIpO1wiICtcbiAgICAgIFwiYm9yZGVyLXJhZGl1czo4cHg7bWFyZ2luLWJvdHRvbToxNnB4O2JhY2tncm91bmQ6dmFyKC0tYmFja2dyb3VuZC1zZWNvbmRhcnkpO1wiO1xuXG4gICAgY29uc3QgaWNvbiA9IGJhbm5lci5jcmVhdGVEaXYoKTtcbiAgICBpY29uLnN0eWxlLmNzc1RleHQgPVxuICAgICAgXCJ3aWR0aDo0MHB4O2hlaWdodDo0MHB4O2JvcmRlci1yYWRpdXM6NTAlO2JhY2tncm91bmQ6dmFyKC0taW50ZXJhY3RpdmUtYWNjZW50KTtcIiArXG4gICAgICBcImRpc3BsYXk6ZmxleDthbGlnbi1pdGVtczpjZW50ZXI7anVzdGlmeS1jb250ZW50OmNlbnRlcjtcIiArXG4gICAgICBcImZvbnQtc2l6ZToxLjJyZW07ZmxleC1zaHJpbms6MDtcIjtcbiAgICBpY29uLnRleHRDb250ZW50ID0gXCJcdUQ4M0NcdURGMTBcIjtcblxuICAgIGNvbnN0IGluZm8gPSBiYW5uZXIuY3JlYXRlRGl2KCk7XG4gICAgaW5mby5jcmVhdGVFbChcImRpdlwiLCB7XG4gICAgICB0ZXh0OiBcIlNpZ25lZCBpblwiLFxuICAgICAgYXR0cjogeyBzdHlsZTogXCJmb250LXNpemU6Ljc1cmVtO2NvbG9yOnZhcigtLXRleHQtbXV0ZWQpO21hcmdpbi1ib3R0b206MnB4XCIgfSxcbiAgICB9KTtcbiAgICBpbmZvLmNyZWF0ZUVsKFwiZGl2XCIsIHtcbiAgICAgIHRleHQ6IG1lLFxuICAgICAgYXR0cjogeyBzdHlsZTogXCJmb250LXdlaWdodDo1MDA7d29yZC1icmVhazpicmVhay1hbGxcIiB9LFxuICAgIH0pO1xuXG4gICAgbmV3IFNldHRpbmcoY29udGFpbmVyRWwpXG4gICAgICAuc2V0TmFtZShcIlNpdGUgVVJMXCIpXG4gICAgICAuYWRkVGV4dCgodGV4dCkgPT5cbiAgICAgICAgdGV4dFxuICAgICAgICAgIC5zZXRWYWx1ZSh0aGlzLnBsdWdpbi5zZXR0aW5ncy5zaXRlVXJsKVxuICAgICAgICAgIC5zZXREaXNhYmxlZCh0cnVlKSxcbiAgICAgIClcbiAgICAgIC5hZGRCdXR0b24oKGJ0bikgPT5cbiAgICAgICAgYnRuXG4gICAgICAgICAgLnNldEJ1dHRvblRleHQoXCJTaWduIG91dFwiKVxuICAgICAgICAgIC5zZXRXYXJuaW5nKClcbiAgICAgICAgICAub25DbGljayhhc3luYyAoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLnBsdWdpbi5zZXR0aW5ncy5hY2Nlc3NUb2tlbiA9IFwiXCI7XG4gICAgICAgICAgICB0aGlzLnBsdWdpbi5zZXR0aW5ncy5tZSA9IFwiXCI7XG4gICAgICAgICAgICB0aGlzLnBsdWdpbi5zZXR0aW5ncy5hdXRob3JpemF0aW9uRW5kcG9pbnQgPSBcIlwiO1xuICAgICAgICAgICAgdGhpcy5wbHVnaW4uc2V0dGluZ3MudG9rZW5FbmRwb2ludCA9IFwiXCI7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLnBsdWdpbi5zYXZlU2V0dGluZ3MoKTtcbiAgICAgICAgICAgIHRoaXMuZGlzcGxheSgpO1xuICAgICAgICAgIH0pLFxuICAgICAgKTtcbiAgfVxufVxuIiwgIi8qKlxuICogTWljcm9wdWJDbGllbnQudHNcbiAqXG4gKiBMb3ctbGV2ZWwgSFRUUCBjbGllbnQgZm9yIE1pY3JvcHViIGFuZCBNZWRpYSBlbmRwb2ludCByZXF1ZXN0cy5cbiAqIFVzZXMgT2JzaWRpYW4ncyByZXF1ZXN0VXJsKCkgc28gcmVxdWVzdHMgYXJlIG1hZGUgZnJvbSB0aGUgZGVza3RvcCBhcHBcbiAqIChubyBDT1JTIGlzc3VlcykgcmF0aGVyIHRoYW4gYSBicm93c2VyIGZldGNoLlxuICovXG5cbmltcG9ydCB7IHJlcXVlc3RVcmwsIFJlcXVlc3RVcmxQYXJhbSB9IGZyb20gXCJvYnNpZGlhblwiO1xuaW1wb3J0IHR5cGUgeyBNaWNyb3B1YkNvbmZpZywgUHVibGlzaFJlc3VsdCB9IGZyb20gXCIuL3R5cGVzXCI7XG5cbmV4cG9ydCBjbGFzcyBNaWNyb3B1YkNsaWVudCB7XG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcmVhZG9ubHkgZ2V0RW5kcG9pbnQ6ICgpID0+IHN0cmluZyxcbiAgICBwcml2YXRlIHJlYWRvbmx5IGdldE1lZGlhRW5kcG9pbnQ6ICgpID0+IHN0cmluZyxcbiAgICBwcml2YXRlIHJlYWRvbmx5IGdldFRva2VuOiAoKSA9PiBzdHJpbmcsXG4gICkge31cblxuICAvLyBcdTI1MDBcdTI1MDAgQ29uZmlnIGRpc2NvdmVyeSBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcblxuICAvKiogRmV0Y2ggTWljcm9wdWIgc2VydmVyIGNvbmZpZyAoc3luZGljYXRpb24gdGFyZ2V0cywgbWVkaWEgZW5kcG9pbnQsIGV0Yy4pICovXG4gIGFzeW5jIGZldGNoQ29uZmlnKCk6IFByb21pc2U8TWljcm9wdWJDb25maWc+IHtcbiAgICBjb25zdCB1cmwgPSBgJHt0aGlzLmdldEVuZHBvaW50KCl9P3E9Y29uZmlnYDtcbiAgICBjb25zdCByZXNwID0gYXdhaXQgcmVxdWVzdFVybCh7XG4gICAgICB1cmwsXG4gICAgICBtZXRob2Q6IFwiR0VUXCIsXG4gICAgICBoZWFkZXJzOiB0aGlzLmF1dGhIZWFkZXJzKCksXG4gICAgfSk7XG4gICAgcmV0dXJuIHJlc3AuanNvbiBhcyBNaWNyb3B1YkNvbmZpZztcbiAgfVxuXG4gIC8qKlxuICAgKiBEaXNjb3ZlciBtaWNyb3B1YiArIHRva2VuIGVuZHBvaW50IFVSTHMgZnJvbSBhIHNpdGUncyBob21lIHBhZ2VcbiAgICogYnkgcmVhZGluZyA8bGluayByZWw9XCJtaWNyb3B1YlwiPiBhbmQgPGxpbmsgcmVsPVwidG9rZW5fZW5kcG9pbnRcIj4gdGFncy5cbiAgICovXG4gIGFzeW5jIGRpc2NvdmVyRW5kcG9pbnRzKHNpdGVVcmw6IHN0cmluZyk6IFByb21pc2U8e1xuICAgIG1pY3JvcHViRW5kcG9pbnQ/OiBzdHJpbmc7XG4gICAgdG9rZW5FbmRwb2ludD86IHN0cmluZztcbiAgICBtZWRpYUVuZHBvaW50Pzogc3RyaW5nO1xuICB9PiB7XG4gICAgY29uc3QgcmVzcCA9IGF3YWl0IHJlcXVlc3RVcmwoeyB1cmw6IHNpdGVVcmwsIG1ldGhvZDogXCJHRVRcIiB9KTtcbiAgICBjb25zdCBodG1sID0gcmVzcC50ZXh0O1xuXG4gICAgY29uc3QgbWljcm9wdWIgPSB0aGlzLmV4dHJhY3RMaW5rUmVsKGh0bWwsIFwibWljcm9wdWJcIik7XG4gICAgY29uc3QgdG9rZW5FbmRwb2ludCA9IHRoaXMuZXh0cmFjdExpbmtSZWwoaHRtbCwgXCJ0b2tlbl9lbmRwb2ludFwiKTtcblxuICAgIC8vIEFmdGVyIGRpc2NvdmVyaW5nIHRoZSBNaWNyb3B1YiBlbmRwb2ludCwgZmV0Y2ggaXRzIGNvbmZpZyBmb3IgdGhlIG1lZGlhIFVSTFxuICAgIGxldCBtZWRpYUVuZHBvaW50OiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gICAgaWYgKG1pY3JvcHViKSB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCBjZmcgPSBhd2FpdCB0aGlzLmZldGNoQ29uZmlnRnJvbShtaWNyb3B1Yik7XG4gICAgICAgIG1lZGlhRW5kcG9pbnQgPSBjZmdbXCJtZWRpYS1lbmRwb2ludFwiXTtcbiAgICAgIH0gY2F0Y2gge1xuICAgICAgICAvLyBOb24tZmF0YWwgXHUyMDE0IG1lZGlhIGVuZHBvaW50IHN0YXlzIHVuZGVmaW5lZFxuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB7IG1pY3JvcHViRW5kcG9pbnQ6IG1pY3JvcHViLCB0b2tlbkVuZHBvaW50LCBtZWRpYUVuZHBvaW50IH07XG4gIH1cblxuICAvLyBcdTI1MDBcdTI1MDAgUG9zdCBwdWJsaXNoaW5nIFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFxuXG4gIC8qKlxuICAgKiBDcmVhdGUgYSBuZXcgcG9zdCB2aWEgTWljcm9wdWIuXG4gICAqIFNlbmRzIGEgSlNPTiBib2R5IHdpdGggaC1lbnRyeSBwcm9wZXJ0aWVzLlxuICAgKiBSZXR1cm5zIHRoZSBMb2NhdGlvbiBoZWFkZXIgVVJMIG9uIHN1Y2Nlc3MuXG4gICAqL1xuICBhc3luYyBjcmVhdGVQb3N0KHByb3BlcnRpZXM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+KTogUHJvbWlzZTxQdWJsaXNoUmVzdWx0PiB7XG4gICAgY29uc3QgYm9keSA9IHtcbiAgICAgIHR5cGU6IFtcImgtZW50cnlcIl0sXG4gICAgICBwcm9wZXJ0aWVzLFxuICAgIH07XG5cbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVzcCA9IGF3YWl0IHJlcXVlc3RVcmwoe1xuICAgICAgICB1cmw6IHRoaXMuZ2V0RW5kcG9pbnQoKSxcbiAgICAgICAgbWV0aG9kOiBcIlBPU1RcIixcbiAgICAgICAgaGVhZGVyczoge1xuICAgICAgICAgIC4uLnRoaXMuYXV0aEhlYWRlcnMoKSxcbiAgICAgICAgICBcIkNvbnRlbnQtVHlwZVwiOiBcImFwcGxpY2F0aW9uL2pzb25cIixcbiAgICAgICAgfSxcbiAgICAgICAgYm9keTogSlNPTi5zdHJpbmdpZnkoYm9keSksXG4gICAgICAgIHRocm93OiBmYWxzZSxcbiAgICAgIH0pO1xuXG4gICAgICBpZiAocmVzcC5zdGF0dXMgPT09IDIwMSB8fCByZXNwLnN0YXR1cyA9PT0gMjAyKSB7XG4gICAgICAgIGNvbnN0IGxvY2F0aW9uID1cbiAgICAgICAgICByZXNwLmhlYWRlcnM/LltcImxvY2F0aW9uXCJdIHx8XG4gICAgICAgICAgcmVzcC5oZWFkZXJzPy5bXCJMb2NhdGlvblwiXSB8fFxuICAgICAgICAgIChyZXNwLmpzb24gYXMgeyB1cmw/OiBzdHJpbmcgfSk/LnVybDtcbiAgICAgICAgcmV0dXJuIHsgc3VjY2VzczogdHJ1ZSwgdXJsOiBsb2NhdGlvbiB9O1xuICAgICAgfVxuXG4gICAgICBjb25zdCBkZXRhaWwgPSB0aGlzLmV4dHJhY3RFcnJvcihyZXNwLnRleHQpO1xuICAgICAgcmV0dXJuIHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBgSFRUUCAke3Jlc3Auc3RhdHVzfTogJHtkZXRhaWx9YCB9O1xuICAgIH0gY2F0Y2ggKGVycjogdW5rbm93bikge1xuICAgICAgcmV0dXJuIHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBTdHJpbmcoZXJyKSB9O1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBVcGRhdGUgYW4gZXhpc3RpbmcgcG9zdC5cbiAgICogQHBhcmFtIHBvc3RVcmwgIFRoZSBjYW5vbmljYWwgVVJMIG9mIHRoZSBwb3N0IHRvIHVwZGF0ZVxuICAgKiBAcGFyYW0gcmVwbGFjZSAgUHJvcGVydGllcyB0byByZXBsYWNlICh3aWxsIG92ZXJ3cml0ZSBleGlzdGluZyB2YWx1ZXMpXG4gICAqL1xuICBhc3luYyB1cGRhdGVQb3N0KFxuICAgIHBvc3RVcmw6IHN0cmluZyxcbiAgICByZXBsYWNlOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duW10+LFxuICApOiBQcm9taXNlPFB1Ymxpc2hSZXN1bHQ+IHtcbiAgICBjb25zdCBib2R5ID0geyBhY3Rpb246IFwidXBkYXRlXCIsIHVybDogcG9zdFVybCwgcmVwbGFjZSB9O1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3AgPSBhd2FpdCByZXF1ZXN0VXJsKHtcbiAgICAgICAgdXJsOiB0aGlzLmdldEVuZHBvaW50KCksXG4gICAgICAgIG1ldGhvZDogXCJQT1NUXCIsXG4gICAgICAgIGhlYWRlcnM6IHtcbiAgICAgICAgICAuLi50aGlzLmF1dGhIZWFkZXJzKCksXG4gICAgICAgICAgXCJDb250ZW50LVR5cGVcIjogXCJhcHBsaWNhdGlvbi9qc29uXCIsXG4gICAgICAgIH0sXG4gICAgICAgIGJvZHk6IEpTT04uc3RyaW5naWZ5KGJvZHkpLFxuICAgICAgICB0aHJvdzogZmFsc2UsXG4gICAgICB9KTtcblxuICAgICAgaWYgKHJlc3Auc3RhdHVzID49IDIwMCAmJiByZXNwLnN0YXR1cyA8IDMwMCkge1xuICAgICAgICByZXR1cm4geyBzdWNjZXNzOiB0cnVlLCB1cmw6IHBvc3RVcmwgfTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgIGVycm9yOiBgSFRUUCAke3Jlc3Auc3RhdHVzfTogJHt0aGlzLmV4dHJhY3RFcnJvcihyZXNwLnRleHQpfWAsXG4gICAgICB9O1xuICAgIH0gY2F0Y2ggKGVycjogdW5rbm93bikge1xuICAgICAgcmV0dXJuIHsgc3VjY2VzczogZmFsc2UsIGVycm9yOiBTdHJpbmcoZXJyKSB9O1xuICAgIH1cbiAgfVxuXG4gIC8vIFx1MjUwMFx1MjUwMCBNZWRpYSB1cGxvYWQgXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXG5cbiAgLyoqXG4gICAqIFVwbG9hZCBhIGJpbmFyeSBmaWxlIHRvIHRoZSBtZWRpYSBlbmRwb2ludC5cbiAgICogQHJldHVybnMgVGhlIFVSTCBvZiB0aGUgdXBsb2FkZWQgbWVkaWEsIG9yIHRocm93cyBvbiBmYWlsdXJlLlxuICAgKi9cbiAgYXN5bmMgdXBsb2FkTWVkaWEoXG4gICAgZmlsZUJ1ZmZlcjogQXJyYXlCdWZmZXIsXG4gICAgZmlsZU5hbWU6IHN0cmluZyxcbiAgICBtaW1lVHlwZTogc3RyaW5nLFxuICApOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIGNvbnN0IGVuZHBvaW50ID0gdGhpcy5nZXRNZWRpYUVuZHBvaW50KCkgfHwgYCR7dGhpcy5nZXRFbmRwb2ludCgpfS9tZWRpYWA7XG5cbiAgICAvLyBCdWlsZCBtdWx0aXBhcnQvZm9ybS1kYXRhIG1hbnVhbGx5IFx1MjAxNCBPYnNpZGlhbidzIHJlcXVlc3RVcmwgZG9lc24ndFxuICAgIC8vIHN1cHBvcnQgRm9ybURhdGEgZGlyZWN0bHksIHNvIHdlIGVuY29kZSB0aGUgYm91bmRhcnkgb3Vyc2VsdmVzLlxuICAgIGNvbnN0IGJvdW5kYXJ5ID0gYC0tLS1NaWNyb3B1YkJvdW5kYXJ5JHtEYXRlLm5vdygpfWA7XG4gICAgY29uc3QgaGVhZGVyID1cbiAgICAgIGAtLSR7Ym91bmRhcnl9XFxyXFxuYCArXG4gICAgICBgQ29udGVudC1EaXNwb3NpdGlvbjogZm9ybS1kYXRhOyBuYW1lPVwiZmlsZVwiOyBmaWxlbmFtZT1cIiR7ZmlsZU5hbWV9XCJcXHJcXG5gICtcbiAgICAgIGBDb250ZW50LVR5cGU6ICR7bWltZVR5cGV9XFxyXFxuXFxyXFxuYDtcbiAgICBjb25zdCBmb290ZXIgPSBgXFxyXFxuLS0ke2JvdW5kYXJ5fS0tXFxyXFxuYDtcblxuICAgIGNvbnN0IGhlYWRlckJ1ZiA9IG5ldyBUZXh0RW5jb2RlcigpLmVuY29kZShoZWFkZXIpO1xuICAgIGNvbnN0IGZvb3RlckJ1ZiA9IG5ldyBUZXh0RW5jb2RlcigpLmVuY29kZShmb290ZXIpO1xuICAgIGNvbnN0IGZpbGVCdWYgPSBuZXcgVWludDhBcnJheShmaWxlQnVmZmVyKTtcblxuICAgIGNvbnN0IGNvbWJpbmVkID0gbmV3IFVpbnQ4QXJyYXkoXG4gICAgICBoZWFkZXJCdWYubGVuZ3RoICsgZmlsZUJ1Zi5sZW5ndGggKyBmb290ZXJCdWYubGVuZ3RoLFxuICAgICk7XG4gICAgY29tYmluZWQuc2V0KGhlYWRlckJ1ZiwgMCk7XG4gICAgY29tYmluZWQuc2V0KGZpbGVCdWYsIGhlYWRlckJ1Zi5sZW5ndGgpO1xuICAgIGNvbWJpbmVkLnNldChmb290ZXJCdWYsIGhlYWRlckJ1Zi5sZW5ndGggKyBmaWxlQnVmLmxlbmd0aCk7XG5cbiAgICBjb25zdCByZXNwID0gYXdhaXQgcmVxdWVzdFVybCh7XG4gICAgICB1cmw6IGVuZHBvaW50LFxuICAgICAgbWV0aG9kOiBcIlBPU1RcIixcbiAgICAgIGhlYWRlcnM6IHtcbiAgICAgICAgLi4udGhpcy5hdXRoSGVhZGVycygpLFxuICAgICAgICBcIkNvbnRlbnQtVHlwZVwiOiBgbXVsdGlwYXJ0L2Zvcm0tZGF0YTsgYm91bmRhcnk9JHtib3VuZGFyeX1gLFxuICAgICAgfSxcbiAgICAgIGJvZHk6IGNvbWJpbmVkLmJ1ZmZlcixcbiAgICAgIHRocm93OiBmYWxzZSxcbiAgICB9KTtcblxuICAgIGlmIChyZXNwLnN0YXR1cyA9PT0gMjAxIHx8IHJlc3Auc3RhdHVzID09PSAyMDIpIHtcbiAgICAgIGNvbnN0IGxvY2F0aW9uID1cbiAgICAgICAgcmVzcC5oZWFkZXJzPy5bXCJsb2NhdGlvblwiXSB8fFxuICAgICAgICByZXNwLmhlYWRlcnM/LltcIkxvY2F0aW9uXCJdIHx8XG4gICAgICAgIChyZXNwLmpzb24gYXMgeyB1cmw/OiBzdHJpbmcgfSk/LnVybDtcbiAgICAgIGlmIChsb2NhdGlvbikgcmV0dXJuIGxvY2F0aW9uO1xuICAgIH1cblxuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgIGBNZWRpYSB1cGxvYWQgZmFpbGVkIChIVFRQICR7cmVzcC5zdGF0dXN9KTogJHt0aGlzLmV4dHJhY3RFcnJvcihyZXNwLnRleHQpfWAsXG4gICAgKTtcbiAgfVxuXG4gIC8vIFx1MjUwMFx1MjUwMCBIZWxwZXJzIFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFxuXG4gIHByaXZhdGUgYXV0aEhlYWRlcnMoKTogUmVjb3JkPHN0cmluZywgc3RyaW5nPiB7XG4gICAgcmV0dXJuIHsgQXV0aG9yaXphdGlvbjogYEJlYXJlciAke3RoaXMuZ2V0VG9rZW4oKX1gIH07XG4gIH1cblxuICBwcml2YXRlIGV4dHJhY3RMaW5rUmVsKGh0bWw6IHN0cmluZywgcmVsOiBzdHJpbmcpOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICAgIC8vIE1hdGNoIGJvdGggPGxpbms+IGFuZCBIVFRQIExpbmsgaGVhZGVycyBlbWJlZGRlZCBpbiBIVE1MXG4gICAgY29uc3QgcmUgPSBuZXcgUmVnRXhwKFxuICAgICAgYDxsaW5rW14+XStyZWw9W1wiJ10ke3JlbH1bXCInXVtePl0raHJlZj1bXCInXShbXlwiJ10rKVtcIiddfDxsaW5rW14+XStocmVmPVtcIiddKFteXCInXSspW1wiJ11bXj5dK3JlbD1bXCInXSR7cmVsfVtcIiddYCxcbiAgICAgIFwiaVwiLFxuICAgICk7XG4gICAgY29uc3QgbSA9IGh0bWwubWF0Y2gocmUpO1xuICAgIHJldHVybiBtPy5bMV0gPz8gbT8uWzJdO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBmZXRjaENvbmZpZ0Zyb20oZW5kcG9pbnQ6IHN0cmluZyk6IFByb21pc2U8TWljcm9wdWJDb25maWc+IHtcbiAgICBjb25zdCByZXNwID0gYXdhaXQgcmVxdWVzdFVybCh7XG4gICAgICB1cmw6IGAke2VuZHBvaW50fT9xPWNvbmZpZ2AsXG4gICAgICBtZXRob2Q6IFwiR0VUXCIsXG4gICAgICBoZWFkZXJzOiB0aGlzLmF1dGhIZWFkZXJzKCksXG4gICAgfSk7XG4gICAgcmV0dXJuIHJlc3AuanNvbiBhcyBNaWNyb3B1YkNvbmZpZztcbiAgfVxuXG4gIHByaXZhdGUgZXh0cmFjdEVycm9yKHRleHQ6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IG9iaiA9IEpTT04ucGFyc2UodGV4dCkgYXMgeyBlcnJvcl9kZXNjcmlwdGlvbj86IHN0cmluZzsgZXJyb3I/OiBzdHJpbmcgfTtcbiAgICAgIHJldHVybiBvYmouZXJyb3JfZGVzY3JpcHRpb24gPz8gb2JqLmVycm9yID8/IHRleHQuc2xpY2UoMCwgMjAwKTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIHJldHVybiB0ZXh0LnNsaWNlKDAsIDIwMCk7XG4gICAgfVxuICB9XG59XG4iLCAiLyoqXG4gKiBJbmRpZUF1dGgudHMgXHUyMDE0IEluZGllQXV0aCBQS0NFIHNpZ24taW4gZmxvdyBmb3Igb2JzaWRpYW4tbWljcm9wdWJcbiAqXG4gKiBXaHkgbm8gbG9jYWwgSFRUUCBzZXJ2ZXI6XG4gKiAgIEluZGllS2l0IChhbmQgbW9zdCBJbmRpZUF1dGggc2VydmVycykgZmV0Y2ggdGhlIGNsaWVudF9pZCBVUkwgc2VydmVyLXNpZGVcbiAqICAgdG8gcmV0cmlldmUgYXBwIG1ldGFkYXRhLiBBIGxvY2FsIDEyNy4wLjAuMSBhZGRyZXNzIGlzIHVucmVhY2hhYmxlIGZyb20gYVxuICogICByZW1vdGUgc2VydmVyLCBzbyB0aGF0IGFwcHJvYWNoIGFsd2F5cyBmYWlscyB3aXRoIFwiZmV0Y2ggZmFpbGVkXCIuXG4gKlxuICogVGhlIHNvbHV0aW9uIFx1MjAxNCBHaXRIdWIgUGFnZXMgcmVsYXk6XG4gKiAgIGNsaWVudF9pZCAgPSBodHRwczovL3N2ZW1hZ2llLmdpdGh1Yi5pby9vYnNpZGlhbi1taWNyb3B1Yi9cbiAqICAgcmVkaXJlY3RfdXJpID0gaHR0cHM6Ly9zdmVtYWdpZS5naXRodWIuaW8vb2JzaWRpYW4tbWljcm9wdWIvY2FsbGJhY2tcbiAqXG4gKiAgIEJvdGggYXJlIG9uIHRoZSBzYW1lIGhvc3QgXHUyMTkyIEluZGllS2l0J3MgaG9zdC1tYXRjaGluZyBjaGVjayBwYXNzZXMgXHUyNzEzXG4gKiAgIFRoZSBjYWxsYmFjayBwYWdlIGlzIGEgc3RhdGljIEhUTUwgZmlsZSB0aGF0IGltbWVkaWF0ZWx5IHJlZGlyZWN0cyB0b1xuICogICBvYnNpZGlhbjovL21pY3JvcHViLWF1dGg/Y29kZT1DT0RFJnN0YXRlPVNUQVRFXG4gKiAgIE9ic2lkaWFuJ3MgcHJvdG9jb2wgaGFuZGxlciAocmVnaXN0ZXJlZCBpbiBtYWluLnRzKSByZWNlaXZlcyB0aGUgY29kZS5cbiAqXG4gKiBGbG93OlxuICogICAxLiBEaXNjb3ZlciBhdXRob3JpemF0aW9uX2VuZHBvaW50ICsgdG9rZW5fZW5kcG9pbnQgZnJvbSBzaXRlIEhUTUxcbiAqICAgMi4gR2VuZXJhdGUgUEtDRSBjb2RlX3ZlcmlmaWVyICsgY29kZV9jaGFsbGVuZ2UgKFNIQS0yNTYpXG4gKiAgIDMuIE9wZW4gYnJvd3NlciBcdTIxOTIgdXNlcidzIEluZGllQXV0aCBsb2dpbiBwYWdlXG4gKiAgIDQuIFVzZXIgbG9ncyBpbiBcdTIxOTIgc2VydmVyIHJlZGlyZWN0cyB0byBHaXRIdWIgUGFnZXMgY2FsbGJhY2tcbiAqICAgNS4gQ2FsbGJhY2sgcGFnZSByZWRpcmVjdHMgdG8gb2JzaWRpYW46Ly9taWNyb3B1Yi1hdXRoP2NvZGU9Li4uXG4gKiAgIDYuIFBsdWdpbiBwcm90b2NvbCBoYW5kbGVyIHJlc29sdmVzIHRoZSBwZW5kaW5nIFByb21pc2VcbiAqICAgNy4gRXhjaGFuZ2UgY29kZSBmb3IgdG9rZW4gYXQgdG9rZW5fZW5kcG9pbnRcbiAqL1xuXG5pbXBvcnQgKiBhcyBjcnlwdG8gZnJvbSBcImNyeXB0b1wiO1xuaW1wb3J0IHsgcmVxdWVzdFVybCB9IGZyb20gXCJvYnNpZGlhblwiO1xuXG5leHBvcnQgY29uc3QgQ0xJRU5UX0lEICAgPSBcImh0dHBzOi8vc3ZlbWFnaWUuZ2l0aHViLmlvL29ic2lkaWFuLW1pY3JvcHViL1wiO1xuZXhwb3J0IGNvbnN0IFJFRElSRUNUX1VSSSA9IFwiaHR0cHM6Ly9zdmVtYWdpZS5naXRodWIuaW8vb2JzaWRpYW4tbWljcm9wdWIvY2FsbGJhY2tcIjtcblxuY29uc3QgU0NPUEUgPSBcImNyZWF0ZSB1cGRhdGUgbWVkaWFcIjtcbmNvbnN0IEFVVEhfVElNRU9VVF9NUyA9IDUgKiA2MCAqIDEwMDA7IC8vIDUgbWludXRlc1xuXG5leHBvcnQgaW50ZXJmYWNlIEluZGllQXV0aFJlc3VsdCB7XG4gIGFjY2Vzc1Rva2VuOiBzdHJpbmc7XG4gIHNjb3BlOiBzdHJpbmc7XG4gIC8qKiBDYW5vbmljYWwgXCJtZVwiIFVSTCByZXR1cm5lZCBieSB0aGUgdG9rZW4gZW5kcG9pbnQgKi9cbiAgbWU6IHN0cmluZztcbiAgYXV0aG9yaXphdGlvbkVuZHBvaW50OiBzdHJpbmc7XG4gIHRva2VuRW5kcG9pbnQ6IHN0cmluZztcbiAgbWljcm9wdWJFbmRwb2ludD86IHN0cmluZztcbiAgbWVkaWFFbmRwb2ludD86IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBEaXNjb3ZlcmVkRW5kcG9pbnRzIHtcbiAgYXV0aG9yaXphdGlvbkVuZHBvaW50OiBzdHJpbmc7XG4gIHRva2VuRW5kcG9pbnQ6IHN0cmluZztcbiAgbWljcm9wdWJFbmRwb2ludD86IHN0cmluZztcbn1cblxuLyoqIFBlbmRpbmcgY2FsbGJhY2sgc2V0IGJ5IG1haW4udHMgcHJvdG9jb2wgaGFuZGxlciAqL1xubGV0IHBlbmRpbmdDYWxsYmFjazpcbiAgfCB7IHJlc29sdmU6IChwYXJhbXM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4pID0+IHZvaWQ7IHN0YXRlOiBzdHJpbmcgfVxuICB8IG51bGwgPSBudWxsO1xuXG4vKipcbiAqIENhbGxlZCBieSB0aGUgT2JzaWRpYW4gcHJvdG9jb2wgaGFuZGxlciBpbiBtYWluLnRzIHdoZW5cbiAqIG9ic2lkaWFuOi8vbWljcm9wdWItYXV0aCBpcyBvcGVuZWQgYnkgdGhlIGJyb3dzZXIuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBoYW5kbGVQcm90b2NvbENhbGxiYWNrKHBhcmFtczogUmVjb3JkPHN0cmluZywgc3RyaW5nPik6IHZvaWQge1xuICBpZiAoIXBlbmRpbmdDYWxsYmFjaykgcmV0dXJuO1xuXG4gIGNvbnN0IHsgcmVzb2x2ZSwgc3RhdGU6IGV4cGVjdGVkU3RhdGUgfSA9IHBlbmRpbmdDYWxsYmFjaztcbiAgcGVuZGluZ0NhbGxiYWNrID0gbnVsbDtcbiAgcmVzb2x2ZShwYXJhbXMpOyAvLyBsZXQgc2lnbkluKCkgdmFsaWRhdGUgc3RhdGUgKyBleHRyYWN0IGNvZGVcbn1cblxuZXhwb3J0IGNsYXNzIEluZGllQXV0aCB7XG4gIC8vIFx1MjUwMFx1MjUwMCBQdWJsaWMgQVBJIFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFxuXG4gIC8qKlxuICAgKiBEaXNjb3ZlciBJbmRpZUF1dGggKyBNaWNyb3B1YiBlbmRwb2ludCBVUkxzIGZyb20gdGhlIHNpdGUncyBob21lIHBhZ2VcbiAgICogYnkgcmVhZGluZyA8bGluayByZWw9XCIuLi5cIj4gdGFncyBpbiB0aGUgSFRNTCA8aGVhZD4uXG4gICAqL1xuICBzdGF0aWMgYXN5bmMgZGlzY292ZXJFbmRwb2ludHMoc2l0ZVVybDogc3RyaW5nKTogUHJvbWlzZTxEaXNjb3ZlcmVkRW5kcG9pbnRzPiB7XG4gICAgY29uc3QgcmVzcCA9IGF3YWl0IHJlcXVlc3RVcmwoeyB1cmw6IHNpdGVVcmwsIG1ldGhvZDogXCJHRVRcIiB9KTtcbiAgICBjb25zdCBodG1sID0gcmVzcC50ZXh0O1xuXG4gICAgY29uc3QgYXV0aG9yaXphdGlvbkVuZHBvaW50ID0gSW5kaWVBdXRoLmV4dHJhY3RMaW5rUmVsKGh0bWwsIFwiYXV0aG9yaXphdGlvbl9lbmRwb2ludFwiKTtcbiAgICBjb25zdCB0b2tlbkVuZHBvaW50ICAgICAgICAgPSBJbmRpZUF1dGguZXh0cmFjdExpbmtSZWwoaHRtbCwgXCJ0b2tlbl9lbmRwb2ludFwiKTtcbiAgICBjb25zdCBtaWNyb3B1YkVuZHBvaW50ICAgICAgPSBJbmRpZUF1dGguZXh0cmFjdExpbmtSZWwoaHRtbCwgXCJtaWNyb3B1YlwiKTtcblxuICAgIGlmICghYXV0aG9yaXphdGlvbkVuZHBvaW50KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGBObyA8bGluayByZWw9XCJhdXRob3JpemF0aW9uX2VuZHBvaW50XCI+IGZvdW5kIGF0ICR7c2l0ZVVybH0uIGAgK1xuICAgICAgICBcIk1ha2Ugc3VyZSBJbmRpZWtpdCBpcyBydW5uaW5nIGFuZCBTSVRFX1VSTCBpcyBzZXQgY29ycmVjdGx5LlwiLFxuICAgICAgKTtcbiAgICB9XG4gICAgaWYgKCF0b2tlbkVuZHBvaW50KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYE5vIDxsaW5rIHJlbD1cInRva2VuX2VuZHBvaW50XCI+IGZvdW5kIGF0ICR7c2l0ZVVybH0uYCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHsgYXV0aG9yaXphdGlvbkVuZHBvaW50LCB0b2tlbkVuZHBvaW50LCBtaWNyb3B1YkVuZHBvaW50IH07XG4gIH1cblxuICAvKipcbiAgICogUnVuIHRoZSBmdWxsIEluZGllQXV0aCBQS0NFIHNpZ24taW4gZmxvdy5cbiAgICpcbiAgICogT3BlbnMgdGhlIGJyb3dzZXIgYXQgdGhlIHVzZXIncyBJbmRpZUF1dGggbG9naW4gcGFnZS4gQWZ0ZXIgbG9naW4gdGhlXG4gICAqIGJyb3dzZXIgaXMgcmVkaXJlY3RlZCB0byB0aGUgR2l0SHViIFBhZ2VzIGNhbGxiYWNrLCB3aGljaCB0cmlnZ2Vyc1xuICAgKiB0aGUgb2JzaWRpYW46Ly9taWNyb3B1Yi1hdXRoIHByb3RvY29sLCB3aGljaCByZXNvbHZlcyB0aGUgUHJvbWlzZSBoZXJlLlxuICAgKlxuICAgKiBSZXF1aXJlcyBoYW5kbGVQcm90b2NvbENhbGxiYWNrKCkgdG8gYmUgd2lyZWQgdXAgaW4gbWFpbi50cyB2aWFcbiAgICogdGhpcy5yZWdpc3Rlck9ic2lkaWFuUHJvdG9jb2xIYW5kbGVyKFwibWljcm9wdWItYXV0aFwiLCBoYW5kbGVQcm90b2NvbENhbGxiYWNrKVxuICAgKi9cbiAgc3RhdGljIGFzeW5jIHNpZ25JbihzaXRlVXJsOiBzdHJpbmcpOiBQcm9taXNlPEluZGllQXV0aFJlc3VsdD4ge1xuICAgIC8vIDEuIERpc2NvdmVyIGVuZHBvaW50c1xuICAgIGNvbnN0IHsgYXV0aG9yaXphdGlvbkVuZHBvaW50LCB0b2tlbkVuZHBvaW50LCBtaWNyb3B1YkVuZHBvaW50IH0gPVxuICAgICAgYXdhaXQgSW5kaWVBdXRoLmRpc2NvdmVyRW5kcG9pbnRzKHNpdGVVcmwpO1xuXG4gICAgLy8gMi4gR2VuZXJhdGUgUEtDRSArIHN0YXRlXG4gICAgY29uc3Qgc3RhdGUgICAgICAgID0gSW5kaWVBdXRoLmJhc2U2NHVybChjcnlwdG8ucmFuZG9tQnl0ZXMoMTYpKTtcbiAgICBjb25zdCBjb2RlVmVyaWZpZXIgPSBJbmRpZUF1dGguYmFzZTY0dXJsKGNyeXB0by5yYW5kb21CeXRlcyg2NCkpO1xuICAgIGNvbnN0IGNvZGVDaGFsbGVuZ2UgPSBJbmRpZUF1dGguYmFzZTY0dXJsKFxuICAgICAgY3J5cHRvLmNyZWF0ZUhhc2goXCJzaGEyNTZcIikudXBkYXRlKGNvZGVWZXJpZmllcikuZGlnZXN0KCksXG4gICAgKTtcblxuICAgIC8vIDMuIFJlZ2lzdGVyIHBlbmRpbmcgY2FsbGJhY2sgXHUyMDE0IHJlc29sdmVkIGJ5IGhhbmRsZVByb3RvY29sQ2FsbGJhY2soKVxuICAgIGNvbnN0IGNhbGxiYWNrUHJvbWlzZSA9IG5ldyBQcm9taXNlPFJlY29yZDxzdHJpbmcsIHN0cmluZz4+KFxuICAgICAgKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICBjb25zdCB0aW1lb3V0ID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgcGVuZGluZ0NhbGxiYWNrID0gbnVsbDtcbiAgICAgICAgICByZWplY3QobmV3IEVycm9yKFwiU2lnbi1pbiB0aW1lZCBvdXQgKDUgbWluKS4gUGxlYXNlIHRyeSBhZ2Fpbi5cIikpO1xuICAgICAgICB9LCBBVVRIX1RJTUVPVVRfTVMpO1xuXG4gICAgICAgIHBlbmRpbmdDYWxsYmFjayA9IHtcbiAgICAgICAgICBzdGF0ZSxcbiAgICAgICAgICByZXNvbHZlOiAocGFyYW1zKSA9PiB7XG4gICAgICAgICAgICBjbGVhclRpbWVvdXQodGltZW91dCk7XG4gICAgICAgICAgICByZXNvbHZlKHBhcmFtcyk7XG4gICAgICAgICAgfSxcbiAgICAgICAgfTtcbiAgICAgIH0sXG4gICAgKTtcblxuICAgIC8vIDQuIEJ1aWxkIHRoZSBhdXRob3JpemF0aW9uIFVSTCBhbmQgb3BlbiB0aGUgYnJvd3NlclxuICAgIGNvbnN0IGF1dGhVcmwgPSBuZXcgVVJMKGF1dGhvcml6YXRpb25FbmRwb2ludCk7XG4gICAgYXV0aFVybC5zZWFyY2hQYXJhbXMuc2V0KFwicmVzcG9uc2VfdHlwZVwiLCAgICAgICAgXCJjb2RlXCIpO1xuICAgIGF1dGhVcmwuc2VhcmNoUGFyYW1zLnNldChcImNsaWVudF9pZFwiLCAgICAgICAgICAgIENMSUVOVF9JRCk7XG4gICAgYXV0aFVybC5zZWFyY2hQYXJhbXMuc2V0KFwicmVkaXJlY3RfdXJpXCIsICAgICAgICAgUkVESVJFQ1RfVVJJKTtcbiAgICBhdXRoVXJsLnNlYXJjaFBhcmFtcy5zZXQoXCJzdGF0ZVwiLCAgICAgICAgICAgICAgICBzdGF0ZSk7XG4gICAgYXV0aFVybC5zZWFyY2hQYXJhbXMuc2V0KFwiY29kZV9jaGFsbGVuZ2VcIiwgICAgICAgY29kZUNoYWxsZW5nZSk7XG4gICAgYXV0aFVybC5zZWFyY2hQYXJhbXMuc2V0KFwiY29kZV9jaGFsbGVuZ2VfbWV0aG9kXCIsXCJTMjU2XCIpO1xuICAgIGF1dGhVcmwuc2VhcmNoUGFyYW1zLnNldChcInNjb3BlXCIsICAgICAgICAgICAgICAgIFNDT1BFKTtcbiAgICBhdXRoVXJsLnNlYXJjaFBhcmFtcy5zZXQoXCJtZVwiLCAgICAgICAgICAgICAgICAgICBzaXRlVXJsKTtcblxuICAgIHdpbmRvdy5vcGVuKGF1dGhVcmwudG9TdHJpbmcoKSk7XG5cbiAgICAvLyA1LiBXYWl0IGZvciBvYnNpZGlhbjovL21pY3JvcHViLWF1dGggdG8gYmUgY2FsbGVkXG4gICAgY29uc3QgY2FsbGJhY2tQYXJhbXMgPSBhd2FpdCBjYWxsYmFja1Byb21pc2U7XG5cbiAgICAvLyA2LiBWYWxpZGF0ZSBzdGF0ZSAoQ1NSRiBwcm90ZWN0aW9uKVxuICAgIGlmIChjYWxsYmFja1BhcmFtcy5zdGF0ZSAhPT0gc3RhdGUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIlN0YXRlIG1pc21hdGNoIFx1MjAxNCBwb3NzaWJsZSBDU1JGIGF0dGFjay4gUGxlYXNlIHRyeSBhZ2Fpbi5cIik7XG4gICAgfVxuXG4gICAgY29uc3QgY29kZSA9IGNhbGxiYWNrUGFyYW1zLmNvZGU7XG4gICAgaWYgKCFjb2RlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGNhbGxiYWNrUGFyYW1zLmVycm9yX2Rlc2NyaXB0aW9uID8/XG4gICAgICAgIGNhbGxiYWNrUGFyYW1zLmVycm9yID8/XG4gICAgICAgIFwiTm8gYXV0aG9yaXphdGlvbiBjb2RlIHJlY2VpdmVkLlwiLFxuICAgICAgKTtcbiAgICB9XG5cbiAgICAvLyA3LiBFeGNoYW5nZSBjb2RlIGZvciB0b2tlblxuICAgIGNvbnN0IHRva2VuUmVzcCA9IGF3YWl0IHJlcXVlc3RVcmwoe1xuICAgICAgdXJsOiB0b2tlbkVuZHBvaW50LFxuICAgICAgbWV0aG9kOiBcIlBPU1RcIixcbiAgICAgIGhlYWRlcnM6IHtcbiAgICAgICAgXCJDb250ZW50LVR5cGVcIjogXCJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWRcIixcbiAgICAgICAgQWNjZXB0OiBcImFwcGxpY2F0aW9uL2pzb25cIixcbiAgICAgIH0sXG4gICAgICBib2R5OiBuZXcgVVJMU2VhcmNoUGFyYW1zKHtcbiAgICAgICAgZ3JhbnRfdHlwZTogICAgXCJhdXRob3JpemF0aW9uX2NvZGVcIixcbiAgICAgICAgY29kZSxcbiAgICAgICAgY2xpZW50X2lkOiAgICAgQ0xJRU5UX0lELFxuICAgICAgICByZWRpcmVjdF91cmk6ICBSRURJUkVDVF9VUkksXG4gICAgICAgIGNvZGVfdmVyaWZpZXI6IGNvZGVWZXJpZmllcixcbiAgICAgIH0pLnRvU3RyaW5nKCksXG4gICAgICB0aHJvdzogZmFsc2UsXG4gICAgfSk7XG5cbiAgICBjb25zdCBkYXRhID0gdG9rZW5SZXNwLmpzb24gYXMge1xuICAgICAgYWNjZXNzX3Rva2VuPzogc3RyaW5nO1xuICAgICAgc2NvcGU/OiBzdHJpbmc7XG4gICAgICBtZT86IHN0cmluZztcbiAgICAgIGVycm9yPzogc3RyaW5nO1xuICAgICAgZXJyb3JfZGVzY3JpcHRpb24/OiBzdHJpbmc7XG4gICAgfTtcblxuICAgIGlmICghZGF0YS5hY2Nlc3NfdG9rZW4pIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgZGF0YS5lcnJvcl9kZXNjcmlwdGlvbiA/P1xuICAgICAgICBkYXRhLmVycm9yID8/XG4gICAgICAgIGBUb2tlbiBleGNoYW5nZSBmYWlsZWQgKEhUVFAgJHt0b2tlblJlc3Auc3RhdHVzfSlgLFxuICAgICAgKTtcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgYWNjZXNzVG9rZW46ICAgICAgICAgICBkYXRhLmFjY2Vzc190b2tlbixcbiAgICAgIHNjb3BlOiAgICAgICAgICAgICAgICAgZGF0YS5zY29wZSA/PyBTQ09QRSxcbiAgICAgIG1lOiAgICAgICAgICAgICAgICAgICAgZGF0YS5tZSA/PyBzaXRlVXJsLFxuICAgICAgYXV0aG9yaXphdGlvbkVuZHBvaW50LFxuICAgICAgdG9rZW5FbmRwb2ludCxcbiAgICAgIG1pY3JvcHViRW5kcG9pbnQsXG4gICAgfTtcbiAgfVxuXG4gIC8vIFx1MjUwMFx1MjUwMCBIZWxwZXJzIFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFxuXG4gIHByaXZhdGUgc3RhdGljIGJhc2U2NHVybChidWY6IEJ1ZmZlcik6IHN0cmluZyB7XG4gICAgcmV0dXJuIGJ1Zi50b1N0cmluZyhcImJhc2U2NFwiKVxuICAgICAgLnJlcGxhY2UoL1xcKy9nLCBcIi1cIilcbiAgICAgIC5yZXBsYWNlKC9cXC8vZywgXCJfXCIpXG4gICAgICAucmVwbGFjZSgvPS9nLCBcIlwiKTtcbiAgfVxuXG4gIHN0YXRpYyBleHRyYWN0TGlua1JlbChodG1sOiBzdHJpbmcsIHJlbDogc3RyaW5nKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgICBjb25zdCByZSA9IG5ldyBSZWdFeHAoXG4gICAgICBgPGxpbmtbXj5dK3JlbD1bXCInXVteXCInXSpcXFxcYiR7cmVsfVxcXFxiW15cIiddKltcIiddW14+XStocmVmPVtcIiddKFteXCInXSspW1wiJ11gICtcbiAgICAgIGB8PGxpbmtbXj5dK2hyZWY9W1wiJ10oW15cIiddKylbXCInXVtePl0rcmVsPVtcIiddW15cIiddKlxcXFxiJHtyZWx9XFxcXGJbXlwiJ10qW1wiJ11gLFxuICAgICAgXCJpXCIsXG4gICAgKTtcbiAgICBjb25zdCBtID0gaHRtbC5tYXRjaChyZSk7XG4gICAgcmV0dXJuIG0/LlsxXSA/PyBtPy5bMl07XG4gIH1cbn1cbiIsICIvKipcbiAqIFB1Ymxpc2hlci50c1xuICpcbiAqIE9yY2hlc3RyYXRlcyBhIGZ1bGwgcHVibGlzaCBmbG93OlxuICogICAxLiBQYXJzZSB0aGUgYWN0aXZlIG5vdGUncyBmcm9udG1hdHRlciArIGJvZHlcbiAqICAgMi4gVXBsb2FkIGFueSBsb2NhbCBpbWFnZXMgdG8gdGhlIG1lZGlhIGVuZHBvaW50XG4gKiAgIDMuIEJ1aWxkIHRoZSBNaWNyb3B1YiBwcm9wZXJ0aWVzIG9iamVjdFxuICogICA0LiBQT1NUIHRvIHRoZSBNaWNyb3B1YiBlbmRwb2ludFxuICogICA1LiBPcHRpb25hbGx5IHdyaXRlIHRoZSByZXR1cm5lZCBVUkwgYmFjayB0byBmcm9udG1hdHRlclxuICpcbiAqIEdhcmRlbiB0YWcgbWFwcGluZzpcbiAqICAgT2JzaWRpYW4gdGFncyAjZ2FyZGVuL3BsYW50IFx1MjE5MiBnYXJkZW5TdGFnZTogXCJwbGFudFwiIGluIHByb3BlcnRpZXNcbiAqICAgVGhlIGJsb2cgcmVhZHMgdGhpcyBhcyBgZ2FyZGVuU3RhZ2VgIGZyb250bWF0dGVyLCBzbyB0aGUgSW5kaWVraXRcbiAqICAgTWljcm9wdWIgc2VydmVyIG11c3QgYmUgY29uZmlndXJlZCB0byBwYXNzIHRocm91Z2ggdW5rbm93biBwcm9wZXJ0aWVzLlxuICovXG5cbmltcG9ydCB7IEFwcCwgVEZpbGUsIHBhcnNlRnJvbnRNYXR0ZXJBbGlhc2VzLCBwYXJzZVlhbWwsIHN0cmluZ2lmeVlhbWwgfSBmcm9tIFwib2JzaWRpYW5cIjtcbmltcG9ydCB0eXBlIHsgTWljcm9wdWJTZXR0aW5ncywgR2FyZGVuU3RhZ2UsIFB1Ymxpc2hSZXN1bHQgfSBmcm9tIFwiLi90eXBlc1wiO1xuaW1wb3J0IHsgTWljcm9wdWJDbGllbnQgfSBmcm9tIFwiLi9NaWNyb3B1YkNsaWVudFwiO1xuXG5jb25zdCBHQVJERU5fVEFHX1BSRUZJWCA9IFwiZ2FyZGVuL1wiO1xuXG5leHBvcnQgY2xhc3MgUHVibGlzaGVyIHtcbiAgcHJpdmF0ZSBjbGllbnQ6IE1pY3JvcHViQ2xpZW50O1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcmVhZG9ubHkgYXBwOiBBcHAsXG4gICAgcHJpdmF0ZSByZWFkb25seSBzZXR0aW5nczogTWljcm9wdWJTZXR0aW5ncyxcbiAgKSB7XG4gICAgdGhpcy5jbGllbnQgPSBuZXcgTWljcm9wdWJDbGllbnQoXG4gICAgICAoKSA9PiBzZXR0aW5ncy5taWNyb3B1YkVuZHBvaW50LFxuICAgICAgKCkgPT4gc2V0dGluZ3MubWVkaWFFbmRwb2ludCxcbiAgICAgICgpID0+IHNldHRpbmdzLmFjY2Vzc1Rva2VuLFxuICAgICk7XG4gIH1cblxuICAvKiogUHVibGlzaCB0aGUgZ2l2ZW4gZmlsZS4gUmV0dXJucyBQdWJsaXNoUmVzdWx0LiAqL1xuICBhc3luYyBwdWJsaXNoKGZpbGU6IFRGaWxlKTogUHJvbWlzZTxQdWJsaXNoUmVzdWx0PiB7XG4gICAgY29uc3QgcmF3ID0gYXdhaXQgdGhpcy5hcHAudmF1bHQucmVhZChmaWxlKTtcbiAgICBjb25zdCB7IGZyb250bWF0dGVyLCBib2R5IH0gPSB0aGlzLnBhcnNlRnJvbnRtYXR0ZXIocmF3KTtcblxuICAgIC8vIERldGVybWluZSBpZiB0aGlzIGlzIGFuIHVwZGF0ZSAocG9zdCBhbHJlYWR5IGhhcyBhIFVSTCkgb3IgbmV3IHBvc3RcbiAgICBjb25zdCBleGlzdGluZ1VybDogc3RyaW5nIHwgdW5kZWZpbmVkID1cbiAgICAgIGZyb250bWF0dGVyW1wibXAtdXJsXCJdID8/IGZyb250bWF0dGVyW1widXJsXCJdID8/IHVuZGVmaW5lZDtcblxuICAgIC8vIFVwbG9hZCBsb2NhbCBpbWFnZXMgYW5kIHJld3JpdGUgbWFya2Rvd24gcmVmZXJlbmNlc1xuICAgIGNvbnN0IHsgY29udGVudDogcHJvY2Vzc2VkQm9keSwgdXBsb2FkZWRVcmxzIH0gPVxuICAgICAgYXdhaXQgdGhpcy5wcm9jZXNzSW1hZ2VzKGJvZHkpO1xuXG4gICAgLy8gQnVpbGQgTWljcm9wdWIgcHJvcGVydGllc1xuICAgIGNvbnN0IHByb3BlcnRpZXMgPSB0aGlzLmJ1aWxkUHJvcGVydGllcyhmcm9udG1hdHRlciwgcHJvY2Vzc2VkQm9keSwgdXBsb2FkZWRVcmxzKTtcblxuICAgIGxldCByZXN1bHQ6IFB1Ymxpc2hSZXN1bHQ7XG5cbiAgICBpZiAoZXhpc3RpbmdVcmwpIHtcbiAgICAgIC8vIFVwZGF0ZSBleGlzdGluZyBwb3N0XG4gICAgICBjb25zdCByZXBsYWNlOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duW10+ID0ge307XG4gICAgICBmb3IgKGNvbnN0IFtrLCB2XSBvZiBPYmplY3QuZW50cmllcyhwcm9wZXJ0aWVzKSkge1xuICAgICAgICByZXBsYWNlW2tdID0gQXJyYXkuaXNBcnJheSh2KSA/IHYgOiBbdl07XG4gICAgICB9XG4gICAgICByZXN1bHQgPSBhd2FpdCB0aGlzLmNsaWVudC51cGRhdGVQb3N0KGV4aXN0aW5nVXJsLCByZXBsYWNlKTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gQ3JlYXRlIG5ldyBwb3N0XG4gICAgICByZXN1bHQgPSBhd2FpdCB0aGlzLmNsaWVudC5jcmVhdGVQb3N0KHByb3BlcnRpZXMpO1xuICAgIH1cblxuICAgIC8vIFdyaXRlIFVSTCBiYWNrIHRvIGZyb250bWF0dGVyXG4gICAgaWYgKHJlc3VsdC5zdWNjZXNzICYmIHJlc3VsdC51cmwgJiYgdGhpcy5zZXR0aW5ncy53cml0ZVVybFRvRnJvbnRtYXR0ZXIpIHtcbiAgICAgIGF3YWl0IHRoaXMud3JpdGVVcmxUb05vdGUoZmlsZSwgcmF3LCByZXN1bHQudXJsKTtcbiAgICB9XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgLy8gXHUyNTAwXHUyNTAwIFByb3BlcnR5IGJ1aWxkZXIgXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXG5cbiAgcHJpdmF0ZSBidWlsZFByb3BlcnRpZXMoXG4gICAgZm06IFJlY29yZDxzdHJpbmcsIHVua25vd24+LFxuICAgIGJvZHk6IHN0cmluZyxcbiAgICB1cGxvYWRlZFVybHM6IHN0cmluZ1tdLFxuICApOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB7XG4gICAgY29uc3QgcHJvcHM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0ge307XG5cbiAgICAvLyBcdTI1MDBcdTI1MDAgUG9zdCB0eXBlIGRldGVjdGlvbiBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcdTI1MDBcbiAgICAvLyBJbnRlcmFjdGlvbiBwb3N0cyAoYm9va21hcmssIGxpa2UsIHJlcGx5LCByZXBvc3QpIGhhdmUgbm8gYm9keSBjb250ZW50LlxuICAgIC8vIEZvciB0aG9zZSwgb25seSBpbmNsdWRlIGNvbnRlbnQgaWYgdGhlIG5vdGUgYm9keSBpcyBub24tZW1wdHkgKGkuZS4gYSBjb21tZW50L3F1b3RlKS5cbiAgICBjb25zdCB0cmltbWVkQm9keSA9IGJvZHkudHJpbSgpO1xuXG4gICAgLy8gXHUyNTAwXHUyNTAwIEludGVyYWN0aW9uIFVSTCBwcm9wZXJ0aWVzIFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFxuICAgIC8vIFN1cHBvcnQgYm90aCBjYW1lbENhc2UgKE9ic2lkaWFuLWZyaWVuZGx5KSBhbmQgaHlwaGVuYXRlZCAoTWljcm9wdWItc3BlYykuXG4gICAgY29uc3QgYm9va21hcmtPZiA9IGZtW1wiYm9va21hcmtPZlwiXSA/PyBmbVtcImJvb2ttYXJrLW9mXCJdO1xuICAgIGNvbnN0IGxpa2VPZiAgICAgPSBmbVtcImxpa2VPZlwiXSAgICAgPz8gZm1bXCJsaWtlLW9mXCJdO1xuICAgIGNvbnN0IGluUmVwbHlUbyAgPSBmbVtcImluUmVwbHlUb1wiXSAgPz8gZm1bXCJpbi1yZXBseS10b1wiXTtcbiAgICBjb25zdCByZXBvc3RPZiAgID0gZm1bXCJyZXBvc3RPZlwiXSAgID8/IGZtW1wicmVwb3N0LW9mXCJdO1xuXG4gICAgaWYgKGJvb2ttYXJrT2YpIHByb3BzW1wiYm9va21hcmstb2ZcIl0gPSBbU3RyaW5nKGJvb2ttYXJrT2YpXTtcbiAgICBpZiAobGlrZU9mKSAgICAgcHJvcHNbXCJsaWtlLW9mXCJdICAgICA9IFtTdHJpbmcobGlrZU9mKV07XG4gICAgaWYgKGluUmVwbHlUbykgIHByb3BzW1wiaW4tcmVwbHktdG9cIl0gPSBbU3RyaW5nKGluUmVwbHlUbyldO1xuICAgIGlmIChyZXBvc3RPZikgICBwcm9wc1tcInJlcG9zdC1vZlwiXSAgID0gW1N0cmluZyhyZXBvc3RPZildO1xuXG4gICAgLy8gQ29udGVudCBcdTIwMTQgb21pdCBmb3IgYmFyZSBsaWtlcy9yZXBvc3RzIHdpdGggbm8gYm9keSB0ZXh0XG4gICAgY29uc3QgaXNJbnRlcmFjdGlvbldpdGhvdXRCb2R5ID1cbiAgICAgIChsaWtlT2YgfHwgcmVwb3N0T2YpICYmICF0cmltbWVkQm9keTtcbiAgICBpZiAoIWlzSW50ZXJhY3Rpb25XaXRob3V0Qm9keSkge1xuICAgICAgcHJvcHNbXCJjb250ZW50XCJdID0gdHJpbW1lZEJvZHkgPyBbeyBodG1sOiB0cmltbWVkQm9keSB9XSA6IFt7IGh0bWw6IFwiXCIgfV07XG4gICAgfVxuXG4gICAgLy8gXHUyNTAwXHUyNTAwIFN0YW5kYXJkIHByb3BlcnRpZXMgXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXG5cbiAgICAvLyBUaXRsZSAoYXJ0aWNsZXMgaGF2ZSB0aXRsZXM7IG5vdGVzL21pY3JvLXBvc3RzIGRvbid0KVxuICAgIGlmIChmbVtcInRpdGxlXCJdKSB7XG4gICAgICBwcm9wc1tcIm5hbWVcIl0gPSBbU3RyaW5nKGZtW1widGl0bGVcIl0pXTtcbiAgICB9XG5cbiAgICAvLyBTdW1tYXJ5IC8gZXhjZXJwdFxuICAgIGlmIChmbVtcInN1bW1hcnlcIl0gPz8gZm1bXCJleGNlcnB0XCJdKSB7XG4gICAgICBwcm9wc1tcInN1bW1hcnlcIl0gPSBbU3RyaW5nKGZtW1wic3VtbWFyeVwiXSA/PyBmbVtcImV4Y2VycHRcIl0pXTtcbiAgICB9XG5cbiAgICAvLyBQdWJsaXNoZWQgZGF0ZVxuICAgIGlmIChmbVtcImRhdGVcIl0pIHtcbiAgICAgIHByb3BzW1wicHVibGlzaGVkXCJdID0gW25ldyBEYXRlKFN0cmluZyhmbVtcImRhdGVcIl0pKS50b0lTT1N0cmluZygpXTtcbiAgICB9XG5cbiAgICAvLyBDYXRlZ29yaWVzIGZyb20gZnJvbnRtYXR0ZXIgYGNhdGVnb3J5YCBvciBgdGFnc2AgKGV4Y2x1ZGluZyBnYXJkZW4vKiB0YWdzKVxuICAgIGNvbnN0IHJhd1RhZ3MgPSB0aGlzLnJlc29sdmVBcnJheShmbVtcInRhZ3NcIl0gPz8gZm1bXCJjYXRlZ29yeVwiXSk7XG4gICAgY29uc3QgZ2FyZGVuU3RhZ2UgPSB0aGlzLmV4dHJhY3RHYXJkZW5TdGFnZShyYXdUYWdzKTtcbiAgICBjb25zdCBub3JtYWxUYWdzID0gcmF3VGFncy5maWx0ZXIoXG4gICAgICAodCkgPT4gIXQuc3RhcnRzV2l0aChHQVJERU5fVEFHX1BSRUZJWCkgJiYgdCAhPT0gXCJnYXJkZW5cIixcbiAgICApO1xuICAgIGlmIChub3JtYWxUYWdzLmxlbmd0aCA+IDApIHtcbiAgICAgIHByb3BzW1wiY2F0ZWdvcnlcIl0gPSBub3JtYWxUYWdzO1xuICAgIH1cblxuICAgIC8vIEdhcmRlbiBzdGFnZSBcdTIxOTIgZGVkaWNhdGVkIHByb3BlcnR5XG4gICAgaWYgKHRoaXMuc2V0dGluZ3MubWFwR2FyZGVuVGFncyAmJiBnYXJkZW5TdGFnZSkge1xuICAgICAgcHJvcHNbXCJnYXJkZW4tc3RhZ2VcIl0gPSBbZ2FyZGVuU3RhZ2VdO1xuICAgIH1cblxuICAgIC8vIFN5bmRpY2F0aW9uIHRhcmdldHNcbiAgICAvLyBTdXBwb3J0IGJvdGggY2FtZWxDYXNlIChtcFN5bmRpY2F0ZVRvKSB1c2VkIGluIGV4aXN0aW5nIGJsb2cgcG9zdHMgYW5kIG1wLXN5bmRpY2F0ZS10b1xuICAgIGNvbnN0IHN5bmRpY2F0ZVRvID0gdGhpcy5yZXNvbHZlQXJyYXkoXG4gICAgICBmbVtcIm1wLXN5bmRpY2F0ZS10b1wiXSA/PyBmbVtcIm1wU3luZGljYXRlVG9cIl0sXG4gICAgKTtcbiAgICBjb25zdCBhbGxTeW5kaWNhdGVUbyA9IFtcbiAgICAgIC4uLm5ldyBTZXQoWy4uLnRoaXMuc2V0dGluZ3MuZGVmYXVsdFN5bmRpY2F0ZVRvLCAuLi5zeW5kaWNhdGVUb10pLFxuICAgIF07XG4gICAgaWYgKGFsbFN5bmRpY2F0ZVRvLmxlbmd0aCA+IDApIHtcbiAgICAgIHByb3BzW1wibXAtc3luZGljYXRlLXRvXCJdID0gYWxsU3luZGljYXRlVG87XG4gICAgfVxuXG4gICAgLy8gVmlzaWJpbGl0eVxuICAgIGNvbnN0IHZpc2liaWxpdHkgPVxuICAgICAgKGZtW1widmlzaWJpbGl0eVwiXSBhcyBzdHJpbmcpID8/IHRoaXMuc2V0dGluZ3MuZGVmYXVsdFZpc2liaWxpdHk7XG4gICAgaWYgKHZpc2liaWxpdHkgJiYgdmlzaWJpbGl0eSAhPT0gXCJwdWJsaWNcIikge1xuICAgICAgcHJvcHNbXCJ2aXNpYmlsaXR5XCJdID0gW3Zpc2liaWxpdHldO1xuICAgIH1cblxuICAgIC8vIEFJIGRpc2Nsb3N1cmUgKGN1c3RvbSBwcm9wZXJ0eSBwYXNzZWQgdGhyb3VnaCB0byBJbmRpZWtpdClcbiAgICBpZiAoZm1bXCJhaVwiXSAmJiB0eXBlb2YgZm1bXCJhaVwiXSA9PT0gXCJvYmplY3RcIikge1xuICAgICAgcHJvcHNbXCJhaVwiXSA9IFtmbVtcImFpXCJdXTtcbiAgICB9XG5cbiAgICAvLyBQaG90b3M6IHByZWZlciBzdHJ1Y3R1cmVkIHBob3RvIGFycmF5IGZyb20gZnJvbnRtYXR0ZXIgKHdpdGggYWx0IHRleHQpLFxuICAgIC8vIGZhbGwgYmFjayB0byB1cGxvYWRlZCBsb2NhbCBpbWFnZXMuXG4gICAgY29uc3QgZm1QaG90b3MgPSB0aGlzLnJlc29sdmVQaG90b0FycmF5KGZtW1wicGhvdG9cIl0pO1xuICAgIGlmIChmbVBob3Rvcy5sZW5ndGggPiAwKSB7XG4gICAgICBwcm9wc1tcInBob3RvXCJdID0gZm1QaG90b3M7XG4gICAgfSBlbHNlIGlmICh1cGxvYWRlZFVybHMubGVuZ3RoID4gMCkge1xuICAgICAgcHJvcHNbXCJwaG90b1wiXSA9IHVwbG9hZGVkVXJscy5tYXAoKHVybCkgPT4gKHsgdmFsdWU6IHVybCB9KSk7XG4gICAgfVxuXG4gICAgLy8gUGFzcyB0aHJvdWdoIGFueSBgbXAtKmAgcHJvcGVydGllcyBmcm9tIGZyb250bWF0dGVyIHZlcmJhdGltXG4gICAgZm9yIChjb25zdCBbaywgdl0gb2YgT2JqZWN0LmVudHJpZXMoZm0pKSB7XG4gICAgICBpZiAoay5zdGFydHNXaXRoKFwibXAtXCIpICYmIGsgIT09IFwibXAtdXJsXCIgJiYgayAhPT0gXCJtcC1zeW5kaWNhdGUtdG9cIikge1xuICAgICAgICBwcm9wc1trXSA9IHRoaXMucmVzb2x2ZUFycmF5KHYpO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBwcm9wcztcbiAgfVxuXG4gIC8qKlxuICAgKiBOb3JtYWxpc2UgdGhlIGBwaG90b2AgZnJvbnRtYXR0ZXIgZmllbGQgaW50byBNaWNyb3B1YiBwaG90byBvYmplY3RzLlxuICAgKiBIYW5kbGVzIHRocmVlIGZvcm1hdHM6XG4gICAqICAgLSBzdHJpbmcgVVJMOiBcImh0dHBzOi8vLi4uXCJcbiAgICogICAtIGFycmF5IG9mIHN0cmluZ3M6IFtcImh0dHBzOi8vLi4uXCJdXG4gICAqICAgLSBhcnJheSBvZiBvYmplY3RzOiBbe3VybDogXCJodHRwczovLy4uLlwiLCBhbHQ6IFwiLi4uXCJ9XVxuICAgKi9cbiAgcHJpdmF0ZSByZXNvbHZlUGhvdG9BcnJheShcbiAgICB2YWx1ZTogdW5rbm93bixcbiAgKTogQXJyYXk8eyB2YWx1ZTogc3RyaW5nOyBhbHQ/OiBzdHJpbmcgfT4ge1xuICAgIGlmICghdmFsdWUpIHJldHVybiBbXTtcbiAgICBjb25zdCBpdGVtcyA9IEFycmF5LmlzQXJyYXkodmFsdWUpID8gdmFsdWUgOiBbdmFsdWVdO1xuICAgIHJldHVybiBpdGVtc1xuICAgICAgLm1hcCgoaXRlbSkgPT4ge1xuICAgICAgICBpZiAodHlwZW9mIGl0ZW0gPT09IFwic3RyaW5nXCIpIHJldHVybiB7IHZhbHVlOiBpdGVtIH07XG4gICAgICAgIGlmICh0eXBlb2YgaXRlbSA9PT0gXCJvYmplY3RcIiAmJiBpdGVtICE9PSBudWxsKSB7XG4gICAgICAgICAgY29uc3Qgb2JqID0gaXRlbSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICAgICAgICBjb25zdCB1cmwgPSBTdHJpbmcob2JqW1widXJsXCJdID8/IG9ialtcInZhbHVlXCJdID8/IFwiXCIpO1xuICAgICAgICAgIGlmICghdXJsKSByZXR1cm4gbnVsbDtcbiAgICAgICAgICByZXR1cm4gb2JqW1wiYWx0XCJdXG4gICAgICAgICAgICA/IHsgdmFsdWU6IHVybCwgYWx0OiBTdHJpbmcob2JqW1wiYWx0XCJdKSB9XG4gICAgICAgICAgICA6IHsgdmFsdWU6IHVybCB9O1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgICAgfSlcbiAgICAgIC5maWx0ZXIoKHgpOiB4IGlzIHsgdmFsdWU6IHN0cmluZzsgYWx0Pzogc3RyaW5nIH0gPT4geCAhPT0gbnVsbCk7XG4gIH1cblxuICAvLyBcdTI1MDBcdTI1MDAgR2FyZGVuIHRhZyBleHRyYWN0aW9uIFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFxuXG4gIC8qKlxuICAgKiBGaW5kIHRoZSBmaXJzdCAjZ2FyZGVuLzxzdGFnZT4gdGFnIGFuZCByZXR1cm4gdGhlIHN0YWdlIG5hbWUuXG4gICAqIFN1cHBvcnRzIGJvdGggXCJnYXJkZW4vcGxhbnRcIiAoT2JzaWRpYW4gYXJyYXkpIGFuZCBcIiNnYXJkZW4vcGxhbnRcIiAoaW5saW5lKS5cbiAgICovXG4gIHByaXZhdGUgZXh0cmFjdEdhcmRlblN0YWdlKHRhZ3M6IHN0cmluZ1tdKTogR2FyZGVuU3RhZ2UgfCB1bmRlZmluZWQge1xuICAgIGZvciAoY29uc3QgdGFnIG9mIHRhZ3MpIHtcbiAgICAgIGNvbnN0IGNsZWFuID0gdGFnLnJlcGxhY2UoL14jLywgXCJcIik7XG4gICAgICBpZiAoY2xlYW4uc3RhcnRzV2l0aChHQVJERU5fVEFHX1BSRUZJWCkpIHtcbiAgICAgICAgY29uc3Qgc3RhZ2UgPSBjbGVhbi5zbGljZShHQVJERU5fVEFHX1BSRUZJWC5sZW5ndGgpIGFzIEdhcmRlblN0YWdlO1xuICAgICAgICBjb25zdCB2YWxpZDogR2FyZGVuU3RhZ2VbXSA9IFtcbiAgICAgICAgICBcInBsYW50XCIsIFwiY3VsdGl2YXRlXCIsIFwicXVlc3Rpb25cIiwgXCJyZXBvdFwiLCBcInJldml0YWxpemVcIiwgXCJyZXZpc2l0XCIsXG4gICAgICAgIF07XG4gICAgICAgIGlmICh2YWxpZC5pbmNsdWRlcyhzdGFnZSkpIHJldHVybiBzdGFnZTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuXG4gIC8vIFx1MjUwMFx1MjUwMCBJbWFnZSBwcm9jZXNzaW5nIFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFx1MjUwMFxuXG4gIC8qKlxuICAgKiBGaW5kIGFsbCBgIVtbbG9jYWwtaW1hZ2UucG5nXV1gIG9yIGAhW2FsdF0ocmVsYXRpdmUvcGF0aC5qcGcpYCBpbiB0aGUgYm9keSxcbiAgICogdXBsb2FkIHRoZW0gdG8gdGhlIG1lZGlhIGVuZHBvaW50LCBhbmQgcmVwbGFjZSB0aGUgcmVmZXJlbmNlcyB3aXRoIHJlbW90ZSBVUkxzLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBwcm9jZXNzSW1hZ2VzKFxuICAgIGJvZHk6IHN0cmluZyxcbiAgKTogUHJvbWlzZTx7IGNvbnRlbnQ6IHN0cmluZzsgdXBsb2FkZWRVcmxzOiBzdHJpbmdbXSB9PiB7XG4gICAgY29uc3QgdXBsb2FkZWRVcmxzOiBzdHJpbmdbXSA9IFtdO1xuXG4gICAgLy8gTWF0Y2ggd2lraS1zdHlsZSBlbWJlZHM6ICFbW2ZpbGVuYW1lLmV4dF1dXG4gICAgY29uc3Qgd2lraVBhdHRlcm4gPSAvIVxcW1xcWyhbXlxcXV0rXFwuKHBuZ3xqcGd8anBlZ3xnaWZ8d2VicHxzdmcpKVxcXVxcXS9naTtcbiAgICAvLyBNYXRjaCBtYXJrZG93biBpbWFnZXM6ICFbYWx0XShwYXRoKVxuICAgIGNvbnN0IG1kUGF0dGVybiA9IC8hXFxbKFteXFxdXSopXFxdXFwoKFteKV0rXFwuKHBuZ3xqcGd8anBlZ3xnaWZ8d2VicHxzdmcpKVxcKS9naTtcblxuICAgIGxldCBjb250ZW50ID0gYm9keTtcblxuICAgIC8vIFByb2Nlc3Mgd2lraS1zdHlsZSBlbWJlZHNcbiAgICBjb25zdCB3aWtpTWF0Y2hlcyA9IFsuLi5ib2R5Lm1hdGNoQWxsKHdpa2lQYXR0ZXJuKV07XG4gICAgZm9yIChjb25zdCBtYXRjaCBvZiB3aWtpTWF0Y2hlcykge1xuICAgICAgY29uc3QgZmlsZW5hbWUgPSBtYXRjaFsxXTtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHJlbW90ZVVybCA9IGF3YWl0IHRoaXMudXBsb2FkTG9jYWxGaWxlKGZpbGVuYW1lKTtcbiAgICAgICAgaWYgKHJlbW90ZVVybCkge1xuICAgICAgICAgIHVwbG9hZGVkVXJscy5wdXNoKHJlbW90ZVVybCk7XG4gICAgICAgICAgY29udGVudCA9IGNvbnRlbnQucmVwbGFjZShtYXRjaFswXSwgYCFbJHtmaWxlbmFtZX1dKCR7cmVtb3RlVXJsfSlgKTtcbiAgICAgICAgfVxuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGNvbnNvbGUud2FybihgW21pY3JvcHViXSBGYWlsZWQgdG8gdXBsb2FkICR7ZmlsZW5hbWV9OmAsIGVycik7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gUHJvY2VzcyBtYXJrZG93biBpbWFnZSByZWZlcmVuY2VzXG4gICAgY29uc3QgbWRNYXRjaGVzID0gWy4uLmNvbnRlbnQubWF0Y2hBbGwobWRQYXR0ZXJuKV07XG4gICAgZm9yIChjb25zdCBtYXRjaCBvZiBtZE1hdGNoZXMpIHtcbiAgICAgIGNvbnN0IGFsdCA9IG1hdGNoWzFdO1xuICAgICAgY29uc3QgcGF0aCA9IG1hdGNoWzJdO1xuICAgICAgaWYgKHBhdGguc3RhcnRzV2l0aChcImh0dHBcIikpIGNvbnRpbnVlOyAvLyBhbHJlYWR5IHJlbW90ZVxuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgcmVtb3RlVXJsID0gYXdhaXQgdGhpcy51cGxvYWRMb2NhbEZpbGUocGF0aCk7XG4gICAgICAgIGlmIChyZW1vdGVVcmwpIHtcbiAgICAgICAgICB1cGxvYWRlZFVybHMucHVzaChyZW1vdGVVcmwpO1xuICAgICAgICAgIGNvbnRlbnQgPSBjb250ZW50LnJlcGxhY2UobWF0Y2hbMF0sIGAhWyR7YWx0fV0oJHtyZW1vdGVVcmx9KWApO1xuICAgICAgICB9XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgY29uc29sZS53YXJuKGBbbWljcm9wdWJdIEZhaWxlZCB0byB1cGxvYWQgJHtwYXRofTpgLCBlcnIpO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB7IGNvbnRlbnQsIHVwbG9hZGVkVXJscyB9O1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyB1cGxvYWRMb2NhbEZpbGUocGF0aDogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmcgfCB1bmRlZmluZWQ+IHtcbiAgICBjb25zdCBmaWxlID0gdGhpcy5hcHAudmF1bHQuZ2V0RmlsZXMoKS5maW5kKFxuICAgICAgKGYpID0+IGYubmFtZSA9PT0gcGF0aCB8fCBmLnBhdGggPT09IHBhdGgsXG4gICAgKTtcbiAgICBpZiAoIWZpbGUpIHJldHVybiB1bmRlZmluZWQ7XG5cbiAgICBjb25zdCBidWZmZXIgPSBhd2FpdCB0aGlzLmFwcC52YXVsdC5yZWFkQmluYXJ5KGZpbGUpO1xuICAgIGNvbnN0IG1pbWVUeXBlID0gdGhpcy5ndWVzc01pbWVUeXBlKGZpbGUuZXh0ZW5zaW9uKTtcblxuICAgIHJldHVybiB0aGlzLmNsaWVudC51cGxvYWRNZWRpYShidWZmZXIsIGZpbGUubmFtZSwgbWltZVR5cGUpO1xuICB9XG5cbiAgLy8gXHUyNTAwXHUyNTAwIEZyb250bWF0dGVyIGhlbHBlcnMgXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXHUyNTAwXG5cbiAgcHJpdmF0ZSBwYXJzZUZyb250bWF0dGVyKHJhdzogc3RyaW5nKToge1xuICAgIGZyb250bWF0dGVyOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICBib2R5OiBzdHJpbmc7XG4gIH0ge1xuICAgIGNvbnN0IGZtTWF0Y2ggPSByYXcubWF0Y2goL14tLS1cXHI/XFxuKFtcXHNcXFNdKj8pXFxyP1xcbi0tLVxccj9cXG4oW1xcc1xcU10qKSQvKTtcbiAgICBpZiAoIWZtTWF0Y2gpIHJldHVybiB7IGZyb250bWF0dGVyOiB7fSwgYm9keTogcmF3IH07XG5cbiAgICBsZXQgZnJvbnRtYXR0ZXI6IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0ge307XG4gICAgdHJ5IHtcbiAgICAgIGZyb250bWF0dGVyID0gKHBhcnNlWWFtbChmbU1hdGNoWzFdKSA/PyB7fSkgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG4gICAgfSBjYXRjaCB7XG4gICAgICAvLyBNYWxmb3JtZWQgZnJvbnRtYXR0ZXIgXHUyMDE0IHRyZWF0IGFzIGVtcHR5XG4gICAgfVxuXG4gICAgcmV0dXJuIHsgZnJvbnRtYXR0ZXIsIGJvZHk6IGZtTWF0Y2hbMl0gfTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgd3JpdGVVcmxUb05vdGUoXG4gICAgZmlsZTogVEZpbGUsXG4gICAgb3JpZ2luYWxDb250ZW50OiBzdHJpbmcsXG4gICAgdXJsOiBzdHJpbmcsXG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IGZtTWF0Y2ggPSBvcmlnaW5hbENvbnRlbnQubWF0Y2goXG4gICAgICAvXigtLS1cXHI/XFxuW1xcc1xcU10qP1xccj9cXG4tLS1cXHI/XFxuKShbXFxzXFxTXSopJC8sXG4gICAgKTtcblxuICAgIGlmICghZm1NYXRjaCkge1xuICAgICAgLy8gTm8gZXhpc3RpbmcgZnJvbnRtYXR0ZXIgXHUyMDE0IHByZXBlbmQgaXRcbiAgICAgIGNvbnN0IG5ld0ZtID0gYC0tLVxcbm1wLXVybDogXCIke3VybH1cIlxcbi0tLVxcbmA7XG4gICAgICBhd2FpdCB0aGlzLmFwcC52YXVsdC5tb2RpZnkoZmlsZSwgbmV3Rm0gKyBvcmlnaW5hbENvbnRlbnQpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIEluamVjdCBtcC11cmwgaW50byBleGlzdGluZyBmcm9udG1hdHRlciBibG9ja1xuICAgIGNvbnN0IGZtQmxvY2sgPSBmbU1hdGNoWzFdO1xuICAgIGNvbnN0IGJvZHkgPSBmbU1hdGNoWzJdO1xuXG4gICAgaWYgKGZtQmxvY2suaW5jbHVkZXMoXCJtcC11cmw6XCIpKSB7XG4gICAgICAvLyBSZXBsYWNlIGV4aXN0aW5nIG1wLXVybCBsaW5lXG4gICAgICBjb25zdCB1cGRhdGVkID0gZm1CbG9jay5yZXBsYWNlKFxuICAgICAgICAvbXAtdXJsOi4qKFxccj9cXG4pLyxcbiAgICAgICAgYG1wLXVybDogXCIke3VybH1cIiQxYCxcbiAgICAgICk7XG4gICAgICBhd2FpdCB0aGlzLmFwcC52YXVsdC5tb2RpZnkoZmlsZSwgdXBkYXRlZCArIGJvZHkpO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBJbnNlcnQgbXAtdXJsIGJlZm9yZSBjbG9zaW5nIC0tLVxuICAgICAgY29uc3QgdXBkYXRlZCA9IGZtQmxvY2sucmVwbGFjZShcbiAgICAgICAgLyhcXHI/XFxuLS0tXFxyP1xcbikkLyxcbiAgICAgICAgYFxcbm1wLXVybDogXCIke3VybH1cIiQxYCxcbiAgICAgICk7XG4gICAgICBhd2FpdCB0aGlzLmFwcC52YXVsdC5tb2RpZnkoZmlsZSwgdXBkYXRlZCArIGJvZHkpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgcmVzb2x2ZUFycmF5KHZhbHVlOiB1bmtub3duKTogc3RyaW5nW10ge1xuICAgIGlmICghdmFsdWUpIHJldHVybiBbXTtcbiAgICBpZiAoQXJyYXkuaXNBcnJheSh2YWx1ZSkpIHJldHVybiB2YWx1ZS5tYXAoU3RyaW5nKTtcbiAgICByZXR1cm4gW1N0cmluZyh2YWx1ZSldO1xuICB9XG5cbiAgcHJpdmF0ZSBndWVzc01pbWVUeXBlKGV4dDogc3RyaW5nKTogc3RyaW5nIHtcbiAgICBjb25zdCBtYXA6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7XG4gICAgICBwbmc6IFwiaW1hZ2UvcG5nXCIsXG4gICAgICBqcGc6IFwiaW1hZ2UvanBlZ1wiLFxuICAgICAganBlZzogXCJpbWFnZS9qcGVnXCIsXG4gICAgICBnaWY6IFwiaW1hZ2UvZ2lmXCIsXG4gICAgICB3ZWJwOiBcImltYWdlL3dlYnBcIixcbiAgICAgIHN2ZzogXCJpbWFnZS9zdmcreG1sXCIsXG4gICAgfTtcbiAgICByZXR1cm4gbWFwW2V4dC50b0xvd2VyQ2FzZSgpXSA/PyBcImFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbVwiO1xuICB9XG59XG4iXSwKICAibWFwcGluZ3MiOiAiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFpQkEsSUFBQUEsbUJBQXNDOzs7QUN1RC9CLElBQU0sbUJBQXFDO0FBQUEsRUFDaEQsa0JBQWtCO0FBQUEsRUFDbEIsZUFBZTtBQUFBLEVBQ2YsYUFBYTtBQUFBLEVBQ2Isb0JBQW9CLENBQUM7QUFBQSxFQUNyQixjQUFjO0FBQUEsRUFDZCxTQUFTO0FBQUEsRUFDVCx1QkFBdUI7QUFBQSxFQUN2QixlQUFlO0FBQUEsRUFDZixJQUFJO0FBQUEsRUFDSix1QkFBdUI7QUFBQSxFQUN2QixlQUFlO0FBQUEsRUFDZixtQkFBbUI7QUFDckI7OztBQ3hFQSxJQUFBQyxtQkFBdUQ7OztBQ0x2RCxzQkFBNEM7QUFHckMsSUFBTSxpQkFBTixNQUFxQjtBQUFBLEVBQzFCLFlBQ21CLGFBQ0Esa0JBQ0EsVUFDakI7QUFIaUI7QUFDQTtBQUNBO0FBQUEsRUFDaEI7QUFBQTtBQUFBO0FBQUEsRUFLSCxNQUFNLGNBQXVDO0FBQzNDLFVBQU0sTUFBTSxHQUFHLEtBQUssWUFBWSxDQUFDO0FBQ2pDLFVBQU0sT0FBTyxVQUFNLDRCQUFXO0FBQUEsTUFDNUI7QUFBQSxNQUNBLFFBQVE7QUFBQSxNQUNSLFNBQVMsS0FBSyxZQUFZO0FBQUEsSUFDNUIsQ0FBQztBQUNELFdBQU8sS0FBSztBQUFBLEVBQ2Q7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBTUEsTUFBTSxrQkFBa0IsU0FJckI7QUFDRCxVQUFNLE9BQU8sVUFBTSw0QkFBVyxFQUFFLEtBQUssU0FBUyxRQUFRLE1BQU0sQ0FBQztBQUM3RCxVQUFNLE9BQU8sS0FBSztBQUVsQixVQUFNLFdBQVcsS0FBSyxlQUFlLE1BQU0sVUFBVTtBQUNyRCxVQUFNLGdCQUFnQixLQUFLLGVBQWUsTUFBTSxnQkFBZ0I7QUFHaEUsUUFBSTtBQUNKLFFBQUksVUFBVTtBQUNaLFVBQUk7QUFDRixjQUFNLE1BQU0sTUFBTSxLQUFLLGdCQUFnQixRQUFRO0FBQy9DLHdCQUFnQixJQUFJLGdCQUFnQjtBQUFBLE1BQ3RDLFNBQVE7QUFBQSxNQUVSO0FBQUEsSUFDRjtBQUVBLFdBQU8sRUFBRSxrQkFBa0IsVUFBVSxlQUFlLGNBQWM7QUFBQSxFQUNwRTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBU0EsTUFBTSxXQUFXLFlBQTZEO0FBbkVoRjtBQW9FSSxVQUFNLE9BQU87QUFBQSxNQUNYLE1BQU0sQ0FBQyxTQUFTO0FBQUEsTUFDaEI7QUFBQSxJQUNGO0FBRUEsUUFBSTtBQUNGLFlBQU0sT0FBTyxVQUFNLDRCQUFXO0FBQUEsUUFDNUIsS0FBSyxLQUFLLFlBQVk7QUFBQSxRQUN0QixRQUFRO0FBQUEsUUFDUixTQUFTO0FBQUEsVUFDUCxHQUFHLEtBQUssWUFBWTtBQUFBLFVBQ3BCLGdCQUFnQjtBQUFBLFFBQ2xCO0FBQUEsUUFDQSxNQUFNLEtBQUssVUFBVSxJQUFJO0FBQUEsUUFDekIsT0FBTztBQUFBLE1BQ1QsQ0FBQztBQUVELFVBQUksS0FBSyxXQUFXLE9BQU8sS0FBSyxXQUFXLEtBQUs7QUFDOUMsY0FBTSxhQUNKLFVBQUssWUFBTCxtQkFBZSxrQkFDZixVQUFLLFlBQUwsbUJBQWUsa0JBQ2QsVUFBSyxTQUFMLG1CQUFnQztBQUNuQyxlQUFPLEVBQUUsU0FBUyxNQUFNLEtBQUssU0FBUztBQUFBLE1BQ3hDO0FBRUEsWUFBTSxTQUFTLEtBQUssYUFBYSxLQUFLLElBQUk7QUFDMUMsYUFBTyxFQUFFLFNBQVMsT0FBTyxPQUFPLFFBQVEsS0FBSyxNQUFNLEtBQUssTUFBTSxHQUFHO0FBQUEsSUFDbkUsU0FBUyxLQUFjO0FBQ3JCLGFBQU8sRUFBRSxTQUFTLE9BQU8sT0FBTyxPQUFPLEdBQUcsRUFBRTtBQUFBLElBQzlDO0FBQUEsRUFDRjtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQU9BLE1BQU0sV0FDSixTQUNBLFNBQ3dCO0FBQ3hCLFVBQU0sT0FBTyxFQUFFLFFBQVEsVUFBVSxLQUFLLFNBQVMsUUFBUTtBQUV2RCxRQUFJO0FBQ0YsWUFBTSxPQUFPLFVBQU0sNEJBQVc7QUFBQSxRQUM1QixLQUFLLEtBQUssWUFBWTtBQUFBLFFBQ3RCLFFBQVE7QUFBQSxRQUNSLFNBQVM7QUFBQSxVQUNQLEdBQUcsS0FBSyxZQUFZO0FBQUEsVUFDcEIsZ0JBQWdCO0FBQUEsUUFDbEI7QUFBQSxRQUNBLE1BQU0sS0FBSyxVQUFVLElBQUk7QUFBQSxRQUN6QixPQUFPO0FBQUEsTUFDVCxDQUFDO0FBRUQsVUFBSSxLQUFLLFVBQVUsT0FBTyxLQUFLLFNBQVMsS0FBSztBQUMzQyxlQUFPLEVBQUUsU0FBUyxNQUFNLEtBQUssUUFBUTtBQUFBLE1BQ3ZDO0FBRUEsYUFBTztBQUFBLFFBQ0wsU0FBUztBQUFBLFFBQ1QsT0FBTyxRQUFRLEtBQUssTUFBTSxLQUFLLEtBQUssYUFBYSxLQUFLLElBQUksQ0FBQztBQUFBLE1BQzdEO0FBQUEsSUFDRixTQUFTLEtBQWM7QUFDckIsYUFBTyxFQUFFLFNBQVMsT0FBTyxPQUFPLE9BQU8sR0FBRyxFQUFFO0FBQUEsSUFDOUM7QUFBQSxFQUNGO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBUUEsTUFBTSxZQUNKLFlBQ0EsVUFDQSxVQUNpQjtBQWxKckI7QUFtSkksVUFBTSxXQUFXLEtBQUssaUJBQWlCLEtBQUssR0FBRyxLQUFLLFlBQVksQ0FBQztBQUlqRSxVQUFNLFdBQVcsdUJBQXVCLEtBQUssSUFBSSxDQUFDO0FBQ2xELFVBQU0sU0FDSixLQUFLLFFBQVE7QUFBQSx5REFDNkMsUUFBUTtBQUFBLGdCQUNqRCxRQUFRO0FBQUE7QUFBQTtBQUMzQixVQUFNLFNBQVM7QUFBQSxJQUFTLFFBQVE7QUFBQTtBQUVoQyxVQUFNLFlBQVksSUFBSSxZQUFZLEVBQUUsT0FBTyxNQUFNO0FBQ2pELFVBQU0sWUFBWSxJQUFJLFlBQVksRUFBRSxPQUFPLE1BQU07QUFDakQsVUFBTSxVQUFVLElBQUksV0FBVyxVQUFVO0FBRXpDLFVBQU0sV0FBVyxJQUFJO0FBQUEsTUFDbkIsVUFBVSxTQUFTLFFBQVEsU0FBUyxVQUFVO0FBQUEsSUFDaEQ7QUFDQSxhQUFTLElBQUksV0FBVyxDQUFDO0FBQ3pCLGFBQVMsSUFBSSxTQUFTLFVBQVUsTUFBTTtBQUN0QyxhQUFTLElBQUksV0FBVyxVQUFVLFNBQVMsUUFBUSxNQUFNO0FBRXpELFVBQU0sT0FBTyxVQUFNLDRCQUFXO0FBQUEsTUFDNUIsS0FBSztBQUFBLE1BQ0wsUUFBUTtBQUFBLE1BQ1IsU0FBUztBQUFBLFFBQ1AsR0FBRyxLQUFLLFlBQVk7QUFBQSxRQUNwQixnQkFBZ0IsaUNBQWlDLFFBQVE7QUFBQSxNQUMzRDtBQUFBLE1BQ0EsTUFBTSxTQUFTO0FBQUEsTUFDZixPQUFPO0FBQUEsSUFDVCxDQUFDO0FBRUQsUUFBSSxLQUFLLFdBQVcsT0FBTyxLQUFLLFdBQVcsS0FBSztBQUM5QyxZQUFNLGFBQ0osVUFBSyxZQUFMLG1CQUFlLGtCQUNmLFVBQUssWUFBTCxtQkFBZSxrQkFDZCxVQUFLLFNBQUwsbUJBQWdDO0FBQ25DLFVBQUksU0FBVSxRQUFPO0FBQUEsSUFDdkI7QUFFQSxVQUFNLElBQUk7QUFBQSxNQUNSLDZCQUE2QixLQUFLLE1BQU0sTUFBTSxLQUFLLGFBQWEsS0FBSyxJQUFJLENBQUM7QUFBQSxJQUM1RTtBQUFBLEVBQ0Y7QUFBQTtBQUFBLEVBSVEsY0FBc0M7QUFDNUMsV0FBTyxFQUFFLGVBQWUsVUFBVSxLQUFLLFNBQVMsQ0FBQyxHQUFHO0FBQUEsRUFDdEQ7QUFBQSxFQUVRLGVBQWUsTUFBYyxLQUFpQztBQXZNeEU7QUF5TUksVUFBTSxLQUFLLElBQUk7QUFBQSxNQUNiLHFCQUFxQixHQUFHLDhFQUE4RSxHQUFHO0FBQUEsTUFDekc7QUFBQSxJQUNGO0FBQ0EsVUFBTSxJQUFJLEtBQUssTUFBTSxFQUFFO0FBQ3ZCLFlBQU8sNEJBQUksT0FBSixZQUFVLHVCQUFJO0FBQUEsRUFDdkI7QUFBQSxFQUVBLE1BQWMsZ0JBQWdCLFVBQTJDO0FBQ3ZFLFVBQU0sT0FBTyxVQUFNLDRCQUFXO0FBQUEsTUFDNUIsS0FBSyxHQUFHLFFBQVE7QUFBQSxNQUNoQixRQUFRO0FBQUEsTUFDUixTQUFTLEtBQUssWUFBWTtBQUFBLElBQzVCLENBQUM7QUFDRCxXQUFPLEtBQUs7QUFBQSxFQUNkO0FBQUEsRUFFUSxhQUFhLE1BQXNCO0FBMU43QztBQTJOSSxRQUFJO0FBQ0YsWUFBTSxNQUFNLEtBQUssTUFBTSxJQUFJO0FBQzNCLGNBQU8sZUFBSSxzQkFBSixZQUF5QixJQUFJLFVBQTdCLFlBQXNDLEtBQUssTUFBTSxHQUFHLEdBQUc7QUFBQSxJQUNoRSxTQUFRO0FBQ04sYUFBTyxLQUFLLE1BQU0sR0FBRyxHQUFHO0FBQUEsSUFDMUI7QUFBQSxFQUNGO0FBQ0Y7OztBQ3ZNQSxhQUF3QjtBQUN4QixJQUFBQyxtQkFBMkI7QUFFcEIsSUFBTSxZQUFjO0FBQ3BCLElBQU0sZUFBZTtBQUU1QixJQUFNLFFBQVE7QUFDZCxJQUFNLGtCQUFrQixJQUFJLEtBQUs7QUFvQmpDLElBQUksa0JBRU87QUFNSixTQUFTLHVCQUF1QixRQUFzQztBQUMzRSxNQUFJLENBQUMsZ0JBQWlCO0FBRXRCLFFBQU0sRUFBRSxTQUFTLE9BQU8sY0FBYyxJQUFJO0FBQzFDLG9CQUFrQjtBQUNsQixVQUFRLE1BQU07QUFDaEI7QUFFTyxJQUFNLFlBQU4sTUFBTSxXQUFVO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBT3JCLGFBQWEsa0JBQWtCLFNBQStDO0FBQzVFLFVBQU0sT0FBTyxVQUFNLDZCQUFXLEVBQUUsS0FBSyxTQUFTLFFBQVEsTUFBTSxDQUFDO0FBQzdELFVBQU0sT0FBTyxLQUFLO0FBRWxCLFVBQU0sd0JBQXdCLFdBQVUsZUFBZSxNQUFNLHdCQUF3QjtBQUNyRixVQUFNLGdCQUF3QixXQUFVLGVBQWUsTUFBTSxnQkFBZ0I7QUFDN0UsVUFBTSxtQkFBd0IsV0FBVSxlQUFlLE1BQU0sVUFBVTtBQUV2RSxRQUFJLENBQUMsdUJBQXVCO0FBQzFCLFlBQU0sSUFBSTtBQUFBLFFBQ1IsbURBQW1ELE9BQU87QUFBQSxNQUU1RDtBQUFBLElBQ0Y7QUFDQSxRQUFJLENBQUMsZUFBZTtBQUNsQixZQUFNLElBQUksTUFBTSwyQ0FBMkMsT0FBTyxHQUFHO0FBQUEsSUFDdkU7QUFFQSxXQUFPLEVBQUUsdUJBQXVCLGVBQWUsaUJBQWlCO0FBQUEsRUFDbEU7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBWUEsYUFBYSxPQUFPLFNBQTJDO0FBNUdqRTtBQThHSSxVQUFNLEVBQUUsdUJBQXVCLGVBQWUsaUJBQWlCLElBQzdELE1BQU0sV0FBVSxrQkFBa0IsT0FBTztBQUczQyxVQUFNLFFBQWUsV0FBVSxVQUFpQixtQkFBWSxFQUFFLENBQUM7QUFDL0QsVUFBTSxlQUFlLFdBQVUsVUFBaUIsbUJBQVksRUFBRSxDQUFDO0FBQy9ELFVBQU0sZ0JBQWdCLFdBQVU7QUFBQSxNQUN2QixrQkFBVyxRQUFRLEVBQUUsT0FBTyxZQUFZLEVBQUUsT0FBTztBQUFBLElBQzFEO0FBR0EsVUFBTSxrQkFBa0IsSUFBSTtBQUFBLE1BQzFCLENBQUMsU0FBUyxXQUFXO0FBQ25CLGNBQU0sVUFBVSxXQUFXLE1BQU07QUFDL0IsNEJBQWtCO0FBQ2xCLGlCQUFPLElBQUksTUFBTSw4Q0FBOEMsQ0FBQztBQUFBLFFBQ2xFLEdBQUcsZUFBZTtBQUVsQiwwQkFBa0I7QUFBQSxVQUNoQjtBQUFBLFVBQ0EsU0FBUyxDQUFDLFdBQVc7QUFDbkIseUJBQWEsT0FBTztBQUNwQixvQkFBUSxNQUFNO0FBQUEsVUFDaEI7QUFBQSxRQUNGO0FBQUEsTUFDRjtBQUFBLElBQ0Y7QUFHQSxVQUFNLFVBQVUsSUFBSSxJQUFJLHFCQUFxQjtBQUM3QyxZQUFRLGFBQWEsSUFBSSxpQkFBd0IsTUFBTTtBQUN2RCxZQUFRLGFBQWEsSUFBSSxhQUF3QixTQUFTO0FBQzFELFlBQVEsYUFBYSxJQUFJLGdCQUF3QixZQUFZO0FBQzdELFlBQVEsYUFBYSxJQUFJLFNBQXdCLEtBQUs7QUFDdEQsWUFBUSxhQUFhLElBQUksa0JBQXdCLGFBQWE7QUFDOUQsWUFBUSxhQUFhLElBQUkseUJBQXdCLE1BQU07QUFDdkQsWUFBUSxhQUFhLElBQUksU0FBd0IsS0FBSztBQUN0RCxZQUFRLGFBQWEsSUFBSSxNQUF3QixPQUFPO0FBRXhELFdBQU8sS0FBSyxRQUFRLFNBQVMsQ0FBQztBQUc5QixVQUFNLGlCQUFpQixNQUFNO0FBRzdCLFFBQUksZUFBZSxVQUFVLE9BQU87QUFDbEMsWUFBTSxJQUFJLE1BQU0sK0RBQTBEO0FBQUEsSUFDNUU7QUFFQSxVQUFNLE9BQU8sZUFBZTtBQUM1QixRQUFJLENBQUMsTUFBTTtBQUNULFlBQU0sSUFBSTtBQUFBLFNBQ1IsMEJBQWUsc0JBQWYsWUFDQSxlQUFlLFVBRGYsWUFFQTtBQUFBLE1BQ0Y7QUFBQSxJQUNGO0FBR0EsVUFBTSxZQUFZLFVBQU0sNkJBQVc7QUFBQSxNQUNqQyxLQUFLO0FBQUEsTUFDTCxRQUFRO0FBQUEsTUFDUixTQUFTO0FBQUEsUUFDUCxnQkFBZ0I7QUFBQSxRQUNoQixRQUFRO0FBQUEsTUFDVjtBQUFBLE1BQ0EsTUFBTSxJQUFJLGdCQUFnQjtBQUFBLFFBQ3hCLFlBQWU7QUFBQSxRQUNmO0FBQUEsUUFDQSxXQUFlO0FBQUEsUUFDZixjQUFlO0FBQUEsUUFDZixlQUFlO0FBQUEsTUFDakIsQ0FBQyxFQUFFLFNBQVM7QUFBQSxNQUNaLE9BQU87QUFBQSxJQUNULENBQUM7QUFFRCxVQUFNLE9BQU8sVUFBVTtBQVF2QixRQUFJLENBQUMsS0FBSyxjQUFjO0FBQ3RCLFlBQU0sSUFBSTtBQUFBLFNBQ1IsZ0JBQUssc0JBQUwsWUFDQSxLQUFLLFVBREwsWUFFQSwrQkFBK0IsVUFBVSxNQUFNO0FBQUEsTUFDakQ7QUFBQSxJQUNGO0FBRUEsV0FBTztBQUFBLE1BQ0wsYUFBdUIsS0FBSztBQUFBLE1BQzVCLFFBQXVCLFVBQUssVUFBTCxZQUFjO0FBQUEsTUFDckMsS0FBdUIsVUFBSyxPQUFMLFlBQVc7QUFBQSxNQUNsQztBQUFBLE1BQ0E7QUFBQSxNQUNBO0FBQUEsSUFDRjtBQUFBLEVBQ0Y7QUFBQTtBQUFBLEVBSUEsT0FBZSxVQUFVLEtBQXFCO0FBQzVDLFdBQU8sSUFBSSxTQUFTLFFBQVEsRUFDekIsUUFBUSxPQUFPLEdBQUcsRUFDbEIsUUFBUSxPQUFPLEdBQUcsRUFDbEIsUUFBUSxNQUFNLEVBQUU7QUFBQSxFQUNyQjtBQUFBLEVBRUEsT0FBTyxlQUFlLE1BQWMsS0FBaUM7QUE3TnZFO0FBOE5JLFVBQU0sS0FBSyxJQUFJO0FBQUEsTUFDYiw4QkFBOEIsR0FBRyxnR0FDd0IsR0FBRztBQUFBLE1BQzVEO0FBQUEsSUFDRjtBQUNBLFVBQU0sSUFBSSxLQUFLLE1BQU0sRUFBRTtBQUN2QixZQUFPLDRCQUFJLE9BQUosWUFBVSx1QkFBSTtBQUFBLEVBQ3ZCO0FBQ0Y7OztBRnBOTyxJQUFNLHNCQUFOLGNBQWtDLGtDQUFpQjtBQUFBLEVBQ3hELFlBQ0UsS0FDaUIsUUFDakI7QUFDQSxVQUFNLEtBQUssTUFBTTtBQUZBO0FBQUEsRUFHbkI7QUFBQSxFQUVBLFVBQWdCO0FBQ2QsVUFBTSxFQUFFLFlBQVksSUFBSTtBQUN4QixnQkFBWSxNQUFNO0FBRWxCLGdCQUFZLFNBQVMsTUFBTSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFHekQsZ0JBQVksU0FBUyxNQUFNLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFHOUMsUUFBSSxLQUFLLE9BQU8sU0FBUyxNQUFNLEtBQUssT0FBTyxTQUFTLGFBQWE7QUFDL0QsV0FBSyxlQUFlLFdBQVc7QUFBQSxJQUNqQyxPQUFPO0FBQ0wsV0FBSyxnQkFBZ0IsV0FBVztBQUFBLElBQ2xDO0FBR0EsZ0JBQVksU0FBUyxNQUFNLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFFaEQsZ0JBQVksU0FBUyxLQUFLO0FBQUEsTUFDeEIsTUFBTTtBQUFBLE1BQ04sS0FBSztBQUFBLElBQ1AsQ0FBQztBQUVELFFBQUkseUJBQVEsV0FBVyxFQUNwQixRQUFRLG1CQUFtQixFQUMzQixRQUFRLHVDQUF1QyxFQUMvQztBQUFBLE1BQVEsQ0FBQyxTQUNSLEtBQ0csZUFBZSw4QkFBOEIsRUFDN0MsU0FBUyxLQUFLLE9BQU8sU0FBUyxnQkFBZ0IsRUFDOUMsU0FBUyxPQUFPLFVBQVU7QUFDekIsYUFBSyxPQUFPLFNBQVMsbUJBQW1CLE1BQU0sS0FBSztBQUNuRCxjQUFNLEtBQUssT0FBTyxhQUFhO0FBQUEsTUFDakMsQ0FBQztBQUFBLElBQ0w7QUFFRixRQUFJLHlCQUFRLFdBQVcsRUFDcEIsUUFBUSxnQkFBZ0IsRUFDeEIsUUFBUSw4Q0FBOEMsRUFDdEQ7QUFBQSxNQUFRLENBQUMsU0FDUixLQUNHLGVBQWUsb0NBQW9DLEVBQ25ELFNBQVMsS0FBSyxPQUFPLFNBQVMsYUFBYSxFQUMzQyxTQUFTLE9BQU8sVUFBVTtBQUN6QixhQUFLLE9BQU8sU0FBUyxnQkFBZ0IsTUFBTSxLQUFLO0FBQ2hELGNBQU0sS0FBSyxPQUFPLGFBQWE7QUFBQSxNQUNqQyxDQUFDO0FBQUEsSUFDTDtBQUdGLGdCQUFZLFNBQVMsTUFBTSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFFeEQsUUFBSSx5QkFBUSxXQUFXLEVBQ3BCLFFBQVEsb0JBQW9CLEVBQzVCLFFBQVEsNERBQTRELEVBQ3BFO0FBQUEsTUFBWSxDQUFDLFNBQ1osS0FDRyxVQUFVLFVBQVUsUUFBUSxFQUM1QixVQUFVLFlBQVksVUFBVSxFQUNoQyxVQUFVLFdBQVcsU0FBUyxFQUM5QixTQUFTLEtBQUssT0FBTyxTQUFTLGlCQUFpQixFQUMvQyxTQUFTLE9BQU8sVUFBVTtBQUN6QixhQUFLLE9BQU8sU0FBUyxvQkFBb0I7QUFJekMsY0FBTSxLQUFLLE9BQU8sYUFBYTtBQUFBLE1BQ2pDLENBQUM7QUFBQSxJQUNMO0FBRUYsUUFBSSx5QkFBUSxXQUFXLEVBQ3BCLFFBQVEsd0JBQXdCLEVBQ2hDO0FBQUEsTUFDQztBQUFBLElBRUYsRUFDQztBQUFBLE1BQVUsQ0FBQyxXQUNWLE9BQ0csU0FBUyxLQUFLLE9BQU8sU0FBUyxxQkFBcUIsRUFDbkQsU0FBUyxPQUFPLFVBQVU7QUFDekIsYUFBSyxPQUFPLFNBQVMsd0JBQXdCO0FBQzdDLGNBQU0sS0FBSyxPQUFPLGFBQWE7QUFBQSxNQUNqQyxDQUFDO0FBQUEsSUFDTDtBQUdGLGdCQUFZLFNBQVMsTUFBTSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFckQsUUFBSSx5QkFBUSxXQUFXLEVBQ3BCLFFBQVEsbUNBQW1DLEVBQzNDO0FBQUEsTUFDQztBQUFBLElBRUYsRUFDQztBQUFBLE1BQVUsQ0FBQyxXQUNWLE9BQ0csU0FBUyxLQUFLLE9BQU8sU0FBUyxhQUFhLEVBQzNDLFNBQVMsT0FBTyxVQUFVO0FBQ3pCLGFBQUssT0FBTyxTQUFTLGdCQUFnQjtBQUNyQyxjQUFNLEtBQUssT0FBTyxhQUFhO0FBQUEsTUFDakMsQ0FBQztBQUFBLElBQ0w7QUFFRixnQkFBWSxTQUFTLEtBQUs7QUFBQSxNQUN4QixNQUFNO0FBQUEsTUFDTixLQUFLO0FBQUEsSUFDUCxDQUFDO0FBQUEsRUFDSDtBQUFBO0FBQUEsRUFJUSxnQkFBZ0IsYUFBZ0M7QUFFdEQsUUFBSSx5QkFBUSxXQUFXLEVBQ3BCLFFBQVEsVUFBVSxFQUNsQjtBQUFBLE1BQ0M7QUFBQSxJQUVGLEVBQ0M7QUFBQSxNQUFRLENBQUMsU0FDUixLQUNHLGVBQWUseUJBQXlCLEVBQ3hDLFNBQVMsS0FBSyxPQUFPLFNBQVMsT0FBTyxFQUNyQyxTQUFTLE9BQU8sVUFBVTtBQUN6QixhQUFLLE9BQU8sU0FBUyxVQUFVLE1BQU0sS0FBSztBQUMxQyxjQUFNLEtBQUssT0FBTyxhQUFhO0FBQUEsTUFDakMsQ0FBQztBQUFBLElBQ0wsRUFDQyxVQUFVLENBQUMsUUFBUTtBQUNsQixVQUNHLGNBQWMsU0FBUyxFQUN2QixPQUFPLEVBQ1AsUUFBUSxZQUFZO0FBQ25CLGNBQU0sVUFBVSxLQUFLLE9BQU8sU0FBUyxRQUFRLEtBQUs7QUFDbEQsWUFBSSxDQUFDLFNBQVM7QUFDWixjQUFJLHdCQUFPLDRCQUE0QjtBQUN2QztBQUFBLFFBQ0Y7QUFFQSxZQUFJLFlBQVksSUFBSTtBQUNwQixZQUFJLGNBQWMsdUJBQWtCO0FBRXBDLFlBQUk7QUFDRixnQkFBTSxTQUFTLE1BQU0sVUFBVSxPQUFPLE9BQU87QUFHN0MsZUFBSyxPQUFPLFNBQVMsY0FBbUIsT0FBTztBQUMvQyxlQUFLLE9BQU8sU0FBUyxLQUFtQixPQUFPO0FBQy9DLGVBQUssT0FBTyxTQUFTLHdCQUF3QixPQUFPO0FBQ3BELGVBQUssT0FBTyxTQUFTLGdCQUFtQixPQUFPO0FBQy9DLGNBQUksT0FBTyxrQkFBa0I7QUFDM0IsaUJBQUssT0FBTyxTQUFTLG1CQUFtQixPQUFPO0FBQUEsVUFDakQ7QUFDQSxjQUFJLE9BQU8sZUFBZTtBQUN4QixpQkFBSyxPQUFPLFNBQVMsZ0JBQWdCLE9BQU87QUFBQSxVQUM5QztBQUVBLGdCQUFNLEtBQUssT0FBTyxhQUFhO0FBRy9CLGNBQUksQ0FBQyxLQUFLLE9BQU8sU0FBUyxlQUFlO0FBQ3ZDLGdCQUFJO0FBQ0Ysb0JBQU0sU0FBUyxJQUFJO0FBQUEsZ0JBQ2pCLE1BQU0sS0FBSyxPQUFPLFNBQVM7QUFBQSxnQkFDM0IsTUFBTSxLQUFLLE9BQU8sU0FBUztBQUFBLGdCQUMzQixNQUFNLEtBQUssT0FBTyxTQUFTO0FBQUEsY0FDN0I7QUFDQSxvQkFBTSxNQUFNLE1BQU0sT0FBTyxZQUFZO0FBQ3JDLGtCQUFJLElBQUksZ0JBQWdCLEdBQUc7QUFDekIscUJBQUssT0FBTyxTQUFTLGdCQUFnQixJQUFJLGdCQUFnQjtBQUN6RCxzQkFBTSxLQUFLLE9BQU8sYUFBYTtBQUFBLGNBQ2pDO0FBQUEsWUFDRixTQUFRO0FBQUEsWUFFUjtBQUFBLFVBQ0Y7QUFFQSxjQUFJLHdCQUFPLHVCQUFrQixPQUFPLEVBQUUsRUFBRTtBQUN4QyxlQUFLLFFBQVE7QUFBQSxRQUNmLFNBQVMsS0FBYztBQUNyQixjQUFJLHdCQUFPLG1CQUFtQixPQUFPLEdBQUcsQ0FBQyxJQUFJLEdBQUk7QUFDakQsY0FBSSxZQUFZLEtBQUs7QUFDckIsY0FBSSxjQUFjLFNBQVM7QUFBQSxRQUM3QjtBQUFBLE1BQ0YsQ0FBQztBQUFBLElBQ0wsQ0FBQztBQUdILFVBQU0sVUFBVSxZQUFZLFNBQVMsU0FBUztBQUM5QyxZQUFRLFNBQVMsV0FBVztBQUFBLE1BQzFCLE1BQU07QUFBQSxNQUNOLEtBQUs7QUFBQSxJQUNQLENBQUM7QUFDRCxZQUFRLE1BQU0sWUFBWTtBQUMxQixZQUFRLE1BQU0sZUFBZTtBQUU3QixRQUFJLHlCQUFRLE9BQU8sRUFDaEIsUUFBUSxjQUFjLEVBQ3RCLFFBQVEsOENBQThDLEVBQ3RELFFBQVEsQ0FBQyxTQUFTO0FBQ2pCLFdBQ0csZUFBZSxtQkFBbUIsRUFDbEMsU0FBUyxLQUFLLE9BQU8sU0FBUyxXQUFXLEVBQ3pDLFNBQVMsT0FBTyxVQUFVO0FBQ3pCLGFBQUssT0FBTyxTQUFTLGNBQWMsTUFBTSxLQUFLO0FBQzlDLGNBQU0sS0FBSyxPQUFPLGFBQWE7QUFBQSxNQUNqQyxDQUFDO0FBQ0gsV0FBSyxRQUFRLE9BQU87QUFBQSxJQUN0QixDQUFDLEVBQ0E7QUFBQSxNQUFVLENBQUMsUUFDVixJQUFJLGNBQWMsUUFBUSxFQUFFLFFBQVEsWUFBWTtBQUM5QyxZQUNFLENBQUMsS0FBSyxPQUFPLFNBQVMsb0JBQ3RCLENBQUMsS0FBSyxPQUFPLFNBQVMsYUFDdEI7QUFDQSxjQUFJLHdCQUFPLDRDQUE0QztBQUN2RDtBQUFBLFFBQ0Y7QUFDQSxZQUFJLFlBQVksSUFBSTtBQUNwQixZQUFJO0FBQ0YsZ0JBQU0sU0FBUyxJQUFJO0FBQUEsWUFDakIsTUFBTSxLQUFLLE9BQU8sU0FBUztBQUFBLFlBQzNCLE1BQU0sS0FBSyxPQUFPLFNBQVM7QUFBQSxZQUMzQixNQUFNLEtBQUssT0FBTyxTQUFTO0FBQUEsVUFDN0I7QUFDQSxnQkFBTSxPQUFPLFlBQVk7QUFDekIsY0FBSSx3QkFBTyx3QkFBbUI7QUFBQSxRQUNoQyxTQUFTLEtBQWM7QUFDckIsY0FBSSx3QkFBTyx1QkFBdUIsT0FBTyxHQUFHLENBQUMsRUFBRTtBQUFBLFFBQ2pELFVBQUU7QUFDQSxjQUFJLFlBQVksS0FBSztBQUFBLFFBQ3ZCO0FBQUEsTUFDRixDQUFDO0FBQUEsSUFDSDtBQUFBLEVBQ0o7QUFBQTtBQUFBLEVBSVEsZUFBZSxhQUFnQztBQUNyRCxVQUFNLEtBQUssS0FBSyxPQUFPLFNBQVM7QUFHaEMsVUFBTSxTQUFTLFlBQVksVUFBVTtBQUFBLE1BQ25DLEtBQUs7QUFBQSxJQUNQLENBQUM7QUFDRCxXQUFPLE1BQU0sVUFDWDtBQUlGLFVBQU0sT0FBTyxPQUFPLFVBQVU7QUFDOUIsU0FBSyxNQUFNLFVBQ1Q7QUFHRixTQUFLLGNBQWM7QUFFbkIsVUFBTSxPQUFPLE9BQU8sVUFBVTtBQUM5QixTQUFLLFNBQVMsT0FBTztBQUFBLE1BQ25CLE1BQU07QUFBQSxNQUNOLE1BQU0sRUFBRSxPQUFPLDZEQUE2RDtBQUFBLElBQzlFLENBQUM7QUFDRCxTQUFLLFNBQVMsT0FBTztBQUFBLE1BQ25CLE1BQU07QUFBQSxNQUNOLE1BQU0sRUFBRSxPQUFPLHVDQUF1QztBQUFBLElBQ3hELENBQUM7QUFFRCxRQUFJLHlCQUFRLFdBQVcsRUFDcEIsUUFBUSxVQUFVLEVBQ2xCO0FBQUEsTUFBUSxDQUFDLFNBQ1IsS0FDRyxTQUFTLEtBQUssT0FBTyxTQUFTLE9BQU8sRUFDckMsWUFBWSxJQUFJO0FBQUEsSUFDckIsRUFDQztBQUFBLE1BQVUsQ0FBQyxRQUNWLElBQ0csY0FBYyxVQUFVLEVBQ3hCLFdBQVcsRUFDWCxRQUFRLFlBQVk7QUFDbkIsYUFBSyxPQUFPLFNBQVMsY0FBYztBQUNuQyxhQUFLLE9BQU8sU0FBUyxLQUFLO0FBQzFCLGFBQUssT0FBTyxTQUFTLHdCQUF3QjtBQUM3QyxhQUFLLE9BQU8sU0FBUyxnQkFBZ0I7QUFDckMsY0FBTSxLQUFLLE9BQU8sYUFBYTtBQUMvQixhQUFLLFFBQVE7QUFBQSxNQUNmLENBQUM7QUFBQSxJQUNMO0FBQUEsRUFDSjtBQUNGOzs7QUczU0EsSUFBQUMsbUJBQThFO0FBSTlFLElBQU0sb0JBQW9CO0FBRW5CLElBQU0sWUFBTixNQUFnQjtBQUFBLEVBR3JCLFlBQ21CLEtBQ0EsVUFDakI7QUFGaUI7QUFDQTtBQUVqQixTQUFLLFNBQVMsSUFBSTtBQUFBLE1BQ2hCLE1BQU0sU0FBUztBQUFBLE1BQ2YsTUFBTSxTQUFTO0FBQUEsTUFDZixNQUFNLFNBQVM7QUFBQSxJQUNqQjtBQUFBLEVBQ0Y7QUFBQTtBQUFBLEVBR0EsTUFBTSxRQUFRLE1BQXFDO0FBckNyRDtBQXNDSSxVQUFNLE1BQU0sTUFBTSxLQUFLLElBQUksTUFBTSxLQUFLLElBQUk7QUFDMUMsVUFBTSxFQUFFLGFBQWEsS0FBSyxJQUFJLEtBQUssaUJBQWlCLEdBQUc7QUFHdkQsVUFBTSxlQUNKLHVCQUFZLFFBQVEsTUFBcEIsWUFBeUIsWUFBWSxLQUFLLE1BQTFDLFlBQStDO0FBR2pELFVBQU0sRUFBRSxTQUFTLGVBQWUsYUFBYSxJQUMzQyxNQUFNLEtBQUssY0FBYyxJQUFJO0FBRy9CLFVBQU0sYUFBYSxLQUFLLGdCQUFnQixhQUFhLGVBQWUsWUFBWTtBQUVoRixRQUFJO0FBRUosUUFBSSxhQUFhO0FBRWYsWUFBTSxVQUFxQyxDQUFDO0FBQzVDLGlCQUFXLENBQUMsR0FBRyxDQUFDLEtBQUssT0FBTyxRQUFRLFVBQVUsR0FBRztBQUMvQyxnQkFBUSxDQUFDLElBQUksTUFBTSxRQUFRLENBQUMsSUFBSSxJQUFJLENBQUMsQ0FBQztBQUFBLE1BQ3hDO0FBQ0EsZUFBUyxNQUFNLEtBQUssT0FBTyxXQUFXLGFBQWEsT0FBTztBQUFBLElBQzVELE9BQU87QUFFTCxlQUFTLE1BQU0sS0FBSyxPQUFPLFdBQVcsVUFBVTtBQUFBLElBQ2xEO0FBR0EsUUFBSSxPQUFPLFdBQVcsT0FBTyxPQUFPLEtBQUssU0FBUyx1QkFBdUI7QUFDdkUsWUFBTSxLQUFLLGVBQWUsTUFBTSxLQUFLLE9BQU8sR0FBRztBQUFBLElBQ2pEO0FBRUEsV0FBTztBQUFBLEVBQ1Q7QUFBQTtBQUFBLEVBSVEsZ0JBQ04sSUFDQSxNQUNBLGNBQ3lCO0FBaEY3QjtBQWlGSSxVQUFNLFFBQWlDLENBQUM7QUFLeEMsVUFBTSxjQUFjLEtBQUssS0FBSztBQUk5QixVQUFNLGNBQWEsUUFBRyxZQUFZLE1BQWYsWUFBb0IsR0FBRyxhQUFhO0FBQ3ZELFVBQU0sVUFBYSxRQUFHLFFBQVEsTUFBWCxZQUFvQixHQUFHLFNBQVM7QUFDbkQsVUFBTSxhQUFhLFFBQUcsV0FBVyxNQUFkLFlBQW9CLEdBQUcsYUFBYTtBQUN2RCxVQUFNLFlBQWEsUUFBRyxVQUFVLE1BQWIsWUFBb0IsR0FBRyxXQUFXO0FBRXJELFFBQUksV0FBWSxPQUFNLGFBQWEsSUFBSSxDQUFDLE9BQU8sVUFBVSxDQUFDO0FBQzFELFFBQUksT0FBWSxPQUFNLFNBQVMsSUFBUSxDQUFDLE9BQU8sTUFBTSxDQUFDO0FBQ3RELFFBQUksVUFBWSxPQUFNLGFBQWEsSUFBSSxDQUFDLE9BQU8sU0FBUyxDQUFDO0FBQ3pELFFBQUksU0FBWSxPQUFNLFdBQVcsSUFBTSxDQUFDLE9BQU8sUUFBUSxDQUFDO0FBR3hELFVBQU0sNEJBQ0gsVUFBVSxhQUFhLENBQUM7QUFDM0IsUUFBSSxDQUFDLDBCQUEwQjtBQUM3QixZQUFNLFNBQVMsSUFBSSxjQUFjLENBQUMsRUFBRSxNQUFNLFlBQVksQ0FBQyxJQUFJLENBQUMsRUFBRSxNQUFNLEdBQUcsQ0FBQztBQUFBLElBQzFFO0FBS0EsUUFBSSxHQUFHLE9BQU8sR0FBRztBQUNmLFlBQU0sTUFBTSxJQUFJLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQyxDQUFDO0FBQUEsSUFDdEM7QUFHQSxTQUFJLFFBQUcsU0FBUyxNQUFaLFlBQWlCLEdBQUcsU0FBUyxHQUFHO0FBQ2xDLFlBQU0sU0FBUyxJQUFJLENBQUMsUUFBTyxRQUFHLFNBQVMsTUFBWixZQUFpQixHQUFHLFNBQVMsQ0FBQyxDQUFDO0FBQUEsSUFDNUQ7QUFHQSxRQUFJLEdBQUcsTUFBTSxHQUFHO0FBQ2QsWUFBTSxXQUFXLElBQUksQ0FBQyxJQUFJLEtBQUssT0FBTyxHQUFHLE1BQU0sQ0FBQyxDQUFDLEVBQUUsWUFBWSxDQUFDO0FBQUEsSUFDbEU7QUFHQSxVQUFNLFVBQVUsS0FBSyxjQUFhLFFBQUcsTUFBTSxNQUFULFlBQWMsR0FBRyxVQUFVLENBQUM7QUFDOUQsVUFBTSxjQUFjLEtBQUssbUJBQW1CLE9BQU87QUFDbkQsVUFBTSxhQUFhLFFBQVE7QUFBQSxNQUN6QixDQUFDLE1BQU0sQ0FBQyxFQUFFLFdBQVcsaUJBQWlCLEtBQUssTUFBTTtBQUFBLElBQ25EO0FBQ0EsUUFBSSxXQUFXLFNBQVMsR0FBRztBQUN6QixZQUFNLFVBQVUsSUFBSTtBQUFBLElBQ3RCO0FBR0EsUUFBSSxLQUFLLFNBQVMsaUJBQWlCLGFBQWE7QUFDOUMsWUFBTSxjQUFjLElBQUksQ0FBQyxXQUFXO0FBQUEsSUFDdEM7QUFJQSxVQUFNLGNBQWMsS0FBSztBQUFBLE9BQ3ZCLFFBQUcsaUJBQWlCLE1BQXBCLFlBQXlCLEdBQUcsZUFBZTtBQUFBLElBQzdDO0FBQ0EsVUFBTSxpQkFBaUI7QUFBQSxNQUNyQixHQUFHLG9CQUFJLElBQUksQ0FBQyxHQUFHLEtBQUssU0FBUyxvQkFBb0IsR0FBRyxXQUFXLENBQUM7QUFBQSxJQUNsRTtBQUNBLFFBQUksZUFBZSxTQUFTLEdBQUc7QUFDN0IsWUFBTSxpQkFBaUIsSUFBSTtBQUFBLElBQzdCO0FBR0EsVUFBTSxjQUNILFFBQUcsWUFBWSxNQUFmLFlBQStCLEtBQUssU0FBUztBQUNoRCxRQUFJLGNBQWMsZUFBZSxVQUFVO0FBQ3pDLFlBQU0sWUFBWSxJQUFJLENBQUMsVUFBVTtBQUFBLElBQ25DO0FBR0EsUUFBSSxHQUFHLElBQUksS0FBSyxPQUFPLEdBQUcsSUFBSSxNQUFNLFVBQVU7QUFDNUMsWUFBTSxJQUFJLElBQUksQ0FBQyxHQUFHLElBQUksQ0FBQztBQUFBLElBQ3pCO0FBSUEsVUFBTSxXQUFXLEtBQUssa0JBQWtCLEdBQUcsT0FBTyxDQUFDO0FBQ25ELFFBQUksU0FBUyxTQUFTLEdBQUc7QUFDdkIsWUFBTSxPQUFPLElBQUk7QUFBQSxJQUNuQixXQUFXLGFBQWEsU0FBUyxHQUFHO0FBQ2xDLFlBQU0sT0FBTyxJQUFJLGFBQWEsSUFBSSxDQUFDLFNBQVMsRUFBRSxPQUFPLElBQUksRUFBRTtBQUFBLElBQzdEO0FBR0EsZUFBVyxDQUFDLEdBQUcsQ0FBQyxLQUFLLE9BQU8sUUFBUSxFQUFFLEdBQUc7QUFDdkMsVUFBSSxFQUFFLFdBQVcsS0FBSyxLQUFLLE1BQU0sWUFBWSxNQUFNLG1CQUFtQjtBQUNwRSxjQUFNLENBQUMsSUFBSSxLQUFLLGFBQWEsQ0FBQztBQUFBLE1BQ2hDO0FBQUEsSUFDRjtBQUVBLFdBQU87QUFBQSxFQUNUO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQVNRLGtCQUNOLE9BQ3dDO0FBQ3hDLFFBQUksQ0FBQyxNQUFPLFFBQU8sQ0FBQztBQUNwQixVQUFNLFFBQVEsTUFBTSxRQUFRLEtBQUssSUFBSSxRQUFRLENBQUMsS0FBSztBQUNuRCxXQUFPLE1BQ0osSUFBSSxDQUFDLFNBQVM7QUFuTXJCO0FBb01RLFVBQUksT0FBTyxTQUFTLFNBQVUsUUFBTyxFQUFFLE9BQU8sS0FBSztBQUNuRCxVQUFJLE9BQU8sU0FBUyxZQUFZLFNBQVMsTUFBTTtBQUM3QyxjQUFNLE1BQU07QUFDWixjQUFNLE1BQU0sUUFBTyxlQUFJLEtBQUssTUFBVCxZQUFjLElBQUksT0FBTyxNQUF6QixZQUE4QixFQUFFO0FBQ25ELFlBQUksQ0FBQyxJQUFLLFFBQU87QUFDakIsZUFBTyxJQUFJLEtBQUssSUFDWixFQUFFLE9BQU8sS0FBSyxLQUFLLE9BQU8sSUFBSSxLQUFLLENBQUMsRUFBRSxJQUN0QyxFQUFFLE9BQU8sSUFBSTtBQUFBLE1BQ25CO0FBQ0EsYUFBTztBQUFBLElBQ1QsQ0FBQyxFQUNBLE9BQU8sQ0FBQyxNQUE0QyxNQUFNLElBQUk7QUFBQSxFQUNuRTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQVFRLG1CQUFtQixNQUF5QztBQUNsRSxlQUFXLE9BQU8sTUFBTTtBQUN0QixZQUFNLFFBQVEsSUFBSSxRQUFRLE1BQU0sRUFBRTtBQUNsQyxVQUFJLE1BQU0sV0FBVyxpQkFBaUIsR0FBRztBQUN2QyxjQUFNLFFBQVEsTUFBTSxNQUFNLGtCQUFrQixNQUFNO0FBQ2xELGNBQU0sUUFBdUI7QUFBQSxVQUMzQjtBQUFBLFVBQVM7QUFBQSxVQUFhO0FBQUEsVUFBWTtBQUFBLFVBQVM7QUFBQSxVQUFjO0FBQUEsUUFDM0Q7QUFDQSxZQUFJLE1BQU0sU0FBUyxLQUFLLEVBQUcsUUFBTztBQUFBLE1BQ3BDO0FBQUEsSUFDRjtBQUNBLFdBQU87QUFBQSxFQUNUO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBUUEsTUFBYyxjQUNaLE1BQ3NEO0FBQ3RELFVBQU0sZUFBeUIsQ0FBQztBQUdoQyxVQUFNLGNBQWM7QUFFcEIsVUFBTSxZQUFZO0FBRWxCLFFBQUksVUFBVTtBQUdkLFVBQU0sY0FBYyxDQUFDLEdBQUcsS0FBSyxTQUFTLFdBQVcsQ0FBQztBQUNsRCxlQUFXLFNBQVMsYUFBYTtBQUMvQixZQUFNLFdBQVcsTUFBTSxDQUFDO0FBQ3hCLFVBQUk7QUFDRixjQUFNLFlBQVksTUFBTSxLQUFLLGdCQUFnQixRQUFRO0FBQ3JELFlBQUksV0FBVztBQUNiLHVCQUFhLEtBQUssU0FBUztBQUMzQixvQkFBVSxRQUFRLFFBQVEsTUFBTSxDQUFDLEdBQUcsS0FBSyxRQUFRLEtBQUssU0FBUyxHQUFHO0FBQUEsUUFDcEU7QUFBQSxNQUNGLFNBQVMsS0FBSztBQUNaLGdCQUFRLEtBQUssK0JBQStCLFFBQVEsS0FBSyxHQUFHO0FBQUEsTUFDOUQ7QUFBQSxJQUNGO0FBR0EsVUFBTSxZQUFZLENBQUMsR0FBRyxRQUFRLFNBQVMsU0FBUyxDQUFDO0FBQ2pELGVBQVcsU0FBUyxXQUFXO0FBQzdCLFlBQU0sTUFBTSxNQUFNLENBQUM7QUFDbkIsWUFBTSxPQUFPLE1BQU0sQ0FBQztBQUNwQixVQUFJLEtBQUssV0FBVyxNQUFNLEVBQUc7QUFDN0IsVUFBSTtBQUNGLGNBQU0sWUFBWSxNQUFNLEtBQUssZ0JBQWdCLElBQUk7QUFDakQsWUFBSSxXQUFXO0FBQ2IsdUJBQWEsS0FBSyxTQUFTO0FBQzNCLG9CQUFVLFFBQVEsUUFBUSxNQUFNLENBQUMsR0FBRyxLQUFLLEdBQUcsS0FBSyxTQUFTLEdBQUc7QUFBQSxRQUMvRDtBQUFBLE1BQ0YsU0FBUyxLQUFLO0FBQ1osZ0JBQVEsS0FBSywrQkFBK0IsSUFBSSxLQUFLLEdBQUc7QUFBQSxNQUMxRDtBQUFBLElBQ0Y7QUFFQSxXQUFPLEVBQUUsU0FBUyxhQUFhO0FBQUEsRUFDakM7QUFBQSxFQUVBLE1BQWMsZ0JBQWdCLE1BQTJDO0FBQ3ZFLFVBQU0sT0FBTyxLQUFLLElBQUksTUFBTSxTQUFTLEVBQUU7QUFBQSxNQUNyQyxDQUFDLE1BQU0sRUFBRSxTQUFTLFFBQVEsRUFBRSxTQUFTO0FBQUEsSUFDdkM7QUFDQSxRQUFJLENBQUMsS0FBTSxRQUFPO0FBRWxCLFVBQU0sU0FBUyxNQUFNLEtBQUssSUFBSSxNQUFNLFdBQVcsSUFBSTtBQUNuRCxVQUFNLFdBQVcsS0FBSyxjQUFjLEtBQUssU0FBUztBQUVsRCxXQUFPLEtBQUssT0FBTyxZQUFZLFFBQVEsS0FBSyxNQUFNLFFBQVE7QUFBQSxFQUM1RDtBQUFBO0FBQUEsRUFJUSxpQkFBaUIsS0FHdkI7QUE1U0o7QUE2U0ksVUFBTSxVQUFVLElBQUksTUFBTSw0Q0FBNEM7QUFDdEUsUUFBSSxDQUFDLFFBQVMsUUFBTyxFQUFFLGFBQWEsQ0FBQyxHQUFHLE1BQU0sSUFBSTtBQUVsRCxRQUFJLGNBQXVDLENBQUM7QUFDNUMsUUFBSTtBQUNGLHFCQUFlLHFDQUFVLFFBQVEsQ0FBQyxDQUFDLE1BQXBCLFlBQXlCLENBQUM7QUFBQSxJQUMzQyxTQUFRO0FBQUEsSUFFUjtBQUVBLFdBQU8sRUFBRSxhQUFhLE1BQU0sUUFBUSxDQUFDLEVBQUU7QUFBQSxFQUN6QztBQUFBLEVBRUEsTUFBYyxlQUNaLE1BQ0EsaUJBQ0EsS0FDZTtBQUNmLFVBQU0sVUFBVSxnQkFBZ0I7QUFBQSxNQUM5QjtBQUFBLElBQ0Y7QUFFQSxRQUFJLENBQUMsU0FBUztBQUVaLFlBQU0sUUFBUTtBQUFBLFdBQWlCLEdBQUc7QUFBQTtBQUFBO0FBQ2xDLFlBQU0sS0FBSyxJQUFJLE1BQU0sT0FBTyxNQUFNLFFBQVEsZUFBZTtBQUN6RDtBQUFBLElBQ0Y7QUFHQSxVQUFNLFVBQVUsUUFBUSxDQUFDO0FBQ3pCLFVBQU0sT0FBTyxRQUFRLENBQUM7QUFFdEIsUUFBSSxRQUFRLFNBQVMsU0FBUyxHQUFHO0FBRS9CLFlBQU0sVUFBVSxRQUFRO0FBQUEsUUFDdEI7QUFBQSxRQUNBLFlBQVksR0FBRztBQUFBLE1BQ2pCO0FBQ0EsWUFBTSxLQUFLLElBQUksTUFBTSxPQUFPLE1BQU0sVUFBVSxJQUFJO0FBQUEsSUFDbEQsT0FBTztBQUVMLFlBQU0sVUFBVSxRQUFRO0FBQUEsUUFDdEI7QUFBQSxRQUNBO0FBQUEsV0FBYyxHQUFHO0FBQUEsTUFDbkI7QUFDQSxZQUFNLEtBQUssSUFBSSxNQUFNLE9BQU8sTUFBTSxVQUFVLElBQUk7QUFBQSxJQUNsRDtBQUFBLEVBQ0Y7QUFBQSxFQUVRLGFBQWEsT0FBMEI7QUFDN0MsUUFBSSxDQUFDLE1BQU8sUUFBTyxDQUFDO0FBQ3BCLFFBQUksTUFBTSxRQUFRLEtBQUssRUFBRyxRQUFPLE1BQU0sSUFBSSxNQUFNO0FBQ2pELFdBQU8sQ0FBQyxPQUFPLEtBQUssQ0FBQztBQUFBLEVBQ3ZCO0FBQUEsRUFFUSxjQUFjLEtBQXFCO0FBclc3QztBQXNXSSxVQUFNLE1BQThCO0FBQUEsTUFDbEMsS0FBSztBQUFBLE1BQ0wsS0FBSztBQUFBLE1BQ0wsTUFBTTtBQUFBLE1BQ04sS0FBSztBQUFBLE1BQ0wsTUFBTTtBQUFBLE1BQ04sS0FBSztBQUFBLElBQ1A7QUFDQSxZQUFPLFNBQUksSUFBSSxZQUFZLENBQUMsTUFBckIsWUFBMEI7QUFBQSxFQUNuQztBQUNGOzs7QUx6VkEsSUFBcUIsaUJBQXJCLGNBQTRDLHdCQUFPO0FBQUEsRUFHakQsTUFBTSxTQUF3QjtBQUM1QixVQUFNLEtBQUssYUFBYTtBQUl4QixTQUFLLFdBQVc7QUFBQSxNQUNkLElBQUk7QUFBQSxNQUNKLE1BQU07QUFBQSxNQUNOLGVBQWUsQ0FBQyxhQUFzQjtBQUNwQyxjQUFNLE9BQU8sS0FBSyxJQUFJLFVBQVUsY0FBYztBQUM5QyxZQUFJLENBQUMsUUFBUSxLQUFLLGNBQWMsS0FBTSxRQUFPO0FBQzdDLFlBQUksU0FBVSxRQUFPO0FBRXJCLGFBQUssa0JBQWtCLElBQUk7QUFDM0IsZUFBTztBQUFBLE1BQ1Q7QUFBQSxJQUNGLENBQUM7QUFFRCxTQUFLLFdBQVc7QUFBQSxNQUNkLElBQUk7QUFBQSxNQUNKLE1BQU07QUFBQSxNQUNOLGVBQWUsQ0FBQyxhQUFzQjtBQUNwQyxjQUFNLE9BQU8sS0FBSyxJQUFJLFVBQVUsY0FBYztBQUM5QyxZQUFJLENBQUMsUUFBUSxLQUFLLGNBQWMsS0FBTSxRQUFPO0FBQzdDLFlBQUksU0FBVSxRQUFPO0FBR3JCLGFBQUssa0JBQWtCLElBQUk7QUFDM0IsZUFBTztBQUFBLE1BQ1Q7QUFBQSxJQUNGLENBQUM7QUFNRCxTQUFLLGdDQUFnQyxpQkFBaUIsQ0FBQyxXQUFXO0FBQ2hFLDZCQUF1QixNQUFnQztBQUFBLElBQ3pELENBQUM7QUFJRCxTQUFLLGNBQWMsSUFBSSxvQkFBb0IsS0FBSyxLQUFLLElBQUksQ0FBQztBQUkxRCxTQUFLLGNBQWMsUUFBUSx1QkFBdUIsTUFBTTtBQUN0RCxZQUFNLE9BQU8sS0FBSyxJQUFJLFVBQVUsY0FBYztBQUM5QyxVQUFJLENBQUMsUUFBUSxLQUFLLGNBQWMsTUFBTTtBQUNwQyxZQUFJLHdCQUFPLGtDQUFrQztBQUM3QztBQUFBLE1BQ0Y7QUFDQSxXQUFLLGtCQUFrQixJQUFJO0FBQUEsSUFDN0IsQ0FBQztBQUFBLEVBQ0g7QUFBQSxFQUVBLFdBQWlCO0FBQUEsRUFFakI7QUFBQTtBQUFBLEVBSUEsTUFBYyxrQkFBa0IsTUFBNEI7QUFDMUQsUUFBSSxDQUFDLEtBQUssU0FBUyxrQkFBa0I7QUFDbkMsVUFBSTtBQUFBLFFBQ0Y7QUFBQSxNQUNGO0FBQ0E7QUFBQSxJQUNGO0FBRUEsUUFBSSxDQUFDLEtBQUssU0FBUyxhQUFhO0FBQzlCLFVBQUk7QUFBQSxRQUNGO0FBQUEsTUFDRjtBQUNBO0FBQUEsSUFDRjtBQUVBLFVBQU0sU0FBUyxJQUFJO0FBQUEsTUFBTztBQUFBLE1BQWU7QUFBQTtBQUFBLElBQStCO0FBRXhFLFFBQUk7QUFDRixZQUFNLFlBQVksSUFBSSxVQUFVLEtBQUssS0FBSyxLQUFLLFFBQVE7QUFDdkQsWUFBTSxTQUFTLE1BQU0sVUFBVSxRQUFRLElBQUk7QUFFM0MsYUFBTyxLQUFLO0FBRVosVUFBSSxPQUFPLFNBQVM7QUFDbEIsY0FBTSxhQUFhLE9BQU8sTUFDdEI7QUFBQSxFQUFLLE9BQU8sR0FBRyxLQUNmO0FBQ0osWUFBSSx3QkFBTyxvQkFBZSxVQUFVLElBQUksR0FBSTtBQUFBLE1BQzlDLE9BQU87QUFDTCxZQUFJLHdCQUFPLDBCQUFxQixPQUFPLEtBQUssSUFBSSxHQUFLO0FBQ3JELGdCQUFRLE1BQU0sOEJBQThCLE9BQU8sS0FBSztBQUFBLE1BQzFEO0FBQUEsSUFDRixTQUFTLEtBQWM7QUFDckIsYUFBTyxLQUFLO0FBQ1osWUFBTSxNQUFNLGVBQWUsUUFBUSxJQUFJLFVBQVUsT0FBTyxHQUFHO0FBQzNELFVBQUksd0JBQU8saUJBQVksR0FBRyxJQUFJLEdBQUs7QUFDbkMsY0FBUSxNQUFNLGdDQUFnQyxHQUFHO0FBQUEsSUFDbkQ7QUFBQSxFQUNGO0FBQUE7QUFBQSxFQUlBLE1BQU0sZUFBOEI7QUFDbEMsU0FBSyxXQUFXLE9BQU87QUFBQSxNQUNyQixDQUFDO0FBQUEsTUFDRDtBQUFBLE1BQ0EsTUFBTSxLQUFLLFNBQVM7QUFBQSxJQUN0QjtBQUFBLEVBQ0Y7QUFBQSxFQUVBLE1BQU0sZUFBOEI7QUFDbEMsVUFBTSxLQUFLLFNBQVMsS0FBSyxRQUFRO0FBQUEsRUFDbkM7QUFDRjsiLAogICJuYW1lcyI6IFsiaW1wb3J0X29ic2lkaWFuIiwgImltcG9ydF9vYnNpZGlhbiIsICJpbXBvcnRfb2JzaWRpYW4iLCAiaW1wb3J0X29ic2lkaWFuIl0KfQo= diff --git a/node_modules/.bin/esbuild b/node_modules/.bin/esbuild new file mode 120000 index 0000000..c83ac07 --- /dev/null +++ b/node_modules/.bin/esbuild @@ -0,0 +1 @@ +../esbuild/bin/esbuild \ No newline at end of file diff --git a/node_modules/.bin/tsc b/node_modules/.bin/tsc new file mode 120000 index 0000000..0863208 --- /dev/null +++ b/node_modules/.bin/tsc @@ -0,0 +1 @@ +../typescript/bin/tsc \ No newline at end of file diff --git a/node_modules/.bin/tsserver b/node_modules/.bin/tsserver new file mode 120000 index 0000000..f8f8f1a --- /dev/null +++ b/node_modules/.bin/tsserver @@ -0,0 +1 @@ +../typescript/bin/tsserver \ No newline at end of file diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json new file mode 100644 index 0000000..b85d85c --- /dev/null +++ b/node_modules/.package-lock.json @@ -0,0 +1,237 @@ +{ + "name": "obsidian-micropub", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "node_modules/@codemirror/state": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.0.tgz", + "integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/view": { + "version": "6.38.6", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.6.tgz", + "integrity": "sha512-qiS0z1bKs5WOvHIAC0Cybmv4AJSkAXgX5aD6Mqd2epSLlVJsQl8NG23jCVouIgkh4All/mrbdsf2UOLFnJw0tw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@codemirror/state": "^6.5.0", + "crelt": "^1.0.6", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/codemirror": { + "version": "5.60.8", + "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.8.tgz", + "integrity": "sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/tern": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.37", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.37.tgz", + "integrity": "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/qrcode": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.6.tgz", + "integrity": "sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/tern": { + "version": "0.23.9", + "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz", + "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/builtin-modules": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-4.0.0.tgz", + "integrity": "sha512-p1n8zyCkt1BVrKNFymOHjcDSAl7oq/gUvfgULv2EblgpPVQlQr9yHnWjg9IJ2MhfwPqiYqMMrr01OY7yQoK2yA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/obsidian": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.12.3.tgz", + "integrity": "sha512-HxWqe763dOqzXjnNiHmAJTRERN8KILBSqxDSEqbeSr7W8R8Jxezzbca+nz1LiiqXnMpM8lV2jzAezw3CZ4xNUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/codemirror": "5.60.8", + "moment": "2.29.4" + }, + "peerDependencies": { + "@codemirror/state": "6.5.0", + "@codemirror/view": "6.38.6" + } + }, + "node_modules/style-mod": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", + "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "dev": true, + "license": "MIT", + "peer": true + } + } +} diff --git a/node_modules/@codemirror/state/.github/workflows/dispatch.yml b/node_modules/@codemirror/state/.github/workflows/dispatch.yml new file mode 100644 index 0000000..d050072 --- /dev/null +++ b/node_modules/@codemirror/state/.github/workflows/dispatch.yml @@ -0,0 +1,16 @@ +name: Trigger CI +on: push + +jobs: + build: + name: Dispatch to main repo + runs-on: ubuntu-latest + steps: + - name: Emit repository_dispatch + uses: mvasigh/dispatch-action@main + with: + # You should create a personal access token and store it in your repository + token: ${{ secrets.DISPATCH_AUTH }} + repo: dev + owner: codemirror + event_type: push diff --git a/node_modules/@codemirror/state/CHANGELOG.md b/node_modules/@codemirror/state/CHANGELOG.md new file mode 100644 index 0000000..636d52b --- /dev/null +++ b/node_modules/@codemirror/state/CHANGELOG.md @@ -0,0 +1,274 @@ +## 6.5.0 (2024-12-09) + +### New features + +`RangeSet.compare` now supports a `boundChange` callback that is called when there's a change in the way ranges are split. + +## 6.4.1 (2024-02-19) + +### Bug fixes + +Fix an issue that caused widgets at the end of a mark decoration to be rendered in their own separate mark DOM element. + +## 6.4.0 (2023-12-28) + +### Bug fixes + +When multiple ranges in a single range set overlap, put the smaller ones inside the bigger ones, so that overlapping decorations don't break up each other's elements when coming from the same source. + +### New features + +Selection and selection range `eq` methods now support an optional argument that makes them also compare by cursor associativity. + +The `RangeSet.join` function can be used to join multiple range sets together. + +## 6.3.3 (2023-12-06) + +### Bug fixes + +Fix an issue where `Text.slice` and `Text.replace` could return objects with incorrect `length` when the given `from`/`to` values were out of range for the text. + +## 6.3.2 (2023-11-27) + +### Bug fixes + +Make sure transactions cannot add multiple selections when `allowMultipleSelections` is false. + +Fix a bug that caused `Text.iterLines` to not return empty lines at the end of the iterated ranges. + +## 6.3.1 (2023-10-18) + +### Bug fixes + +Give the tag property on `FacetReader` the type of the output type parameter to force TypeScript to infer the proper type when converting from `Facet` to `FacetReader`. + +## 6.3.0 (2023-10-12) + +### New features + +The new `FacetReader` type provides a way to export a read-only handle to a `Facet`. + +## 6.2.1 (2023-05-23) + +### Bug fixes + +Fix an issue that could cause `RangeSet.compare` to miss changes in the set of active ranges around a point range. + +## 6.2.0 (2022-12-26) + +### New features + +`EditorSelection.range` now accepts an optional 4th argument to specify the bidi level of the range's head position. + +## 6.1.4 (2022-11-15) + +### Bug fixes + +Fix a bug that caused the `openStart` value passed to span iterators to be incorrect around widgets in some circumstances. + +## 6.1.3 (2022-11-10) + +### Bug fixes + +Avoid unnecessary calls to computed facet getters when a state is reconfigured but no dependencies of the computed facet change. + +Fix an infinite loop in `RangeSet.eq` when the `to` parameter isn't given. + +## 6.1.2 (2022-09-21) + +### Bug fixes + +Fix an issue where, when multiple transaction extenders took effect, only the highest-precedence one was actually included in the transaction. + +## 6.1.1 (2022-08-03) + +### Bug fixes + +Fix a bug in range set span iteration that would cause decorations to be inappropriately split in some situations. + +## 6.1.0 (2022-06-30) + +### Bug fixes + +Refine change mapping to preserve insertions made by concurrent changes. + +### New features + +The `enables` option to `Facet.define` may now take a function, which will be called with the facet value to create the extensions. + +## 6.0.1 (2022-06-17) + +### Bug fixes + +Fix a problem that caused effects' `map` methods to be called with an incorrect change set when filtering changes. + +## 6.0.0 (2022-06-08) + +### Breaking changes + +Update dependencies to 6.0.0 + +## 0.20.1 (2022-06-02) + +### New features + +`EditorView.phrase` now accepts additional arguments, which it will interpolate into the phrase in the place of `$` markers. + +## 0.20.0 (2022-04-20) + +### Breaking changes + +The deprecated precedence names `fallback`, `extend`, and `override` were removed from the library. + +### Bug fixes + +Fix a bug where, if an extension value occurs multiple times, its lowest, rather than highest precedence is used. + +Fix an issue where facets with computed inputs would unneccesarily have their outputs recreated on state reconfiguration. + +Fix a bug in the order in which new values for state fields and facets were computed, which could cause dynamic facets to hold the wrong value in some situations. + +### New features + +The exports from @codemirror/rangeset now live in this package. + +The exports from @codemirror/text now live in this package. + +## 0.19.9 (2022-02-16) + +### Bug fixes + +Mapping a non-empty selection range now always puts any newly inserted text on the sides of the range outside of the mapped version. + +## 0.19.8 (2022-02-15) + +### Bug fixes + +Fix a bug where facet values with computed inputs could incorrectly retain their old value on reconfiguration. + +## 0.19.7 (2022-02-11) + +### Bug fixes + +Avoid recomputing facets on state reconfiguration if that facet's inputs stayed precisely the same. + +Selection ranges created with `EditorSelection.range` will now have an assoc pointing at their anchor, when non-empty. + +## 0.19.6 (2021-11-19) + +### Bug fixes + +Fix a bug that caused facet compare functions to be called with an invalid value in some situations. + +Fix a bug that caused dynamic facet values to be incorrectly kept unchanged when reconfiguration changed one of their dependencies. + +## 0.19.5 (2021-11-10) + +### Bug fixes + +Fix a bug that would cause dynamic facet values influenced by a state reconfiguration to not properly recompute. + +## 0.19.4 (2021-11-05) + +### Bug fixes + +When reconfiguring a state, effects from the reconfiguring transaction can now be seen by newly added state fields. + +## 0.19.3 (2021-11-03) + +### New features + +The precedence levels (under `Prec`) now have more generic names, because their 'meaningful' names were entirely inappropriate in many situations. + +## 0.19.2 (2021-09-13) + +### New features + +The editor state now has a `readOnly` property with a matching facet to control its value. + +## 0.19.1 (2021-08-15) + +### Bug fixes + +Fix a bug where `wordAt` never returned a useful result. + +## 0.19.0 (2021-08-11) + +### Breaking changes + +User event strings now work differently—the events emitted by the core packages follow a different system, and hierarchical event tags can be created by separating the words with dots. + +### New features + +`languageDataAt` now takes an optional `side` argument to specificy which side of the position you're interested in. + +It is now possible to add a user event annotation with a direct `userEvent` property on a transaction spec. + +Transactions now have an `isUserEvent` method that can be used to check if it is (a subtype of) some user event type. + +## 0.18.7 (2021-05-04) + +### Bug fixes + +Fix an issue where state fields might be initialized with a state that they aren't actually part of during reconfiguration. + +## 0.18.6 (2021-04-12) + +### New features + +The new `EditorState.wordAt` method finds the word at a given position. + +## 0.18.5 (2021-04-08) + +### Bug fixes + +Fix an issue in the compiled output that would break the code when minified with terser. + +## 0.18.4 (2021-04-06) + +### New features + +The new `Transaction.remote` annotation can be used to mark and recognize transactions created by other actors. + +## 0.18.3 (2021-03-23) + +### New features + +The `ChangeDesc` class now has `toJSON` and `fromJSON` methods. + +## 0.18.2 (2021-03-14) + +### Bug fixes + +Fix unintended ES2020 output (the package contains ES6 code again). + +## 0.18.1 (2021-03-10) + +### New features + +The new `Compartment.get` method can be used to get the content of a compartment in a given state. + +## 0.18.0 (2021-03-03) + +### Breaking changes + +`tagExtension` and the `reconfigure` transaction spec property have been replaced with the concept of configuration compartments and reconfiguration effects (see `Compartment`, `StateEffect.reconfigure`, and `StateEffect.appendConfig`). + +## 0.17.2 (2021-02-19) + +### New features + +`EditorSelection.map` and `SelectionRange.map` now take an optional second argument to indicate which direction to map to. + +## 0.17.1 (2021-01-06) + +### New features + +The package now also exports a CommonJS module. + +## 0.17.0 (2020-12-29) + +### Breaking changes + +First numbered release. + diff --git a/node_modules/@codemirror/state/LICENSE b/node_modules/@codemirror/state/LICENSE new file mode 100644 index 0000000..9a91f48 --- /dev/null +++ b/node_modules/@codemirror/state/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (C) 2018-2021 by Marijn Haverbeke and others + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/@codemirror/state/README.md b/node_modules/@codemirror/state/README.md new file mode 100644 index 0000000..5f1a325 --- /dev/null +++ b/node_modules/@codemirror/state/README.md @@ -0,0 +1,18 @@ +# @codemirror/state [![NPM version](https://img.shields.io/npm/v/@codemirror/state.svg)](https://www.npmjs.org/package/@codemirror/state) + +[ [**WEBSITE**](https://codemirror.net/) | [**DOCS**](https://codemirror.net/docs/ref/#state) | [**ISSUES**](https://github.com/codemirror/dev/issues) | [**FORUM**](https://discuss.codemirror.net/c/next/) | [**CHANGELOG**](https://github.com/codemirror/state/blob/main/CHANGELOG.md) ] + +This package implements the editor state data structures for the +[CodeMirror](https://codemirror.net/) code editor. + +The [project page](https://codemirror.net/) has more information, a +number of [examples](https://codemirror.net/examples/) and the +[documentation](https://codemirror.net/docs/). + +This code is released under an +[MIT license](https://github.com/codemirror/state/tree/main/LICENSE). + +We aim to be an inclusive, welcoming community. To make that explicit, +we have a [code of +conduct](http://contributor-covenant.org/version/1/1/0/) that applies +to communication around the project. diff --git a/node_modules/@codemirror/state/dist/index.cjs b/node_modules/@codemirror/state/dist/index.cjs new file mode 100644 index 0000000..fbf4421 --- /dev/null +++ b/node_modules/@codemirror/state/dist/index.cjs @@ -0,0 +1,3906 @@ +'use strict'; + +var findClusterBreak$1 = require('@marijn/find-cluster-break'); + +/** +The data structure for documents. @nonabstract +*/ +class Text { + /** + Get the line description around the given position. + */ + lineAt(pos) { + if (pos < 0 || pos > this.length) + throw new RangeError(`Invalid position ${pos} in document of length ${this.length}`); + return this.lineInner(pos, false, 1, 0); + } + /** + Get the description for the given (1-based) line number. + */ + line(n) { + if (n < 1 || n > this.lines) + throw new RangeError(`Invalid line number ${n} in ${this.lines}-line document`); + return this.lineInner(n, true, 1, 0); + } + /** + Replace a range of the text with the given content. + */ + replace(from, to, text) { + [from, to] = clip(this, from, to); + let parts = []; + this.decompose(0, from, parts, 2 /* Open.To */); + if (text.length) + text.decompose(0, text.length, parts, 1 /* Open.From */ | 2 /* Open.To */); + this.decompose(to, this.length, parts, 1 /* Open.From */); + return TextNode.from(parts, this.length - (to - from) + text.length); + } + /** + Append another document to this one. + */ + append(other) { + return this.replace(this.length, this.length, other); + } + /** + Retrieve the text between the given points. + */ + slice(from, to = this.length) { + [from, to] = clip(this, from, to); + let parts = []; + this.decompose(from, to, parts, 0); + return TextNode.from(parts, to - from); + } + /** + Test whether this text is equal to another instance. + */ + eq(other) { + if (other == this) + return true; + if (other.length != this.length || other.lines != this.lines) + return false; + let start = this.scanIdentical(other, 1), end = this.length - this.scanIdentical(other, -1); + let a = new RawTextCursor(this), b = new RawTextCursor(other); + for (let skip = start, pos = start;;) { + a.next(skip); + b.next(skip); + skip = 0; + if (a.lineBreak != b.lineBreak || a.done != b.done || a.value != b.value) + return false; + pos += a.value.length; + if (a.done || pos >= end) + return true; + } + } + /** + Iterate over the text. When `dir` is `-1`, iteration happens + from end to start. This will return lines and the breaks between + them as separate strings. + */ + iter(dir = 1) { return new RawTextCursor(this, dir); } + /** + Iterate over a range of the text. When `from` > `to`, the + iterator will run in reverse. + */ + iterRange(from, to = this.length) { return new PartialTextCursor(this, from, to); } + /** + Return a cursor that iterates over the given range of lines, + _without_ returning the line breaks between, and yielding empty + strings for empty lines. + + When `from` and `to` are given, they should be 1-based line numbers. + */ + iterLines(from, to) { + let inner; + if (from == null) { + inner = this.iter(); + } + else { + if (to == null) + to = this.lines + 1; + let start = this.line(from).from; + inner = this.iterRange(start, Math.max(start, to == this.lines + 1 ? this.length : to <= 1 ? 0 : this.line(to - 1).to)); + } + return new LineCursor(inner); + } + /** + Return the document as a string, using newline characters to + separate lines. + */ + toString() { return this.sliceString(0); } + /** + Convert the document to an array of lines (which can be + deserialized again via [`Text.of`](https://codemirror.net/6/docs/ref/#state.Text^of)). + */ + toJSON() { + let lines = []; + this.flatten(lines); + return lines; + } + /** + @internal + */ + constructor() { } + /** + Create a `Text` instance for the given array of lines. + */ + static of(text) { + if (text.length == 0) + throw new RangeError("A document must have at least one line"); + if (text.length == 1 && !text[0]) + return Text.empty; + return text.length <= 32 /* Tree.Branch */ ? new TextLeaf(text) : TextNode.from(TextLeaf.split(text, [])); + } +} +// Leaves store an array of line strings. There are always line breaks +// between these strings. Leaves are limited in size and have to be +// contained in TextNode instances for bigger documents. +class TextLeaf extends Text { + constructor(text, length = textLength(text)) { + super(); + this.text = text; + this.length = length; + } + get lines() { return this.text.length; } + get children() { return null; } + lineInner(target, isLine, line, offset) { + for (let i = 0;; i++) { + let string = this.text[i], end = offset + string.length; + if ((isLine ? line : end) >= target) + return new Line(offset, end, line, string); + offset = end + 1; + line++; + } + } + decompose(from, to, target, open) { + let text = from <= 0 && to >= this.length ? this + : new TextLeaf(sliceText(this.text, from, to), Math.min(to, this.length) - Math.max(0, from)); + if (open & 1 /* Open.From */) { + let prev = target.pop(); + let joined = appendText(text.text, prev.text.slice(), 0, text.length); + if (joined.length <= 32 /* Tree.Branch */) { + target.push(new TextLeaf(joined, prev.length + text.length)); + } + else { + let mid = joined.length >> 1; + target.push(new TextLeaf(joined.slice(0, mid)), new TextLeaf(joined.slice(mid))); + } + } + else { + target.push(text); + } + } + replace(from, to, text) { + if (!(text instanceof TextLeaf)) + return super.replace(from, to, text); + [from, to] = clip(this, from, to); + let lines = appendText(this.text, appendText(text.text, sliceText(this.text, 0, from)), to); + let newLen = this.length + text.length - (to - from); + if (lines.length <= 32 /* Tree.Branch */) + return new TextLeaf(lines, newLen); + return TextNode.from(TextLeaf.split(lines, []), newLen); + } + sliceString(from, to = this.length, lineSep = "\n") { + [from, to] = clip(this, from, to); + let result = ""; + for (let pos = 0, i = 0; pos <= to && i < this.text.length; i++) { + let line = this.text[i], end = pos + line.length; + if (pos > from && i) + result += lineSep; + if (from < end && to > pos) + result += line.slice(Math.max(0, from - pos), to - pos); + pos = end + 1; + } + return result; + } + flatten(target) { + for (let line of this.text) + target.push(line); + } + scanIdentical() { return 0; } + static split(text, target) { + let part = [], len = -1; + for (let line of text) { + part.push(line); + len += line.length + 1; + if (part.length == 32 /* Tree.Branch */) { + target.push(new TextLeaf(part, len)); + part = []; + len = -1; + } + } + if (len > -1) + target.push(new TextLeaf(part, len)); + return target; + } +} +// Nodes provide the tree structure of the `Text` type. They store a +// number of other nodes or leaves, taking care to balance themselves +// on changes. There are implied line breaks _between_ the children of +// a node (but not before the first or after the last child). +class TextNode extends Text { + constructor(children, length) { + super(); + this.children = children; + this.length = length; + this.lines = 0; + for (let child of children) + this.lines += child.lines; + } + lineInner(target, isLine, line, offset) { + for (let i = 0;; i++) { + let child = this.children[i], end = offset + child.length, endLine = line + child.lines - 1; + if ((isLine ? endLine : end) >= target) + return child.lineInner(target, isLine, line, offset); + offset = end + 1; + line = endLine + 1; + } + } + decompose(from, to, target, open) { + for (let i = 0, pos = 0; pos <= to && i < this.children.length; i++) { + let child = this.children[i], end = pos + child.length; + if (from <= end && to >= pos) { + let childOpen = open & ((pos <= from ? 1 /* Open.From */ : 0) | (end >= to ? 2 /* Open.To */ : 0)); + if (pos >= from && end <= to && !childOpen) + target.push(child); + else + child.decompose(from - pos, to - pos, target, childOpen); + } + pos = end + 1; + } + } + replace(from, to, text) { + [from, to] = clip(this, from, to); + if (text.lines < this.lines) + for (let i = 0, pos = 0; i < this.children.length; i++) { + let child = this.children[i], end = pos + child.length; + // Fast path: if the change only affects one child and the + // child's size remains in the acceptable range, only update + // that child + if (from >= pos && to <= end) { + let updated = child.replace(from - pos, to - pos, text); + let totalLines = this.lines - child.lines + updated.lines; + if (updated.lines < (totalLines >> (5 /* Tree.BranchShift */ - 1)) && + updated.lines > (totalLines >> (5 /* Tree.BranchShift */ + 1))) { + let copy = this.children.slice(); + copy[i] = updated; + return new TextNode(copy, this.length - (to - from) + text.length); + } + return super.replace(pos, end, updated); + } + pos = end + 1; + } + return super.replace(from, to, text); + } + sliceString(from, to = this.length, lineSep = "\n") { + [from, to] = clip(this, from, to); + let result = ""; + for (let i = 0, pos = 0; i < this.children.length && pos <= to; i++) { + let child = this.children[i], end = pos + child.length; + if (pos > from && i) + result += lineSep; + if (from < end && to > pos) + result += child.sliceString(from - pos, to - pos, lineSep); + pos = end + 1; + } + return result; + } + flatten(target) { + for (let child of this.children) + child.flatten(target); + } + scanIdentical(other, dir) { + if (!(other instanceof TextNode)) + return 0; + let length = 0; + let [iA, iB, eA, eB] = dir > 0 ? [0, 0, this.children.length, other.children.length] + : [this.children.length - 1, other.children.length - 1, -1, -1]; + for (;; iA += dir, iB += dir) { + if (iA == eA || iB == eB) + return length; + let chA = this.children[iA], chB = other.children[iB]; + if (chA != chB) + return length + chA.scanIdentical(chB, dir); + length += chA.length + 1; + } + } + static from(children, length = children.reduce((l, ch) => l + ch.length + 1, -1)) { + let lines = 0; + for (let ch of children) + lines += ch.lines; + if (lines < 32 /* Tree.Branch */) { + let flat = []; + for (let ch of children) + ch.flatten(flat); + return new TextLeaf(flat, length); + } + let chunk = Math.max(32 /* Tree.Branch */, lines >> 5 /* Tree.BranchShift */), maxChunk = chunk << 1, minChunk = chunk >> 1; + let chunked = [], currentLines = 0, currentLen = -1, currentChunk = []; + function add(child) { + let last; + if (child.lines > maxChunk && child instanceof TextNode) { + for (let node of child.children) + add(node); + } + else if (child.lines > minChunk && (currentLines > minChunk || !currentLines)) { + flush(); + chunked.push(child); + } + else if (child instanceof TextLeaf && currentLines && + (last = currentChunk[currentChunk.length - 1]) instanceof TextLeaf && + child.lines + last.lines <= 32 /* Tree.Branch */) { + currentLines += child.lines; + currentLen += child.length + 1; + currentChunk[currentChunk.length - 1] = new TextLeaf(last.text.concat(child.text), last.length + 1 + child.length); + } + else { + if (currentLines + child.lines > chunk) + flush(); + currentLines += child.lines; + currentLen += child.length + 1; + currentChunk.push(child); + } + } + function flush() { + if (currentLines == 0) + return; + chunked.push(currentChunk.length == 1 ? currentChunk[0] : TextNode.from(currentChunk, currentLen)); + currentLen = -1; + currentLines = currentChunk.length = 0; + } + for (let child of children) + add(child); + flush(); + return chunked.length == 1 ? chunked[0] : new TextNode(chunked, length); + } +} +Text.empty = new TextLeaf([""], 0); +function textLength(text) { + let length = -1; + for (let line of text) + length += line.length + 1; + return length; +} +function appendText(text, target, from = 0, to = 1e9) { + for (let pos = 0, i = 0, first = true; i < text.length && pos <= to; i++) { + let line = text[i], end = pos + line.length; + if (end >= from) { + if (end > to) + line = line.slice(0, to - pos); + if (pos < from) + line = line.slice(from - pos); + if (first) { + target[target.length - 1] += line; + first = false; + } + else + target.push(line); + } + pos = end + 1; + } + return target; +} +function sliceText(text, from, to) { + return appendText(text, [""], from, to); +} +class RawTextCursor { + constructor(text, dir = 1) { + this.dir = dir; + this.done = false; + this.lineBreak = false; + this.value = ""; + this.nodes = [text]; + this.offsets = [dir > 0 ? 1 : (text instanceof TextLeaf ? text.text.length : text.children.length) << 1]; + } + nextInner(skip, dir) { + this.done = this.lineBreak = false; + for (;;) { + let last = this.nodes.length - 1; + let top = this.nodes[last], offsetValue = this.offsets[last], offset = offsetValue >> 1; + let size = top instanceof TextLeaf ? top.text.length : top.children.length; + if (offset == (dir > 0 ? size : 0)) { + if (last == 0) { + this.done = true; + this.value = ""; + return this; + } + if (dir > 0) + this.offsets[last - 1]++; + this.nodes.pop(); + this.offsets.pop(); + } + else if ((offsetValue & 1) == (dir > 0 ? 0 : 1)) { + this.offsets[last] += dir; + if (skip == 0) { + this.lineBreak = true; + this.value = "\n"; + return this; + } + skip--; + } + else if (top instanceof TextLeaf) { + // Move to the next string + let next = top.text[offset + (dir < 0 ? -1 : 0)]; + this.offsets[last] += dir; + if (next.length > Math.max(0, skip)) { + this.value = skip == 0 ? next : dir > 0 ? next.slice(skip) : next.slice(0, next.length - skip); + return this; + } + skip -= next.length; + } + else { + let next = top.children[offset + (dir < 0 ? -1 : 0)]; + if (skip > next.length) { + skip -= next.length; + this.offsets[last] += dir; + } + else { + if (dir < 0) + this.offsets[last]--; + this.nodes.push(next); + this.offsets.push(dir > 0 ? 1 : (next instanceof TextLeaf ? next.text.length : next.children.length) << 1); + } + } + } + } + next(skip = 0) { + if (skip < 0) { + this.nextInner(-skip, (-this.dir)); + skip = this.value.length; + } + return this.nextInner(skip, this.dir); + } +} +class PartialTextCursor { + constructor(text, start, end) { + this.value = ""; + this.done = false; + this.cursor = new RawTextCursor(text, start > end ? -1 : 1); + this.pos = start > end ? text.length : 0; + this.from = Math.min(start, end); + this.to = Math.max(start, end); + } + nextInner(skip, dir) { + if (dir < 0 ? this.pos <= this.from : this.pos >= this.to) { + this.value = ""; + this.done = true; + return this; + } + skip += Math.max(0, dir < 0 ? this.pos - this.to : this.from - this.pos); + let limit = dir < 0 ? this.pos - this.from : this.to - this.pos; + if (skip > limit) + skip = limit; + limit -= skip; + let { value } = this.cursor.next(skip); + this.pos += (value.length + skip) * dir; + this.value = value.length <= limit ? value : dir < 0 ? value.slice(value.length - limit) : value.slice(0, limit); + this.done = !this.value; + return this; + } + next(skip = 0) { + if (skip < 0) + skip = Math.max(skip, this.from - this.pos); + else if (skip > 0) + skip = Math.min(skip, this.to - this.pos); + return this.nextInner(skip, this.cursor.dir); + } + get lineBreak() { return this.cursor.lineBreak && this.value != ""; } +} +class LineCursor { + constructor(inner) { + this.inner = inner; + this.afterBreak = true; + this.value = ""; + this.done = false; + } + next(skip = 0) { + let { done, lineBreak, value } = this.inner.next(skip); + if (done && this.afterBreak) { + this.value = ""; + this.afterBreak = false; + } + else if (done) { + this.done = true; + this.value = ""; + } + else if (lineBreak) { + if (this.afterBreak) { + this.value = ""; + } + else { + this.afterBreak = true; + this.next(); + } + } + else { + this.value = value; + this.afterBreak = false; + } + return this; + } + get lineBreak() { return false; } +} +if (typeof Symbol != "undefined") { + Text.prototype[Symbol.iterator] = function () { return this.iter(); }; + RawTextCursor.prototype[Symbol.iterator] = PartialTextCursor.prototype[Symbol.iterator] = + LineCursor.prototype[Symbol.iterator] = function () { return this; }; +} +/** +This type describes a line in the document. It is created +on-demand when lines are [queried](https://codemirror.net/6/docs/ref/#state.Text.lineAt). +*/ +class Line { + /** + @internal + */ + constructor( + /** + The position of the start of the line. + */ + from, + /** + The position at the end of the line (_before_ the line break, + or at the end of document for the last line). + */ + to, + /** + This line's line number (1-based). + */ + number, + /** + The line's content. + */ + text) { + this.from = from; + this.to = to; + this.number = number; + this.text = text; + } + /** + The length of the line (not including any line break after it). + */ + get length() { return this.to - this.from; } +} +function clip(text, from, to) { + from = Math.max(0, Math.min(text.length, from)); + return [from, Math.max(from, Math.min(text.length, to))]; +} + +/** +Returns a next grapheme cluster break _after_ (not equal to) +`pos`, if `forward` is true, or before otherwise. Returns `pos` +itself if no further cluster break is available in the string. +Moves across surrogate pairs, extending characters (when +`includeExtending` is true), characters joined with zero-width +joiners, and flag emoji. +*/ +function findClusterBreak(str, pos, forward = true, includeExtending = true) { + return findClusterBreak$1.findClusterBreak(str, pos, forward, includeExtending); +} +function surrogateLow(ch) { return ch >= 0xDC00 && ch < 0xE000; } +function surrogateHigh(ch) { return ch >= 0xD800 && ch < 0xDC00; } +/** +Find the code point at the given position in a string (like the +[`codePointAt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt) +string method). +*/ +function codePointAt(str, pos) { + let code0 = str.charCodeAt(pos); + if (!surrogateHigh(code0) || pos + 1 == str.length) + return code0; + let code1 = str.charCodeAt(pos + 1); + if (!surrogateLow(code1)) + return code0; + return ((code0 - 0xd800) << 10) + (code1 - 0xdc00) + 0x10000; +} +/** +Given a Unicode codepoint, return the JavaScript string that +respresents it (like +[`String.fromCodePoint`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint)). +*/ +function fromCodePoint(code) { + if (code <= 0xffff) + return String.fromCharCode(code); + code -= 0x10000; + return String.fromCharCode((code >> 10) + 0xd800, (code & 1023) + 0xdc00); +} +/** +The amount of positions a character takes up in a JavaScript string. +*/ +function codePointSize(code) { return code < 0x10000 ? 1 : 2; } + +const DefaultSplit = /\r\n?|\n/; +/** +Distinguishes different ways in which positions can be mapped. +*/ +exports.MapMode = void 0; +(function (MapMode) { + /** + Map a position to a valid new position, even when its context + was deleted. + */ + MapMode[MapMode["Simple"] = 0] = "Simple"; + /** + Return null if deletion happens across the position. + */ + MapMode[MapMode["TrackDel"] = 1] = "TrackDel"; + /** + Return null if the character _before_ the position is deleted. + */ + MapMode[MapMode["TrackBefore"] = 2] = "TrackBefore"; + /** + Return null if the character _after_ the position is deleted. + */ + MapMode[MapMode["TrackAfter"] = 3] = "TrackAfter"; +})(exports.MapMode || (exports.MapMode = {})); +/** +A change description is a variant of [change set](https://codemirror.net/6/docs/ref/#state.ChangeSet) +that doesn't store the inserted text. As such, it can't be +applied, but is cheaper to store and manipulate. +*/ +class ChangeDesc { + // Sections are encoded as pairs of integers. The first is the + // length in the current document, and the second is -1 for + // unaffected sections, and the length of the replacement content + // otherwise. So an insertion would be (0, n>0), a deletion (n>0, + // 0), and a replacement two positive numbers. + /** + @internal + */ + constructor( + /** + @internal + */ + sections) { + this.sections = sections; + } + /** + The length of the document before the change. + */ + get length() { + let result = 0; + for (let i = 0; i < this.sections.length; i += 2) + result += this.sections[i]; + return result; + } + /** + The length of the document after the change. + */ + get newLength() { + let result = 0; + for (let i = 0; i < this.sections.length; i += 2) { + let ins = this.sections[i + 1]; + result += ins < 0 ? this.sections[i] : ins; + } + return result; + } + /** + False when there are actual changes in this set. + */ + get empty() { return this.sections.length == 0 || this.sections.length == 2 && this.sections[1] < 0; } + /** + Iterate over the unchanged parts left by these changes. `posA` + provides the position of the range in the old document, `posB` + the new position in the changed document. + */ + iterGaps(f) { + for (let i = 0, posA = 0, posB = 0; i < this.sections.length;) { + let len = this.sections[i++], ins = this.sections[i++]; + if (ins < 0) { + f(posA, posB, len); + posB += len; + } + else { + posB += ins; + } + posA += len; + } + } + /** + Iterate over the ranges changed by these changes. (See + [`ChangeSet.iterChanges`](https://codemirror.net/6/docs/ref/#state.ChangeSet.iterChanges) for a + variant that also provides you with the inserted text.) + `fromA`/`toA` provides the extent of the change in the starting + document, `fromB`/`toB` the extent of the replacement in the + changed document. + + When `individual` is true, adjacent changes (which are kept + separate for [position mapping](https://codemirror.net/6/docs/ref/#state.ChangeDesc.mapPos)) are + reported separately. + */ + iterChangedRanges(f, individual = false) { + iterChanges(this, f, individual); + } + /** + Get a description of the inverted form of these changes. + */ + get invertedDesc() { + let sections = []; + for (let i = 0; i < this.sections.length;) { + let len = this.sections[i++], ins = this.sections[i++]; + if (ins < 0) + sections.push(len, ins); + else + sections.push(ins, len); + } + return new ChangeDesc(sections); + } + /** + Compute the combined effect of applying another set of changes + after this one. The length of the document after this set should + match the length before `other`. + */ + composeDesc(other) { return this.empty ? other : other.empty ? this : composeSets(this, other); } + /** + Map this description, which should start with the same document + as `other`, over another set of changes, so that it can be + applied after it. When `before` is true, map as if the changes + in `this` happened before the ones in `other`. + */ + mapDesc(other, before = false) { return other.empty ? this : mapSet(this, other, before); } + mapPos(pos, assoc = -1, mode = exports.MapMode.Simple) { + let posA = 0, posB = 0; + for (let i = 0; i < this.sections.length;) { + let len = this.sections[i++], ins = this.sections[i++], endA = posA + len; + if (ins < 0) { + if (endA > pos) + return posB + (pos - posA); + posB += len; + } + else { + if (mode != exports.MapMode.Simple && endA >= pos && + (mode == exports.MapMode.TrackDel && posA < pos && endA > pos || + mode == exports.MapMode.TrackBefore && posA < pos || + mode == exports.MapMode.TrackAfter && endA > pos)) + return null; + if (endA > pos || endA == pos && assoc < 0 && !len) + return pos == posA || assoc < 0 ? posB : posB + ins; + posB += ins; + } + posA = endA; + } + if (pos > posA) + throw new RangeError(`Position ${pos} is out of range for changeset of length ${posA}`); + return posB; + } + /** + Check whether these changes touch a given range. When one of the + changes entirely covers the range, the string `"cover"` is + returned. + */ + touchesRange(from, to = from) { + for (let i = 0, pos = 0; i < this.sections.length && pos <= to;) { + let len = this.sections[i++], ins = this.sections[i++], end = pos + len; + if (ins >= 0 && pos <= to && end >= from) + return pos < from && end > to ? "cover" : true; + pos = end; + } + return false; + } + /** + @internal + */ + toString() { + let result = ""; + for (let i = 0; i < this.sections.length;) { + let len = this.sections[i++], ins = this.sections[i++]; + result += (result ? " " : "") + len + (ins >= 0 ? ":" + ins : ""); + } + return result; + } + /** + Serialize this change desc to a JSON-representable value. + */ + toJSON() { return this.sections; } + /** + Create a change desc from its JSON representation (as produced + by [`toJSON`](https://codemirror.net/6/docs/ref/#state.ChangeDesc.toJSON). + */ + static fromJSON(json) { + if (!Array.isArray(json) || json.length % 2 || json.some(a => typeof a != "number")) + throw new RangeError("Invalid JSON representation of ChangeDesc"); + return new ChangeDesc(json); + } + /** + @internal + */ + static create(sections) { return new ChangeDesc(sections); } +} +/** +A change set represents a group of modifications to a document. It +stores the document length, and can only be applied to documents +with exactly that length. +*/ +class ChangeSet extends ChangeDesc { + constructor(sections, + /** + @internal + */ + inserted) { + super(sections); + this.inserted = inserted; + } + /** + Apply the changes to a document, returning the modified + document. + */ + apply(doc) { + if (this.length != doc.length) + throw new RangeError("Applying change set to a document with the wrong length"); + iterChanges(this, (fromA, toA, fromB, _toB, text) => doc = doc.replace(fromB, fromB + (toA - fromA), text), false); + return doc; + } + mapDesc(other, before = false) { return mapSet(this, other, before, true); } + /** + Given the document as it existed _before_ the changes, return a + change set that represents the inverse of this set, which could + be used to go from the document created by the changes back to + the document as it existed before the changes. + */ + invert(doc) { + let sections = this.sections.slice(), inserted = []; + for (let i = 0, pos = 0; i < sections.length; i += 2) { + let len = sections[i], ins = sections[i + 1]; + if (ins >= 0) { + sections[i] = ins; + sections[i + 1] = len; + let index = i >> 1; + while (inserted.length < index) + inserted.push(Text.empty); + inserted.push(len ? doc.slice(pos, pos + len) : Text.empty); + } + pos += len; + } + return new ChangeSet(sections, inserted); + } + /** + Combine two subsequent change sets into a single set. `other` + must start in the document produced by `this`. If `this` goes + `docA` → `docB` and `other` represents `docB` → `docC`, the + returned value will represent the change `docA` → `docC`. + */ + compose(other) { return this.empty ? other : other.empty ? this : composeSets(this, other, true); } + /** + Given another change set starting in the same document, maps this + change set over the other, producing a new change set that can be + applied to the document produced by applying `other`. When + `before` is `true`, order changes as if `this` comes before + `other`, otherwise (the default) treat `other` as coming first. + + Given two changes `A` and `B`, `A.compose(B.map(A))` and + `B.compose(A.map(B, true))` will produce the same document. This + provides a basic form of [operational + transformation](https://en.wikipedia.org/wiki/Operational_transformation), + and can be used for collaborative editing. + */ + map(other, before = false) { return other.empty ? this : mapSet(this, other, before, true); } + /** + Iterate over the changed ranges in the document, calling `f` for + each, with the range in the original document (`fromA`-`toA`) + and the range that replaces it in the new document + (`fromB`-`toB`). + + When `individual` is true, adjacent changes are reported + separately. + */ + iterChanges(f, individual = false) { + iterChanges(this, f, individual); + } + /** + Get a [change description](https://codemirror.net/6/docs/ref/#state.ChangeDesc) for this change + set. + */ + get desc() { return ChangeDesc.create(this.sections); } + /** + @internal + */ + filter(ranges) { + let resultSections = [], resultInserted = [], filteredSections = []; + let iter = new SectionIter(this); + done: for (let i = 0, pos = 0;;) { + let next = i == ranges.length ? 1e9 : ranges[i++]; + while (pos < next || pos == next && iter.len == 0) { + if (iter.done) + break done; + let len = Math.min(iter.len, next - pos); + addSection(filteredSections, len, -1); + let ins = iter.ins == -1 ? -1 : iter.off == 0 ? iter.ins : 0; + addSection(resultSections, len, ins); + if (ins > 0) + addInsert(resultInserted, resultSections, iter.text); + iter.forward(len); + pos += len; + } + let end = ranges[i++]; + while (pos < end) { + if (iter.done) + break done; + let len = Math.min(iter.len, end - pos); + addSection(resultSections, len, -1); + addSection(filteredSections, len, iter.ins == -1 ? -1 : iter.off == 0 ? iter.ins : 0); + iter.forward(len); + pos += len; + } + } + return { changes: new ChangeSet(resultSections, resultInserted), + filtered: ChangeDesc.create(filteredSections) }; + } + /** + Serialize this change set to a JSON-representable value. + */ + toJSON() { + let parts = []; + for (let i = 0; i < this.sections.length; i += 2) { + let len = this.sections[i], ins = this.sections[i + 1]; + if (ins < 0) + parts.push(len); + else if (ins == 0) + parts.push([len]); + else + parts.push([len].concat(this.inserted[i >> 1].toJSON())); + } + return parts; + } + /** + Create a change set for the given changes, for a document of the + given length, using `lineSep` as line separator. + */ + static of(changes, length, lineSep) { + let sections = [], inserted = [], pos = 0; + let total = null; + function flush(force = false) { + if (!force && !sections.length) + return; + if (pos < length) + addSection(sections, length - pos, -1); + let set = new ChangeSet(sections, inserted); + total = total ? total.compose(set.map(total)) : set; + sections = []; + inserted = []; + pos = 0; + } + function process(spec) { + if (Array.isArray(spec)) { + for (let sub of spec) + process(sub); + } + else if (spec instanceof ChangeSet) { + if (spec.length != length) + throw new RangeError(`Mismatched change set length (got ${spec.length}, expected ${length})`); + flush(); + total = total ? total.compose(spec.map(total)) : spec; + } + else { + let { from, to = from, insert } = spec; + if (from > to || from < 0 || to > length) + throw new RangeError(`Invalid change range ${from} to ${to} (in doc of length ${length})`); + let insText = !insert ? Text.empty : typeof insert == "string" ? Text.of(insert.split(lineSep || DefaultSplit)) : insert; + let insLen = insText.length; + if (from == to && insLen == 0) + return; + if (from < pos) + flush(); + if (from > pos) + addSection(sections, from - pos, -1); + addSection(sections, to - from, insLen); + addInsert(inserted, sections, insText); + pos = to; + } + } + process(changes); + flush(!total); + return total; + } + /** + Create an empty changeset of the given length. + */ + static empty(length) { + return new ChangeSet(length ? [length, -1] : [], []); + } + /** + Create a changeset from its JSON representation (as produced by + [`toJSON`](https://codemirror.net/6/docs/ref/#state.ChangeSet.toJSON). + */ + static fromJSON(json) { + if (!Array.isArray(json)) + throw new RangeError("Invalid JSON representation of ChangeSet"); + let sections = [], inserted = []; + for (let i = 0; i < json.length; i++) { + let part = json[i]; + if (typeof part == "number") { + sections.push(part, -1); + } + else if (!Array.isArray(part) || typeof part[0] != "number" || part.some((e, i) => i && typeof e != "string")) { + throw new RangeError("Invalid JSON representation of ChangeSet"); + } + else if (part.length == 1) { + sections.push(part[0], 0); + } + else { + while (inserted.length < i) + inserted.push(Text.empty); + inserted[i] = Text.of(part.slice(1)); + sections.push(part[0], inserted[i].length); + } + } + return new ChangeSet(sections, inserted); + } + /** + @internal + */ + static createSet(sections, inserted) { + return new ChangeSet(sections, inserted); + } +} +function addSection(sections, len, ins, forceJoin = false) { + if (len == 0 && ins <= 0) + return; + let last = sections.length - 2; + if (last >= 0 && ins <= 0 && ins == sections[last + 1]) + sections[last] += len; + else if (last >= 0 && len == 0 && sections[last] == 0) + sections[last + 1] += ins; + else if (forceJoin) { + sections[last] += len; + sections[last + 1] += ins; + } + else + sections.push(len, ins); +} +function addInsert(values, sections, value) { + if (value.length == 0) + return; + let index = (sections.length - 2) >> 1; + if (index < values.length) { + values[values.length - 1] = values[values.length - 1].append(value); + } + else { + while (values.length < index) + values.push(Text.empty); + values.push(value); + } +} +function iterChanges(desc, f, individual) { + let inserted = desc.inserted; + for (let posA = 0, posB = 0, i = 0; i < desc.sections.length;) { + let len = desc.sections[i++], ins = desc.sections[i++]; + if (ins < 0) { + posA += len; + posB += len; + } + else { + let endA = posA, endB = posB, text = Text.empty; + for (;;) { + endA += len; + endB += ins; + if (ins && inserted) + text = text.append(inserted[(i - 2) >> 1]); + if (individual || i == desc.sections.length || desc.sections[i + 1] < 0) + break; + len = desc.sections[i++]; + ins = desc.sections[i++]; + } + f(posA, endA, posB, endB, text); + posA = endA; + posB = endB; + } + } +} +function mapSet(setA, setB, before, mkSet = false) { + // Produce a copy of setA that applies to the document after setB + // has been applied (assuming both start at the same document). + let sections = [], insert = mkSet ? [] : null; + let a = new SectionIter(setA), b = new SectionIter(setB); + // Iterate over both sets in parallel. inserted tracks, for changes + // in A that have to be processed piece-by-piece, whether their + // content has been inserted already, and refers to the section + // index. + for (let inserted = -1;;) { + if (a.done && b.len || b.done && a.len) { + throw new Error("Mismatched change set lengths"); + } + else if (a.ins == -1 && b.ins == -1) { + // Move across ranges skipped by both sets. + let len = Math.min(a.len, b.len); + addSection(sections, len, -1); + a.forward(len); + b.forward(len); + } + else if (b.ins >= 0 && (a.ins < 0 || inserted == a.i || a.off == 0 && (b.len < a.len || b.len == a.len && !before))) { + // If there's a change in B that comes before the next change in + // A (ordered by start pos, then len, then before flag), skip + // that (and process any changes in A it covers). + let len = b.len; + addSection(sections, b.ins, -1); + while (len) { + let piece = Math.min(a.len, len); + if (a.ins >= 0 && inserted < a.i && a.len <= piece) { + addSection(sections, 0, a.ins); + if (insert) + addInsert(insert, sections, a.text); + inserted = a.i; + } + a.forward(piece); + len -= piece; + } + b.next(); + } + else if (a.ins >= 0) { + // Process the part of a change in A up to the start of the next + // non-deletion change in B (if overlapping). + let len = 0, left = a.len; + while (left) { + if (b.ins == -1) { + let piece = Math.min(left, b.len); + len += piece; + left -= piece; + b.forward(piece); + } + else if (b.ins == 0 && b.len < left) { + left -= b.len; + b.next(); + } + else { + break; + } + } + addSection(sections, len, inserted < a.i ? a.ins : 0); + if (insert && inserted < a.i) + addInsert(insert, sections, a.text); + inserted = a.i; + a.forward(a.len - left); + } + else if (a.done && b.done) { + return insert ? ChangeSet.createSet(sections, insert) : ChangeDesc.create(sections); + } + else { + throw new Error("Mismatched change set lengths"); + } + } +} +function composeSets(setA, setB, mkSet = false) { + let sections = []; + let insert = mkSet ? [] : null; + let a = new SectionIter(setA), b = new SectionIter(setB); + for (let open = false;;) { + if (a.done && b.done) { + return insert ? ChangeSet.createSet(sections, insert) : ChangeDesc.create(sections); + } + else if (a.ins == 0) { // Deletion in A + addSection(sections, a.len, 0, open); + a.next(); + } + else if (b.len == 0 && !b.done) { // Insertion in B + addSection(sections, 0, b.ins, open); + if (insert) + addInsert(insert, sections, b.text); + b.next(); + } + else if (a.done || b.done) { + throw new Error("Mismatched change set lengths"); + } + else { + let len = Math.min(a.len2, b.len), sectionLen = sections.length; + if (a.ins == -1) { + let insB = b.ins == -1 ? -1 : b.off ? 0 : b.ins; + addSection(sections, len, insB, open); + if (insert && insB) + addInsert(insert, sections, b.text); + } + else if (b.ins == -1) { + addSection(sections, a.off ? 0 : a.len, len, open); + if (insert) + addInsert(insert, sections, a.textBit(len)); + } + else { + addSection(sections, a.off ? 0 : a.len, b.off ? 0 : b.ins, open); + if (insert && !b.off) + addInsert(insert, sections, b.text); + } + open = (a.ins > len || b.ins >= 0 && b.len > len) && (open || sections.length > sectionLen); + a.forward2(len); + b.forward(len); + } + } +} +class SectionIter { + constructor(set) { + this.set = set; + this.i = 0; + this.next(); + } + next() { + let { sections } = this.set; + if (this.i < sections.length) { + this.len = sections[this.i++]; + this.ins = sections[this.i++]; + } + else { + this.len = 0; + this.ins = -2; + } + this.off = 0; + } + get done() { return this.ins == -2; } + get len2() { return this.ins < 0 ? this.len : this.ins; } + get text() { + let { inserted } = this.set, index = (this.i - 2) >> 1; + return index >= inserted.length ? Text.empty : inserted[index]; + } + textBit(len) { + let { inserted } = this.set, index = (this.i - 2) >> 1; + return index >= inserted.length && !len ? Text.empty + : inserted[index].slice(this.off, len == null ? undefined : this.off + len); + } + forward(len) { + if (len == this.len) + this.next(); + else { + this.len -= len; + this.off += len; + } + } + forward2(len) { + if (this.ins == -1) + this.forward(len); + else if (len == this.ins) + this.next(); + else { + this.ins -= len; + this.off += len; + } + } +} + +/** +A single selection range. When +[`allowMultipleSelections`](https://codemirror.net/6/docs/ref/#state.EditorState^allowMultipleSelections) +is enabled, a [selection](https://codemirror.net/6/docs/ref/#state.EditorSelection) may hold +multiple ranges. By default, selections hold exactly one range. +*/ +class SelectionRange { + constructor( + /** + The lower boundary of the range. + */ + from, + /** + The upper boundary of the range. + */ + to, flags) { + this.from = from; + this.to = to; + this.flags = flags; + } + /** + The anchor of the range—the side that doesn't move when you + extend it. + */ + get anchor() { return this.flags & 32 /* RangeFlag.Inverted */ ? this.to : this.from; } + /** + The head of the range, which is moved when the range is + [extended](https://codemirror.net/6/docs/ref/#state.SelectionRange.extend). + */ + get head() { return this.flags & 32 /* RangeFlag.Inverted */ ? this.from : this.to; } + /** + True when `anchor` and `head` are at the same position. + */ + get empty() { return this.from == this.to; } + /** + If this is a cursor that is explicitly associated with the + character on one of its sides, this returns the side. -1 means + the character before its position, 1 the character after, and 0 + means no association. + */ + get assoc() { return this.flags & 8 /* RangeFlag.AssocBefore */ ? -1 : this.flags & 16 /* RangeFlag.AssocAfter */ ? 1 : 0; } + /** + The bidirectional text level associated with this cursor, if + any. + */ + get bidiLevel() { + let level = this.flags & 7 /* RangeFlag.BidiLevelMask */; + return level == 7 ? null : level; + } + /** + The goal column (stored vertical offset) associated with a + cursor. This is used to preserve the vertical position when + [moving](https://codemirror.net/6/docs/ref/#view.EditorView.moveVertically) across + lines of different length. + */ + get goalColumn() { + let value = this.flags >> 6 /* RangeFlag.GoalColumnOffset */; + return value == 16777215 /* RangeFlag.NoGoalColumn */ ? undefined : value; + } + /** + Map this range through a change, producing a valid range in the + updated document. + */ + map(change, assoc = -1) { + let from, to; + if (this.empty) { + from = to = change.mapPos(this.from, assoc); + } + else { + from = change.mapPos(this.from, 1); + to = change.mapPos(this.to, -1); + } + return from == this.from && to == this.to ? this : new SelectionRange(from, to, this.flags); + } + /** + Extend this range to cover at least `from` to `to`. + */ + extend(from, to = from) { + if (from <= this.anchor && to >= this.anchor) + return EditorSelection.range(from, to); + let head = Math.abs(from - this.anchor) > Math.abs(to - this.anchor) ? from : to; + return EditorSelection.range(this.anchor, head); + } + /** + Compare this range to another range. + */ + eq(other, includeAssoc = false) { + return this.anchor == other.anchor && this.head == other.head && + (!includeAssoc || !this.empty || this.assoc == other.assoc); + } + /** + Return a JSON-serializable object representing the range. + */ + toJSON() { return { anchor: this.anchor, head: this.head }; } + /** + Convert a JSON representation of a range to a `SelectionRange` + instance. + */ + static fromJSON(json) { + if (!json || typeof json.anchor != "number" || typeof json.head != "number") + throw new RangeError("Invalid JSON representation for SelectionRange"); + return EditorSelection.range(json.anchor, json.head); + } + /** + @internal + */ + static create(from, to, flags) { + return new SelectionRange(from, to, flags); + } +} +/** +An editor selection holds one or more selection ranges. +*/ +class EditorSelection { + constructor( + /** + The ranges in the selection, sorted by position. Ranges cannot + overlap (but they may touch, if they aren't empty). + */ + ranges, + /** + The index of the _main_ range in the selection (which is + usually the range that was added last). + */ + mainIndex) { + this.ranges = ranges; + this.mainIndex = mainIndex; + } + /** + Map a selection through a change. Used to adjust the selection + position for changes. + */ + map(change, assoc = -1) { + if (change.empty) + return this; + return EditorSelection.create(this.ranges.map(r => r.map(change, assoc)), this.mainIndex); + } + /** + Compare this selection to another selection. By default, ranges + are compared only by position. When `includeAssoc` is true, + cursor ranges must also have the same + [`assoc`](https://codemirror.net/6/docs/ref/#state.SelectionRange.assoc) value. + */ + eq(other, includeAssoc = false) { + if (this.ranges.length != other.ranges.length || + this.mainIndex != other.mainIndex) + return false; + for (let i = 0; i < this.ranges.length; i++) + if (!this.ranges[i].eq(other.ranges[i], includeAssoc)) + return false; + return true; + } + /** + Get the primary selection range. Usually, you should make sure + your code applies to _all_ ranges, by using methods like + [`changeByRange`](https://codemirror.net/6/docs/ref/#state.EditorState.changeByRange). + */ + get main() { return this.ranges[this.mainIndex]; } + /** + Make sure the selection only has one range. Returns a selection + holding only the main range from this selection. + */ + asSingle() { + return this.ranges.length == 1 ? this : new EditorSelection([this.main], 0); + } + /** + Extend this selection with an extra range. + */ + addRange(range, main = true) { + return EditorSelection.create([range].concat(this.ranges), main ? 0 : this.mainIndex + 1); + } + /** + Replace a given range with another range, and then normalize the + selection to merge and sort ranges if necessary. + */ + replaceRange(range, which = this.mainIndex) { + let ranges = this.ranges.slice(); + ranges[which] = range; + return EditorSelection.create(ranges, this.mainIndex); + } + /** + Convert this selection to an object that can be serialized to + JSON. + */ + toJSON() { + return { ranges: this.ranges.map(r => r.toJSON()), main: this.mainIndex }; + } + /** + Create a selection from a JSON representation. + */ + static fromJSON(json) { + if (!json || !Array.isArray(json.ranges) || typeof json.main != "number" || json.main >= json.ranges.length) + throw new RangeError("Invalid JSON representation for EditorSelection"); + return new EditorSelection(json.ranges.map((r) => SelectionRange.fromJSON(r)), json.main); + } + /** + Create a selection holding a single range. + */ + static single(anchor, head = anchor) { + return new EditorSelection([EditorSelection.range(anchor, head)], 0); + } + /** + Sort and merge the given set of ranges, creating a valid + selection. + */ + static create(ranges, mainIndex = 0) { + if (ranges.length == 0) + throw new RangeError("A selection needs at least one range"); + for (let pos = 0, i = 0; i < ranges.length; i++) { + let range = ranges[i]; + if (range.empty ? range.from <= pos : range.from < pos) + return EditorSelection.normalized(ranges.slice(), mainIndex); + pos = range.to; + } + return new EditorSelection(ranges, mainIndex); + } + /** + Create a cursor selection range at the given position. You can + safely ignore the optional arguments in most situations. + */ + static cursor(pos, assoc = 0, bidiLevel, goalColumn) { + return SelectionRange.create(pos, pos, (assoc == 0 ? 0 : assoc < 0 ? 8 /* RangeFlag.AssocBefore */ : 16 /* RangeFlag.AssocAfter */) | + (bidiLevel == null ? 7 : Math.min(6, bidiLevel)) | + ((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 16777215 /* RangeFlag.NoGoalColumn */) << 6 /* RangeFlag.GoalColumnOffset */)); + } + /** + Create a selection range. + */ + static range(anchor, head, goalColumn, bidiLevel) { + let flags = ((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 16777215 /* RangeFlag.NoGoalColumn */) << 6 /* RangeFlag.GoalColumnOffset */) | + (bidiLevel == null ? 7 : Math.min(6, bidiLevel)); + return head < anchor ? SelectionRange.create(head, anchor, 32 /* RangeFlag.Inverted */ | 16 /* RangeFlag.AssocAfter */ | flags) + : SelectionRange.create(anchor, head, (head > anchor ? 8 /* RangeFlag.AssocBefore */ : 0) | flags); + } + /** + @internal + */ + static normalized(ranges, mainIndex = 0) { + let main = ranges[mainIndex]; + ranges.sort((a, b) => a.from - b.from); + mainIndex = ranges.indexOf(main); + for (let i = 1; i < ranges.length; i++) { + let range = ranges[i], prev = ranges[i - 1]; + if (range.empty ? range.from <= prev.to : range.from < prev.to) { + let from = prev.from, to = Math.max(range.to, prev.to); + if (i <= mainIndex) + mainIndex--; + ranges.splice(--i, 2, range.anchor > range.head ? EditorSelection.range(to, from) : EditorSelection.range(from, to)); + } + } + return new EditorSelection(ranges, mainIndex); + } +} +function checkSelection(selection, docLength) { + for (let range of selection.ranges) + if (range.to > docLength) + throw new RangeError("Selection points outside of document"); +} + +let nextID = 0; +/** +A facet is a labeled value that is associated with an editor +state. It takes inputs from any number of extensions, and combines +those into a single output value. + +Examples of uses of facets are the [tab +size](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize), [editor +attributes](https://codemirror.net/6/docs/ref/#view.EditorView^editorAttributes), and [update +listeners](https://codemirror.net/6/docs/ref/#view.EditorView^updateListener). + +Note that `Facet` instances can be used anywhere where +[`FacetReader`](https://codemirror.net/6/docs/ref/#state.FacetReader) is expected. +*/ +class Facet { + constructor( + /** + @internal + */ + combine, + /** + @internal + */ + compareInput, + /** + @internal + */ + compare, isStatic, enables) { + this.combine = combine; + this.compareInput = compareInput; + this.compare = compare; + this.isStatic = isStatic; + /** + @internal + */ + this.id = nextID++; + this.default = combine([]); + this.extensions = typeof enables == "function" ? enables(this) : enables; + } + /** + Returns a facet reader for this facet, which can be used to + [read](https://codemirror.net/6/docs/ref/#state.EditorState.facet) it but not to define values for it. + */ + get reader() { return this; } + /** + Define a new facet. + */ + static define(config = {}) { + return new Facet(config.combine || ((a) => a), config.compareInput || ((a, b) => a === b), config.compare || (!config.combine ? sameArray : (a, b) => a === b), !!config.static, config.enables); + } + /** + Returns an extension that adds the given value to this facet. + */ + of(value) { + return new FacetProvider([], this, 0 /* Provider.Static */, value); + } + /** + Create an extension that computes a value for the facet from a + state. You must take care to declare the parts of the state that + this value depends on, since your function is only called again + for a new state when one of those parts changed. + + In cases where your value depends only on a single field, you'll + want to use the [`from`](https://codemirror.net/6/docs/ref/#state.Facet.from) method instead. + */ + compute(deps, get) { + if (this.isStatic) + throw new Error("Can't compute a static facet"); + return new FacetProvider(deps, this, 1 /* Provider.Single */, get); + } + /** + Create an extension that computes zero or more values for this + facet from a state. + */ + computeN(deps, get) { + if (this.isStatic) + throw new Error("Can't compute a static facet"); + return new FacetProvider(deps, this, 2 /* Provider.Multi */, get); + } + from(field, get) { + if (!get) + get = x => x; + return this.compute([field], state => get(state.field(field))); + } +} +function sameArray(a, b) { + return a == b || a.length == b.length && a.every((e, i) => e === b[i]); +} +class FacetProvider { + constructor(dependencies, facet, type, value) { + this.dependencies = dependencies; + this.facet = facet; + this.type = type; + this.value = value; + this.id = nextID++; + } + dynamicSlot(addresses) { + var _a; + let getter = this.value; + let compare = this.facet.compareInput; + let id = this.id, idx = addresses[id] >> 1, multi = this.type == 2 /* Provider.Multi */; + let depDoc = false, depSel = false, depAddrs = []; + for (let dep of this.dependencies) { + if (dep == "doc") + depDoc = true; + else if (dep == "selection") + depSel = true; + else if ((((_a = addresses[dep.id]) !== null && _a !== void 0 ? _a : 1) & 1) == 0) + depAddrs.push(addresses[dep.id]); + } + return { + create(state) { + state.values[idx] = getter(state); + return 1 /* SlotStatus.Changed */; + }, + update(state, tr) { + if ((depDoc && tr.docChanged) || (depSel && (tr.docChanged || tr.selection)) || ensureAll(state, depAddrs)) { + let newVal = getter(state); + if (multi ? !compareArray(newVal, state.values[idx], compare) : !compare(newVal, state.values[idx])) { + state.values[idx] = newVal; + return 1 /* SlotStatus.Changed */; + } + } + return 0; + }, + reconfigure: (state, oldState) => { + let newVal, oldAddr = oldState.config.address[id]; + if (oldAddr != null) { + let oldVal = getAddr(oldState, oldAddr); + if (this.dependencies.every(dep => { + return dep instanceof Facet ? oldState.facet(dep) === state.facet(dep) : + dep instanceof StateField ? oldState.field(dep, false) == state.field(dep, false) : true; + }) || (multi ? compareArray(newVal = getter(state), oldVal, compare) : compare(newVal = getter(state), oldVal))) { + state.values[idx] = oldVal; + return 0; + } + } + else { + newVal = getter(state); + } + state.values[idx] = newVal; + return 1 /* SlotStatus.Changed */; + } + }; + } +} +function compareArray(a, b, compare) { + if (a.length != b.length) + return false; + for (let i = 0; i < a.length; i++) + if (!compare(a[i], b[i])) + return false; + return true; +} +function ensureAll(state, addrs) { + let changed = false; + for (let addr of addrs) + if (ensureAddr(state, addr) & 1 /* SlotStatus.Changed */) + changed = true; + return changed; +} +function dynamicFacetSlot(addresses, facet, providers) { + let providerAddrs = providers.map(p => addresses[p.id]); + let providerTypes = providers.map(p => p.type); + let dynamic = providerAddrs.filter(p => !(p & 1)); + let idx = addresses[facet.id] >> 1; + function get(state) { + let values = []; + for (let i = 0; i < providerAddrs.length; i++) { + let value = getAddr(state, providerAddrs[i]); + if (providerTypes[i] == 2 /* Provider.Multi */) + for (let val of value) + values.push(val); + else + values.push(value); + } + return facet.combine(values); + } + return { + create(state) { + for (let addr of providerAddrs) + ensureAddr(state, addr); + state.values[idx] = get(state); + return 1 /* SlotStatus.Changed */; + }, + update(state, tr) { + if (!ensureAll(state, dynamic)) + return 0; + let value = get(state); + if (facet.compare(value, state.values[idx])) + return 0; + state.values[idx] = value; + return 1 /* SlotStatus.Changed */; + }, + reconfigure(state, oldState) { + let depChanged = ensureAll(state, providerAddrs); + let oldProviders = oldState.config.facets[facet.id], oldValue = oldState.facet(facet); + if (oldProviders && !depChanged && sameArray(providers, oldProviders)) { + state.values[idx] = oldValue; + return 0; + } + let value = get(state); + if (facet.compare(value, oldValue)) { + state.values[idx] = oldValue; + return 0; + } + state.values[idx] = value; + return 1 /* SlotStatus.Changed */; + } + }; +} +const initField = Facet.define({ static: true }); +/** +Fields can store additional information in an editor state, and +keep it in sync with the rest of the state. +*/ +class StateField { + constructor( + /** + @internal + */ + id, createF, updateF, compareF, + /** + @internal + */ + spec) { + this.id = id; + this.createF = createF; + this.updateF = updateF; + this.compareF = compareF; + this.spec = spec; + /** + @internal + */ + this.provides = undefined; + } + /** + Define a state field. + */ + static define(config) { + let field = new StateField(nextID++, config.create, config.update, config.compare || ((a, b) => a === b), config); + if (config.provide) + field.provides = config.provide(field); + return field; + } + create(state) { + let init = state.facet(initField).find(i => i.field == this); + return ((init === null || init === void 0 ? void 0 : init.create) || this.createF)(state); + } + /** + @internal + */ + slot(addresses) { + let idx = addresses[this.id] >> 1; + return { + create: (state) => { + state.values[idx] = this.create(state); + return 1 /* SlotStatus.Changed */; + }, + update: (state, tr) => { + let oldVal = state.values[idx]; + let value = this.updateF(oldVal, tr); + if (this.compareF(oldVal, value)) + return 0; + state.values[idx] = value; + return 1 /* SlotStatus.Changed */; + }, + reconfigure: (state, oldState) => { + if (oldState.config.address[this.id] != null) { + state.values[idx] = oldState.field(this); + return 0; + } + state.values[idx] = this.create(state); + return 1 /* SlotStatus.Changed */; + } + }; + } + /** + Returns an extension that enables this field and overrides the + way it is initialized. Can be useful when you need to provide a + non-default starting value for the field. + */ + init(create) { + return [this, initField.of({ field: this, create })]; + } + /** + State field instances can be used as + [`Extension`](https://codemirror.net/6/docs/ref/#state.Extension) values to enable the field in a + given state. + */ + get extension() { return this; } +} +const Prec_ = { lowest: 4, low: 3, default: 2, high: 1, highest: 0 }; +function prec(value) { + return (ext) => new PrecExtension(ext, value); +} +/** +By default extensions are registered in the order they are found +in the flattened form of nested array that was provided. +Individual extension values can be assigned a precedence to +override this. Extensions that do not have a precedence set get +the precedence of the nearest parent with a precedence, or +[`default`](https://codemirror.net/6/docs/ref/#state.Prec.default) if there is no such parent. The +final ordering of extensions is determined by first sorting by +precedence and then by order within each precedence. +*/ +const Prec = { + /** + The highest precedence level, for extensions that should end up + near the start of the precedence ordering. + */ + highest: prec(Prec_.highest), + /** + A higher-than-default precedence, for extensions that should + come before those with default precedence. + */ + high: prec(Prec_.high), + /** + The default precedence, which is also used for extensions + without an explicit precedence. + */ + default: prec(Prec_.default), + /** + A lower-than-default precedence. + */ + low: prec(Prec_.low), + /** + The lowest precedence level. Meant for things that should end up + near the end of the extension order. + */ + lowest: prec(Prec_.lowest) +}; +class PrecExtension { + constructor(inner, prec) { + this.inner = inner; + this.prec = prec; + } +} +/** +Extension compartments can be used to make a configuration +dynamic. By [wrapping](https://codemirror.net/6/docs/ref/#state.Compartment.of) part of your +configuration in a compartment, you can later +[replace](https://codemirror.net/6/docs/ref/#state.Compartment.reconfigure) that part through a +transaction. +*/ +class Compartment { + /** + Create an instance of this compartment to add to your [state + configuration](https://codemirror.net/6/docs/ref/#state.EditorStateConfig.extensions). + */ + of(ext) { return new CompartmentInstance(this, ext); } + /** + Create an [effect](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) that + reconfigures this compartment. + */ + reconfigure(content) { + return Compartment.reconfigure.of({ compartment: this, extension: content }); + } + /** + Get the current content of the compartment in the state, or + `undefined` if it isn't present. + */ + get(state) { + return state.config.compartments.get(this); + } +} +class CompartmentInstance { + constructor(compartment, inner) { + this.compartment = compartment; + this.inner = inner; + } +} +class Configuration { + constructor(base, compartments, dynamicSlots, address, staticValues, facets) { + this.base = base; + this.compartments = compartments; + this.dynamicSlots = dynamicSlots; + this.address = address; + this.staticValues = staticValues; + this.facets = facets; + this.statusTemplate = []; + while (this.statusTemplate.length < dynamicSlots.length) + this.statusTemplate.push(0 /* SlotStatus.Unresolved */); + } + staticFacet(facet) { + let addr = this.address[facet.id]; + return addr == null ? facet.default : this.staticValues[addr >> 1]; + } + static resolve(base, compartments, oldState) { + let fields = []; + let facets = Object.create(null); + let newCompartments = new Map(); + for (let ext of flatten(base, compartments, newCompartments)) { + if (ext instanceof StateField) + fields.push(ext); + else + (facets[ext.facet.id] || (facets[ext.facet.id] = [])).push(ext); + } + let address = Object.create(null); + let staticValues = []; + let dynamicSlots = []; + for (let field of fields) { + address[field.id] = dynamicSlots.length << 1; + dynamicSlots.push(a => field.slot(a)); + } + let oldFacets = oldState === null || oldState === void 0 ? void 0 : oldState.config.facets; + for (let id in facets) { + let providers = facets[id], facet = providers[0].facet; + let oldProviders = oldFacets && oldFacets[id] || []; + if (providers.every(p => p.type == 0 /* Provider.Static */)) { + address[facet.id] = (staticValues.length << 1) | 1; + if (sameArray(oldProviders, providers)) { + staticValues.push(oldState.facet(facet)); + } + else { + let value = facet.combine(providers.map(p => p.value)); + staticValues.push(oldState && facet.compare(value, oldState.facet(facet)) ? oldState.facet(facet) : value); + } + } + else { + for (let p of providers) { + if (p.type == 0 /* Provider.Static */) { + address[p.id] = (staticValues.length << 1) | 1; + staticValues.push(p.value); + } + else { + address[p.id] = dynamicSlots.length << 1; + dynamicSlots.push(a => p.dynamicSlot(a)); + } + } + address[facet.id] = dynamicSlots.length << 1; + dynamicSlots.push(a => dynamicFacetSlot(a, facet, providers)); + } + } + let dynamic = dynamicSlots.map(f => f(address)); + return new Configuration(base, newCompartments, dynamic, address, staticValues, facets); + } +} +function flatten(extension, compartments, newCompartments) { + let result = [[], [], [], [], []]; + let seen = new Map(); + function inner(ext, prec) { + let known = seen.get(ext); + if (known != null) { + if (known <= prec) + return; + let found = result[known].indexOf(ext); + if (found > -1) + result[known].splice(found, 1); + if (ext instanceof CompartmentInstance) + newCompartments.delete(ext.compartment); + } + seen.set(ext, prec); + if (Array.isArray(ext)) { + for (let e of ext) + inner(e, prec); + } + else if (ext instanceof CompartmentInstance) { + if (newCompartments.has(ext.compartment)) + throw new RangeError(`Duplicate use of compartment in extensions`); + let content = compartments.get(ext.compartment) || ext.inner; + newCompartments.set(ext.compartment, content); + inner(content, prec); + } + else if (ext instanceof PrecExtension) { + inner(ext.inner, ext.prec); + } + else if (ext instanceof StateField) { + result[prec].push(ext); + if (ext.provides) + inner(ext.provides, prec); + } + else if (ext instanceof FacetProvider) { + result[prec].push(ext); + if (ext.facet.extensions) + inner(ext.facet.extensions, Prec_.default); + } + else { + let content = ext.extension; + if (!content) + throw new Error(`Unrecognized extension value in extension set (${ext}). This sometimes happens because multiple instances of @codemirror/state are loaded, breaking instanceof checks.`); + inner(content, prec); + } + } + inner(extension, Prec_.default); + return result.reduce((a, b) => a.concat(b)); +} +function ensureAddr(state, addr) { + if (addr & 1) + return 2 /* SlotStatus.Computed */; + let idx = addr >> 1; + let status = state.status[idx]; + if (status == 4 /* SlotStatus.Computing */) + throw new Error("Cyclic dependency between fields and/or facets"); + if (status & 2 /* SlotStatus.Computed */) + return status; + state.status[idx] = 4 /* SlotStatus.Computing */; + let changed = state.computeSlot(state, state.config.dynamicSlots[idx]); + return state.status[idx] = 2 /* SlotStatus.Computed */ | changed; +} +function getAddr(state, addr) { + return addr & 1 ? state.config.staticValues[addr >> 1] : state.values[addr >> 1]; +} + +const languageData = Facet.define(); +const allowMultipleSelections = Facet.define({ + combine: values => values.some(v => v), + static: true +}); +const lineSeparator = Facet.define({ + combine: values => values.length ? values[0] : undefined, + static: true +}); +const changeFilter = Facet.define(); +const transactionFilter = Facet.define(); +const transactionExtender = Facet.define(); +const readOnly = Facet.define({ + combine: values => values.length ? values[0] : false +}); + +/** +Annotations are tagged values that are used to add metadata to +transactions in an extensible way. They should be used to model +things that effect the entire transaction (such as its [time +stamp](https://codemirror.net/6/docs/ref/#state.Transaction^time) or information about its +[origin](https://codemirror.net/6/docs/ref/#state.Transaction^userEvent)). For effects that happen +_alongside_ the other changes made by the transaction, [state +effects](https://codemirror.net/6/docs/ref/#state.StateEffect) are more appropriate. +*/ +class Annotation { + /** + @internal + */ + constructor( + /** + The annotation type. + */ + type, + /** + The value of this annotation. + */ + value) { + this.type = type; + this.value = value; + } + /** + Define a new type of annotation. + */ + static define() { return new AnnotationType(); } +} +/** +Marker that identifies a type of [annotation](https://codemirror.net/6/docs/ref/#state.Annotation). +*/ +class AnnotationType { + /** + Create an instance of this annotation. + */ + of(value) { return new Annotation(this, value); } +} +/** +Representation of a type of state effect. Defined with +[`StateEffect.define`](https://codemirror.net/6/docs/ref/#state.StateEffect^define). +*/ +class StateEffectType { + /** + @internal + */ + constructor( + // The `any` types in these function types are there to work + // around TypeScript issue #37631, where the type guard on + // `StateEffect.is` mysteriously stops working when these properly + // have type `Value`. + /** + @internal + */ + map) { + this.map = map; + } + /** + Create a [state effect](https://codemirror.net/6/docs/ref/#state.StateEffect) instance of this + type. + */ + of(value) { return new StateEffect(this, value); } +} +/** +State effects can be used to represent additional effects +associated with a [transaction](https://codemirror.net/6/docs/ref/#state.Transaction.effects). They +are often useful to model changes to custom [state +fields](https://codemirror.net/6/docs/ref/#state.StateField), when those changes aren't implicit in +document or selection changes. +*/ +class StateEffect { + /** + @internal + */ + constructor( + /** + @internal + */ + type, + /** + The value of this effect. + */ + value) { + this.type = type; + this.value = value; + } + /** + Map this effect through a position mapping. Will return + `undefined` when that ends up deleting the effect. + */ + map(mapping) { + let mapped = this.type.map(this.value, mapping); + return mapped === undefined ? undefined : mapped == this.value ? this : new StateEffect(this.type, mapped); + } + /** + Tells you whether this effect object is of a given + [type](https://codemirror.net/6/docs/ref/#state.StateEffectType). + */ + is(type) { return this.type == type; } + /** + Define a new effect type. The type parameter indicates the type + of values that his effect holds. It should be a type that + doesn't include `undefined`, since that is used in + [mapping](https://codemirror.net/6/docs/ref/#state.StateEffect.map) to indicate that an effect is + removed. + */ + static define(spec = {}) { + return new StateEffectType(spec.map || (v => v)); + } + /** + Map an array of effects through a change set. + */ + static mapEffects(effects, mapping) { + if (!effects.length) + return effects; + let result = []; + for (let effect of effects) { + let mapped = effect.map(mapping); + if (mapped) + result.push(mapped); + } + return result; + } +} +/** +This effect can be used to reconfigure the root extensions of +the editor. Doing this will discard any extensions +[appended](https://codemirror.net/6/docs/ref/#state.StateEffect^appendConfig), but does not reset +the content of [reconfigured](https://codemirror.net/6/docs/ref/#state.Compartment.reconfigure) +compartments. +*/ +StateEffect.reconfigure = StateEffect.define(); +/** +Append extensions to the top-level configuration of the editor. +*/ +StateEffect.appendConfig = StateEffect.define(); +/** +Changes to the editor state are grouped into transactions. +Typically, a user action creates a single transaction, which may +contain any number of document changes, may change the selection, +or have other effects. Create a transaction by calling +[`EditorState.update`](https://codemirror.net/6/docs/ref/#state.EditorState.update), or immediately +dispatch one by calling +[`EditorView.dispatch`](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch). +*/ +class Transaction { + constructor( + /** + The state from which the transaction starts. + */ + startState, + /** + The document changes made by this transaction. + */ + changes, + /** + The selection set by this transaction, or undefined if it + doesn't explicitly set a selection. + */ + selection, + /** + The effects added to the transaction. + */ + effects, + /** + @internal + */ + annotations, + /** + Whether the selection should be scrolled into view after this + transaction is dispatched. + */ + scrollIntoView) { + this.startState = startState; + this.changes = changes; + this.selection = selection; + this.effects = effects; + this.annotations = annotations; + this.scrollIntoView = scrollIntoView; + /** + @internal + */ + this._doc = null; + /** + @internal + */ + this._state = null; + if (selection) + checkSelection(selection, changes.newLength); + if (!annotations.some((a) => a.type == Transaction.time)) + this.annotations = annotations.concat(Transaction.time.of(Date.now())); + } + /** + @internal + */ + static create(startState, changes, selection, effects, annotations, scrollIntoView) { + return new Transaction(startState, changes, selection, effects, annotations, scrollIntoView); + } + /** + The new document produced by the transaction. Contrary to + [`.state`](https://codemirror.net/6/docs/ref/#state.Transaction.state)`.doc`, accessing this won't + force the entire new state to be computed right away, so it is + recommended that [transaction + filters](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter) use this getter + when they need to look at the new document. + */ + get newDoc() { + return this._doc || (this._doc = this.changes.apply(this.startState.doc)); + } + /** + The new selection produced by the transaction. If + [`this.selection`](https://codemirror.net/6/docs/ref/#state.Transaction.selection) is undefined, + this will [map](https://codemirror.net/6/docs/ref/#state.EditorSelection.map) the start state's + current selection through the changes made by the transaction. + */ + get newSelection() { + return this.selection || this.startState.selection.map(this.changes); + } + /** + The new state created by the transaction. Computed on demand + (but retained for subsequent access), so it is recommended not to + access it in [transaction + filters](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter) when possible. + */ + get state() { + if (!this._state) + this.startState.applyTransaction(this); + return this._state; + } + /** + Get the value of the given annotation type, if any. + */ + annotation(type) { + for (let ann of this.annotations) + if (ann.type == type) + return ann.value; + return undefined; + } + /** + Indicates whether the transaction changed the document. + */ + get docChanged() { return !this.changes.empty; } + /** + Indicates whether this transaction reconfigures the state + (through a [configuration compartment](https://codemirror.net/6/docs/ref/#state.Compartment) or + with a top-level configuration + [effect](https://codemirror.net/6/docs/ref/#state.StateEffect^reconfigure). + */ + get reconfigured() { return this.startState.config != this.state.config; } + /** + Returns true if the transaction has a [user + event](https://codemirror.net/6/docs/ref/#state.Transaction^userEvent) annotation that is equal to + or more specific than `event`. For example, if the transaction + has `"select.pointer"` as user event, `"select"` and + `"select.pointer"` will match it. + */ + isUserEvent(event) { + let e = this.annotation(Transaction.userEvent); + return !!(e && (e == event || e.length > event.length && e.slice(0, event.length) == event && e[event.length] == ".")); + } +} +/** +Annotation used to store transaction timestamps. Automatically +added to every transaction, holding `Date.now()`. +*/ +Transaction.time = Annotation.define(); +/** +Annotation used to associate a transaction with a user interface +event. Holds a string identifying the event, using a +dot-separated format to support attaching more specific +information. The events used by the core libraries are: + + - `"input"` when content is entered + - `"input.type"` for typed input + - `"input.type.compose"` for composition + - `"input.paste"` for pasted input + - `"input.drop"` when adding content with drag-and-drop + - `"input.complete"` when autocompleting + - `"delete"` when the user deletes content + - `"delete.selection"` when deleting the selection + - `"delete.forward"` when deleting forward from the selection + - `"delete.backward"` when deleting backward from the selection + - `"delete.cut"` when cutting to the clipboard + - `"move"` when content is moved + - `"move.drop"` when content is moved within the editor through drag-and-drop + - `"select"` when explicitly changing the selection + - `"select.pointer"` when selecting with a mouse or other pointing device + - `"undo"` and `"redo"` for history actions + +Use [`isUserEvent`](https://codemirror.net/6/docs/ref/#state.Transaction.isUserEvent) to check +whether the annotation matches a given event. +*/ +Transaction.userEvent = Annotation.define(); +/** +Annotation indicating whether a transaction should be added to +the undo history or not. +*/ +Transaction.addToHistory = Annotation.define(); +/** +Annotation indicating (when present and true) that a transaction +represents a change made by some other actor, not the user. This +is used, for example, to tag other people's changes in +collaborative editing. +*/ +Transaction.remote = Annotation.define(); +function joinRanges(a, b) { + let result = []; + for (let iA = 0, iB = 0;;) { + let from, to; + if (iA < a.length && (iB == b.length || b[iB] >= a[iA])) { + from = a[iA++]; + to = a[iA++]; + } + else if (iB < b.length) { + from = b[iB++]; + to = b[iB++]; + } + else + return result; + if (!result.length || result[result.length - 1] < from) + result.push(from, to); + else if (result[result.length - 1] < to) + result[result.length - 1] = to; + } +} +function mergeTransaction(a, b, sequential) { + var _a; + let mapForA, mapForB, changes; + if (sequential) { + mapForA = b.changes; + mapForB = ChangeSet.empty(b.changes.length); + changes = a.changes.compose(b.changes); + } + else { + mapForA = b.changes.map(a.changes); + mapForB = a.changes.mapDesc(b.changes, true); + changes = a.changes.compose(mapForA); + } + return { + changes, + selection: b.selection ? b.selection.map(mapForB) : (_a = a.selection) === null || _a === void 0 ? void 0 : _a.map(mapForA), + effects: StateEffect.mapEffects(a.effects, mapForA).concat(StateEffect.mapEffects(b.effects, mapForB)), + annotations: a.annotations.length ? a.annotations.concat(b.annotations) : b.annotations, + scrollIntoView: a.scrollIntoView || b.scrollIntoView + }; +} +function resolveTransactionInner(state, spec, docSize) { + let sel = spec.selection, annotations = asArray(spec.annotations); + if (spec.userEvent) + annotations = annotations.concat(Transaction.userEvent.of(spec.userEvent)); + return { + changes: spec.changes instanceof ChangeSet ? spec.changes + : ChangeSet.of(spec.changes || [], docSize, state.facet(lineSeparator)), + selection: sel && (sel instanceof EditorSelection ? sel : EditorSelection.single(sel.anchor, sel.head)), + effects: asArray(spec.effects), + annotations, + scrollIntoView: !!spec.scrollIntoView + }; +} +function resolveTransaction(state, specs, filter) { + let s = resolveTransactionInner(state, specs.length ? specs[0] : {}, state.doc.length); + if (specs.length && specs[0].filter === false) + filter = false; + for (let i = 1; i < specs.length; i++) { + if (specs[i].filter === false) + filter = false; + let seq = !!specs[i].sequential; + s = mergeTransaction(s, resolveTransactionInner(state, specs[i], seq ? s.changes.newLength : state.doc.length), seq); + } + let tr = Transaction.create(state, s.changes, s.selection, s.effects, s.annotations, s.scrollIntoView); + return extendTransaction(filter ? filterTransaction(tr) : tr); +} +// Finish a transaction by applying filters if necessary. +function filterTransaction(tr) { + let state = tr.startState; + // Change filters + let result = true; + for (let filter of state.facet(changeFilter)) { + let value = filter(tr); + if (value === false) { + result = false; + break; + } + if (Array.isArray(value)) + result = result === true ? value : joinRanges(result, value); + } + if (result !== true) { + let changes, back; + if (result === false) { + back = tr.changes.invertedDesc; + changes = ChangeSet.empty(state.doc.length); + } + else { + let filtered = tr.changes.filter(result); + changes = filtered.changes; + back = filtered.filtered.mapDesc(filtered.changes).invertedDesc; + } + tr = Transaction.create(state, changes, tr.selection && tr.selection.map(back), StateEffect.mapEffects(tr.effects, back), tr.annotations, tr.scrollIntoView); + } + // Transaction filters + let filters = state.facet(transactionFilter); + for (let i = filters.length - 1; i >= 0; i--) { + let filtered = filters[i](tr); + if (filtered instanceof Transaction) + tr = filtered; + else if (Array.isArray(filtered) && filtered.length == 1 && filtered[0] instanceof Transaction) + tr = filtered[0]; + else + tr = resolveTransaction(state, asArray(filtered), false); + } + return tr; +} +function extendTransaction(tr) { + let state = tr.startState, extenders = state.facet(transactionExtender), spec = tr; + for (let i = extenders.length - 1; i >= 0; i--) { + let extension = extenders[i](tr); + if (extension && Object.keys(extension).length) + spec = mergeTransaction(spec, resolveTransactionInner(state, extension, tr.changes.newLength), true); + } + return spec == tr ? tr : Transaction.create(state, tr.changes, tr.selection, spec.effects, spec.annotations, spec.scrollIntoView); +} +const none = []; +function asArray(value) { + return value == null ? none : Array.isArray(value) ? value : [value]; +} + +/** +The categories produced by a [character +categorizer](https://codemirror.net/6/docs/ref/#state.EditorState.charCategorizer). These are used +do things like selecting by word. +*/ +exports.CharCategory = void 0; +(function (CharCategory) { + /** + Word characters. + */ + CharCategory[CharCategory["Word"] = 0] = "Word"; + /** + Whitespace. + */ + CharCategory[CharCategory["Space"] = 1] = "Space"; + /** + Anything else. + */ + CharCategory[CharCategory["Other"] = 2] = "Other"; +})(exports.CharCategory || (exports.CharCategory = {})); +const nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; +let wordChar; +try { + wordChar = new RegExp("[\\p{Alphabetic}\\p{Number}_]", "u"); +} +catch (_) { } +function hasWordChar(str) { + if (wordChar) + return wordChar.test(str); + for (let i = 0; i < str.length; i++) { + let ch = str[i]; + if (/\w/.test(ch) || ch > "\x80" && (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))) + return true; + } + return false; +} +function makeCategorizer(wordChars) { + return (char) => { + if (!/\S/.test(char)) + return exports.CharCategory.Space; + if (hasWordChar(char)) + return exports.CharCategory.Word; + for (let i = 0; i < wordChars.length; i++) + if (char.indexOf(wordChars[i]) > -1) + return exports.CharCategory.Word; + return exports.CharCategory.Other; + }; +} + +/** +The editor state class is a persistent (immutable) data structure. +To update a state, you [create](https://codemirror.net/6/docs/ref/#state.EditorState.update) a +[transaction](https://codemirror.net/6/docs/ref/#state.Transaction), which produces a _new_ state +instance, without modifying the original object. + +As such, _never_ mutate properties of a state directly. That'll +just break things. +*/ +class EditorState { + constructor( + /** + @internal + */ + config, + /** + The current document. + */ + doc, + /** + The current selection. + */ + selection, + /** + @internal + */ + values, computeSlot, tr) { + this.config = config; + this.doc = doc; + this.selection = selection; + this.values = values; + this.status = config.statusTemplate.slice(); + this.computeSlot = computeSlot; + // Fill in the computed state immediately, so that further queries + // for it made during the update return this state + if (tr) + tr._state = this; + for (let i = 0; i < this.config.dynamicSlots.length; i++) + ensureAddr(this, i << 1); + this.computeSlot = null; + } + field(field, require = true) { + let addr = this.config.address[field.id]; + if (addr == null) { + if (require) + throw new RangeError("Field is not present in this state"); + return undefined; + } + ensureAddr(this, addr); + return getAddr(this, addr); + } + /** + Create a [transaction](https://codemirror.net/6/docs/ref/#state.Transaction) that updates this + state. Any number of [transaction specs](https://codemirror.net/6/docs/ref/#state.TransactionSpec) + can be passed. Unless + [`sequential`](https://codemirror.net/6/docs/ref/#state.TransactionSpec.sequential) is set, the + [changes](https://codemirror.net/6/docs/ref/#state.TransactionSpec.changes) (if any) of each spec + are assumed to start in the _current_ document (not the document + produced by previous specs), and its + [selection](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection) and + [effects](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) are assumed to refer + to the document created by its _own_ changes. The resulting + transaction contains the combined effect of all the different + specs. For [selection](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection), later + specs take precedence over earlier ones. + */ + update(...specs) { + return resolveTransaction(this, specs, true); + } + /** + @internal + */ + applyTransaction(tr) { + let conf = this.config, { base, compartments } = conf; + for (let effect of tr.effects) { + if (effect.is(Compartment.reconfigure)) { + if (conf) { + compartments = new Map; + conf.compartments.forEach((val, key) => compartments.set(key, val)); + conf = null; + } + compartments.set(effect.value.compartment, effect.value.extension); + } + else if (effect.is(StateEffect.reconfigure)) { + conf = null; + base = effect.value; + } + else if (effect.is(StateEffect.appendConfig)) { + conf = null; + base = asArray(base).concat(effect.value); + } + } + let startValues; + if (!conf) { + conf = Configuration.resolve(base, compartments, this); + let intermediateState = new EditorState(conf, this.doc, this.selection, conf.dynamicSlots.map(() => null), (state, slot) => slot.reconfigure(state, this), null); + startValues = intermediateState.values; + } + else { + startValues = tr.startState.values.slice(); + } + let selection = tr.startState.facet(allowMultipleSelections) ? tr.newSelection : tr.newSelection.asSingle(); + new EditorState(conf, tr.newDoc, selection, startValues, (state, slot) => slot.update(state, tr), tr); + } + /** + Create a [transaction spec](https://codemirror.net/6/docs/ref/#state.TransactionSpec) that + replaces every selection range with the given content. + */ + replaceSelection(text) { + if (typeof text == "string") + text = this.toText(text); + return this.changeByRange(range => ({ changes: { from: range.from, to: range.to, insert: text }, + range: EditorSelection.cursor(range.from + text.length) })); + } + /** + Create a set of changes and a new selection by running the given + function for each range in the active selection. The function + can return an optional set of changes (in the coordinate space + of the start document), plus an updated range (in the coordinate + space of the document produced by the call's own changes). This + method will merge all the changes and ranges into a single + changeset and selection, and return it as a [transaction + spec](https://codemirror.net/6/docs/ref/#state.TransactionSpec), which can be passed to + [`update`](https://codemirror.net/6/docs/ref/#state.EditorState.update). + */ + changeByRange(f) { + let sel = this.selection; + let result1 = f(sel.ranges[0]); + let changes = this.changes(result1.changes), ranges = [result1.range]; + let effects = asArray(result1.effects); + for (let i = 1; i < sel.ranges.length; i++) { + let result = f(sel.ranges[i]); + let newChanges = this.changes(result.changes), newMapped = newChanges.map(changes); + for (let j = 0; j < i; j++) + ranges[j] = ranges[j].map(newMapped); + let mapBy = changes.mapDesc(newChanges, true); + ranges.push(result.range.map(mapBy)); + changes = changes.compose(newMapped); + effects = StateEffect.mapEffects(effects, newMapped).concat(StateEffect.mapEffects(asArray(result.effects), mapBy)); + } + return { + changes, + selection: EditorSelection.create(ranges, sel.mainIndex), + effects + }; + } + /** + Create a [change set](https://codemirror.net/6/docs/ref/#state.ChangeSet) from the given change + description, taking the state's document length and line + separator into account. + */ + changes(spec = []) { + if (spec instanceof ChangeSet) + return spec; + return ChangeSet.of(spec, this.doc.length, this.facet(EditorState.lineSeparator)); + } + /** + Using the state's [line + separator](https://codemirror.net/6/docs/ref/#state.EditorState^lineSeparator), create a + [`Text`](https://codemirror.net/6/docs/ref/#state.Text) instance from the given string. + */ + toText(string) { + return Text.of(string.split(this.facet(EditorState.lineSeparator) || DefaultSplit)); + } + /** + Return the given range of the document as a string. + */ + sliceDoc(from = 0, to = this.doc.length) { + return this.doc.sliceString(from, to, this.lineBreak); + } + /** + Get the value of a state [facet](https://codemirror.net/6/docs/ref/#state.Facet). + */ + facet(facet) { + let addr = this.config.address[facet.id]; + if (addr == null) + return facet.default; + ensureAddr(this, addr); + return getAddr(this, addr); + } + /** + Convert this state to a JSON-serializable object. When custom + fields should be serialized, you can pass them in as an object + mapping property names (in the resulting object, which should + not use `doc` or `selection`) to fields. + */ + toJSON(fields) { + let result = { + doc: this.sliceDoc(), + selection: this.selection.toJSON() + }; + if (fields) + for (let prop in fields) { + let value = fields[prop]; + if (value instanceof StateField && this.config.address[value.id] != null) + result[prop] = value.spec.toJSON(this.field(fields[prop]), this); + } + return result; + } + /** + Deserialize a state from its JSON representation. When custom + fields should be deserialized, pass the same object you passed + to [`toJSON`](https://codemirror.net/6/docs/ref/#state.EditorState.toJSON) when serializing as + third argument. + */ + static fromJSON(json, config = {}, fields) { + if (!json || typeof json.doc != "string") + throw new RangeError("Invalid JSON representation for EditorState"); + let fieldInit = []; + if (fields) + for (let prop in fields) { + if (Object.prototype.hasOwnProperty.call(json, prop)) { + let field = fields[prop], value = json[prop]; + fieldInit.push(field.init(state => field.spec.fromJSON(value, state))); + } + } + return EditorState.create({ + doc: json.doc, + selection: EditorSelection.fromJSON(json.selection), + extensions: config.extensions ? fieldInit.concat([config.extensions]) : fieldInit + }); + } + /** + Create a new state. You'll usually only need this when + initializing an editor—updated states are created by applying + transactions. + */ + static create(config = {}) { + let configuration = Configuration.resolve(config.extensions || [], new Map); + let doc = config.doc instanceof Text ? config.doc + : Text.of((config.doc || "").split(configuration.staticFacet(EditorState.lineSeparator) || DefaultSplit)); + let selection = !config.selection ? EditorSelection.single(0) + : config.selection instanceof EditorSelection ? config.selection + : EditorSelection.single(config.selection.anchor, config.selection.head); + checkSelection(selection, doc.length); + if (!configuration.staticFacet(allowMultipleSelections)) + selection = selection.asSingle(); + return new EditorState(configuration, doc, selection, configuration.dynamicSlots.map(() => null), (state, slot) => slot.create(state), null); + } + /** + The size (in columns) of a tab in the document, determined by + the [`tabSize`](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize) facet. + */ + get tabSize() { return this.facet(EditorState.tabSize); } + /** + Get the proper [line-break](https://codemirror.net/6/docs/ref/#state.EditorState^lineSeparator) + string for this state. + */ + get lineBreak() { return this.facet(EditorState.lineSeparator) || "\n"; } + /** + Returns true when the editor is + [configured](https://codemirror.net/6/docs/ref/#state.EditorState^readOnly) to be read-only. + */ + get readOnly() { return this.facet(readOnly); } + /** + Look up a translation for the given phrase (via the + [`phrases`](https://codemirror.net/6/docs/ref/#state.EditorState^phrases) facet), or return the + original string if no translation is found. + + If additional arguments are passed, they will be inserted in + place of markers like `$1` (for the first value) and `$2`, etc. + A single `$` is equivalent to `$1`, and `$$` will produce a + literal dollar sign. + */ + phrase(phrase, ...insert) { + for (let map of this.facet(EditorState.phrases)) + if (Object.prototype.hasOwnProperty.call(map, phrase)) { + phrase = map[phrase]; + break; + } + if (insert.length) + phrase = phrase.replace(/\$(\$|\d*)/g, (m, i) => { + if (i == "$") + return "$"; + let n = +(i || 1); + return !n || n > insert.length ? m : insert[n - 1]; + }); + return phrase; + } + /** + Find the values for a given language data field, provided by the + the [`languageData`](https://codemirror.net/6/docs/ref/#state.EditorState^languageData) facet. + + Examples of language data fields are... + + - [`"commentTokens"`](https://codemirror.net/6/docs/ref/#commands.CommentTokens) for specifying + comment syntax. + - [`"autocomplete"`](https://codemirror.net/6/docs/ref/#autocomplete.autocompletion^config.override) + for providing language-specific completion sources. + - [`"wordChars"`](https://codemirror.net/6/docs/ref/#state.EditorState.charCategorizer) for adding + characters that should be considered part of words in this + language. + - [`"closeBrackets"`](https://codemirror.net/6/docs/ref/#autocomplete.CloseBracketConfig) controls + bracket closing behavior. + */ + languageDataAt(name, pos, side = -1) { + let values = []; + for (let provider of this.facet(languageData)) { + for (let result of provider(this, pos, side)) { + if (Object.prototype.hasOwnProperty.call(result, name)) + values.push(result[name]); + } + } + return values; + } + /** + Return a function that can categorize strings (expected to + represent a single [grapheme cluster](https://codemirror.net/6/docs/ref/#state.findClusterBreak)) + into one of: + + - Word (contains an alphanumeric character or a character + explicitly listed in the local language's `"wordChars"` + language data, which should be a string) + - Space (contains only whitespace) + - Other (anything else) + */ + charCategorizer(at) { + return makeCategorizer(this.languageDataAt("wordChars", at).join("")); + } + /** + Find the word at the given position, meaning the range + containing all [word](https://codemirror.net/6/docs/ref/#state.CharCategory.Word) characters + around it. If no word characters are adjacent to the position, + this returns null. + */ + wordAt(pos) { + let { text, from, length } = this.doc.lineAt(pos); + let cat = this.charCategorizer(pos); + let start = pos - from, end = pos - from; + while (start > 0) { + let prev = findClusterBreak(text, start, false); + if (cat(text.slice(prev, start)) != exports.CharCategory.Word) + break; + start = prev; + } + while (end < length) { + let next = findClusterBreak(text, end); + if (cat(text.slice(end, next)) != exports.CharCategory.Word) + break; + end = next; + } + return start == end ? null : EditorSelection.range(start + from, end + from); + } +} +/** +A facet that, when enabled, causes the editor to allow multiple +ranges to be selected. Be careful though, because by default the +editor relies on the native DOM selection, which cannot handle +multiple selections. An extension like +[`drawSelection`](https://codemirror.net/6/docs/ref/#view.drawSelection) can be used to make +secondary selections visible to the user. +*/ +EditorState.allowMultipleSelections = allowMultipleSelections; +/** +Configures the tab size to use in this state. The first +(highest-precedence) value of the facet is used. If no value is +given, this defaults to 4. +*/ +EditorState.tabSize = Facet.define({ + combine: values => values.length ? values[0] : 4 +}); +/** +The line separator to use. By default, any of `"\n"`, `"\r\n"` +and `"\r"` is treated as a separator when splitting lines, and +lines are joined with `"\n"`. + +When you configure a value here, only that precise separator +will be used, allowing you to round-trip documents through the +editor without normalizing line separators. +*/ +EditorState.lineSeparator = lineSeparator; +/** +This facet controls the value of the +[`readOnly`](https://codemirror.net/6/docs/ref/#state.EditorState.readOnly) getter, which is +consulted by commands and extensions that implement editing +functionality to determine whether they should apply. It +defaults to false, but when its highest-precedence value is +`true`, such functionality disables itself. + +Not to be confused with +[`EditorView.editable`](https://codemirror.net/6/docs/ref/#view.EditorView^editable), which +controls whether the editor's DOM is set to be editable (and +thus focusable). +*/ +EditorState.readOnly = readOnly; +/** +Registers translation phrases. The +[`phrase`](https://codemirror.net/6/docs/ref/#state.EditorState.phrase) method will look through +all objects registered with this facet to find translations for +its argument. +*/ +EditorState.phrases = Facet.define({ + compare(a, b) { + let kA = Object.keys(a), kB = Object.keys(b); + return kA.length == kB.length && kA.every(k => a[k] == b[k]); + } +}); +/** +A facet used to register [language +data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) providers. +*/ +EditorState.languageData = languageData; +/** +Facet used to register change filters, which are called for each +transaction (unless explicitly +[disabled](https://codemirror.net/6/docs/ref/#state.TransactionSpec.filter)), and can suppress +part of the transaction's changes. + +Such a function can return `true` to indicate that it doesn't +want to do anything, `false` to completely stop the changes in +the transaction, or a set of ranges in which changes should be +suppressed. Such ranges are represented as an array of numbers, +with each pair of two numbers indicating the start and end of a +range. So for example `[10, 20, 100, 110]` suppresses changes +between 10 and 20, and between 100 and 110. +*/ +EditorState.changeFilter = changeFilter; +/** +Facet used to register a hook that gets a chance to update or +replace transaction specs before they are applied. This will +only be applied for transactions that don't have +[`filter`](https://codemirror.net/6/docs/ref/#state.TransactionSpec.filter) set to `false`. You +can either return a single transaction spec (possibly the input +transaction), or an array of specs (which will be combined in +the same way as the arguments to +[`EditorState.update`](https://codemirror.net/6/docs/ref/#state.EditorState.update)). + +When possible, it is recommended to avoid accessing +[`Transaction.state`](https://codemirror.net/6/docs/ref/#state.Transaction.state) in a filter, +since it will force creation of a state that will then be +discarded again, if the transaction is actually filtered. + +(This functionality should be used with care. Indiscriminately +modifying transaction is likely to break something or degrade +the user experience.) +*/ +EditorState.transactionFilter = transactionFilter; +/** +This is a more limited form of +[`transactionFilter`](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter), +which can only add +[annotations](https://codemirror.net/6/docs/ref/#state.TransactionSpec.annotations) and +[effects](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects). _But_, this type +of filter runs even if the transaction has disabled regular +[filtering](https://codemirror.net/6/docs/ref/#state.TransactionSpec.filter), making it suitable +for effects that don't need to touch the changes or selection, +but do want to process every transaction. + +Extenders run _after_ filters, when both are present. +*/ +EditorState.transactionExtender = transactionExtender; +Compartment.reconfigure = StateEffect.define(); + +/** +Utility function for combining behaviors to fill in a config +object from an array of provided configs. `defaults` should hold +default values for all optional fields in `Config`. + +The function will, by default, error +when a field gets two values that aren't `===`-equal, but you can +provide combine functions per field to do something else. +*/ +function combineConfig(configs, defaults, // Should hold only the optional properties of Config, but I haven't managed to express that +combine = {}) { + let result = {}; + for (let config of configs) + for (let key of Object.keys(config)) { + let value = config[key], current = result[key]; + if (current === undefined) + result[key] = value; + else if (current === value || value === undefined) ; // No conflict + else if (Object.hasOwnProperty.call(combine, key)) + result[key] = combine[key](current, value); + else + throw new Error("Config merge conflict for field " + key); + } + for (let key in defaults) + if (result[key] === undefined) + result[key] = defaults[key]; + return result; +} + +/** +Each range is associated with a value, which must inherit from +this class. +*/ +class RangeValue { + /** + Compare this value with another value. Used when comparing + rangesets. The default implementation compares by identity. + Unless you are only creating a fixed number of unique instances + of your value type, it is a good idea to implement this + properly. + */ + eq(other) { return this == other; } + /** + Create a [range](https://codemirror.net/6/docs/ref/#state.Range) with this value. + */ + range(from, to = from) { return Range.create(from, to, this); } +} +RangeValue.prototype.startSide = RangeValue.prototype.endSide = 0; +RangeValue.prototype.point = false; +RangeValue.prototype.mapMode = exports.MapMode.TrackDel; +/** +A range associates a value with a range of positions. +*/ +class Range { + constructor( + /** + The range's start position. + */ + from, + /** + Its end position. + */ + to, + /** + The value associated with this range. + */ + value) { + this.from = from; + this.to = to; + this.value = value; + } + /** + @internal + */ + static create(from, to, value) { + return new Range(from, to, value); + } +} +function cmpRange(a, b) { + return a.from - b.from || a.value.startSide - b.value.startSide; +} +class Chunk { + constructor(from, to, value, + // Chunks are marked with the largest point that occurs + // in them (or -1 for no points), so that scans that are + // only interested in points (such as the + // heightmap-related logic) can skip range-only chunks. + maxPoint) { + this.from = from; + this.to = to; + this.value = value; + this.maxPoint = maxPoint; + } + get length() { return this.to[this.to.length - 1]; } + // Find the index of the given position and side. Use the ranges' + // `from` pos when `end == false`, `to` when `end == true`. + findIndex(pos, side, end, startAt = 0) { + let arr = end ? this.to : this.from; + for (let lo = startAt, hi = arr.length;;) { + if (lo == hi) + return lo; + let mid = (lo + hi) >> 1; + let diff = arr[mid] - pos || (end ? this.value[mid].endSide : this.value[mid].startSide) - side; + if (mid == lo) + return diff >= 0 ? lo : hi; + if (diff >= 0) + hi = mid; + else + lo = mid + 1; + } + } + between(offset, from, to, f) { + for (let i = this.findIndex(from, -1000000000 /* C.Far */, true), e = this.findIndex(to, 1000000000 /* C.Far */, false, i); i < e; i++) + if (f(this.from[i] + offset, this.to[i] + offset, this.value[i]) === false) + return false; + } + map(offset, changes) { + let value = [], from = [], to = [], newPos = -1, maxPoint = -1; + for (let i = 0; i < this.value.length; i++) { + let val = this.value[i], curFrom = this.from[i] + offset, curTo = this.to[i] + offset, newFrom, newTo; + if (curFrom == curTo) { + let mapped = changes.mapPos(curFrom, val.startSide, val.mapMode); + if (mapped == null) + continue; + newFrom = newTo = mapped; + if (val.startSide != val.endSide) { + newTo = changes.mapPos(curFrom, val.endSide); + if (newTo < newFrom) + continue; + } + } + else { + newFrom = changes.mapPos(curFrom, val.startSide); + newTo = changes.mapPos(curTo, val.endSide); + if (newFrom > newTo || newFrom == newTo && val.startSide > 0 && val.endSide <= 0) + continue; + } + if ((newTo - newFrom || val.endSide - val.startSide) < 0) + continue; + if (newPos < 0) + newPos = newFrom; + if (val.point) + maxPoint = Math.max(maxPoint, newTo - newFrom); + value.push(val); + from.push(newFrom - newPos); + to.push(newTo - newPos); + } + return { mapped: value.length ? new Chunk(from, to, value, maxPoint) : null, pos: newPos }; + } +} +/** +A range set stores a collection of [ranges](https://codemirror.net/6/docs/ref/#state.Range) in a +way that makes them efficient to [map](https://codemirror.net/6/docs/ref/#state.RangeSet.map) and +[update](https://codemirror.net/6/docs/ref/#state.RangeSet.update). This is an immutable data +structure. +*/ +class RangeSet { + constructor( + /** + @internal + */ + chunkPos, + /** + @internal + */ + chunk, + /** + @internal + */ + nextLayer, + /** + @internal + */ + maxPoint) { + this.chunkPos = chunkPos; + this.chunk = chunk; + this.nextLayer = nextLayer; + this.maxPoint = maxPoint; + } + /** + @internal + */ + static create(chunkPos, chunk, nextLayer, maxPoint) { + return new RangeSet(chunkPos, chunk, nextLayer, maxPoint); + } + /** + @internal + */ + get length() { + let last = this.chunk.length - 1; + return last < 0 ? 0 : Math.max(this.chunkEnd(last), this.nextLayer.length); + } + /** + The number of ranges in the set. + */ + get size() { + if (this.isEmpty) + return 0; + let size = this.nextLayer.size; + for (let chunk of this.chunk) + size += chunk.value.length; + return size; + } + /** + @internal + */ + chunkEnd(index) { + return this.chunkPos[index] + this.chunk[index].length; + } + /** + Update the range set, optionally adding new ranges or filtering + out existing ones. + + (Note: The type parameter is just there as a kludge to work + around TypeScript variance issues that prevented `RangeSet` + from being a subtype of `RangeSet` when `X` is a subtype of + `Y`.) + */ + update(updateSpec) { + let { add = [], sort = false, filterFrom = 0, filterTo = this.length } = updateSpec; + let filter = updateSpec.filter; + if (add.length == 0 && !filter) + return this; + if (sort) + add = add.slice().sort(cmpRange); + if (this.isEmpty) + return add.length ? RangeSet.of(add) : this; + let cur = new LayerCursor(this, null, -1).goto(0), i = 0, spill = []; + let builder = new RangeSetBuilder(); + while (cur.value || i < add.length) { + if (i < add.length && (cur.from - add[i].from || cur.startSide - add[i].value.startSide) >= 0) { + let range = add[i++]; + if (!builder.addInner(range.from, range.to, range.value)) + spill.push(range); + } + else if (cur.rangeIndex == 1 && cur.chunkIndex < this.chunk.length && + (i == add.length || this.chunkEnd(cur.chunkIndex) < add[i].from) && + (!filter || filterFrom > this.chunkEnd(cur.chunkIndex) || filterTo < this.chunkPos[cur.chunkIndex]) && + builder.addChunk(this.chunkPos[cur.chunkIndex], this.chunk[cur.chunkIndex])) { + cur.nextChunk(); + } + else { + if (!filter || filterFrom > cur.to || filterTo < cur.from || filter(cur.from, cur.to, cur.value)) { + if (!builder.addInner(cur.from, cur.to, cur.value)) + spill.push(Range.create(cur.from, cur.to, cur.value)); + } + cur.next(); + } + } + return builder.finishInner(this.nextLayer.isEmpty && !spill.length ? RangeSet.empty + : this.nextLayer.update({ add: spill, filter, filterFrom, filterTo })); + } + /** + Map this range set through a set of changes, return the new set. + */ + map(changes) { + if (changes.empty || this.isEmpty) + return this; + let chunks = [], chunkPos = [], maxPoint = -1; + for (let i = 0; i < this.chunk.length; i++) { + let start = this.chunkPos[i], chunk = this.chunk[i]; + let touch = changes.touchesRange(start, start + chunk.length); + if (touch === false) { + maxPoint = Math.max(maxPoint, chunk.maxPoint); + chunks.push(chunk); + chunkPos.push(changes.mapPos(start)); + } + else if (touch === true) { + let { mapped, pos } = chunk.map(start, changes); + if (mapped) { + maxPoint = Math.max(maxPoint, mapped.maxPoint); + chunks.push(mapped); + chunkPos.push(pos); + } + } + } + let next = this.nextLayer.map(changes); + return chunks.length == 0 ? next : new RangeSet(chunkPos, chunks, next || RangeSet.empty, maxPoint); + } + /** + Iterate over the ranges that touch the region `from` to `to`, + calling `f` for each. There is no guarantee that the ranges will + be reported in any specific order. When the callback returns + `false`, iteration stops. + */ + between(from, to, f) { + if (this.isEmpty) + return; + for (let i = 0; i < this.chunk.length; i++) { + let start = this.chunkPos[i], chunk = this.chunk[i]; + if (to >= start && from <= start + chunk.length && + chunk.between(start, from - start, to - start, f) === false) + return; + } + this.nextLayer.between(from, to, f); + } + /** + Iterate over the ranges in this set, in order, including all + ranges that end at or after `from`. + */ + iter(from = 0) { + return HeapCursor.from([this]).goto(from); + } + /** + @internal + */ + get isEmpty() { return this.nextLayer == this; } + /** + Iterate over the ranges in a collection of sets, in order, + starting from `from`. + */ + static iter(sets, from = 0) { + return HeapCursor.from(sets).goto(from); + } + /** + Iterate over two groups of sets, calling methods on `comparator` + to notify it of possible differences. + */ + static compare(oldSets, newSets, + /** + This indicates how the underlying data changed between these + ranges, and is needed to synchronize the iteration. + */ + textDiff, comparator, + /** + Can be used to ignore all non-point ranges, and points below + the given size. When -1, all ranges are compared. + */ + minPointSize = -1) { + let a = oldSets.filter(set => set.maxPoint > 0 || !set.isEmpty && set.maxPoint >= minPointSize); + let b = newSets.filter(set => set.maxPoint > 0 || !set.isEmpty && set.maxPoint >= minPointSize); + let sharedChunks = findSharedChunks(a, b, textDiff); + let sideA = new SpanCursor(a, sharedChunks, minPointSize); + let sideB = new SpanCursor(b, sharedChunks, minPointSize); + textDiff.iterGaps((fromA, fromB, length) => compare(sideA, fromA, sideB, fromB, length, comparator)); + if (textDiff.empty && textDiff.length == 0) + compare(sideA, 0, sideB, 0, 0, comparator); + } + /** + Compare the contents of two groups of range sets, returning true + if they are equivalent in the given range. + */ + static eq(oldSets, newSets, from = 0, to) { + if (to == null) + to = 1000000000 /* C.Far */ - 1; + let a = oldSets.filter(set => !set.isEmpty && newSets.indexOf(set) < 0); + let b = newSets.filter(set => !set.isEmpty && oldSets.indexOf(set) < 0); + if (a.length != b.length) + return false; + if (!a.length) + return true; + let sharedChunks = findSharedChunks(a, b); + let sideA = new SpanCursor(a, sharedChunks, 0).goto(from), sideB = new SpanCursor(b, sharedChunks, 0).goto(from); + for (;;) { + if (sideA.to != sideB.to || + !sameValues(sideA.active, sideB.active) || + sideA.point && (!sideB.point || !sideA.point.eq(sideB.point))) + return false; + if (sideA.to > to) + return true; + sideA.next(); + sideB.next(); + } + } + /** + Iterate over a group of range sets at the same time, notifying + the iterator about the ranges covering every given piece of + content. Returns the open count (see + [`SpanIterator.span`](https://codemirror.net/6/docs/ref/#state.SpanIterator.span)) at the end + of the iteration. + */ + static spans(sets, from, to, iterator, + /** + When given and greater than -1, only points of at least this + size are taken into account. + */ + minPointSize = -1) { + let cursor = new SpanCursor(sets, null, minPointSize).goto(from), pos = from; + let openRanges = cursor.openStart; + for (;;) { + let curTo = Math.min(cursor.to, to); + if (cursor.point) { + let active = cursor.activeForPoint(cursor.to); + let openCount = cursor.pointFrom < from ? active.length + 1 + : cursor.point.startSide < 0 ? active.length + : Math.min(active.length, openRanges); + iterator.point(pos, curTo, cursor.point, active, openCount, cursor.pointRank); + openRanges = Math.min(cursor.openEnd(curTo), active.length); + } + else if (curTo > pos) { + iterator.span(pos, curTo, cursor.active, openRanges); + openRanges = cursor.openEnd(curTo); + } + if (cursor.to > to) + return openRanges + (cursor.point && cursor.to > to ? 1 : 0); + pos = cursor.to; + cursor.next(); + } + } + /** + Create a range set for the given range or array of ranges. By + default, this expects the ranges to be _sorted_ (by start + position and, if two start at the same position, + `value.startSide`). You can pass `true` as second argument to + cause the method to sort them. + */ + static of(ranges, sort = false) { + let build = new RangeSetBuilder(); + for (let range of ranges instanceof Range ? [ranges] : sort ? lazySort(ranges) : ranges) + build.add(range.from, range.to, range.value); + return build.finish(); + } + /** + Join an array of range sets into a single set. + */ + static join(sets) { + if (!sets.length) + return RangeSet.empty; + let result = sets[sets.length - 1]; + for (let i = sets.length - 2; i >= 0; i--) { + for (let layer = sets[i]; layer != RangeSet.empty; layer = layer.nextLayer) + result = new RangeSet(layer.chunkPos, layer.chunk, result, Math.max(layer.maxPoint, result.maxPoint)); + } + return result; + } +} +/** +The empty set of ranges. +*/ +RangeSet.empty = new RangeSet([], [], null, -1); +function lazySort(ranges) { + if (ranges.length > 1) + for (let prev = ranges[0], i = 1; i < ranges.length; i++) { + let cur = ranges[i]; + if (cmpRange(prev, cur) > 0) + return ranges.slice().sort(cmpRange); + prev = cur; + } + return ranges; +} +RangeSet.empty.nextLayer = RangeSet.empty; +/** +A range set builder is a data structure that helps build up a +[range set](https://codemirror.net/6/docs/ref/#state.RangeSet) directly, without first allocating +an array of [`Range`](https://codemirror.net/6/docs/ref/#state.Range) objects. +*/ +class RangeSetBuilder { + finishChunk(newArrays) { + this.chunks.push(new Chunk(this.from, this.to, this.value, this.maxPoint)); + this.chunkPos.push(this.chunkStart); + this.chunkStart = -1; + this.setMaxPoint = Math.max(this.setMaxPoint, this.maxPoint); + this.maxPoint = -1; + if (newArrays) { + this.from = []; + this.to = []; + this.value = []; + } + } + /** + Create an empty builder. + */ + constructor() { + this.chunks = []; + this.chunkPos = []; + this.chunkStart = -1; + this.last = null; + this.lastFrom = -1000000000 /* C.Far */; + this.lastTo = -1000000000 /* C.Far */; + this.from = []; + this.to = []; + this.value = []; + this.maxPoint = -1; + this.setMaxPoint = -1; + this.nextLayer = null; + } + /** + Add a range. Ranges should be added in sorted (by `from` and + `value.startSide`) order. + */ + add(from, to, value) { + if (!this.addInner(from, to, value)) + (this.nextLayer || (this.nextLayer = new RangeSetBuilder)).add(from, to, value); + } + /** + @internal + */ + addInner(from, to, value) { + let diff = from - this.lastTo || value.startSide - this.last.endSide; + if (diff <= 0 && (from - this.lastFrom || value.startSide - this.last.startSide) < 0) + throw new Error("Ranges must be added sorted by `from` position and `startSide`"); + if (diff < 0) + return false; + if (this.from.length == 250 /* C.ChunkSize */) + this.finishChunk(true); + if (this.chunkStart < 0) + this.chunkStart = from; + this.from.push(from - this.chunkStart); + this.to.push(to - this.chunkStart); + this.last = value; + this.lastFrom = from; + this.lastTo = to; + this.value.push(value); + if (value.point) + this.maxPoint = Math.max(this.maxPoint, to - from); + return true; + } + /** + @internal + */ + addChunk(from, chunk) { + if ((from - this.lastTo || chunk.value[0].startSide - this.last.endSide) < 0) + return false; + if (this.from.length) + this.finishChunk(true); + this.setMaxPoint = Math.max(this.setMaxPoint, chunk.maxPoint); + this.chunks.push(chunk); + this.chunkPos.push(from); + let last = chunk.value.length - 1; + this.last = chunk.value[last]; + this.lastFrom = chunk.from[last] + from; + this.lastTo = chunk.to[last] + from; + return true; + } + /** + Finish the range set. Returns the new set. The builder can't be + used anymore after this has been called. + */ + finish() { return this.finishInner(RangeSet.empty); } + /** + @internal + */ + finishInner(next) { + if (this.from.length) + this.finishChunk(false); + if (this.chunks.length == 0) + return next; + let result = RangeSet.create(this.chunkPos, this.chunks, this.nextLayer ? this.nextLayer.finishInner(next) : next, this.setMaxPoint); + this.from = null; // Make sure further `add` calls produce errors + return result; + } +} +function findSharedChunks(a, b, textDiff) { + let inA = new Map(); + for (let set of a) + for (let i = 0; i < set.chunk.length; i++) + if (set.chunk[i].maxPoint <= 0) + inA.set(set.chunk[i], set.chunkPos[i]); + let shared = new Set(); + for (let set of b) + for (let i = 0; i < set.chunk.length; i++) { + let known = inA.get(set.chunk[i]); + if (known != null && (textDiff ? textDiff.mapPos(known) : known) == set.chunkPos[i] && + !(textDiff === null || textDiff === void 0 ? void 0 : textDiff.touchesRange(known, known + set.chunk[i].length))) + shared.add(set.chunk[i]); + } + return shared; +} +class LayerCursor { + constructor(layer, skip, minPoint, rank = 0) { + this.layer = layer; + this.skip = skip; + this.minPoint = minPoint; + this.rank = rank; + } + get startSide() { return this.value ? this.value.startSide : 0; } + get endSide() { return this.value ? this.value.endSide : 0; } + goto(pos, side = -1000000000 /* C.Far */) { + this.chunkIndex = this.rangeIndex = 0; + this.gotoInner(pos, side, false); + return this; + } + gotoInner(pos, side, forward) { + while (this.chunkIndex < this.layer.chunk.length) { + let next = this.layer.chunk[this.chunkIndex]; + if (!(this.skip && this.skip.has(next) || + this.layer.chunkEnd(this.chunkIndex) < pos || + next.maxPoint < this.minPoint)) + break; + this.chunkIndex++; + forward = false; + } + if (this.chunkIndex < this.layer.chunk.length) { + let rangeIndex = this.layer.chunk[this.chunkIndex].findIndex(pos - this.layer.chunkPos[this.chunkIndex], side, true); + if (!forward || this.rangeIndex < rangeIndex) + this.setRangeIndex(rangeIndex); + } + this.next(); + } + forward(pos, side) { + if ((this.to - pos || this.endSide - side) < 0) + this.gotoInner(pos, side, true); + } + next() { + for (;;) { + if (this.chunkIndex == this.layer.chunk.length) { + this.from = this.to = 1000000000 /* C.Far */; + this.value = null; + break; + } + else { + let chunkPos = this.layer.chunkPos[this.chunkIndex], chunk = this.layer.chunk[this.chunkIndex]; + let from = chunkPos + chunk.from[this.rangeIndex]; + this.from = from; + this.to = chunkPos + chunk.to[this.rangeIndex]; + this.value = chunk.value[this.rangeIndex]; + this.setRangeIndex(this.rangeIndex + 1); + if (this.minPoint < 0 || this.value.point && this.to - this.from >= this.minPoint) + break; + } + } + } + setRangeIndex(index) { + if (index == this.layer.chunk[this.chunkIndex].value.length) { + this.chunkIndex++; + if (this.skip) { + while (this.chunkIndex < this.layer.chunk.length && this.skip.has(this.layer.chunk[this.chunkIndex])) + this.chunkIndex++; + } + this.rangeIndex = 0; + } + else { + this.rangeIndex = index; + } + } + nextChunk() { + this.chunkIndex++; + this.rangeIndex = 0; + this.next(); + } + compare(other) { + return this.from - other.from || this.startSide - other.startSide || this.rank - other.rank || + this.to - other.to || this.endSide - other.endSide; + } +} +class HeapCursor { + constructor(heap) { + this.heap = heap; + } + static from(sets, skip = null, minPoint = -1) { + let heap = []; + for (let i = 0; i < sets.length; i++) { + for (let cur = sets[i]; !cur.isEmpty; cur = cur.nextLayer) { + if (cur.maxPoint >= minPoint) + heap.push(new LayerCursor(cur, skip, minPoint, i)); + } + } + return heap.length == 1 ? heap[0] : new HeapCursor(heap); + } + get startSide() { return this.value ? this.value.startSide : 0; } + goto(pos, side = -1000000000 /* C.Far */) { + for (let cur of this.heap) + cur.goto(pos, side); + for (let i = this.heap.length >> 1; i >= 0; i--) + heapBubble(this.heap, i); + this.next(); + return this; + } + forward(pos, side) { + for (let cur of this.heap) + cur.forward(pos, side); + for (let i = this.heap.length >> 1; i >= 0; i--) + heapBubble(this.heap, i); + if ((this.to - pos || this.value.endSide - side) < 0) + this.next(); + } + next() { + if (this.heap.length == 0) { + this.from = this.to = 1000000000 /* C.Far */; + this.value = null; + this.rank = -1; + } + else { + let top = this.heap[0]; + this.from = top.from; + this.to = top.to; + this.value = top.value; + this.rank = top.rank; + if (top.value) + top.next(); + heapBubble(this.heap, 0); + } + } +} +function heapBubble(heap, index) { + for (let cur = heap[index];;) { + let childIndex = (index << 1) + 1; + if (childIndex >= heap.length) + break; + let child = heap[childIndex]; + if (childIndex + 1 < heap.length && child.compare(heap[childIndex + 1]) >= 0) { + child = heap[childIndex + 1]; + childIndex++; + } + if (cur.compare(child) < 0) + break; + heap[childIndex] = cur; + heap[index] = child; + index = childIndex; + } +} +class SpanCursor { + constructor(sets, skip, minPoint) { + this.minPoint = minPoint; + this.active = []; + this.activeTo = []; + this.activeRank = []; + this.minActive = -1; + // A currently active point range, if any + this.point = null; + this.pointFrom = 0; + this.pointRank = 0; + this.to = -1000000000 /* C.Far */; + this.endSide = 0; + // The amount of open active ranges at the start of the iterator. + // Not including points. + this.openStart = -1; + this.cursor = HeapCursor.from(sets, skip, minPoint); + } + goto(pos, side = -1000000000 /* C.Far */) { + this.cursor.goto(pos, side); + this.active.length = this.activeTo.length = this.activeRank.length = 0; + this.minActive = -1; + this.to = pos; + this.endSide = side; + this.openStart = -1; + this.next(); + return this; + } + forward(pos, side) { + while (this.minActive > -1 && (this.activeTo[this.minActive] - pos || this.active[this.minActive].endSide - side) < 0) + this.removeActive(this.minActive); + this.cursor.forward(pos, side); + } + removeActive(index) { + remove(this.active, index); + remove(this.activeTo, index); + remove(this.activeRank, index); + this.minActive = findMinIndex(this.active, this.activeTo); + } + addActive(trackOpen) { + let i = 0, { value, to, rank } = this.cursor; + // Organize active marks by rank first, then by size + while (i < this.activeRank.length && (rank - this.activeRank[i] || to - this.activeTo[i]) > 0) + i++; + insert(this.active, i, value); + insert(this.activeTo, i, to); + insert(this.activeRank, i, rank); + if (trackOpen) + insert(trackOpen, i, this.cursor.from); + this.minActive = findMinIndex(this.active, this.activeTo); + } + // After calling this, if `this.point` != null, the next range is a + // point. Otherwise, it's a regular range, covered by `this.active`. + next() { + let from = this.to, wasPoint = this.point; + this.point = null; + let trackOpen = this.openStart < 0 ? [] : null; + for (;;) { + let a = this.minActive; + if (a > -1 && (this.activeTo[a] - this.cursor.from || this.active[a].endSide - this.cursor.startSide) < 0) { + if (this.activeTo[a] > from) { + this.to = this.activeTo[a]; + this.endSide = this.active[a].endSide; + break; + } + this.removeActive(a); + if (trackOpen) + remove(trackOpen, a); + } + else if (!this.cursor.value) { + this.to = this.endSide = 1000000000 /* C.Far */; + break; + } + else if (this.cursor.from > from) { + this.to = this.cursor.from; + this.endSide = this.cursor.startSide; + break; + } + else { + let nextVal = this.cursor.value; + if (!nextVal.point) { // Opening a range + this.addActive(trackOpen); + this.cursor.next(); + } + else if (wasPoint && this.cursor.to == this.to && this.cursor.from < this.cursor.to) { + // Ignore any non-empty points that end precisely at the end of the prev point + this.cursor.next(); + } + else { // New point + this.point = nextVal; + this.pointFrom = this.cursor.from; + this.pointRank = this.cursor.rank; + this.to = this.cursor.to; + this.endSide = nextVal.endSide; + this.cursor.next(); + this.forward(this.to, this.endSide); + break; + } + } + } + if (trackOpen) { + this.openStart = 0; + for (let i = trackOpen.length - 1; i >= 0 && trackOpen[i] < from; i--) + this.openStart++; + } + } + activeForPoint(to) { + if (!this.active.length) + return this.active; + let active = []; + for (let i = this.active.length - 1; i >= 0; i--) { + if (this.activeRank[i] < this.pointRank) + break; + if (this.activeTo[i] > to || this.activeTo[i] == to && this.active[i].endSide >= this.point.endSide) + active.push(this.active[i]); + } + return active.reverse(); + } + openEnd(to) { + let open = 0; + for (let i = this.activeTo.length - 1; i >= 0 && this.activeTo[i] > to; i--) + open++; + return open; + } +} +function compare(a, startA, b, startB, length, comparator) { + a.goto(startA); + b.goto(startB); + let endB = startB + length; + let pos = startB, dPos = startB - startA; + for (;;) { + let dEnd = (a.to + dPos) - b.to, diff = dEnd || a.endSide - b.endSide; + let end = diff < 0 ? a.to + dPos : b.to, clipEnd = Math.min(end, endB); + if (a.point || b.point) { + if (!(a.point && b.point && (a.point == b.point || a.point.eq(b.point)) && + sameValues(a.activeForPoint(a.to), b.activeForPoint(b.to)))) + comparator.comparePoint(pos, clipEnd, a.point, b.point); + } + else { + if (clipEnd > pos && !sameValues(a.active, b.active)) + comparator.compareRange(pos, clipEnd, a.active, b.active); + } + if (end > endB) + break; + if ((dEnd || a.openEnd != b.openEnd) && comparator.boundChange) + comparator.boundChange(end); + pos = end; + if (diff <= 0) + a.next(); + if (diff >= 0) + b.next(); + } +} +function sameValues(a, b) { + if (a.length != b.length) + return false; + for (let i = 0; i < a.length; i++) + if (a[i] != b[i] && !a[i].eq(b[i])) + return false; + return true; +} +function remove(array, index) { + for (let i = index, e = array.length - 1; i < e; i++) + array[i] = array[i + 1]; + array.pop(); +} +function insert(array, index, value) { + for (let i = array.length - 1; i >= index; i--) + array[i + 1] = array[i]; + array[index] = value; +} +function findMinIndex(value, array) { + let found = -1, foundPos = 1000000000 /* C.Far */; + for (let i = 0; i < array.length; i++) + if ((array[i] - foundPos || value[i].endSide - value[found].endSide) < 0) { + found = i; + foundPos = array[i]; + } + return found; +} + +/** +Count the column position at the given offset into the string, +taking extending characters and tab size into account. +*/ +function countColumn(string, tabSize, to = string.length) { + let n = 0; + for (let i = 0; i < to;) { + if (string.charCodeAt(i) == 9) { + n += tabSize - (n % tabSize); + i++; + } + else { + n++; + i = findClusterBreak(string, i); + } + } + return n; +} +/** +Find the offset that corresponds to the given column position in a +string, taking extending characters and tab size into account. By +default, the string length is returned when it is too short to +reach the column. Pass `strict` true to make it return -1 in that +situation. +*/ +function findColumn(string, col, tabSize, strict) { + for (let i = 0, n = 0;;) { + if (n >= col) + return i; + if (i == string.length) + break; + n += string.charCodeAt(i) == 9 ? tabSize - (n % tabSize) : 1; + i = findClusterBreak(string, i); + } + return strict === true ? -1 : string.length; +} + +exports.Annotation = Annotation; +exports.AnnotationType = AnnotationType; +exports.ChangeDesc = ChangeDesc; +exports.ChangeSet = ChangeSet; +exports.Compartment = Compartment; +exports.EditorSelection = EditorSelection; +exports.EditorState = EditorState; +exports.Facet = Facet; +exports.Line = Line; +exports.Prec = Prec; +exports.Range = Range; +exports.RangeSet = RangeSet; +exports.RangeSetBuilder = RangeSetBuilder; +exports.RangeValue = RangeValue; +exports.SelectionRange = SelectionRange; +exports.StateEffect = StateEffect; +exports.StateEffectType = StateEffectType; +exports.StateField = StateField; +exports.Text = Text; +exports.Transaction = Transaction; +exports.codePointAt = codePointAt; +exports.codePointSize = codePointSize; +exports.combineConfig = combineConfig; +exports.countColumn = countColumn; +exports.findClusterBreak = findClusterBreak; +exports.findColumn = findColumn; +exports.fromCodePoint = fromCodePoint; diff --git a/node_modules/@codemirror/state/dist/index.d.cts b/node_modules/@codemirror/state/dist/index.d.cts new file mode 100644 index 0000000..f3adc66 --- /dev/null +++ b/node_modules/@codemirror/state/dist/index.d.cts @@ -0,0 +1,1700 @@ +/** +A text iterator iterates over a sequence of strings. When +iterating over a [`Text`](https://codemirror.net/6/docs/ref/#state.Text) document, result values will +either be lines or line breaks. +*/ +interface TextIterator extends Iterator, Iterable { + /** + Retrieve the next string. Optionally skip a given number of + positions after the current position. Always returns the object + itself. + */ + next(skip?: number): this; + /** + The current string. Will be the empty string when the cursor is + at its end or `next` hasn't been called on it yet. + */ + value: string; + /** + Whether the end of the iteration has been reached. You should + probably check this right after calling `next`. + */ + done: boolean; + /** + Whether the current string represents a line break. + */ + lineBreak: boolean; +} +/** +The data structure for documents. @nonabstract +*/ +declare abstract class Text implements Iterable { + /** + The length of the string. + */ + abstract readonly length: number; + /** + The number of lines in the string (always >= 1). + */ + abstract readonly lines: number; + /** + Get the line description around the given position. + */ + lineAt(pos: number): Line; + /** + Get the description for the given (1-based) line number. + */ + line(n: number): Line; + /** + Replace a range of the text with the given content. + */ + replace(from: number, to: number, text: Text): Text; + /** + Append another document to this one. + */ + append(other: Text): Text; + /** + Retrieve the text between the given points. + */ + slice(from: number, to?: number): Text; + /** + Retrieve a part of the document as a string + */ + abstract sliceString(from: number, to?: number, lineSep?: string): string; + /** + Test whether this text is equal to another instance. + */ + eq(other: Text): boolean; + /** + Iterate over the text. When `dir` is `-1`, iteration happens + from end to start. This will return lines and the breaks between + them as separate strings. + */ + iter(dir?: 1 | -1): TextIterator; + /** + Iterate over a range of the text. When `from` > `to`, the + iterator will run in reverse. + */ + iterRange(from: number, to?: number): TextIterator; + /** + Return a cursor that iterates over the given range of lines, + _without_ returning the line breaks between, and yielding empty + strings for empty lines. + + When `from` and `to` are given, they should be 1-based line numbers. + */ + iterLines(from?: number, to?: number): TextIterator; + /** + Return the document as a string, using newline characters to + separate lines. + */ + toString(): string; + /** + Convert the document to an array of lines (which can be + deserialized again via [`Text.of`](https://codemirror.net/6/docs/ref/#state.Text^of)). + */ + toJSON(): string[]; + /** + If this is a branch node, `children` will hold the `Text` + objects that it is made up of. For leaf nodes, this holds null. + */ + abstract readonly children: readonly Text[] | null; + [Symbol.iterator]: () => Iterator; + /** + Create a `Text` instance for the given array of lines. + */ + static of(text: readonly string[]): Text; + /** + The empty document. + */ + static empty: Text; +} +/** +This type describes a line in the document. It is created +on-demand when lines are [queried](https://codemirror.net/6/docs/ref/#state.Text.lineAt). +*/ +declare class Line { + /** + The position of the start of the line. + */ + readonly from: number; + /** + The position at the end of the line (_before_ the line break, + or at the end of document for the last line). + */ + readonly to: number; + /** + This line's line number (1-based). + */ + readonly number: number; + /** + The line's content. + */ + readonly text: string; + /** + The length of the line (not including any line break after it). + */ + get length(): number; +} + +/** +Distinguishes different ways in which positions can be mapped. +*/ +declare enum MapMode { + /** + Map a position to a valid new position, even when its context + was deleted. + */ + Simple = 0, + /** + Return null if deletion happens across the position. + */ + TrackDel = 1, + /** + Return null if the character _before_ the position is deleted. + */ + TrackBefore = 2, + /** + Return null if the character _after_ the position is deleted. + */ + TrackAfter = 3 +} +/** +A change description is a variant of [change set](https://codemirror.net/6/docs/ref/#state.ChangeSet) +that doesn't store the inserted text. As such, it can't be +applied, but is cheaper to store and manipulate. +*/ +declare class ChangeDesc { + /** + The length of the document before the change. + */ + get length(): number; + /** + The length of the document after the change. + */ + get newLength(): number; + /** + False when there are actual changes in this set. + */ + get empty(): boolean; + /** + Iterate over the unchanged parts left by these changes. `posA` + provides the position of the range in the old document, `posB` + the new position in the changed document. + */ + iterGaps(f: (posA: number, posB: number, length: number) => void): void; + /** + Iterate over the ranges changed by these changes. (See + [`ChangeSet.iterChanges`](https://codemirror.net/6/docs/ref/#state.ChangeSet.iterChanges) for a + variant that also provides you with the inserted text.) + `fromA`/`toA` provides the extent of the change in the starting + document, `fromB`/`toB` the extent of the replacement in the + changed document. + + When `individual` is true, adjacent changes (which are kept + separate for [position mapping](https://codemirror.net/6/docs/ref/#state.ChangeDesc.mapPos)) are + reported separately. + */ + iterChangedRanges(f: (fromA: number, toA: number, fromB: number, toB: number) => void, individual?: boolean): void; + /** + Get a description of the inverted form of these changes. + */ + get invertedDesc(): ChangeDesc; + /** + Compute the combined effect of applying another set of changes + after this one. The length of the document after this set should + match the length before `other`. + */ + composeDesc(other: ChangeDesc): ChangeDesc; + /** + Map this description, which should start with the same document + as `other`, over another set of changes, so that it can be + applied after it. When `before` is true, map as if the changes + in `this` happened before the ones in `other`. + */ + mapDesc(other: ChangeDesc, before?: boolean): ChangeDesc; + /** + Map a given position through these changes, to produce a + position pointing into the new document. + + `assoc` indicates which side the position should be associated + with. When it is negative or zero, the mapping will try to keep + the position close to the character before it (if any), and will + move it before insertions at that point or replacements across + that point. When it is positive, the position is associated with + the character after it, and will be moved forward for insertions + at or replacements across the position. Defaults to -1. + + `mode` determines whether deletions should be + [reported](https://codemirror.net/6/docs/ref/#state.MapMode). It defaults to + [`MapMode.Simple`](https://codemirror.net/6/docs/ref/#state.MapMode.Simple) (don't report + deletions). + */ + mapPos(pos: number, assoc?: number): number; + mapPos(pos: number, assoc: number, mode: MapMode): number | null; + /** + Check whether these changes touch a given range. When one of the + changes entirely covers the range, the string `"cover"` is + returned. + */ + touchesRange(from: number, to?: number): boolean | "cover"; + /** + Serialize this change desc to a JSON-representable value. + */ + toJSON(): readonly number[]; + /** + Create a change desc from its JSON representation (as produced + by [`toJSON`](https://codemirror.net/6/docs/ref/#state.ChangeDesc.toJSON). + */ + static fromJSON(json: any): ChangeDesc; +} +/** +This type is used as argument to +[`EditorState.changes`](https://codemirror.net/6/docs/ref/#state.EditorState.changes) and in the +[`changes` field](https://codemirror.net/6/docs/ref/#state.TransactionSpec.changes) of transaction +specs to succinctly describe document changes. It may either be a +plain object describing a change (a deletion, insertion, or +replacement, depending on which fields are present), a [change +set](https://codemirror.net/6/docs/ref/#state.ChangeSet), or an array of change specs. +*/ +type ChangeSpec = { + from: number; + to?: number; + insert?: string | Text; +} | ChangeSet | readonly ChangeSpec[]; +/** +A change set represents a group of modifications to a document. It +stores the document length, and can only be applied to documents +with exactly that length. +*/ +declare class ChangeSet extends ChangeDesc { + private constructor(); + /** + Apply the changes to a document, returning the modified + document. + */ + apply(doc: Text): Text; + mapDesc(other: ChangeDesc, before?: boolean): ChangeDesc; + /** + Given the document as it existed _before_ the changes, return a + change set that represents the inverse of this set, which could + be used to go from the document created by the changes back to + the document as it existed before the changes. + */ + invert(doc: Text): ChangeSet; + /** + Combine two subsequent change sets into a single set. `other` + must start in the document produced by `this`. If `this` goes + `docA` → `docB` and `other` represents `docB` → `docC`, the + returned value will represent the change `docA` → `docC`. + */ + compose(other: ChangeSet): ChangeSet; + /** + Given another change set starting in the same document, maps this + change set over the other, producing a new change set that can be + applied to the document produced by applying `other`. When + `before` is `true`, order changes as if `this` comes before + `other`, otherwise (the default) treat `other` as coming first. + + Given two changes `A` and `B`, `A.compose(B.map(A))` and + `B.compose(A.map(B, true))` will produce the same document. This + provides a basic form of [operational + transformation](https://en.wikipedia.org/wiki/Operational_transformation), + and can be used for collaborative editing. + */ + map(other: ChangeDesc, before?: boolean): ChangeSet; + /** + Iterate over the changed ranges in the document, calling `f` for + each, with the range in the original document (`fromA`-`toA`) + and the range that replaces it in the new document + (`fromB`-`toB`). + + When `individual` is true, adjacent changes are reported + separately. + */ + iterChanges(f: (fromA: number, toA: number, fromB: number, toB: number, inserted: Text) => void, individual?: boolean): void; + /** + Get a [change description](https://codemirror.net/6/docs/ref/#state.ChangeDesc) for this change + set. + */ + get desc(): ChangeDesc; + /** + Serialize this change set to a JSON-representable value. + */ + toJSON(): any; + /** + Create a change set for the given changes, for a document of the + given length, using `lineSep` as line separator. + */ + static of(changes: ChangeSpec, length: number, lineSep?: string): ChangeSet; + /** + Create an empty changeset of the given length. + */ + static empty(length: number): ChangeSet; + /** + Create a changeset from its JSON representation (as produced by + [`toJSON`](https://codemirror.net/6/docs/ref/#state.ChangeSet.toJSON). + */ + static fromJSON(json: any): ChangeSet; +} + +/** +A single selection range. When +[`allowMultipleSelections`](https://codemirror.net/6/docs/ref/#state.EditorState^allowMultipleSelections) +is enabled, a [selection](https://codemirror.net/6/docs/ref/#state.EditorSelection) may hold +multiple ranges. By default, selections hold exactly one range. +*/ +declare class SelectionRange { + /** + The lower boundary of the range. + */ + readonly from: number; + /** + The upper boundary of the range. + */ + readonly to: number; + private flags; + private constructor(); + /** + The anchor of the range—the side that doesn't move when you + extend it. + */ + get anchor(): number; + /** + The head of the range, which is moved when the range is + [extended](https://codemirror.net/6/docs/ref/#state.SelectionRange.extend). + */ + get head(): number; + /** + True when `anchor` and `head` are at the same position. + */ + get empty(): boolean; + /** + If this is a cursor that is explicitly associated with the + character on one of its sides, this returns the side. -1 means + the character before its position, 1 the character after, and 0 + means no association. + */ + get assoc(): -1 | 0 | 1; + /** + The bidirectional text level associated with this cursor, if + any. + */ + get bidiLevel(): number | null; + /** + The goal column (stored vertical offset) associated with a + cursor. This is used to preserve the vertical position when + [moving](https://codemirror.net/6/docs/ref/#view.EditorView.moveVertically) across + lines of different length. + */ + get goalColumn(): number | undefined; + /** + Map this range through a change, producing a valid range in the + updated document. + */ + map(change: ChangeDesc, assoc?: number): SelectionRange; + /** + Extend this range to cover at least `from` to `to`. + */ + extend(from: number, to?: number): SelectionRange; + /** + Compare this range to another range. + */ + eq(other: SelectionRange, includeAssoc?: boolean): boolean; + /** + Return a JSON-serializable object representing the range. + */ + toJSON(): any; + /** + Convert a JSON representation of a range to a `SelectionRange` + instance. + */ + static fromJSON(json: any): SelectionRange; +} +/** +An editor selection holds one or more selection ranges. +*/ +declare class EditorSelection { + /** + The ranges in the selection, sorted by position. Ranges cannot + overlap (but they may touch, if they aren't empty). + */ + readonly ranges: readonly SelectionRange[]; + /** + The index of the _main_ range in the selection (which is + usually the range that was added last). + */ + readonly mainIndex: number; + private constructor(); + /** + Map a selection through a change. Used to adjust the selection + position for changes. + */ + map(change: ChangeDesc, assoc?: number): EditorSelection; + /** + Compare this selection to another selection. By default, ranges + are compared only by position. When `includeAssoc` is true, + cursor ranges must also have the same + [`assoc`](https://codemirror.net/6/docs/ref/#state.SelectionRange.assoc) value. + */ + eq(other: EditorSelection, includeAssoc?: boolean): boolean; + /** + Get the primary selection range. Usually, you should make sure + your code applies to _all_ ranges, by using methods like + [`changeByRange`](https://codemirror.net/6/docs/ref/#state.EditorState.changeByRange). + */ + get main(): SelectionRange; + /** + Make sure the selection only has one range. Returns a selection + holding only the main range from this selection. + */ + asSingle(): EditorSelection; + /** + Extend this selection with an extra range. + */ + addRange(range: SelectionRange, main?: boolean): EditorSelection; + /** + Replace a given range with another range, and then normalize the + selection to merge and sort ranges if necessary. + */ + replaceRange(range: SelectionRange, which?: number): EditorSelection; + /** + Convert this selection to an object that can be serialized to + JSON. + */ + toJSON(): any; + /** + Create a selection from a JSON representation. + */ + static fromJSON(json: any): EditorSelection; + /** + Create a selection holding a single range. + */ + static single(anchor: number, head?: number): EditorSelection; + /** + Sort and merge the given set of ranges, creating a valid + selection. + */ + static create(ranges: readonly SelectionRange[], mainIndex?: number): EditorSelection; + /** + Create a cursor selection range at the given position. You can + safely ignore the optional arguments in most situations. + */ + static cursor(pos: number, assoc?: number, bidiLevel?: number, goalColumn?: number): SelectionRange; + /** + Create a selection range. + */ + static range(anchor: number, head: number, goalColumn?: number, bidiLevel?: number): SelectionRange; +} + +type FacetConfig = { + /** + How to combine the input values into a single output value. When + not given, the array of input values becomes the output. This + function will immediately be called on creating the facet, with + an empty array, to compute the facet's default value when no + inputs are present. + */ + combine?: (value: readonly Input[]) => Output; + /** + How to compare output values to determine whether the value of + the facet changed. Defaults to comparing by `===` or, if no + `combine` function was given, comparing each element of the + array with `===`. + */ + compare?: (a: Output, b: Output) => boolean; + /** + How to compare input values to avoid recomputing the output + value when no inputs changed. Defaults to comparing with `===`. + */ + compareInput?: (a: Input, b: Input) => boolean; + /** + Forbids dynamic inputs to this facet. + */ + static?: boolean; + /** + If given, these extension(s) (or the result of calling the given + function with the facet) will be added to any state where this + facet is provided. (Note that, while a facet's default value can + be read from a state even if the facet wasn't present in the + state at all, these extensions won't be added in that + situation.) + */ + enables?: Extension | ((self: Facet) => Extension); +}; +/** +A facet is a labeled value that is associated with an editor +state. It takes inputs from any number of extensions, and combines +those into a single output value. + +Examples of uses of facets are the [tab +size](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize), [editor +attributes](https://codemirror.net/6/docs/ref/#view.EditorView^editorAttributes), and [update +listeners](https://codemirror.net/6/docs/ref/#view.EditorView^updateListener). + +Note that `Facet` instances can be used anywhere where +[`FacetReader`](https://codemirror.net/6/docs/ref/#state.FacetReader) is expected. +*/ +declare class Facet implements FacetReader { + private isStatic; + private constructor(); + /** + Returns a facet reader for this facet, which can be used to + [read](https://codemirror.net/6/docs/ref/#state.EditorState.facet) it but not to define values for it. + */ + get reader(): FacetReader; + /** + Define a new facet. + */ + static define(config?: FacetConfig): Facet; + /** + Returns an extension that adds the given value to this facet. + */ + of(value: Input): Extension; + /** + Create an extension that computes a value for the facet from a + state. You must take care to declare the parts of the state that + this value depends on, since your function is only called again + for a new state when one of those parts changed. + + In cases where your value depends only on a single field, you'll + want to use the [`from`](https://codemirror.net/6/docs/ref/#state.Facet.from) method instead. + */ + compute(deps: readonly Slot[], get: (state: EditorState) => Input): Extension; + /** + Create an extension that computes zero or more values for this + facet from a state. + */ + computeN(deps: readonly Slot[], get: (state: EditorState) => readonly Input[]): Extension; + /** + Shorthand method for registering a facet source with a state + field as input. If the field's type corresponds to this facet's + input type, the getter function can be omitted. If given, it + will be used to retrieve the input from the field value. + */ + from(field: StateField): Extension; + from(field: StateField, get: (value: T) => Input): Extension; + tag: Output; +} +/** +A facet reader can be used to fetch the value of a facet, through +[`EditorState.facet`](https://codemirror.net/6/docs/ref/#state.EditorState.facet) or as a dependency +in [`Facet.compute`](https://codemirror.net/6/docs/ref/#state.Facet.compute), but not to define new +values for the facet. +*/ +type FacetReader = { + /** + Dummy tag that makes sure TypeScript doesn't consider all object + types as conforming to this type. Not actually present on the + object. + */ + tag: Output; +}; +type Slot = FacetReader | StateField | "doc" | "selection"; +type StateFieldSpec = { + /** + Creates the initial value for the field when a state is created. + */ + create: (state: EditorState) => Value; + /** + Compute a new value from the field's previous value and a + [transaction](https://codemirror.net/6/docs/ref/#state.Transaction). + */ + update: (value: Value, transaction: Transaction) => Value; + /** + Compare two values of the field, returning `true` when they are + the same. This is used to avoid recomputing facets that depend + on the field when its value did not change. Defaults to using + `===`. + */ + compare?: (a: Value, b: Value) => boolean; + /** + Provide extensions based on this field. The given function will + be called once with the initialized field. It will usually want + to call some facet's [`from`](https://codemirror.net/6/docs/ref/#state.Facet.from) method to + create facet inputs from this field, but can also return other + extensions that should be enabled when the field is present in a + configuration. + */ + provide?: (field: StateField) => Extension; + /** + A function used to serialize this field's content to JSON. Only + necessary when this field is included in the argument to + [`EditorState.toJSON`](https://codemirror.net/6/docs/ref/#state.EditorState.toJSON). + */ + toJSON?: (value: Value, state: EditorState) => any; + /** + A function that deserializes the JSON representation of this + field's content. + */ + fromJSON?: (json: any, state: EditorState) => Value; +}; +/** +Fields can store additional information in an editor state, and +keep it in sync with the rest of the state. +*/ +declare class StateField { + private createF; + private updateF; + private compareF; + private constructor(); + /** + Define a state field. + */ + static define(config: StateFieldSpec): StateField; + private create; + /** + Returns an extension that enables this field and overrides the + way it is initialized. Can be useful when you need to provide a + non-default starting value for the field. + */ + init(create: (state: EditorState) => Value): Extension; + /** + State field instances can be used as + [`Extension`](https://codemirror.net/6/docs/ref/#state.Extension) values to enable the field in a + given state. + */ + get extension(): Extension; +} +/** +Extension values can be +[provided](https://codemirror.net/6/docs/ref/#state.EditorStateConfig.extensions) when creating a +state to attach various kinds of configuration and behavior +information. They can either be built-in extension-providing +objects, such as [state fields](https://codemirror.net/6/docs/ref/#state.StateField) or [facet +providers](https://codemirror.net/6/docs/ref/#state.Facet.of), or objects with an extension in its +`extension` property. Extensions can be nested in arrays +arbitrarily deep—they will be flattened when processed. +*/ +type Extension = { + extension: Extension; +} | readonly Extension[]; +/** +By default extensions are registered in the order they are found +in the flattened form of nested array that was provided. +Individual extension values can be assigned a precedence to +override this. Extensions that do not have a precedence set get +the precedence of the nearest parent with a precedence, or +[`default`](https://codemirror.net/6/docs/ref/#state.Prec.default) if there is no such parent. The +final ordering of extensions is determined by first sorting by +precedence and then by order within each precedence. +*/ +declare const Prec: { + /** + The highest precedence level, for extensions that should end up + near the start of the precedence ordering. + */ + highest: (ext: Extension) => Extension; + /** + A higher-than-default precedence, for extensions that should + come before those with default precedence. + */ + high: (ext: Extension) => Extension; + /** + The default precedence, which is also used for extensions + without an explicit precedence. + */ + default: (ext: Extension) => Extension; + /** + A lower-than-default precedence. + */ + low: (ext: Extension) => Extension; + /** + The lowest precedence level. Meant for things that should end up + near the end of the extension order. + */ + lowest: (ext: Extension) => Extension; +}; +/** +Extension compartments can be used to make a configuration +dynamic. By [wrapping](https://codemirror.net/6/docs/ref/#state.Compartment.of) part of your +configuration in a compartment, you can later +[replace](https://codemirror.net/6/docs/ref/#state.Compartment.reconfigure) that part through a +transaction. +*/ +declare class Compartment { + /** + Create an instance of this compartment to add to your [state + configuration](https://codemirror.net/6/docs/ref/#state.EditorStateConfig.extensions). + */ + of(ext: Extension): Extension; + /** + Create an [effect](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) that + reconfigures this compartment. + */ + reconfigure(content: Extension): StateEffect; + /** + Get the current content of the compartment in the state, or + `undefined` if it isn't present. + */ + get(state: EditorState): Extension | undefined; +} + +/** +Annotations are tagged values that are used to add metadata to +transactions in an extensible way. They should be used to model +things that effect the entire transaction (such as its [time +stamp](https://codemirror.net/6/docs/ref/#state.Transaction^time) or information about its +[origin](https://codemirror.net/6/docs/ref/#state.Transaction^userEvent)). For effects that happen +_alongside_ the other changes made by the transaction, [state +effects](https://codemirror.net/6/docs/ref/#state.StateEffect) are more appropriate. +*/ +declare class Annotation { + /** + The annotation type. + */ + readonly type: AnnotationType; + /** + The value of this annotation. + */ + readonly value: T; + /** + Define a new type of annotation. + */ + static define(): AnnotationType; + private _isAnnotation; +} +/** +Marker that identifies a type of [annotation](https://codemirror.net/6/docs/ref/#state.Annotation). +*/ +declare class AnnotationType { + /** + Create an instance of this annotation. + */ + of(value: T): Annotation; +} +interface StateEffectSpec { + /** + Provides a way to map an effect like this through a position + mapping. When not given, the effects will simply not be mapped. + When the function returns `undefined`, that means the mapping + deletes the effect. + */ + map?: (value: Value, mapping: ChangeDesc) => Value | undefined; +} +/** +Representation of a type of state effect. Defined with +[`StateEffect.define`](https://codemirror.net/6/docs/ref/#state.StateEffect^define). +*/ +declare class StateEffectType { + /** + @internal + */ + readonly map: (value: any, mapping: ChangeDesc) => any | undefined; + /** + Create a [state effect](https://codemirror.net/6/docs/ref/#state.StateEffect) instance of this + type. + */ + of(value: Value): StateEffect; +} +/** +State effects can be used to represent additional effects +associated with a [transaction](https://codemirror.net/6/docs/ref/#state.Transaction.effects). They +are often useful to model changes to custom [state +fields](https://codemirror.net/6/docs/ref/#state.StateField), when those changes aren't implicit in +document or selection changes. +*/ +declare class StateEffect { + /** + The value of this effect. + */ + readonly value: Value; + /** + Map this effect through a position mapping. Will return + `undefined` when that ends up deleting the effect. + */ + map(mapping: ChangeDesc): StateEffect | undefined; + /** + Tells you whether this effect object is of a given + [type](https://codemirror.net/6/docs/ref/#state.StateEffectType). + */ + is(type: StateEffectType): this is StateEffect; + /** + Define a new effect type. The type parameter indicates the type + of values that his effect holds. It should be a type that + doesn't include `undefined`, since that is used in + [mapping](https://codemirror.net/6/docs/ref/#state.StateEffect.map) to indicate that an effect is + removed. + */ + static define(spec?: StateEffectSpec): StateEffectType; + /** + Map an array of effects through a change set. + */ + static mapEffects(effects: readonly StateEffect[], mapping: ChangeDesc): readonly StateEffect[]; + /** + This effect can be used to reconfigure the root extensions of + the editor. Doing this will discard any extensions + [appended](https://codemirror.net/6/docs/ref/#state.StateEffect^appendConfig), but does not reset + the content of [reconfigured](https://codemirror.net/6/docs/ref/#state.Compartment.reconfigure) + compartments. + */ + static reconfigure: StateEffectType; + /** + Append extensions to the top-level configuration of the editor. + */ + static appendConfig: StateEffectType; +} +/** +Describes a [transaction](https://codemirror.net/6/docs/ref/#state.Transaction) when calling the +[`EditorState.update`](https://codemirror.net/6/docs/ref/#state.EditorState.update) method. +*/ +interface TransactionSpec { + /** + The changes to the document made by this transaction. + */ + changes?: ChangeSpec; + /** + When set, this transaction explicitly updates the selection. + Offsets in this selection should refer to the document as it is + _after_ the transaction. + */ + selection?: EditorSelection | { + anchor: number; + head?: number; + } | undefined; + /** + Attach [state effects](https://codemirror.net/6/docs/ref/#state.StateEffect) to this transaction. + Again, when they contain positions and this same spec makes + changes, those positions should refer to positions in the + updated document. + */ + effects?: StateEffect | readonly StateEffect[]; + /** + Set [annotations](https://codemirror.net/6/docs/ref/#state.Annotation) for this transaction. + */ + annotations?: Annotation | readonly Annotation[]; + /** + Shorthand for `annotations:` [`Transaction.userEvent`](https://codemirror.net/6/docs/ref/#state.Transaction^userEvent)`.of(...)`. + */ + userEvent?: string; + /** + When set to `true`, the transaction is marked as needing to + scroll the current selection into view. + */ + scrollIntoView?: boolean; + /** + By default, transactions can be modified by [change + filters](https://codemirror.net/6/docs/ref/#state.EditorState^changeFilter) and [transaction + filters](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter). You can set this + to `false` to disable that. This can be necessary for + transactions that, for example, include annotations that must be + kept consistent with their changes. + */ + filter?: boolean; + /** + Normally, when multiple specs are combined (for example by + [`EditorState.update`](https://codemirror.net/6/docs/ref/#state.EditorState.update)), the + positions in `changes` are taken to refer to the document + positions in the initial document. When a spec has `sequental` + set to true, its positions will be taken to refer to the + document created by the specs before it instead. + */ + sequential?: boolean; +} +/** +Changes to the editor state are grouped into transactions. +Typically, a user action creates a single transaction, which may +contain any number of document changes, may change the selection, +or have other effects. Create a transaction by calling +[`EditorState.update`](https://codemirror.net/6/docs/ref/#state.EditorState.update), or immediately +dispatch one by calling +[`EditorView.dispatch`](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch). +*/ +declare class Transaction { + /** + The state from which the transaction starts. + */ + readonly startState: EditorState; + /** + The document changes made by this transaction. + */ + readonly changes: ChangeSet; + /** + The selection set by this transaction, or undefined if it + doesn't explicitly set a selection. + */ + readonly selection: EditorSelection | undefined; + /** + The effects added to the transaction. + */ + readonly effects: readonly StateEffect[]; + /** + Whether the selection should be scrolled into view after this + transaction is dispatched. + */ + readonly scrollIntoView: boolean; + private constructor(); + /** + The new document produced by the transaction. Contrary to + [`.state`](https://codemirror.net/6/docs/ref/#state.Transaction.state)`.doc`, accessing this won't + force the entire new state to be computed right away, so it is + recommended that [transaction + filters](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter) use this getter + when they need to look at the new document. + */ + get newDoc(): Text; + /** + The new selection produced by the transaction. If + [`this.selection`](https://codemirror.net/6/docs/ref/#state.Transaction.selection) is undefined, + this will [map](https://codemirror.net/6/docs/ref/#state.EditorSelection.map) the start state's + current selection through the changes made by the transaction. + */ + get newSelection(): EditorSelection; + /** + The new state created by the transaction. Computed on demand + (but retained for subsequent access), so it is recommended not to + access it in [transaction + filters](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter) when possible. + */ + get state(): EditorState; + /** + Get the value of the given annotation type, if any. + */ + annotation(type: AnnotationType): T | undefined; + /** + Indicates whether the transaction changed the document. + */ + get docChanged(): boolean; + /** + Indicates whether this transaction reconfigures the state + (through a [configuration compartment](https://codemirror.net/6/docs/ref/#state.Compartment) or + with a top-level configuration + [effect](https://codemirror.net/6/docs/ref/#state.StateEffect^reconfigure). + */ + get reconfigured(): boolean; + /** + Returns true if the transaction has a [user + event](https://codemirror.net/6/docs/ref/#state.Transaction^userEvent) annotation that is equal to + or more specific than `event`. For example, if the transaction + has `"select.pointer"` as user event, `"select"` and + `"select.pointer"` will match it. + */ + isUserEvent(event: string): boolean; + /** + Annotation used to store transaction timestamps. Automatically + added to every transaction, holding `Date.now()`. + */ + static time: AnnotationType; + /** + Annotation used to associate a transaction with a user interface + event. Holds a string identifying the event, using a + dot-separated format to support attaching more specific + information. The events used by the core libraries are: + + - `"input"` when content is entered + - `"input.type"` for typed input + - `"input.type.compose"` for composition + - `"input.paste"` for pasted input + - `"input.drop"` when adding content with drag-and-drop + - `"input.complete"` when autocompleting + - `"delete"` when the user deletes content + - `"delete.selection"` when deleting the selection + - `"delete.forward"` when deleting forward from the selection + - `"delete.backward"` when deleting backward from the selection + - `"delete.cut"` when cutting to the clipboard + - `"move"` when content is moved + - `"move.drop"` when content is moved within the editor through drag-and-drop + - `"select"` when explicitly changing the selection + - `"select.pointer"` when selecting with a mouse or other pointing device + - `"undo"` and `"redo"` for history actions + + Use [`isUserEvent`](https://codemirror.net/6/docs/ref/#state.Transaction.isUserEvent) to check + whether the annotation matches a given event. + */ + static userEvent: AnnotationType; + /** + Annotation indicating whether a transaction should be added to + the undo history or not. + */ + static addToHistory: AnnotationType; + /** + Annotation indicating (when present and true) that a transaction + represents a change made by some other actor, not the user. This + is used, for example, to tag other people's changes in + collaborative editing. + */ + static remote: AnnotationType; +} + +/** +The categories produced by a [character +categorizer](https://codemirror.net/6/docs/ref/#state.EditorState.charCategorizer). These are used +do things like selecting by word. +*/ +declare enum CharCategory { + /** + Word characters. + */ + Word = 0, + /** + Whitespace. + */ + Space = 1, + /** + Anything else. + */ + Other = 2 +} + +/** +Options passed when [creating](https://codemirror.net/6/docs/ref/#state.EditorState^create) an +editor state. +*/ +interface EditorStateConfig { + /** + The initial document. Defaults to an empty document. Can be + provided either as a plain string (which will be split into + lines according to the value of the [`lineSeparator` + facet](https://codemirror.net/6/docs/ref/#state.EditorState^lineSeparator)), or an instance of + the [`Text`](https://codemirror.net/6/docs/ref/#state.Text) class (which is what the state will use + to represent the document). + */ + doc?: string | Text; + /** + The starting selection. Defaults to a cursor at the very start + of the document. + */ + selection?: EditorSelection | { + anchor: number; + head?: number; + }; + /** + [Extension(s)](https://codemirror.net/6/docs/ref/#state.Extension) to associate with this state. + */ + extensions?: Extension; +} +/** +The editor state class is a persistent (immutable) data structure. +To update a state, you [create](https://codemirror.net/6/docs/ref/#state.EditorState.update) a +[transaction](https://codemirror.net/6/docs/ref/#state.Transaction), which produces a _new_ state +instance, without modifying the original object. + +As such, _never_ mutate properties of a state directly. That'll +just break things. +*/ +declare class EditorState { + /** + The current document. + */ + readonly doc: Text; + /** + The current selection. + */ + readonly selection: EditorSelection; + private constructor(); + /** + Retrieve the value of a [state field](https://codemirror.net/6/docs/ref/#state.StateField). Throws + an error when the state doesn't have that field, unless you pass + `false` as second parameter. + */ + field(field: StateField): T; + field(field: StateField, require: false): T | undefined; + /** + Create a [transaction](https://codemirror.net/6/docs/ref/#state.Transaction) that updates this + state. Any number of [transaction specs](https://codemirror.net/6/docs/ref/#state.TransactionSpec) + can be passed. Unless + [`sequential`](https://codemirror.net/6/docs/ref/#state.TransactionSpec.sequential) is set, the + [changes](https://codemirror.net/6/docs/ref/#state.TransactionSpec.changes) (if any) of each spec + are assumed to start in the _current_ document (not the document + produced by previous specs), and its + [selection](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection) and + [effects](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) are assumed to refer + to the document created by its _own_ changes. The resulting + transaction contains the combined effect of all the different + specs. For [selection](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection), later + specs take precedence over earlier ones. + */ + update(...specs: readonly TransactionSpec[]): Transaction; + /** + Create a [transaction spec](https://codemirror.net/6/docs/ref/#state.TransactionSpec) that + replaces every selection range with the given content. + */ + replaceSelection(text: string | Text): TransactionSpec; + /** + Create a set of changes and a new selection by running the given + function for each range in the active selection. The function + can return an optional set of changes (in the coordinate space + of the start document), plus an updated range (in the coordinate + space of the document produced by the call's own changes). This + method will merge all the changes and ranges into a single + changeset and selection, and return it as a [transaction + spec](https://codemirror.net/6/docs/ref/#state.TransactionSpec), which can be passed to + [`update`](https://codemirror.net/6/docs/ref/#state.EditorState.update). + */ + changeByRange(f: (range: SelectionRange) => { + range: SelectionRange; + changes?: ChangeSpec; + effects?: StateEffect | readonly StateEffect[]; + }): { + changes: ChangeSet; + selection: EditorSelection; + effects: readonly StateEffect[]; + }; + /** + Create a [change set](https://codemirror.net/6/docs/ref/#state.ChangeSet) from the given change + description, taking the state's document length and line + separator into account. + */ + changes(spec?: ChangeSpec): ChangeSet; + /** + Using the state's [line + separator](https://codemirror.net/6/docs/ref/#state.EditorState^lineSeparator), create a + [`Text`](https://codemirror.net/6/docs/ref/#state.Text) instance from the given string. + */ + toText(string: string): Text; + /** + Return the given range of the document as a string. + */ + sliceDoc(from?: number, to?: number): string; + /** + Get the value of a state [facet](https://codemirror.net/6/docs/ref/#state.Facet). + */ + facet(facet: FacetReader): Output; + /** + Convert this state to a JSON-serializable object. When custom + fields should be serialized, you can pass them in as an object + mapping property names (in the resulting object, which should + not use `doc` or `selection`) to fields. + */ + toJSON(fields?: { + [prop: string]: StateField; + }): any; + /** + Deserialize a state from its JSON representation. When custom + fields should be deserialized, pass the same object you passed + to [`toJSON`](https://codemirror.net/6/docs/ref/#state.EditorState.toJSON) when serializing as + third argument. + */ + static fromJSON(json: any, config?: EditorStateConfig, fields?: { + [prop: string]: StateField; + }): EditorState; + /** + Create a new state. You'll usually only need this when + initializing an editor—updated states are created by applying + transactions. + */ + static create(config?: EditorStateConfig): EditorState; + /** + A facet that, when enabled, causes the editor to allow multiple + ranges to be selected. Be careful though, because by default the + editor relies on the native DOM selection, which cannot handle + multiple selections. An extension like + [`drawSelection`](https://codemirror.net/6/docs/ref/#view.drawSelection) can be used to make + secondary selections visible to the user. + */ + static allowMultipleSelections: Facet; + /** + Configures the tab size to use in this state. The first + (highest-precedence) value of the facet is used. If no value is + given, this defaults to 4. + */ + static tabSize: Facet; + /** + The size (in columns) of a tab in the document, determined by + the [`tabSize`](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize) facet. + */ + get tabSize(): number; + /** + The line separator to use. By default, any of `"\n"`, `"\r\n"` + and `"\r"` is treated as a separator when splitting lines, and + lines are joined with `"\n"`. + + When you configure a value here, only that precise separator + will be used, allowing you to round-trip documents through the + editor without normalizing line separators. + */ + static lineSeparator: Facet; + /** + Get the proper [line-break](https://codemirror.net/6/docs/ref/#state.EditorState^lineSeparator) + string for this state. + */ + get lineBreak(): string; + /** + This facet controls the value of the + [`readOnly`](https://codemirror.net/6/docs/ref/#state.EditorState.readOnly) getter, which is + consulted by commands and extensions that implement editing + functionality to determine whether they should apply. It + defaults to false, but when its highest-precedence value is + `true`, such functionality disables itself. + + Not to be confused with + [`EditorView.editable`](https://codemirror.net/6/docs/ref/#view.EditorView^editable), which + controls whether the editor's DOM is set to be editable (and + thus focusable). + */ + static readOnly: Facet; + /** + Returns true when the editor is + [configured](https://codemirror.net/6/docs/ref/#state.EditorState^readOnly) to be read-only. + */ + get readOnly(): boolean; + /** + Registers translation phrases. The + [`phrase`](https://codemirror.net/6/docs/ref/#state.EditorState.phrase) method will look through + all objects registered with this facet to find translations for + its argument. + */ + static phrases: Facet<{ + [key: string]: string; + }, readonly { + [key: string]: string; + }[]>; + /** + Look up a translation for the given phrase (via the + [`phrases`](https://codemirror.net/6/docs/ref/#state.EditorState^phrases) facet), or return the + original string if no translation is found. + + If additional arguments are passed, they will be inserted in + place of markers like `$1` (for the first value) and `$2`, etc. + A single `$` is equivalent to `$1`, and `$$` will produce a + literal dollar sign. + */ + phrase(phrase: string, ...insert: any[]): string; + /** + A facet used to register [language + data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) providers. + */ + static languageData: Facet<(state: EditorState, pos: number, side: 0 | 1 | -1) => readonly { + [name: string]: any; + }[], readonly ((state: EditorState, pos: number, side: 0 | 1 | -1) => readonly { + [name: string]: any; + }[])[]>; + /** + Find the values for a given language data field, provided by the + the [`languageData`](https://codemirror.net/6/docs/ref/#state.EditorState^languageData) facet. + + Examples of language data fields are... + + - [`"commentTokens"`](https://codemirror.net/6/docs/ref/#commands.CommentTokens) for specifying + comment syntax. + - [`"autocomplete"`](https://codemirror.net/6/docs/ref/#autocomplete.autocompletion^config.override) + for providing language-specific completion sources. + - [`"wordChars"`](https://codemirror.net/6/docs/ref/#state.EditorState.charCategorizer) for adding + characters that should be considered part of words in this + language. + - [`"closeBrackets"`](https://codemirror.net/6/docs/ref/#autocomplete.CloseBracketConfig) controls + bracket closing behavior. + */ + languageDataAt(name: string, pos: number, side?: -1 | 0 | 1): readonly T[]; + /** + Return a function that can categorize strings (expected to + represent a single [grapheme cluster](https://codemirror.net/6/docs/ref/#state.findClusterBreak)) + into one of: + + - Word (contains an alphanumeric character or a character + explicitly listed in the local language's `"wordChars"` + language data, which should be a string) + - Space (contains only whitespace) + - Other (anything else) + */ + charCategorizer(at: number): (char: string) => CharCategory; + /** + Find the word at the given position, meaning the range + containing all [word](https://codemirror.net/6/docs/ref/#state.CharCategory.Word) characters + around it. If no word characters are adjacent to the position, + this returns null. + */ + wordAt(pos: number): SelectionRange | null; + /** + Facet used to register change filters, which are called for each + transaction (unless explicitly + [disabled](https://codemirror.net/6/docs/ref/#state.TransactionSpec.filter)), and can suppress + part of the transaction's changes. + + Such a function can return `true` to indicate that it doesn't + want to do anything, `false` to completely stop the changes in + the transaction, or a set of ranges in which changes should be + suppressed. Such ranges are represented as an array of numbers, + with each pair of two numbers indicating the start and end of a + range. So for example `[10, 20, 100, 110]` suppresses changes + between 10 and 20, and between 100 and 110. + */ + static changeFilter: Facet<(tr: Transaction) => boolean | readonly number[], readonly ((tr: Transaction) => boolean | readonly number[])[]>; + /** + Facet used to register a hook that gets a chance to update or + replace transaction specs before they are applied. This will + only be applied for transactions that don't have + [`filter`](https://codemirror.net/6/docs/ref/#state.TransactionSpec.filter) set to `false`. You + can either return a single transaction spec (possibly the input + transaction), or an array of specs (which will be combined in + the same way as the arguments to + [`EditorState.update`](https://codemirror.net/6/docs/ref/#state.EditorState.update)). + + When possible, it is recommended to avoid accessing + [`Transaction.state`](https://codemirror.net/6/docs/ref/#state.Transaction.state) in a filter, + since it will force creation of a state that will then be + discarded again, if the transaction is actually filtered. + + (This functionality should be used with care. Indiscriminately + modifying transaction is likely to break something or degrade + the user experience.) + */ + static transactionFilter: Facet<(tr: Transaction) => TransactionSpec | readonly TransactionSpec[], readonly ((tr: Transaction) => TransactionSpec | readonly TransactionSpec[])[]>; + /** + This is a more limited form of + [`transactionFilter`](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter), + which can only add + [annotations](https://codemirror.net/6/docs/ref/#state.TransactionSpec.annotations) and + [effects](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects). _But_, this type + of filter runs even if the transaction has disabled regular + [filtering](https://codemirror.net/6/docs/ref/#state.TransactionSpec.filter), making it suitable + for effects that don't need to touch the changes or selection, + but do want to process every transaction. + + Extenders run _after_ filters, when both are present. + */ + static transactionExtender: Facet<(tr: Transaction) => Pick | null, readonly ((tr: Transaction) => Pick | null)[]>; +} + +/** +Subtype of [`Command`](https://codemirror.net/6/docs/ref/#view.Command) that doesn't require access +to the actual editor view. Mostly useful to define commands that +can be run and tested outside of a browser environment. +*/ +type StateCommand = (target: { + state: EditorState; + dispatch: (transaction: Transaction) => void; +}) => boolean; + +/** +Utility function for combining behaviors to fill in a config +object from an array of provided configs. `defaults` should hold +default values for all optional fields in `Config`. + +The function will, by default, error +when a field gets two values that aren't `===`-equal, but you can +provide combine functions per field to do something else. +*/ +declare function combineConfig(configs: readonly Partial[], defaults: Partial, // Should hold only the optional properties of Config, but I haven't managed to express that +combine?: { + [P in keyof Config]?: (first: Config[P], second: Config[P]) => Config[P]; +}): Config; + +/** +Each range is associated with a value, which must inherit from +this class. +*/ +declare abstract class RangeValue { + /** + Compare this value with another value. Used when comparing + rangesets. The default implementation compares by identity. + Unless you are only creating a fixed number of unique instances + of your value type, it is a good idea to implement this + properly. + */ + eq(other: RangeValue): boolean; + /** + The bias value at the start of the range. Determines how the + range is positioned relative to other ranges starting at this + position. Defaults to 0. + */ + startSide: number; + /** + The bias value at the end of the range. Defaults to 0. + */ + endSide: number; + /** + The mode with which the location of the range should be mapped + when its `from` and `to` are the same, to decide whether a + change deletes the range. Defaults to `MapMode.TrackDel`. + */ + mapMode: MapMode; + /** + Determines whether this value marks a point range. Regular + ranges affect the part of the document they cover, and are + meaningless when empty. Point ranges have a meaning on their + own. When non-empty, a point range is treated as atomic and + shadows any ranges contained in it. + */ + point: boolean; + /** + Create a [range](https://codemirror.net/6/docs/ref/#state.Range) with this value. + */ + range(from: number, to?: number): Range; +} +/** +A range associates a value with a range of positions. +*/ +declare class Range { + /** + The range's start position. + */ + readonly from: number; + /** + Its end position. + */ + readonly to: number; + /** + The value associated with this range. + */ + readonly value: T; + private constructor(); +} +/** +Collection of methods used when comparing range sets. +*/ +interface RangeComparator { + /** + Notifies the comparator that a range (in positions in the new + document) has the given sets of values associated with it, which + are different in the old (A) and new (B) sets. + */ + compareRange(from: number, to: number, activeA: T[], activeB: T[]): void; + /** + Notification for a changed (or inserted, or deleted) point range. + */ + comparePoint(from: number, to: number, pointA: T | null, pointB: T | null): void; + /** + Notification for a changed boundary between ranges. For example, + if the same span is covered by two partial ranges before and one + bigger range after, this is called at the point where the ranges + used to be split. + */ + boundChange?(pos: number): void; +} +/** +Methods used when iterating over the spans created by a set of +ranges. The entire iterated range will be covered with either +`span` or `point` calls. +*/ +interface SpanIterator { + /** + Called for any ranges not covered by point decorations. `active` + holds the values that the range is marked with (and may be + empty). `openStart` indicates how many of those ranges are open + (continued) at the start of the span. + */ + span(from: number, to: number, active: readonly T[], openStart: number): void; + /** + Called when going over a point decoration. The active range + decorations that cover the point and have a higher precedence + are provided in `active`. The open count in `openStart` counts + the number of those ranges that started before the point and. If + the point started before the iterated range, `openStart` will be + `active.length + 1` to signal this. + */ + point(from: number, to: number, value: T, active: readonly T[], openStart: number, index: number): void; +} +/** +A range cursor is an object that moves to the next range every +time you call `next` on it. Note that, unlike ES6 iterators, these +start out pointing at the first element, so you should call `next` +only after reading the first range (if any). +*/ +interface RangeCursor { + /** + Move the iterator forward. + */ + next: () => void; + /** + The next range's value. Holds `null` when the cursor has reached + its end. + */ + value: T | null; + /** + The next range's start position. + */ + from: number; + /** + The next end position. + */ + to: number; +} +type RangeSetUpdate = { + /** + An array of ranges to add. If given, this should be sorted by + `from` position and `startSide` unless + [`sort`](https://codemirror.net/6/docs/ref/#state.RangeSet.update^updateSpec.sort) is given as + `true`. + */ + add?: readonly Range[]; + /** + Indicates whether the library should sort the ranges in `add`. + Defaults to `false`. + */ + sort?: boolean; + /** + Filter the ranges already in the set. Only those for which this + function returns `true` are kept. + */ + filter?: (from: number, to: number, value: T) => boolean; + /** + Can be used to limit the range on which the filter is + applied. Filtering only a small range, as opposed to the entire + set, can make updates cheaper. + */ + filterFrom?: number; + /** + The end position to apply the filter to. + */ + filterTo?: number; +}; +/** +A range set stores a collection of [ranges](https://codemirror.net/6/docs/ref/#state.Range) in a +way that makes them efficient to [map](https://codemirror.net/6/docs/ref/#state.RangeSet.map) and +[update](https://codemirror.net/6/docs/ref/#state.RangeSet.update). This is an immutable data +structure. +*/ +declare class RangeSet { + private constructor(); + /** + The number of ranges in the set. + */ + get size(): number; + /** + Update the range set, optionally adding new ranges or filtering + out existing ones. + + (Note: The type parameter is just there as a kludge to work + around TypeScript variance issues that prevented `RangeSet` + from being a subtype of `RangeSet` when `X` is a subtype of + `Y`.) + */ + update(updateSpec: RangeSetUpdate): RangeSet; + /** + Map this range set through a set of changes, return the new set. + */ + map(changes: ChangeDesc): RangeSet; + /** + Iterate over the ranges that touch the region `from` to `to`, + calling `f` for each. There is no guarantee that the ranges will + be reported in any specific order. When the callback returns + `false`, iteration stops. + */ + between(from: number, to: number, f: (from: number, to: number, value: T) => void | false): void; + /** + Iterate over the ranges in this set, in order, including all + ranges that end at or after `from`. + */ + iter(from?: number): RangeCursor; + /** + Iterate over the ranges in a collection of sets, in order, + starting from `from`. + */ + static iter(sets: readonly RangeSet[], from?: number): RangeCursor; + /** + Iterate over two groups of sets, calling methods on `comparator` + to notify it of possible differences. + */ + static compare(oldSets: readonly RangeSet[], newSets: readonly RangeSet[], + /** + This indicates how the underlying data changed between these + ranges, and is needed to synchronize the iteration. + */ + textDiff: ChangeDesc, comparator: RangeComparator, + /** + Can be used to ignore all non-point ranges, and points below + the given size. When -1, all ranges are compared. + */ + minPointSize?: number): void; + /** + Compare the contents of two groups of range sets, returning true + if they are equivalent in the given range. + */ + static eq(oldSets: readonly RangeSet[], newSets: readonly RangeSet[], from?: number, to?: number): boolean; + /** + Iterate over a group of range sets at the same time, notifying + the iterator about the ranges covering every given piece of + content. Returns the open count (see + [`SpanIterator.span`](https://codemirror.net/6/docs/ref/#state.SpanIterator.span)) at the end + of the iteration. + */ + static spans(sets: readonly RangeSet[], from: number, to: number, iterator: SpanIterator, + /** + When given and greater than -1, only points of at least this + size are taken into account. + */ + minPointSize?: number): number; + /** + Create a range set for the given range or array of ranges. By + default, this expects the ranges to be _sorted_ (by start + position and, if two start at the same position, + `value.startSide`). You can pass `true` as second argument to + cause the method to sort them. + */ + static of(ranges: readonly Range[] | Range, sort?: boolean): RangeSet; + /** + Join an array of range sets into a single set. + */ + static join(sets: readonly RangeSet[]): RangeSet; + /** + The empty set of ranges. + */ + static empty: RangeSet; +} +/** +A range set builder is a data structure that helps build up a +[range set](https://codemirror.net/6/docs/ref/#state.RangeSet) directly, without first allocating +an array of [`Range`](https://codemirror.net/6/docs/ref/#state.Range) objects. +*/ +declare class RangeSetBuilder { + private chunks; + private chunkPos; + private chunkStart; + private last; + private lastFrom; + private lastTo; + private from; + private to; + private value; + private maxPoint; + private setMaxPoint; + private nextLayer; + private finishChunk; + /** + Create an empty builder. + */ + constructor(); + /** + Add a range. Ranges should be added in sorted (by `from` and + `value.startSide`) order. + */ + add(from: number, to: number, value: T): void; + /** + Finish the range set. Returns the new set. The builder can't be + used anymore after this has been called. + */ + finish(): RangeSet; +} + +/** +Returns a next grapheme cluster break _after_ (not equal to) +`pos`, if `forward` is true, or before otherwise. Returns `pos` +itself if no further cluster break is available in the string. +Moves across surrogate pairs, extending characters (when +`includeExtending` is true), characters joined with zero-width +joiners, and flag emoji. +*/ +declare function findClusterBreak(str: string, pos: number, forward?: boolean, includeExtending?: boolean): number; +/** +Find the code point at the given position in a string (like the +[`codePointAt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt) +string method). +*/ +declare function codePointAt(str: string, pos: number): number; +/** +Given a Unicode codepoint, return the JavaScript string that +respresents it (like +[`String.fromCodePoint`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint)). +*/ +declare function fromCodePoint(code: number): string; +/** +The amount of positions a character takes up in a JavaScript string. +*/ +declare function codePointSize(code: number): 1 | 2; + +/** +Count the column position at the given offset into the string, +taking extending characters and tab size into account. +*/ +declare function countColumn(string: string, tabSize: number, to?: number): number; +/** +Find the offset that corresponds to the given column position in a +string, taking extending characters and tab size into account. By +default, the string length is returned when it is too short to +reach the column. Pass `strict` true to make it return -1 in that +situation. +*/ +declare function findColumn(string: string, col: number, tabSize: number, strict?: boolean): number; + +export { Annotation, AnnotationType, ChangeDesc, ChangeSet, type ChangeSpec, CharCategory, Compartment, EditorSelection, EditorState, type EditorStateConfig, type Extension, Facet, type FacetReader, Line, MapMode, Prec, Range, type RangeComparator, type RangeCursor, RangeSet, RangeSetBuilder, RangeValue, SelectionRange, type SpanIterator, type StateCommand, StateEffect, StateEffectType, StateField, Text, type TextIterator, Transaction, type TransactionSpec, codePointAt, codePointSize, combineConfig, countColumn, findClusterBreak, findColumn, fromCodePoint }; diff --git a/node_modules/@codemirror/state/dist/index.d.ts b/node_modules/@codemirror/state/dist/index.d.ts new file mode 100644 index 0000000..f3adc66 --- /dev/null +++ b/node_modules/@codemirror/state/dist/index.d.ts @@ -0,0 +1,1700 @@ +/** +A text iterator iterates over a sequence of strings. When +iterating over a [`Text`](https://codemirror.net/6/docs/ref/#state.Text) document, result values will +either be lines or line breaks. +*/ +interface TextIterator extends Iterator, Iterable { + /** + Retrieve the next string. Optionally skip a given number of + positions after the current position. Always returns the object + itself. + */ + next(skip?: number): this; + /** + The current string. Will be the empty string when the cursor is + at its end or `next` hasn't been called on it yet. + */ + value: string; + /** + Whether the end of the iteration has been reached. You should + probably check this right after calling `next`. + */ + done: boolean; + /** + Whether the current string represents a line break. + */ + lineBreak: boolean; +} +/** +The data structure for documents. @nonabstract +*/ +declare abstract class Text implements Iterable { + /** + The length of the string. + */ + abstract readonly length: number; + /** + The number of lines in the string (always >= 1). + */ + abstract readonly lines: number; + /** + Get the line description around the given position. + */ + lineAt(pos: number): Line; + /** + Get the description for the given (1-based) line number. + */ + line(n: number): Line; + /** + Replace a range of the text with the given content. + */ + replace(from: number, to: number, text: Text): Text; + /** + Append another document to this one. + */ + append(other: Text): Text; + /** + Retrieve the text between the given points. + */ + slice(from: number, to?: number): Text; + /** + Retrieve a part of the document as a string + */ + abstract sliceString(from: number, to?: number, lineSep?: string): string; + /** + Test whether this text is equal to another instance. + */ + eq(other: Text): boolean; + /** + Iterate over the text. When `dir` is `-1`, iteration happens + from end to start. This will return lines and the breaks between + them as separate strings. + */ + iter(dir?: 1 | -1): TextIterator; + /** + Iterate over a range of the text. When `from` > `to`, the + iterator will run in reverse. + */ + iterRange(from: number, to?: number): TextIterator; + /** + Return a cursor that iterates over the given range of lines, + _without_ returning the line breaks between, and yielding empty + strings for empty lines. + + When `from` and `to` are given, they should be 1-based line numbers. + */ + iterLines(from?: number, to?: number): TextIterator; + /** + Return the document as a string, using newline characters to + separate lines. + */ + toString(): string; + /** + Convert the document to an array of lines (which can be + deserialized again via [`Text.of`](https://codemirror.net/6/docs/ref/#state.Text^of)). + */ + toJSON(): string[]; + /** + If this is a branch node, `children` will hold the `Text` + objects that it is made up of. For leaf nodes, this holds null. + */ + abstract readonly children: readonly Text[] | null; + [Symbol.iterator]: () => Iterator; + /** + Create a `Text` instance for the given array of lines. + */ + static of(text: readonly string[]): Text; + /** + The empty document. + */ + static empty: Text; +} +/** +This type describes a line in the document. It is created +on-demand when lines are [queried](https://codemirror.net/6/docs/ref/#state.Text.lineAt). +*/ +declare class Line { + /** + The position of the start of the line. + */ + readonly from: number; + /** + The position at the end of the line (_before_ the line break, + or at the end of document for the last line). + */ + readonly to: number; + /** + This line's line number (1-based). + */ + readonly number: number; + /** + The line's content. + */ + readonly text: string; + /** + The length of the line (not including any line break after it). + */ + get length(): number; +} + +/** +Distinguishes different ways in which positions can be mapped. +*/ +declare enum MapMode { + /** + Map a position to a valid new position, even when its context + was deleted. + */ + Simple = 0, + /** + Return null if deletion happens across the position. + */ + TrackDel = 1, + /** + Return null if the character _before_ the position is deleted. + */ + TrackBefore = 2, + /** + Return null if the character _after_ the position is deleted. + */ + TrackAfter = 3 +} +/** +A change description is a variant of [change set](https://codemirror.net/6/docs/ref/#state.ChangeSet) +that doesn't store the inserted text. As such, it can't be +applied, but is cheaper to store and manipulate. +*/ +declare class ChangeDesc { + /** + The length of the document before the change. + */ + get length(): number; + /** + The length of the document after the change. + */ + get newLength(): number; + /** + False when there are actual changes in this set. + */ + get empty(): boolean; + /** + Iterate over the unchanged parts left by these changes. `posA` + provides the position of the range in the old document, `posB` + the new position in the changed document. + */ + iterGaps(f: (posA: number, posB: number, length: number) => void): void; + /** + Iterate over the ranges changed by these changes. (See + [`ChangeSet.iterChanges`](https://codemirror.net/6/docs/ref/#state.ChangeSet.iterChanges) for a + variant that also provides you with the inserted text.) + `fromA`/`toA` provides the extent of the change in the starting + document, `fromB`/`toB` the extent of the replacement in the + changed document. + + When `individual` is true, adjacent changes (which are kept + separate for [position mapping](https://codemirror.net/6/docs/ref/#state.ChangeDesc.mapPos)) are + reported separately. + */ + iterChangedRanges(f: (fromA: number, toA: number, fromB: number, toB: number) => void, individual?: boolean): void; + /** + Get a description of the inverted form of these changes. + */ + get invertedDesc(): ChangeDesc; + /** + Compute the combined effect of applying another set of changes + after this one. The length of the document after this set should + match the length before `other`. + */ + composeDesc(other: ChangeDesc): ChangeDesc; + /** + Map this description, which should start with the same document + as `other`, over another set of changes, so that it can be + applied after it. When `before` is true, map as if the changes + in `this` happened before the ones in `other`. + */ + mapDesc(other: ChangeDesc, before?: boolean): ChangeDesc; + /** + Map a given position through these changes, to produce a + position pointing into the new document. + + `assoc` indicates which side the position should be associated + with. When it is negative or zero, the mapping will try to keep + the position close to the character before it (if any), and will + move it before insertions at that point or replacements across + that point. When it is positive, the position is associated with + the character after it, and will be moved forward for insertions + at or replacements across the position. Defaults to -1. + + `mode` determines whether deletions should be + [reported](https://codemirror.net/6/docs/ref/#state.MapMode). It defaults to + [`MapMode.Simple`](https://codemirror.net/6/docs/ref/#state.MapMode.Simple) (don't report + deletions). + */ + mapPos(pos: number, assoc?: number): number; + mapPos(pos: number, assoc: number, mode: MapMode): number | null; + /** + Check whether these changes touch a given range. When one of the + changes entirely covers the range, the string `"cover"` is + returned. + */ + touchesRange(from: number, to?: number): boolean | "cover"; + /** + Serialize this change desc to a JSON-representable value. + */ + toJSON(): readonly number[]; + /** + Create a change desc from its JSON representation (as produced + by [`toJSON`](https://codemirror.net/6/docs/ref/#state.ChangeDesc.toJSON). + */ + static fromJSON(json: any): ChangeDesc; +} +/** +This type is used as argument to +[`EditorState.changes`](https://codemirror.net/6/docs/ref/#state.EditorState.changes) and in the +[`changes` field](https://codemirror.net/6/docs/ref/#state.TransactionSpec.changes) of transaction +specs to succinctly describe document changes. It may either be a +plain object describing a change (a deletion, insertion, or +replacement, depending on which fields are present), a [change +set](https://codemirror.net/6/docs/ref/#state.ChangeSet), or an array of change specs. +*/ +type ChangeSpec = { + from: number; + to?: number; + insert?: string | Text; +} | ChangeSet | readonly ChangeSpec[]; +/** +A change set represents a group of modifications to a document. It +stores the document length, and can only be applied to documents +with exactly that length. +*/ +declare class ChangeSet extends ChangeDesc { + private constructor(); + /** + Apply the changes to a document, returning the modified + document. + */ + apply(doc: Text): Text; + mapDesc(other: ChangeDesc, before?: boolean): ChangeDesc; + /** + Given the document as it existed _before_ the changes, return a + change set that represents the inverse of this set, which could + be used to go from the document created by the changes back to + the document as it existed before the changes. + */ + invert(doc: Text): ChangeSet; + /** + Combine two subsequent change sets into a single set. `other` + must start in the document produced by `this`. If `this` goes + `docA` → `docB` and `other` represents `docB` → `docC`, the + returned value will represent the change `docA` → `docC`. + */ + compose(other: ChangeSet): ChangeSet; + /** + Given another change set starting in the same document, maps this + change set over the other, producing a new change set that can be + applied to the document produced by applying `other`. When + `before` is `true`, order changes as if `this` comes before + `other`, otherwise (the default) treat `other` as coming first. + + Given two changes `A` and `B`, `A.compose(B.map(A))` and + `B.compose(A.map(B, true))` will produce the same document. This + provides a basic form of [operational + transformation](https://en.wikipedia.org/wiki/Operational_transformation), + and can be used for collaborative editing. + */ + map(other: ChangeDesc, before?: boolean): ChangeSet; + /** + Iterate over the changed ranges in the document, calling `f` for + each, with the range in the original document (`fromA`-`toA`) + and the range that replaces it in the new document + (`fromB`-`toB`). + + When `individual` is true, adjacent changes are reported + separately. + */ + iterChanges(f: (fromA: number, toA: number, fromB: number, toB: number, inserted: Text) => void, individual?: boolean): void; + /** + Get a [change description](https://codemirror.net/6/docs/ref/#state.ChangeDesc) for this change + set. + */ + get desc(): ChangeDesc; + /** + Serialize this change set to a JSON-representable value. + */ + toJSON(): any; + /** + Create a change set for the given changes, for a document of the + given length, using `lineSep` as line separator. + */ + static of(changes: ChangeSpec, length: number, lineSep?: string): ChangeSet; + /** + Create an empty changeset of the given length. + */ + static empty(length: number): ChangeSet; + /** + Create a changeset from its JSON representation (as produced by + [`toJSON`](https://codemirror.net/6/docs/ref/#state.ChangeSet.toJSON). + */ + static fromJSON(json: any): ChangeSet; +} + +/** +A single selection range. When +[`allowMultipleSelections`](https://codemirror.net/6/docs/ref/#state.EditorState^allowMultipleSelections) +is enabled, a [selection](https://codemirror.net/6/docs/ref/#state.EditorSelection) may hold +multiple ranges. By default, selections hold exactly one range. +*/ +declare class SelectionRange { + /** + The lower boundary of the range. + */ + readonly from: number; + /** + The upper boundary of the range. + */ + readonly to: number; + private flags; + private constructor(); + /** + The anchor of the range—the side that doesn't move when you + extend it. + */ + get anchor(): number; + /** + The head of the range, which is moved when the range is + [extended](https://codemirror.net/6/docs/ref/#state.SelectionRange.extend). + */ + get head(): number; + /** + True when `anchor` and `head` are at the same position. + */ + get empty(): boolean; + /** + If this is a cursor that is explicitly associated with the + character on one of its sides, this returns the side. -1 means + the character before its position, 1 the character after, and 0 + means no association. + */ + get assoc(): -1 | 0 | 1; + /** + The bidirectional text level associated with this cursor, if + any. + */ + get bidiLevel(): number | null; + /** + The goal column (stored vertical offset) associated with a + cursor. This is used to preserve the vertical position when + [moving](https://codemirror.net/6/docs/ref/#view.EditorView.moveVertically) across + lines of different length. + */ + get goalColumn(): number | undefined; + /** + Map this range through a change, producing a valid range in the + updated document. + */ + map(change: ChangeDesc, assoc?: number): SelectionRange; + /** + Extend this range to cover at least `from` to `to`. + */ + extend(from: number, to?: number): SelectionRange; + /** + Compare this range to another range. + */ + eq(other: SelectionRange, includeAssoc?: boolean): boolean; + /** + Return a JSON-serializable object representing the range. + */ + toJSON(): any; + /** + Convert a JSON representation of a range to a `SelectionRange` + instance. + */ + static fromJSON(json: any): SelectionRange; +} +/** +An editor selection holds one or more selection ranges. +*/ +declare class EditorSelection { + /** + The ranges in the selection, sorted by position. Ranges cannot + overlap (but they may touch, if they aren't empty). + */ + readonly ranges: readonly SelectionRange[]; + /** + The index of the _main_ range in the selection (which is + usually the range that was added last). + */ + readonly mainIndex: number; + private constructor(); + /** + Map a selection through a change. Used to adjust the selection + position for changes. + */ + map(change: ChangeDesc, assoc?: number): EditorSelection; + /** + Compare this selection to another selection. By default, ranges + are compared only by position. When `includeAssoc` is true, + cursor ranges must also have the same + [`assoc`](https://codemirror.net/6/docs/ref/#state.SelectionRange.assoc) value. + */ + eq(other: EditorSelection, includeAssoc?: boolean): boolean; + /** + Get the primary selection range. Usually, you should make sure + your code applies to _all_ ranges, by using methods like + [`changeByRange`](https://codemirror.net/6/docs/ref/#state.EditorState.changeByRange). + */ + get main(): SelectionRange; + /** + Make sure the selection only has one range. Returns a selection + holding only the main range from this selection. + */ + asSingle(): EditorSelection; + /** + Extend this selection with an extra range. + */ + addRange(range: SelectionRange, main?: boolean): EditorSelection; + /** + Replace a given range with another range, and then normalize the + selection to merge and sort ranges if necessary. + */ + replaceRange(range: SelectionRange, which?: number): EditorSelection; + /** + Convert this selection to an object that can be serialized to + JSON. + */ + toJSON(): any; + /** + Create a selection from a JSON representation. + */ + static fromJSON(json: any): EditorSelection; + /** + Create a selection holding a single range. + */ + static single(anchor: number, head?: number): EditorSelection; + /** + Sort and merge the given set of ranges, creating a valid + selection. + */ + static create(ranges: readonly SelectionRange[], mainIndex?: number): EditorSelection; + /** + Create a cursor selection range at the given position. You can + safely ignore the optional arguments in most situations. + */ + static cursor(pos: number, assoc?: number, bidiLevel?: number, goalColumn?: number): SelectionRange; + /** + Create a selection range. + */ + static range(anchor: number, head: number, goalColumn?: number, bidiLevel?: number): SelectionRange; +} + +type FacetConfig = { + /** + How to combine the input values into a single output value. When + not given, the array of input values becomes the output. This + function will immediately be called on creating the facet, with + an empty array, to compute the facet's default value when no + inputs are present. + */ + combine?: (value: readonly Input[]) => Output; + /** + How to compare output values to determine whether the value of + the facet changed. Defaults to comparing by `===` or, if no + `combine` function was given, comparing each element of the + array with `===`. + */ + compare?: (a: Output, b: Output) => boolean; + /** + How to compare input values to avoid recomputing the output + value when no inputs changed. Defaults to comparing with `===`. + */ + compareInput?: (a: Input, b: Input) => boolean; + /** + Forbids dynamic inputs to this facet. + */ + static?: boolean; + /** + If given, these extension(s) (or the result of calling the given + function with the facet) will be added to any state where this + facet is provided. (Note that, while a facet's default value can + be read from a state even if the facet wasn't present in the + state at all, these extensions won't be added in that + situation.) + */ + enables?: Extension | ((self: Facet) => Extension); +}; +/** +A facet is a labeled value that is associated with an editor +state. It takes inputs from any number of extensions, and combines +those into a single output value. + +Examples of uses of facets are the [tab +size](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize), [editor +attributes](https://codemirror.net/6/docs/ref/#view.EditorView^editorAttributes), and [update +listeners](https://codemirror.net/6/docs/ref/#view.EditorView^updateListener). + +Note that `Facet` instances can be used anywhere where +[`FacetReader`](https://codemirror.net/6/docs/ref/#state.FacetReader) is expected. +*/ +declare class Facet implements FacetReader { + private isStatic; + private constructor(); + /** + Returns a facet reader for this facet, which can be used to + [read](https://codemirror.net/6/docs/ref/#state.EditorState.facet) it but not to define values for it. + */ + get reader(): FacetReader; + /** + Define a new facet. + */ + static define(config?: FacetConfig): Facet; + /** + Returns an extension that adds the given value to this facet. + */ + of(value: Input): Extension; + /** + Create an extension that computes a value for the facet from a + state. You must take care to declare the parts of the state that + this value depends on, since your function is only called again + for a new state when one of those parts changed. + + In cases where your value depends only on a single field, you'll + want to use the [`from`](https://codemirror.net/6/docs/ref/#state.Facet.from) method instead. + */ + compute(deps: readonly Slot[], get: (state: EditorState) => Input): Extension; + /** + Create an extension that computes zero or more values for this + facet from a state. + */ + computeN(deps: readonly Slot[], get: (state: EditorState) => readonly Input[]): Extension; + /** + Shorthand method for registering a facet source with a state + field as input. If the field's type corresponds to this facet's + input type, the getter function can be omitted. If given, it + will be used to retrieve the input from the field value. + */ + from(field: StateField): Extension; + from(field: StateField, get: (value: T) => Input): Extension; + tag: Output; +} +/** +A facet reader can be used to fetch the value of a facet, through +[`EditorState.facet`](https://codemirror.net/6/docs/ref/#state.EditorState.facet) or as a dependency +in [`Facet.compute`](https://codemirror.net/6/docs/ref/#state.Facet.compute), but not to define new +values for the facet. +*/ +type FacetReader = { + /** + Dummy tag that makes sure TypeScript doesn't consider all object + types as conforming to this type. Not actually present on the + object. + */ + tag: Output; +}; +type Slot = FacetReader | StateField | "doc" | "selection"; +type StateFieldSpec = { + /** + Creates the initial value for the field when a state is created. + */ + create: (state: EditorState) => Value; + /** + Compute a new value from the field's previous value and a + [transaction](https://codemirror.net/6/docs/ref/#state.Transaction). + */ + update: (value: Value, transaction: Transaction) => Value; + /** + Compare two values of the field, returning `true` when they are + the same. This is used to avoid recomputing facets that depend + on the field when its value did not change. Defaults to using + `===`. + */ + compare?: (a: Value, b: Value) => boolean; + /** + Provide extensions based on this field. The given function will + be called once with the initialized field. It will usually want + to call some facet's [`from`](https://codemirror.net/6/docs/ref/#state.Facet.from) method to + create facet inputs from this field, but can also return other + extensions that should be enabled when the field is present in a + configuration. + */ + provide?: (field: StateField) => Extension; + /** + A function used to serialize this field's content to JSON. Only + necessary when this field is included in the argument to + [`EditorState.toJSON`](https://codemirror.net/6/docs/ref/#state.EditorState.toJSON). + */ + toJSON?: (value: Value, state: EditorState) => any; + /** + A function that deserializes the JSON representation of this + field's content. + */ + fromJSON?: (json: any, state: EditorState) => Value; +}; +/** +Fields can store additional information in an editor state, and +keep it in sync with the rest of the state. +*/ +declare class StateField { + private createF; + private updateF; + private compareF; + private constructor(); + /** + Define a state field. + */ + static define(config: StateFieldSpec): StateField; + private create; + /** + Returns an extension that enables this field and overrides the + way it is initialized. Can be useful when you need to provide a + non-default starting value for the field. + */ + init(create: (state: EditorState) => Value): Extension; + /** + State field instances can be used as + [`Extension`](https://codemirror.net/6/docs/ref/#state.Extension) values to enable the field in a + given state. + */ + get extension(): Extension; +} +/** +Extension values can be +[provided](https://codemirror.net/6/docs/ref/#state.EditorStateConfig.extensions) when creating a +state to attach various kinds of configuration and behavior +information. They can either be built-in extension-providing +objects, such as [state fields](https://codemirror.net/6/docs/ref/#state.StateField) or [facet +providers](https://codemirror.net/6/docs/ref/#state.Facet.of), or objects with an extension in its +`extension` property. Extensions can be nested in arrays +arbitrarily deep—they will be flattened when processed. +*/ +type Extension = { + extension: Extension; +} | readonly Extension[]; +/** +By default extensions are registered in the order they are found +in the flattened form of nested array that was provided. +Individual extension values can be assigned a precedence to +override this. Extensions that do not have a precedence set get +the precedence of the nearest parent with a precedence, or +[`default`](https://codemirror.net/6/docs/ref/#state.Prec.default) if there is no such parent. The +final ordering of extensions is determined by first sorting by +precedence and then by order within each precedence. +*/ +declare const Prec: { + /** + The highest precedence level, for extensions that should end up + near the start of the precedence ordering. + */ + highest: (ext: Extension) => Extension; + /** + A higher-than-default precedence, for extensions that should + come before those with default precedence. + */ + high: (ext: Extension) => Extension; + /** + The default precedence, which is also used for extensions + without an explicit precedence. + */ + default: (ext: Extension) => Extension; + /** + A lower-than-default precedence. + */ + low: (ext: Extension) => Extension; + /** + The lowest precedence level. Meant for things that should end up + near the end of the extension order. + */ + lowest: (ext: Extension) => Extension; +}; +/** +Extension compartments can be used to make a configuration +dynamic. By [wrapping](https://codemirror.net/6/docs/ref/#state.Compartment.of) part of your +configuration in a compartment, you can later +[replace](https://codemirror.net/6/docs/ref/#state.Compartment.reconfigure) that part through a +transaction. +*/ +declare class Compartment { + /** + Create an instance of this compartment to add to your [state + configuration](https://codemirror.net/6/docs/ref/#state.EditorStateConfig.extensions). + */ + of(ext: Extension): Extension; + /** + Create an [effect](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) that + reconfigures this compartment. + */ + reconfigure(content: Extension): StateEffect; + /** + Get the current content of the compartment in the state, or + `undefined` if it isn't present. + */ + get(state: EditorState): Extension | undefined; +} + +/** +Annotations are tagged values that are used to add metadata to +transactions in an extensible way. They should be used to model +things that effect the entire transaction (such as its [time +stamp](https://codemirror.net/6/docs/ref/#state.Transaction^time) or information about its +[origin](https://codemirror.net/6/docs/ref/#state.Transaction^userEvent)). For effects that happen +_alongside_ the other changes made by the transaction, [state +effects](https://codemirror.net/6/docs/ref/#state.StateEffect) are more appropriate. +*/ +declare class Annotation { + /** + The annotation type. + */ + readonly type: AnnotationType; + /** + The value of this annotation. + */ + readonly value: T; + /** + Define a new type of annotation. + */ + static define(): AnnotationType; + private _isAnnotation; +} +/** +Marker that identifies a type of [annotation](https://codemirror.net/6/docs/ref/#state.Annotation). +*/ +declare class AnnotationType { + /** + Create an instance of this annotation. + */ + of(value: T): Annotation; +} +interface StateEffectSpec { + /** + Provides a way to map an effect like this through a position + mapping. When not given, the effects will simply not be mapped. + When the function returns `undefined`, that means the mapping + deletes the effect. + */ + map?: (value: Value, mapping: ChangeDesc) => Value | undefined; +} +/** +Representation of a type of state effect. Defined with +[`StateEffect.define`](https://codemirror.net/6/docs/ref/#state.StateEffect^define). +*/ +declare class StateEffectType { + /** + @internal + */ + readonly map: (value: any, mapping: ChangeDesc) => any | undefined; + /** + Create a [state effect](https://codemirror.net/6/docs/ref/#state.StateEffect) instance of this + type. + */ + of(value: Value): StateEffect; +} +/** +State effects can be used to represent additional effects +associated with a [transaction](https://codemirror.net/6/docs/ref/#state.Transaction.effects). They +are often useful to model changes to custom [state +fields](https://codemirror.net/6/docs/ref/#state.StateField), when those changes aren't implicit in +document or selection changes. +*/ +declare class StateEffect { + /** + The value of this effect. + */ + readonly value: Value; + /** + Map this effect through a position mapping. Will return + `undefined` when that ends up deleting the effect. + */ + map(mapping: ChangeDesc): StateEffect | undefined; + /** + Tells you whether this effect object is of a given + [type](https://codemirror.net/6/docs/ref/#state.StateEffectType). + */ + is(type: StateEffectType): this is StateEffect; + /** + Define a new effect type. The type parameter indicates the type + of values that his effect holds. It should be a type that + doesn't include `undefined`, since that is used in + [mapping](https://codemirror.net/6/docs/ref/#state.StateEffect.map) to indicate that an effect is + removed. + */ + static define(spec?: StateEffectSpec): StateEffectType; + /** + Map an array of effects through a change set. + */ + static mapEffects(effects: readonly StateEffect[], mapping: ChangeDesc): readonly StateEffect[]; + /** + This effect can be used to reconfigure the root extensions of + the editor. Doing this will discard any extensions + [appended](https://codemirror.net/6/docs/ref/#state.StateEffect^appendConfig), but does not reset + the content of [reconfigured](https://codemirror.net/6/docs/ref/#state.Compartment.reconfigure) + compartments. + */ + static reconfigure: StateEffectType; + /** + Append extensions to the top-level configuration of the editor. + */ + static appendConfig: StateEffectType; +} +/** +Describes a [transaction](https://codemirror.net/6/docs/ref/#state.Transaction) when calling the +[`EditorState.update`](https://codemirror.net/6/docs/ref/#state.EditorState.update) method. +*/ +interface TransactionSpec { + /** + The changes to the document made by this transaction. + */ + changes?: ChangeSpec; + /** + When set, this transaction explicitly updates the selection. + Offsets in this selection should refer to the document as it is + _after_ the transaction. + */ + selection?: EditorSelection | { + anchor: number; + head?: number; + } | undefined; + /** + Attach [state effects](https://codemirror.net/6/docs/ref/#state.StateEffect) to this transaction. + Again, when they contain positions and this same spec makes + changes, those positions should refer to positions in the + updated document. + */ + effects?: StateEffect | readonly StateEffect[]; + /** + Set [annotations](https://codemirror.net/6/docs/ref/#state.Annotation) for this transaction. + */ + annotations?: Annotation | readonly Annotation[]; + /** + Shorthand for `annotations:` [`Transaction.userEvent`](https://codemirror.net/6/docs/ref/#state.Transaction^userEvent)`.of(...)`. + */ + userEvent?: string; + /** + When set to `true`, the transaction is marked as needing to + scroll the current selection into view. + */ + scrollIntoView?: boolean; + /** + By default, transactions can be modified by [change + filters](https://codemirror.net/6/docs/ref/#state.EditorState^changeFilter) and [transaction + filters](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter). You can set this + to `false` to disable that. This can be necessary for + transactions that, for example, include annotations that must be + kept consistent with their changes. + */ + filter?: boolean; + /** + Normally, when multiple specs are combined (for example by + [`EditorState.update`](https://codemirror.net/6/docs/ref/#state.EditorState.update)), the + positions in `changes` are taken to refer to the document + positions in the initial document. When a spec has `sequental` + set to true, its positions will be taken to refer to the + document created by the specs before it instead. + */ + sequential?: boolean; +} +/** +Changes to the editor state are grouped into transactions. +Typically, a user action creates a single transaction, which may +contain any number of document changes, may change the selection, +or have other effects. Create a transaction by calling +[`EditorState.update`](https://codemirror.net/6/docs/ref/#state.EditorState.update), or immediately +dispatch one by calling +[`EditorView.dispatch`](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch). +*/ +declare class Transaction { + /** + The state from which the transaction starts. + */ + readonly startState: EditorState; + /** + The document changes made by this transaction. + */ + readonly changes: ChangeSet; + /** + The selection set by this transaction, or undefined if it + doesn't explicitly set a selection. + */ + readonly selection: EditorSelection | undefined; + /** + The effects added to the transaction. + */ + readonly effects: readonly StateEffect[]; + /** + Whether the selection should be scrolled into view after this + transaction is dispatched. + */ + readonly scrollIntoView: boolean; + private constructor(); + /** + The new document produced by the transaction. Contrary to + [`.state`](https://codemirror.net/6/docs/ref/#state.Transaction.state)`.doc`, accessing this won't + force the entire new state to be computed right away, so it is + recommended that [transaction + filters](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter) use this getter + when they need to look at the new document. + */ + get newDoc(): Text; + /** + The new selection produced by the transaction. If + [`this.selection`](https://codemirror.net/6/docs/ref/#state.Transaction.selection) is undefined, + this will [map](https://codemirror.net/6/docs/ref/#state.EditorSelection.map) the start state's + current selection through the changes made by the transaction. + */ + get newSelection(): EditorSelection; + /** + The new state created by the transaction. Computed on demand + (but retained for subsequent access), so it is recommended not to + access it in [transaction + filters](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter) when possible. + */ + get state(): EditorState; + /** + Get the value of the given annotation type, if any. + */ + annotation(type: AnnotationType): T | undefined; + /** + Indicates whether the transaction changed the document. + */ + get docChanged(): boolean; + /** + Indicates whether this transaction reconfigures the state + (through a [configuration compartment](https://codemirror.net/6/docs/ref/#state.Compartment) or + with a top-level configuration + [effect](https://codemirror.net/6/docs/ref/#state.StateEffect^reconfigure). + */ + get reconfigured(): boolean; + /** + Returns true if the transaction has a [user + event](https://codemirror.net/6/docs/ref/#state.Transaction^userEvent) annotation that is equal to + or more specific than `event`. For example, if the transaction + has `"select.pointer"` as user event, `"select"` and + `"select.pointer"` will match it. + */ + isUserEvent(event: string): boolean; + /** + Annotation used to store transaction timestamps. Automatically + added to every transaction, holding `Date.now()`. + */ + static time: AnnotationType; + /** + Annotation used to associate a transaction with a user interface + event. Holds a string identifying the event, using a + dot-separated format to support attaching more specific + information. The events used by the core libraries are: + + - `"input"` when content is entered + - `"input.type"` for typed input + - `"input.type.compose"` for composition + - `"input.paste"` for pasted input + - `"input.drop"` when adding content with drag-and-drop + - `"input.complete"` when autocompleting + - `"delete"` when the user deletes content + - `"delete.selection"` when deleting the selection + - `"delete.forward"` when deleting forward from the selection + - `"delete.backward"` when deleting backward from the selection + - `"delete.cut"` when cutting to the clipboard + - `"move"` when content is moved + - `"move.drop"` when content is moved within the editor through drag-and-drop + - `"select"` when explicitly changing the selection + - `"select.pointer"` when selecting with a mouse or other pointing device + - `"undo"` and `"redo"` for history actions + + Use [`isUserEvent`](https://codemirror.net/6/docs/ref/#state.Transaction.isUserEvent) to check + whether the annotation matches a given event. + */ + static userEvent: AnnotationType; + /** + Annotation indicating whether a transaction should be added to + the undo history or not. + */ + static addToHistory: AnnotationType; + /** + Annotation indicating (when present and true) that a transaction + represents a change made by some other actor, not the user. This + is used, for example, to tag other people's changes in + collaborative editing. + */ + static remote: AnnotationType; +} + +/** +The categories produced by a [character +categorizer](https://codemirror.net/6/docs/ref/#state.EditorState.charCategorizer). These are used +do things like selecting by word. +*/ +declare enum CharCategory { + /** + Word characters. + */ + Word = 0, + /** + Whitespace. + */ + Space = 1, + /** + Anything else. + */ + Other = 2 +} + +/** +Options passed when [creating](https://codemirror.net/6/docs/ref/#state.EditorState^create) an +editor state. +*/ +interface EditorStateConfig { + /** + The initial document. Defaults to an empty document. Can be + provided either as a plain string (which will be split into + lines according to the value of the [`lineSeparator` + facet](https://codemirror.net/6/docs/ref/#state.EditorState^lineSeparator)), or an instance of + the [`Text`](https://codemirror.net/6/docs/ref/#state.Text) class (which is what the state will use + to represent the document). + */ + doc?: string | Text; + /** + The starting selection. Defaults to a cursor at the very start + of the document. + */ + selection?: EditorSelection | { + anchor: number; + head?: number; + }; + /** + [Extension(s)](https://codemirror.net/6/docs/ref/#state.Extension) to associate with this state. + */ + extensions?: Extension; +} +/** +The editor state class is a persistent (immutable) data structure. +To update a state, you [create](https://codemirror.net/6/docs/ref/#state.EditorState.update) a +[transaction](https://codemirror.net/6/docs/ref/#state.Transaction), which produces a _new_ state +instance, without modifying the original object. + +As such, _never_ mutate properties of a state directly. That'll +just break things. +*/ +declare class EditorState { + /** + The current document. + */ + readonly doc: Text; + /** + The current selection. + */ + readonly selection: EditorSelection; + private constructor(); + /** + Retrieve the value of a [state field](https://codemirror.net/6/docs/ref/#state.StateField). Throws + an error when the state doesn't have that field, unless you pass + `false` as second parameter. + */ + field(field: StateField): T; + field(field: StateField, require: false): T | undefined; + /** + Create a [transaction](https://codemirror.net/6/docs/ref/#state.Transaction) that updates this + state. Any number of [transaction specs](https://codemirror.net/6/docs/ref/#state.TransactionSpec) + can be passed. Unless + [`sequential`](https://codemirror.net/6/docs/ref/#state.TransactionSpec.sequential) is set, the + [changes](https://codemirror.net/6/docs/ref/#state.TransactionSpec.changes) (if any) of each spec + are assumed to start in the _current_ document (not the document + produced by previous specs), and its + [selection](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection) and + [effects](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) are assumed to refer + to the document created by its _own_ changes. The resulting + transaction contains the combined effect of all the different + specs. For [selection](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection), later + specs take precedence over earlier ones. + */ + update(...specs: readonly TransactionSpec[]): Transaction; + /** + Create a [transaction spec](https://codemirror.net/6/docs/ref/#state.TransactionSpec) that + replaces every selection range with the given content. + */ + replaceSelection(text: string | Text): TransactionSpec; + /** + Create a set of changes and a new selection by running the given + function for each range in the active selection. The function + can return an optional set of changes (in the coordinate space + of the start document), plus an updated range (in the coordinate + space of the document produced by the call's own changes). This + method will merge all the changes and ranges into a single + changeset and selection, and return it as a [transaction + spec](https://codemirror.net/6/docs/ref/#state.TransactionSpec), which can be passed to + [`update`](https://codemirror.net/6/docs/ref/#state.EditorState.update). + */ + changeByRange(f: (range: SelectionRange) => { + range: SelectionRange; + changes?: ChangeSpec; + effects?: StateEffect | readonly StateEffect[]; + }): { + changes: ChangeSet; + selection: EditorSelection; + effects: readonly StateEffect[]; + }; + /** + Create a [change set](https://codemirror.net/6/docs/ref/#state.ChangeSet) from the given change + description, taking the state's document length and line + separator into account. + */ + changes(spec?: ChangeSpec): ChangeSet; + /** + Using the state's [line + separator](https://codemirror.net/6/docs/ref/#state.EditorState^lineSeparator), create a + [`Text`](https://codemirror.net/6/docs/ref/#state.Text) instance from the given string. + */ + toText(string: string): Text; + /** + Return the given range of the document as a string. + */ + sliceDoc(from?: number, to?: number): string; + /** + Get the value of a state [facet](https://codemirror.net/6/docs/ref/#state.Facet). + */ + facet(facet: FacetReader): Output; + /** + Convert this state to a JSON-serializable object. When custom + fields should be serialized, you can pass them in as an object + mapping property names (in the resulting object, which should + not use `doc` or `selection`) to fields. + */ + toJSON(fields?: { + [prop: string]: StateField; + }): any; + /** + Deserialize a state from its JSON representation. When custom + fields should be deserialized, pass the same object you passed + to [`toJSON`](https://codemirror.net/6/docs/ref/#state.EditorState.toJSON) when serializing as + third argument. + */ + static fromJSON(json: any, config?: EditorStateConfig, fields?: { + [prop: string]: StateField; + }): EditorState; + /** + Create a new state. You'll usually only need this when + initializing an editor—updated states are created by applying + transactions. + */ + static create(config?: EditorStateConfig): EditorState; + /** + A facet that, when enabled, causes the editor to allow multiple + ranges to be selected. Be careful though, because by default the + editor relies on the native DOM selection, which cannot handle + multiple selections. An extension like + [`drawSelection`](https://codemirror.net/6/docs/ref/#view.drawSelection) can be used to make + secondary selections visible to the user. + */ + static allowMultipleSelections: Facet; + /** + Configures the tab size to use in this state. The first + (highest-precedence) value of the facet is used. If no value is + given, this defaults to 4. + */ + static tabSize: Facet; + /** + The size (in columns) of a tab in the document, determined by + the [`tabSize`](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize) facet. + */ + get tabSize(): number; + /** + The line separator to use. By default, any of `"\n"`, `"\r\n"` + and `"\r"` is treated as a separator when splitting lines, and + lines are joined with `"\n"`. + + When you configure a value here, only that precise separator + will be used, allowing you to round-trip documents through the + editor without normalizing line separators. + */ + static lineSeparator: Facet; + /** + Get the proper [line-break](https://codemirror.net/6/docs/ref/#state.EditorState^lineSeparator) + string for this state. + */ + get lineBreak(): string; + /** + This facet controls the value of the + [`readOnly`](https://codemirror.net/6/docs/ref/#state.EditorState.readOnly) getter, which is + consulted by commands and extensions that implement editing + functionality to determine whether they should apply. It + defaults to false, but when its highest-precedence value is + `true`, such functionality disables itself. + + Not to be confused with + [`EditorView.editable`](https://codemirror.net/6/docs/ref/#view.EditorView^editable), which + controls whether the editor's DOM is set to be editable (and + thus focusable). + */ + static readOnly: Facet; + /** + Returns true when the editor is + [configured](https://codemirror.net/6/docs/ref/#state.EditorState^readOnly) to be read-only. + */ + get readOnly(): boolean; + /** + Registers translation phrases. The + [`phrase`](https://codemirror.net/6/docs/ref/#state.EditorState.phrase) method will look through + all objects registered with this facet to find translations for + its argument. + */ + static phrases: Facet<{ + [key: string]: string; + }, readonly { + [key: string]: string; + }[]>; + /** + Look up a translation for the given phrase (via the + [`phrases`](https://codemirror.net/6/docs/ref/#state.EditorState^phrases) facet), or return the + original string if no translation is found. + + If additional arguments are passed, they will be inserted in + place of markers like `$1` (for the first value) and `$2`, etc. + A single `$` is equivalent to `$1`, and `$$` will produce a + literal dollar sign. + */ + phrase(phrase: string, ...insert: any[]): string; + /** + A facet used to register [language + data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) providers. + */ + static languageData: Facet<(state: EditorState, pos: number, side: 0 | 1 | -1) => readonly { + [name: string]: any; + }[], readonly ((state: EditorState, pos: number, side: 0 | 1 | -1) => readonly { + [name: string]: any; + }[])[]>; + /** + Find the values for a given language data field, provided by the + the [`languageData`](https://codemirror.net/6/docs/ref/#state.EditorState^languageData) facet. + + Examples of language data fields are... + + - [`"commentTokens"`](https://codemirror.net/6/docs/ref/#commands.CommentTokens) for specifying + comment syntax. + - [`"autocomplete"`](https://codemirror.net/6/docs/ref/#autocomplete.autocompletion^config.override) + for providing language-specific completion sources. + - [`"wordChars"`](https://codemirror.net/6/docs/ref/#state.EditorState.charCategorizer) for adding + characters that should be considered part of words in this + language. + - [`"closeBrackets"`](https://codemirror.net/6/docs/ref/#autocomplete.CloseBracketConfig) controls + bracket closing behavior. + */ + languageDataAt(name: string, pos: number, side?: -1 | 0 | 1): readonly T[]; + /** + Return a function that can categorize strings (expected to + represent a single [grapheme cluster](https://codemirror.net/6/docs/ref/#state.findClusterBreak)) + into one of: + + - Word (contains an alphanumeric character or a character + explicitly listed in the local language's `"wordChars"` + language data, which should be a string) + - Space (contains only whitespace) + - Other (anything else) + */ + charCategorizer(at: number): (char: string) => CharCategory; + /** + Find the word at the given position, meaning the range + containing all [word](https://codemirror.net/6/docs/ref/#state.CharCategory.Word) characters + around it. If no word characters are adjacent to the position, + this returns null. + */ + wordAt(pos: number): SelectionRange | null; + /** + Facet used to register change filters, which are called for each + transaction (unless explicitly + [disabled](https://codemirror.net/6/docs/ref/#state.TransactionSpec.filter)), and can suppress + part of the transaction's changes. + + Such a function can return `true` to indicate that it doesn't + want to do anything, `false` to completely stop the changes in + the transaction, or a set of ranges in which changes should be + suppressed. Such ranges are represented as an array of numbers, + with each pair of two numbers indicating the start and end of a + range. So for example `[10, 20, 100, 110]` suppresses changes + between 10 and 20, and between 100 and 110. + */ + static changeFilter: Facet<(tr: Transaction) => boolean | readonly number[], readonly ((tr: Transaction) => boolean | readonly number[])[]>; + /** + Facet used to register a hook that gets a chance to update or + replace transaction specs before they are applied. This will + only be applied for transactions that don't have + [`filter`](https://codemirror.net/6/docs/ref/#state.TransactionSpec.filter) set to `false`. You + can either return a single transaction spec (possibly the input + transaction), or an array of specs (which will be combined in + the same way as the arguments to + [`EditorState.update`](https://codemirror.net/6/docs/ref/#state.EditorState.update)). + + When possible, it is recommended to avoid accessing + [`Transaction.state`](https://codemirror.net/6/docs/ref/#state.Transaction.state) in a filter, + since it will force creation of a state that will then be + discarded again, if the transaction is actually filtered. + + (This functionality should be used with care. Indiscriminately + modifying transaction is likely to break something or degrade + the user experience.) + */ + static transactionFilter: Facet<(tr: Transaction) => TransactionSpec | readonly TransactionSpec[], readonly ((tr: Transaction) => TransactionSpec | readonly TransactionSpec[])[]>; + /** + This is a more limited form of + [`transactionFilter`](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter), + which can only add + [annotations](https://codemirror.net/6/docs/ref/#state.TransactionSpec.annotations) and + [effects](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects). _But_, this type + of filter runs even if the transaction has disabled regular + [filtering](https://codemirror.net/6/docs/ref/#state.TransactionSpec.filter), making it suitable + for effects that don't need to touch the changes or selection, + but do want to process every transaction. + + Extenders run _after_ filters, when both are present. + */ + static transactionExtender: Facet<(tr: Transaction) => Pick | null, readonly ((tr: Transaction) => Pick | null)[]>; +} + +/** +Subtype of [`Command`](https://codemirror.net/6/docs/ref/#view.Command) that doesn't require access +to the actual editor view. Mostly useful to define commands that +can be run and tested outside of a browser environment. +*/ +type StateCommand = (target: { + state: EditorState; + dispatch: (transaction: Transaction) => void; +}) => boolean; + +/** +Utility function for combining behaviors to fill in a config +object from an array of provided configs. `defaults` should hold +default values for all optional fields in `Config`. + +The function will, by default, error +when a field gets two values that aren't `===`-equal, but you can +provide combine functions per field to do something else. +*/ +declare function combineConfig(configs: readonly Partial[], defaults: Partial, // Should hold only the optional properties of Config, but I haven't managed to express that +combine?: { + [P in keyof Config]?: (first: Config[P], second: Config[P]) => Config[P]; +}): Config; + +/** +Each range is associated with a value, which must inherit from +this class. +*/ +declare abstract class RangeValue { + /** + Compare this value with another value. Used when comparing + rangesets. The default implementation compares by identity. + Unless you are only creating a fixed number of unique instances + of your value type, it is a good idea to implement this + properly. + */ + eq(other: RangeValue): boolean; + /** + The bias value at the start of the range. Determines how the + range is positioned relative to other ranges starting at this + position. Defaults to 0. + */ + startSide: number; + /** + The bias value at the end of the range. Defaults to 0. + */ + endSide: number; + /** + The mode with which the location of the range should be mapped + when its `from` and `to` are the same, to decide whether a + change deletes the range. Defaults to `MapMode.TrackDel`. + */ + mapMode: MapMode; + /** + Determines whether this value marks a point range. Regular + ranges affect the part of the document they cover, and are + meaningless when empty. Point ranges have a meaning on their + own. When non-empty, a point range is treated as atomic and + shadows any ranges contained in it. + */ + point: boolean; + /** + Create a [range](https://codemirror.net/6/docs/ref/#state.Range) with this value. + */ + range(from: number, to?: number): Range; +} +/** +A range associates a value with a range of positions. +*/ +declare class Range { + /** + The range's start position. + */ + readonly from: number; + /** + Its end position. + */ + readonly to: number; + /** + The value associated with this range. + */ + readonly value: T; + private constructor(); +} +/** +Collection of methods used when comparing range sets. +*/ +interface RangeComparator { + /** + Notifies the comparator that a range (in positions in the new + document) has the given sets of values associated with it, which + are different in the old (A) and new (B) sets. + */ + compareRange(from: number, to: number, activeA: T[], activeB: T[]): void; + /** + Notification for a changed (or inserted, or deleted) point range. + */ + comparePoint(from: number, to: number, pointA: T | null, pointB: T | null): void; + /** + Notification for a changed boundary between ranges. For example, + if the same span is covered by two partial ranges before and one + bigger range after, this is called at the point where the ranges + used to be split. + */ + boundChange?(pos: number): void; +} +/** +Methods used when iterating over the spans created by a set of +ranges. The entire iterated range will be covered with either +`span` or `point` calls. +*/ +interface SpanIterator { + /** + Called for any ranges not covered by point decorations. `active` + holds the values that the range is marked with (and may be + empty). `openStart` indicates how many of those ranges are open + (continued) at the start of the span. + */ + span(from: number, to: number, active: readonly T[], openStart: number): void; + /** + Called when going over a point decoration. The active range + decorations that cover the point and have a higher precedence + are provided in `active`. The open count in `openStart` counts + the number of those ranges that started before the point and. If + the point started before the iterated range, `openStart` will be + `active.length + 1` to signal this. + */ + point(from: number, to: number, value: T, active: readonly T[], openStart: number, index: number): void; +} +/** +A range cursor is an object that moves to the next range every +time you call `next` on it. Note that, unlike ES6 iterators, these +start out pointing at the first element, so you should call `next` +only after reading the first range (if any). +*/ +interface RangeCursor { + /** + Move the iterator forward. + */ + next: () => void; + /** + The next range's value. Holds `null` when the cursor has reached + its end. + */ + value: T | null; + /** + The next range's start position. + */ + from: number; + /** + The next end position. + */ + to: number; +} +type RangeSetUpdate = { + /** + An array of ranges to add. If given, this should be sorted by + `from` position and `startSide` unless + [`sort`](https://codemirror.net/6/docs/ref/#state.RangeSet.update^updateSpec.sort) is given as + `true`. + */ + add?: readonly Range[]; + /** + Indicates whether the library should sort the ranges in `add`. + Defaults to `false`. + */ + sort?: boolean; + /** + Filter the ranges already in the set. Only those for which this + function returns `true` are kept. + */ + filter?: (from: number, to: number, value: T) => boolean; + /** + Can be used to limit the range on which the filter is + applied. Filtering only a small range, as opposed to the entire + set, can make updates cheaper. + */ + filterFrom?: number; + /** + The end position to apply the filter to. + */ + filterTo?: number; +}; +/** +A range set stores a collection of [ranges](https://codemirror.net/6/docs/ref/#state.Range) in a +way that makes them efficient to [map](https://codemirror.net/6/docs/ref/#state.RangeSet.map) and +[update](https://codemirror.net/6/docs/ref/#state.RangeSet.update). This is an immutable data +structure. +*/ +declare class RangeSet { + private constructor(); + /** + The number of ranges in the set. + */ + get size(): number; + /** + Update the range set, optionally adding new ranges or filtering + out existing ones. + + (Note: The type parameter is just there as a kludge to work + around TypeScript variance issues that prevented `RangeSet` + from being a subtype of `RangeSet` when `X` is a subtype of + `Y`.) + */ + update(updateSpec: RangeSetUpdate): RangeSet; + /** + Map this range set through a set of changes, return the new set. + */ + map(changes: ChangeDesc): RangeSet; + /** + Iterate over the ranges that touch the region `from` to `to`, + calling `f` for each. There is no guarantee that the ranges will + be reported in any specific order. When the callback returns + `false`, iteration stops. + */ + between(from: number, to: number, f: (from: number, to: number, value: T) => void | false): void; + /** + Iterate over the ranges in this set, in order, including all + ranges that end at or after `from`. + */ + iter(from?: number): RangeCursor; + /** + Iterate over the ranges in a collection of sets, in order, + starting from `from`. + */ + static iter(sets: readonly RangeSet[], from?: number): RangeCursor; + /** + Iterate over two groups of sets, calling methods on `comparator` + to notify it of possible differences. + */ + static compare(oldSets: readonly RangeSet[], newSets: readonly RangeSet[], + /** + This indicates how the underlying data changed between these + ranges, and is needed to synchronize the iteration. + */ + textDiff: ChangeDesc, comparator: RangeComparator, + /** + Can be used to ignore all non-point ranges, and points below + the given size. When -1, all ranges are compared. + */ + minPointSize?: number): void; + /** + Compare the contents of two groups of range sets, returning true + if they are equivalent in the given range. + */ + static eq(oldSets: readonly RangeSet[], newSets: readonly RangeSet[], from?: number, to?: number): boolean; + /** + Iterate over a group of range sets at the same time, notifying + the iterator about the ranges covering every given piece of + content. Returns the open count (see + [`SpanIterator.span`](https://codemirror.net/6/docs/ref/#state.SpanIterator.span)) at the end + of the iteration. + */ + static spans(sets: readonly RangeSet[], from: number, to: number, iterator: SpanIterator, + /** + When given and greater than -1, only points of at least this + size are taken into account. + */ + minPointSize?: number): number; + /** + Create a range set for the given range or array of ranges. By + default, this expects the ranges to be _sorted_ (by start + position and, if two start at the same position, + `value.startSide`). You can pass `true` as second argument to + cause the method to sort them. + */ + static of(ranges: readonly Range[] | Range, sort?: boolean): RangeSet; + /** + Join an array of range sets into a single set. + */ + static join(sets: readonly RangeSet[]): RangeSet; + /** + The empty set of ranges. + */ + static empty: RangeSet; +} +/** +A range set builder is a data structure that helps build up a +[range set](https://codemirror.net/6/docs/ref/#state.RangeSet) directly, without first allocating +an array of [`Range`](https://codemirror.net/6/docs/ref/#state.Range) objects. +*/ +declare class RangeSetBuilder { + private chunks; + private chunkPos; + private chunkStart; + private last; + private lastFrom; + private lastTo; + private from; + private to; + private value; + private maxPoint; + private setMaxPoint; + private nextLayer; + private finishChunk; + /** + Create an empty builder. + */ + constructor(); + /** + Add a range. Ranges should be added in sorted (by `from` and + `value.startSide`) order. + */ + add(from: number, to: number, value: T): void; + /** + Finish the range set. Returns the new set. The builder can't be + used anymore after this has been called. + */ + finish(): RangeSet; +} + +/** +Returns a next grapheme cluster break _after_ (not equal to) +`pos`, if `forward` is true, or before otherwise. Returns `pos` +itself if no further cluster break is available in the string. +Moves across surrogate pairs, extending characters (when +`includeExtending` is true), characters joined with zero-width +joiners, and flag emoji. +*/ +declare function findClusterBreak(str: string, pos: number, forward?: boolean, includeExtending?: boolean): number; +/** +Find the code point at the given position in a string (like the +[`codePointAt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt) +string method). +*/ +declare function codePointAt(str: string, pos: number): number; +/** +Given a Unicode codepoint, return the JavaScript string that +respresents it (like +[`String.fromCodePoint`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint)). +*/ +declare function fromCodePoint(code: number): string; +/** +The amount of positions a character takes up in a JavaScript string. +*/ +declare function codePointSize(code: number): 1 | 2; + +/** +Count the column position at the given offset into the string, +taking extending characters and tab size into account. +*/ +declare function countColumn(string: string, tabSize: number, to?: number): number; +/** +Find the offset that corresponds to the given column position in a +string, taking extending characters and tab size into account. By +default, the string length is returned when it is too short to +reach the column. Pass `strict` true to make it return -1 in that +situation. +*/ +declare function findColumn(string: string, col: number, tabSize: number, strict?: boolean): number; + +export { Annotation, AnnotationType, ChangeDesc, ChangeSet, type ChangeSpec, CharCategory, Compartment, EditorSelection, EditorState, type EditorStateConfig, type Extension, Facet, type FacetReader, Line, MapMode, Prec, Range, type RangeComparator, type RangeCursor, RangeSet, RangeSetBuilder, RangeValue, SelectionRange, type SpanIterator, type StateCommand, StateEffect, StateEffectType, StateField, Text, type TextIterator, Transaction, type TransactionSpec, codePointAt, codePointSize, combineConfig, countColumn, findClusterBreak, findColumn, fromCodePoint }; diff --git a/node_modules/@codemirror/state/dist/index.js b/node_modules/@codemirror/state/dist/index.js new file mode 100644 index 0000000..3bd61be --- /dev/null +++ b/node_modules/@codemirror/state/dist/index.js @@ -0,0 +1,3876 @@ +import { findClusterBreak as findClusterBreak$1 } from '@marijn/find-cluster-break'; + +/** +The data structure for documents. @nonabstract +*/ +class Text { + /** + Get the line description around the given position. + */ + lineAt(pos) { + if (pos < 0 || pos > this.length) + throw new RangeError(`Invalid position ${pos} in document of length ${this.length}`); + return this.lineInner(pos, false, 1, 0); + } + /** + Get the description for the given (1-based) line number. + */ + line(n) { + if (n < 1 || n > this.lines) + throw new RangeError(`Invalid line number ${n} in ${this.lines}-line document`); + return this.lineInner(n, true, 1, 0); + } + /** + Replace a range of the text with the given content. + */ + replace(from, to, text) { + [from, to] = clip(this, from, to); + let parts = []; + this.decompose(0, from, parts, 2 /* Open.To */); + if (text.length) + text.decompose(0, text.length, parts, 1 /* Open.From */ | 2 /* Open.To */); + this.decompose(to, this.length, parts, 1 /* Open.From */); + return TextNode.from(parts, this.length - (to - from) + text.length); + } + /** + Append another document to this one. + */ + append(other) { + return this.replace(this.length, this.length, other); + } + /** + Retrieve the text between the given points. + */ + slice(from, to = this.length) { + [from, to] = clip(this, from, to); + let parts = []; + this.decompose(from, to, parts, 0); + return TextNode.from(parts, to - from); + } + /** + Test whether this text is equal to another instance. + */ + eq(other) { + if (other == this) + return true; + if (other.length != this.length || other.lines != this.lines) + return false; + let start = this.scanIdentical(other, 1), end = this.length - this.scanIdentical(other, -1); + let a = new RawTextCursor(this), b = new RawTextCursor(other); + for (let skip = start, pos = start;;) { + a.next(skip); + b.next(skip); + skip = 0; + if (a.lineBreak != b.lineBreak || a.done != b.done || a.value != b.value) + return false; + pos += a.value.length; + if (a.done || pos >= end) + return true; + } + } + /** + Iterate over the text. When `dir` is `-1`, iteration happens + from end to start. This will return lines and the breaks between + them as separate strings. + */ + iter(dir = 1) { return new RawTextCursor(this, dir); } + /** + Iterate over a range of the text. When `from` > `to`, the + iterator will run in reverse. + */ + iterRange(from, to = this.length) { return new PartialTextCursor(this, from, to); } + /** + Return a cursor that iterates over the given range of lines, + _without_ returning the line breaks between, and yielding empty + strings for empty lines. + + When `from` and `to` are given, they should be 1-based line numbers. + */ + iterLines(from, to) { + let inner; + if (from == null) { + inner = this.iter(); + } + else { + if (to == null) + to = this.lines + 1; + let start = this.line(from).from; + inner = this.iterRange(start, Math.max(start, to == this.lines + 1 ? this.length : to <= 1 ? 0 : this.line(to - 1).to)); + } + return new LineCursor(inner); + } + /** + Return the document as a string, using newline characters to + separate lines. + */ + toString() { return this.sliceString(0); } + /** + Convert the document to an array of lines (which can be + deserialized again via [`Text.of`](https://codemirror.net/6/docs/ref/#state.Text^of)). + */ + toJSON() { + let lines = []; + this.flatten(lines); + return lines; + } + /** + @internal + */ + constructor() { } + /** + Create a `Text` instance for the given array of lines. + */ + static of(text) { + if (text.length == 0) + throw new RangeError("A document must have at least one line"); + if (text.length == 1 && !text[0]) + return Text.empty; + return text.length <= 32 /* Tree.Branch */ ? new TextLeaf(text) : TextNode.from(TextLeaf.split(text, [])); + } +} +// Leaves store an array of line strings. There are always line breaks +// between these strings. Leaves are limited in size and have to be +// contained in TextNode instances for bigger documents. +class TextLeaf extends Text { + constructor(text, length = textLength(text)) { + super(); + this.text = text; + this.length = length; + } + get lines() { return this.text.length; } + get children() { return null; } + lineInner(target, isLine, line, offset) { + for (let i = 0;; i++) { + let string = this.text[i], end = offset + string.length; + if ((isLine ? line : end) >= target) + return new Line(offset, end, line, string); + offset = end + 1; + line++; + } + } + decompose(from, to, target, open) { + let text = from <= 0 && to >= this.length ? this + : new TextLeaf(sliceText(this.text, from, to), Math.min(to, this.length) - Math.max(0, from)); + if (open & 1 /* Open.From */) { + let prev = target.pop(); + let joined = appendText(text.text, prev.text.slice(), 0, text.length); + if (joined.length <= 32 /* Tree.Branch */) { + target.push(new TextLeaf(joined, prev.length + text.length)); + } + else { + let mid = joined.length >> 1; + target.push(new TextLeaf(joined.slice(0, mid)), new TextLeaf(joined.slice(mid))); + } + } + else { + target.push(text); + } + } + replace(from, to, text) { + if (!(text instanceof TextLeaf)) + return super.replace(from, to, text); + [from, to] = clip(this, from, to); + let lines = appendText(this.text, appendText(text.text, sliceText(this.text, 0, from)), to); + let newLen = this.length + text.length - (to - from); + if (lines.length <= 32 /* Tree.Branch */) + return new TextLeaf(lines, newLen); + return TextNode.from(TextLeaf.split(lines, []), newLen); + } + sliceString(from, to = this.length, lineSep = "\n") { + [from, to] = clip(this, from, to); + let result = ""; + for (let pos = 0, i = 0; pos <= to && i < this.text.length; i++) { + let line = this.text[i], end = pos + line.length; + if (pos > from && i) + result += lineSep; + if (from < end && to > pos) + result += line.slice(Math.max(0, from - pos), to - pos); + pos = end + 1; + } + return result; + } + flatten(target) { + for (let line of this.text) + target.push(line); + } + scanIdentical() { return 0; } + static split(text, target) { + let part = [], len = -1; + for (let line of text) { + part.push(line); + len += line.length + 1; + if (part.length == 32 /* Tree.Branch */) { + target.push(new TextLeaf(part, len)); + part = []; + len = -1; + } + } + if (len > -1) + target.push(new TextLeaf(part, len)); + return target; + } +} +// Nodes provide the tree structure of the `Text` type. They store a +// number of other nodes or leaves, taking care to balance themselves +// on changes. There are implied line breaks _between_ the children of +// a node (but not before the first or after the last child). +class TextNode extends Text { + constructor(children, length) { + super(); + this.children = children; + this.length = length; + this.lines = 0; + for (let child of children) + this.lines += child.lines; + } + lineInner(target, isLine, line, offset) { + for (let i = 0;; i++) { + let child = this.children[i], end = offset + child.length, endLine = line + child.lines - 1; + if ((isLine ? endLine : end) >= target) + return child.lineInner(target, isLine, line, offset); + offset = end + 1; + line = endLine + 1; + } + } + decompose(from, to, target, open) { + for (let i = 0, pos = 0; pos <= to && i < this.children.length; i++) { + let child = this.children[i], end = pos + child.length; + if (from <= end && to >= pos) { + let childOpen = open & ((pos <= from ? 1 /* Open.From */ : 0) | (end >= to ? 2 /* Open.To */ : 0)); + if (pos >= from && end <= to && !childOpen) + target.push(child); + else + child.decompose(from - pos, to - pos, target, childOpen); + } + pos = end + 1; + } + } + replace(from, to, text) { + [from, to] = clip(this, from, to); + if (text.lines < this.lines) + for (let i = 0, pos = 0; i < this.children.length; i++) { + let child = this.children[i], end = pos + child.length; + // Fast path: if the change only affects one child and the + // child's size remains in the acceptable range, only update + // that child + if (from >= pos && to <= end) { + let updated = child.replace(from - pos, to - pos, text); + let totalLines = this.lines - child.lines + updated.lines; + if (updated.lines < (totalLines >> (5 /* Tree.BranchShift */ - 1)) && + updated.lines > (totalLines >> (5 /* Tree.BranchShift */ + 1))) { + let copy = this.children.slice(); + copy[i] = updated; + return new TextNode(copy, this.length - (to - from) + text.length); + } + return super.replace(pos, end, updated); + } + pos = end + 1; + } + return super.replace(from, to, text); + } + sliceString(from, to = this.length, lineSep = "\n") { + [from, to] = clip(this, from, to); + let result = ""; + for (let i = 0, pos = 0; i < this.children.length && pos <= to; i++) { + let child = this.children[i], end = pos + child.length; + if (pos > from && i) + result += lineSep; + if (from < end && to > pos) + result += child.sliceString(from - pos, to - pos, lineSep); + pos = end + 1; + } + return result; + } + flatten(target) { + for (let child of this.children) + child.flatten(target); + } + scanIdentical(other, dir) { + if (!(other instanceof TextNode)) + return 0; + let length = 0; + let [iA, iB, eA, eB] = dir > 0 ? [0, 0, this.children.length, other.children.length] + : [this.children.length - 1, other.children.length - 1, -1, -1]; + for (;; iA += dir, iB += dir) { + if (iA == eA || iB == eB) + return length; + let chA = this.children[iA], chB = other.children[iB]; + if (chA != chB) + return length + chA.scanIdentical(chB, dir); + length += chA.length + 1; + } + } + static from(children, length = children.reduce((l, ch) => l + ch.length + 1, -1)) { + let lines = 0; + for (let ch of children) + lines += ch.lines; + if (lines < 32 /* Tree.Branch */) { + let flat = []; + for (let ch of children) + ch.flatten(flat); + return new TextLeaf(flat, length); + } + let chunk = Math.max(32 /* Tree.Branch */, lines >> 5 /* Tree.BranchShift */), maxChunk = chunk << 1, minChunk = chunk >> 1; + let chunked = [], currentLines = 0, currentLen = -1, currentChunk = []; + function add(child) { + let last; + if (child.lines > maxChunk && child instanceof TextNode) { + for (let node of child.children) + add(node); + } + else if (child.lines > minChunk && (currentLines > minChunk || !currentLines)) { + flush(); + chunked.push(child); + } + else if (child instanceof TextLeaf && currentLines && + (last = currentChunk[currentChunk.length - 1]) instanceof TextLeaf && + child.lines + last.lines <= 32 /* Tree.Branch */) { + currentLines += child.lines; + currentLen += child.length + 1; + currentChunk[currentChunk.length - 1] = new TextLeaf(last.text.concat(child.text), last.length + 1 + child.length); + } + else { + if (currentLines + child.lines > chunk) + flush(); + currentLines += child.lines; + currentLen += child.length + 1; + currentChunk.push(child); + } + } + function flush() { + if (currentLines == 0) + return; + chunked.push(currentChunk.length == 1 ? currentChunk[0] : TextNode.from(currentChunk, currentLen)); + currentLen = -1; + currentLines = currentChunk.length = 0; + } + for (let child of children) + add(child); + flush(); + return chunked.length == 1 ? chunked[0] : new TextNode(chunked, length); + } +} +Text.empty = /*@__PURE__*/new TextLeaf([""], 0); +function textLength(text) { + let length = -1; + for (let line of text) + length += line.length + 1; + return length; +} +function appendText(text, target, from = 0, to = 1e9) { + for (let pos = 0, i = 0, first = true; i < text.length && pos <= to; i++) { + let line = text[i], end = pos + line.length; + if (end >= from) { + if (end > to) + line = line.slice(0, to - pos); + if (pos < from) + line = line.slice(from - pos); + if (first) { + target[target.length - 1] += line; + first = false; + } + else + target.push(line); + } + pos = end + 1; + } + return target; +} +function sliceText(text, from, to) { + return appendText(text, [""], from, to); +} +class RawTextCursor { + constructor(text, dir = 1) { + this.dir = dir; + this.done = false; + this.lineBreak = false; + this.value = ""; + this.nodes = [text]; + this.offsets = [dir > 0 ? 1 : (text instanceof TextLeaf ? text.text.length : text.children.length) << 1]; + } + nextInner(skip, dir) { + this.done = this.lineBreak = false; + for (;;) { + let last = this.nodes.length - 1; + let top = this.nodes[last], offsetValue = this.offsets[last], offset = offsetValue >> 1; + let size = top instanceof TextLeaf ? top.text.length : top.children.length; + if (offset == (dir > 0 ? size : 0)) { + if (last == 0) { + this.done = true; + this.value = ""; + return this; + } + if (dir > 0) + this.offsets[last - 1]++; + this.nodes.pop(); + this.offsets.pop(); + } + else if ((offsetValue & 1) == (dir > 0 ? 0 : 1)) { + this.offsets[last] += dir; + if (skip == 0) { + this.lineBreak = true; + this.value = "\n"; + return this; + } + skip--; + } + else if (top instanceof TextLeaf) { + // Move to the next string + let next = top.text[offset + (dir < 0 ? -1 : 0)]; + this.offsets[last] += dir; + if (next.length > Math.max(0, skip)) { + this.value = skip == 0 ? next : dir > 0 ? next.slice(skip) : next.slice(0, next.length - skip); + return this; + } + skip -= next.length; + } + else { + let next = top.children[offset + (dir < 0 ? -1 : 0)]; + if (skip > next.length) { + skip -= next.length; + this.offsets[last] += dir; + } + else { + if (dir < 0) + this.offsets[last]--; + this.nodes.push(next); + this.offsets.push(dir > 0 ? 1 : (next instanceof TextLeaf ? next.text.length : next.children.length) << 1); + } + } + } + } + next(skip = 0) { + if (skip < 0) { + this.nextInner(-skip, (-this.dir)); + skip = this.value.length; + } + return this.nextInner(skip, this.dir); + } +} +class PartialTextCursor { + constructor(text, start, end) { + this.value = ""; + this.done = false; + this.cursor = new RawTextCursor(text, start > end ? -1 : 1); + this.pos = start > end ? text.length : 0; + this.from = Math.min(start, end); + this.to = Math.max(start, end); + } + nextInner(skip, dir) { + if (dir < 0 ? this.pos <= this.from : this.pos >= this.to) { + this.value = ""; + this.done = true; + return this; + } + skip += Math.max(0, dir < 0 ? this.pos - this.to : this.from - this.pos); + let limit = dir < 0 ? this.pos - this.from : this.to - this.pos; + if (skip > limit) + skip = limit; + limit -= skip; + let { value } = this.cursor.next(skip); + this.pos += (value.length + skip) * dir; + this.value = value.length <= limit ? value : dir < 0 ? value.slice(value.length - limit) : value.slice(0, limit); + this.done = !this.value; + return this; + } + next(skip = 0) { + if (skip < 0) + skip = Math.max(skip, this.from - this.pos); + else if (skip > 0) + skip = Math.min(skip, this.to - this.pos); + return this.nextInner(skip, this.cursor.dir); + } + get lineBreak() { return this.cursor.lineBreak && this.value != ""; } +} +class LineCursor { + constructor(inner) { + this.inner = inner; + this.afterBreak = true; + this.value = ""; + this.done = false; + } + next(skip = 0) { + let { done, lineBreak, value } = this.inner.next(skip); + if (done && this.afterBreak) { + this.value = ""; + this.afterBreak = false; + } + else if (done) { + this.done = true; + this.value = ""; + } + else if (lineBreak) { + if (this.afterBreak) { + this.value = ""; + } + else { + this.afterBreak = true; + this.next(); + } + } + else { + this.value = value; + this.afterBreak = false; + } + return this; + } + get lineBreak() { return false; } +} +if (typeof Symbol != "undefined") { + Text.prototype[Symbol.iterator] = function () { return this.iter(); }; + RawTextCursor.prototype[Symbol.iterator] = PartialTextCursor.prototype[Symbol.iterator] = + LineCursor.prototype[Symbol.iterator] = function () { return this; }; +} +/** +This type describes a line in the document. It is created +on-demand when lines are [queried](https://codemirror.net/6/docs/ref/#state.Text.lineAt). +*/ +class Line { + /** + @internal + */ + constructor( + /** + The position of the start of the line. + */ + from, + /** + The position at the end of the line (_before_ the line break, + or at the end of document for the last line). + */ + to, + /** + This line's line number (1-based). + */ + number, + /** + The line's content. + */ + text) { + this.from = from; + this.to = to; + this.number = number; + this.text = text; + } + /** + The length of the line (not including any line break after it). + */ + get length() { return this.to - this.from; } +} +function clip(text, from, to) { + from = Math.max(0, Math.min(text.length, from)); + return [from, Math.max(from, Math.min(text.length, to))]; +} + +/** +Returns a next grapheme cluster break _after_ (not equal to) +`pos`, if `forward` is true, or before otherwise. Returns `pos` +itself if no further cluster break is available in the string. +Moves across surrogate pairs, extending characters (when +`includeExtending` is true), characters joined with zero-width +joiners, and flag emoji. +*/ +function findClusterBreak(str, pos, forward = true, includeExtending = true) { + return findClusterBreak$1(str, pos, forward, includeExtending); +} +function surrogateLow(ch) { return ch >= 0xDC00 && ch < 0xE000; } +function surrogateHigh(ch) { return ch >= 0xD800 && ch < 0xDC00; } +/** +Find the code point at the given position in a string (like the +[`codePointAt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt) +string method). +*/ +function codePointAt(str, pos) { + let code0 = str.charCodeAt(pos); + if (!surrogateHigh(code0) || pos + 1 == str.length) + return code0; + let code1 = str.charCodeAt(pos + 1); + if (!surrogateLow(code1)) + return code0; + return ((code0 - 0xd800) << 10) + (code1 - 0xdc00) + 0x10000; +} +/** +Given a Unicode codepoint, return the JavaScript string that +respresents it (like +[`String.fromCodePoint`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint)). +*/ +function fromCodePoint(code) { + if (code <= 0xffff) + return String.fromCharCode(code); + code -= 0x10000; + return String.fromCharCode((code >> 10) + 0xd800, (code & 1023) + 0xdc00); +} +/** +The amount of positions a character takes up in a JavaScript string. +*/ +function codePointSize(code) { return code < 0x10000 ? 1 : 2; } + +const DefaultSplit = /\r\n?|\n/; +/** +Distinguishes different ways in which positions can be mapped. +*/ +var MapMode = /*@__PURE__*/(function (MapMode) { + /** + Map a position to a valid new position, even when its context + was deleted. + */ + MapMode[MapMode["Simple"] = 0] = "Simple"; + /** + Return null if deletion happens across the position. + */ + MapMode[MapMode["TrackDel"] = 1] = "TrackDel"; + /** + Return null if the character _before_ the position is deleted. + */ + MapMode[MapMode["TrackBefore"] = 2] = "TrackBefore"; + /** + Return null if the character _after_ the position is deleted. + */ + MapMode[MapMode["TrackAfter"] = 3] = "TrackAfter"; +return MapMode})(MapMode || (MapMode = {})); +/** +A change description is a variant of [change set](https://codemirror.net/6/docs/ref/#state.ChangeSet) +that doesn't store the inserted text. As such, it can't be +applied, but is cheaper to store and manipulate. +*/ +class ChangeDesc { + // Sections are encoded as pairs of integers. The first is the + // length in the current document, and the second is -1 for + // unaffected sections, and the length of the replacement content + // otherwise. So an insertion would be (0, n>0), a deletion (n>0, + // 0), and a replacement two positive numbers. + /** + @internal + */ + constructor( + /** + @internal + */ + sections) { + this.sections = sections; + } + /** + The length of the document before the change. + */ + get length() { + let result = 0; + for (let i = 0; i < this.sections.length; i += 2) + result += this.sections[i]; + return result; + } + /** + The length of the document after the change. + */ + get newLength() { + let result = 0; + for (let i = 0; i < this.sections.length; i += 2) { + let ins = this.sections[i + 1]; + result += ins < 0 ? this.sections[i] : ins; + } + return result; + } + /** + False when there are actual changes in this set. + */ + get empty() { return this.sections.length == 0 || this.sections.length == 2 && this.sections[1] < 0; } + /** + Iterate over the unchanged parts left by these changes. `posA` + provides the position of the range in the old document, `posB` + the new position in the changed document. + */ + iterGaps(f) { + for (let i = 0, posA = 0, posB = 0; i < this.sections.length;) { + let len = this.sections[i++], ins = this.sections[i++]; + if (ins < 0) { + f(posA, posB, len); + posB += len; + } + else { + posB += ins; + } + posA += len; + } + } + /** + Iterate over the ranges changed by these changes. (See + [`ChangeSet.iterChanges`](https://codemirror.net/6/docs/ref/#state.ChangeSet.iterChanges) for a + variant that also provides you with the inserted text.) + `fromA`/`toA` provides the extent of the change in the starting + document, `fromB`/`toB` the extent of the replacement in the + changed document. + + When `individual` is true, adjacent changes (which are kept + separate for [position mapping](https://codemirror.net/6/docs/ref/#state.ChangeDesc.mapPos)) are + reported separately. + */ + iterChangedRanges(f, individual = false) { + iterChanges(this, f, individual); + } + /** + Get a description of the inverted form of these changes. + */ + get invertedDesc() { + let sections = []; + for (let i = 0; i < this.sections.length;) { + let len = this.sections[i++], ins = this.sections[i++]; + if (ins < 0) + sections.push(len, ins); + else + sections.push(ins, len); + } + return new ChangeDesc(sections); + } + /** + Compute the combined effect of applying another set of changes + after this one. The length of the document after this set should + match the length before `other`. + */ + composeDesc(other) { return this.empty ? other : other.empty ? this : composeSets(this, other); } + /** + Map this description, which should start with the same document + as `other`, over another set of changes, so that it can be + applied after it. When `before` is true, map as if the changes + in `this` happened before the ones in `other`. + */ + mapDesc(other, before = false) { return other.empty ? this : mapSet(this, other, before); } + mapPos(pos, assoc = -1, mode = MapMode.Simple) { + let posA = 0, posB = 0; + for (let i = 0; i < this.sections.length;) { + let len = this.sections[i++], ins = this.sections[i++], endA = posA + len; + if (ins < 0) { + if (endA > pos) + return posB + (pos - posA); + posB += len; + } + else { + if (mode != MapMode.Simple && endA >= pos && + (mode == MapMode.TrackDel && posA < pos && endA > pos || + mode == MapMode.TrackBefore && posA < pos || + mode == MapMode.TrackAfter && endA > pos)) + return null; + if (endA > pos || endA == pos && assoc < 0 && !len) + return pos == posA || assoc < 0 ? posB : posB + ins; + posB += ins; + } + posA = endA; + } + if (pos > posA) + throw new RangeError(`Position ${pos} is out of range for changeset of length ${posA}`); + return posB; + } + /** + Check whether these changes touch a given range. When one of the + changes entirely covers the range, the string `"cover"` is + returned. + */ + touchesRange(from, to = from) { + for (let i = 0, pos = 0; i < this.sections.length && pos <= to;) { + let len = this.sections[i++], ins = this.sections[i++], end = pos + len; + if (ins >= 0 && pos <= to && end >= from) + return pos < from && end > to ? "cover" : true; + pos = end; + } + return false; + } + /** + @internal + */ + toString() { + let result = ""; + for (let i = 0; i < this.sections.length;) { + let len = this.sections[i++], ins = this.sections[i++]; + result += (result ? " " : "") + len + (ins >= 0 ? ":" + ins : ""); + } + return result; + } + /** + Serialize this change desc to a JSON-representable value. + */ + toJSON() { return this.sections; } + /** + Create a change desc from its JSON representation (as produced + by [`toJSON`](https://codemirror.net/6/docs/ref/#state.ChangeDesc.toJSON). + */ + static fromJSON(json) { + if (!Array.isArray(json) || json.length % 2 || json.some(a => typeof a != "number")) + throw new RangeError("Invalid JSON representation of ChangeDesc"); + return new ChangeDesc(json); + } + /** + @internal + */ + static create(sections) { return new ChangeDesc(sections); } +} +/** +A change set represents a group of modifications to a document. It +stores the document length, and can only be applied to documents +with exactly that length. +*/ +class ChangeSet extends ChangeDesc { + constructor(sections, + /** + @internal + */ + inserted) { + super(sections); + this.inserted = inserted; + } + /** + Apply the changes to a document, returning the modified + document. + */ + apply(doc) { + if (this.length != doc.length) + throw new RangeError("Applying change set to a document with the wrong length"); + iterChanges(this, (fromA, toA, fromB, _toB, text) => doc = doc.replace(fromB, fromB + (toA - fromA), text), false); + return doc; + } + mapDesc(other, before = false) { return mapSet(this, other, before, true); } + /** + Given the document as it existed _before_ the changes, return a + change set that represents the inverse of this set, which could + be used to go from the document created by the changes back to + the document as it existed before the changes. + */ + invert(doc) { + let sections = this.sections.slice(), inserted = []; + for (let i = 0, pos = 0; i < sections.length; i += 2) { + let len = sections[i], ins = sections[i + 1]; + if (ins >= 0) { + sections[i] = ins; + sections[i + 1] = len; + let index = i >> 1; + while (inserted.length < index) + inserted.push(Text.empty); + inserted.push(len ? doc.slice(pos, pos + len) : Text.empty); + } + pos += len; + } + return new ChangeSet(sections, inserted); + } + /** + Combine two subsequent change sets into a single set. `other` + must start in the document produced by `this`. If `this` goes + `docA` → `docB` and `other` represents `docB` → `docC`, the + returned value will represent the change `docA` → `docC`. + */ + compose(other) { return this.empty ? other : other.empty ? this : composeSets(this, other, true); } + /** + Given another change set starting in the same document, maps this + change set over the other, producing a new change set that can be + applied to the document produced by applying `other`. When + `before` is `true`, order changes as if `this` comes before + `other`, otherwise (the default) treat `other` as coming first. + + Given two changes `A` and `B`, `A.compose(B.map(A))` and + `B.compose(A.map(B, true))` will produce the same document. This + provides a basic form of [operational + transformation](https://en.wikipedia.org/wiki/Operational_transformation), + and can be used for collaborative editing. + */ + map(other, before = false) { return other.empty ? this : mapSet(this, other, before, true); } + /** + Iterate over the changed ranges in the document, calling `f` for + each, with the range in the original document (`fromA`-`toA`) + and the range that replaces it in the new document + (`fromB`-`toB`). + + When `individual` is true, adjacent changes are reported + separately. + */ + iterChanges(f, individual = false) { + iterChanges(this, f, individual); + } + /** + Get a [change description](https://codemirror.net/6/docs/ref/#state.ChangeDesc) for this change + set. + */ + get desc() { return ChangeDesc.create(this.sections); } + /** + @internal + */ + filter(ranges) { + let resultSections = [], resultInserted = [], filteredSections = []; + let iter = new SectionIter(this); + done: for (let i = 0, pos = 0;;) { + let next = i == ranges.length ? 1e9 : ranges[i++]; + while (pos < next || pos == next && iter.len == 0) { + if (iter.done) + break done; + let len = Math.min(iter.len, next - pos); + addSection(filteredSections, len, -1); + let ins = iter.ins == -1 ? -1 : iter.off == 0 ? iter.ins : 0; + addSection(resultSections, len, ins); + if (ins > 0) + addInsert(resultInserted, resultSections, iter.text); + iter.forward(len); + pos += len; + } + let end = ranges[i++]; + while (pos < end) { + if (iter.done) + break done; + let len = Math.min(iter.len, end - pos); + addSection(resultSections, len, -1); + addSection(filteredSections, len, iter.ins == -1 ? -1 : iter.off == 0 ? iter.ins : 0); + iter.forward(len); + pos += len; + } + } + return { changes: new ChangeSet(resultSections, resultInserted), + filtered: ChangeDesc.create(filteredSections) }; + } + /** + Serialize this change set to a JSON-representable value. + */ + toJSON() { + let parts = []; + for (let i = 0; i < this.sections.length; i += 2) { + let len = this.sections[i], ins = this.sections[i + 1]; + if (ins < 0) + parts.push(len); + else if (ins == 0) + parts.push([len]); + else + parts.push([len].concat(this.inserted[i >> 1].toJSON())); + } + return parts; + } + /** + Create a change set for the given changes, for a document of the + given length, using `lineSep` as line separator. + */ + static of(changes, length, lineSep) { + let sections = [], inserted = [], pos = 0; + let total = null; + function flush(force = false) { + if (!force && !sections.length) + return; + if (pos < length) + addSection(sections, length - pos, -1); + let set = new ChangeSet(sections, inserted); + total = total ? total.compose(set.map(total)) : set; + sections = []; + inserted = []; + pos = 0; + } + function process(spec) { + if (Array.isArray(spec)) { + for (let sub of spec) + process(sub); + } + else if (spec instanceof ChangeSet) { + if (spec.length != length) + throw new RangeError(`Mismatched change set length (got ${spec.length}, expected ${length})`); + flush(); + total = total ? total.compose(spec.map(total)) : spec; + } + else { + let { from, to = from, insert } = spec; + if (from > to || from < 0 || to > length) + throw new RangeError(`Invalid change range ${from} to ${to} (in doc of length ${length})`); + let insText = !insert ? Text.empty : typeof insert == "string" ? Text.of(insert.split(lineSep || DefaultSplit)) : insert; + let insLen = insText.length; + if (from == to && insLen == 0) + return; + if (from < pos) + flush(); + if (from > pos) + addSection(sections, from - pos, -1); + addSection(sections, to - from, insLen); + addInsert(inserted, sections, insText); + pos = to; + } + } + process(changes); + flush(!total); + return total; + } + /** + Create an empty changeset of the given length. + */ + static empty(length) { + return new ChangeSet(length ? [length, -1] : [], []); + } + /** + Create a changeset from its JSON representation (as produced by + [`toJSON`](https://codemirror.net/6/docs/ref/#state.ChangeSet.toJSON). + */ + static fromJSON(json) { + if (!Array.isArray(json)) + throw new RangeError("Invalid JSON representation of ChangeSet"); + let sections = [], inserted = []; + for (let i = 0; i < json.length; i++) { + let part = json[i]; + if (typeof part == "number") { + sections.push(part, -1); + } + else if (!Array.isArray(part) || typeof part[0] != "number" || part.some((e, i) => i && typeof e != "string")) { + throw new RangeError("Invalid JSON representation of ChangeSet"); + } + else if (part.length == 1) { + sections.push(part[0], 0); + } + else { + while (inserted.length < i) + inserted.push(Text.empty); + inserted[i] = Text.of(part.slice(1)); + sections.push(part[0], inserted[i].length); + } + } + return new ChangeSet(sections, inserted); + } + /** + @internal + */ + static createSet(sections, inserted) { + return new ChangeSet(sections, inserted); + } +} +function addSection(sections, len, ins, forceJoin = false) { + if (len == 0 && ins <= 0) + return; + let last = sections.length - 2; + if (last >= 0 && ins <= 0 && ins == sections[last + 1]) + sections[last] += len; + else if (last >= 0 && len == 0 && sections[last] == 0) + sections[last + 1] += ins; + else if (forceJoin) { + sections[last] += len; + sections[last + 1] += ins; + } + else + sections.push(len, ins); +} +function addInsert(values, sections, value) { + if (value.length == 0) + return; + let index = (sections.length - 2) >> 1; + if (index < values.length) { + values[values.length - 1] = values[values.length - 1].append(value); + } + else { + while (values.length < index) + values.push(Text.empty); + values.push(value); + } +} +function iterChanges(desc, f, individual) { + let inserted = desc.inserted; + for (let posA = 0, posB = 0, i = 0; i < desc.sections.length;) { + let len = desc.sections[i++], ins = desc.sections[i++]; + if (ins < 0) { + posA += len; + posB += len; + } + else { + let endA = posA, endB = posB, text = Text.empty; + for (;;) { + endA += len; + endB += ins; + if (ins && inserted) + text = text.append(inserted[(i - 2) >> 1]); + if (individual || i == desc.sections.length || desc.sections[i + 1] < 0) + break; + len = desc.sections[i++]; + ins = desc.sections[i++]; + } + f(posA, endA, posB, endB, text); + posA = endA; + posB = endB; + } + } +} +function mapSet(setA, setB, before, mkSet = false) { + // Produce a copy of setA that applies to the document after setB + // has been applied (assuming both start at the same document). + let sections = [], insert = mkSet ? [] : null; + let a = new SectionIter(setA), b = new SectionIter(setB); + // Iterate over both sets in parallel. inserted tracks, for changes + // in A that have to be processed piece-by-piece, whether their + // content has been inserted already, and refers to the section + // index. + for (let inserted = -1;;) { + if (a.done && b.len || b.done && a.len) { + throw new Error("Mismatched change set lengths"); + } + else if (a.ins == -1 && b.ins == -1) { + // Move across ranges skipped by both sets. + let len = Math.min(a.len, b.len); + addSection(sections, len, -1); + a.forward(len); + b.forward(len); + } + else if (b.ins >= 0 && (a.ins < 0 || inserted == a.i || a.off == 0 && (b.len < a.len || b.len == a.len && !before))) { + // If there's a change in B that comes before the next change in + // A (ordered by start pos, then len, then before flag), skip + // that (and process any changes in A it covers). + let len = b.len; + addSection(sections, b.ins, -1); + while (len) { + let piece = Math.min(a.len, len); + if (a.ins >= 0 && inserted < a.i && a.len <= piece) { + addSection(sections, 0, a.ins); + if (insert) + addInsert(insert, sections, a.text); + inserted = a.i; + } + a.forward(piece); + len -= piece; + } + b.next(); + } + else if (a.ins >= 0) { + // Process the part of a change in A up to the start of the next + // non-deletion change in B (if overlapping). + let len = 0, left = a.len; + while (left) { + if (b.ins == -1) { + let piece = Math.min(left, b.len); + len += piece; + left -= piece; + b.forward(piece); + } + else if (b.ins == 0 && b.len < left) { + left -= b.len; + b.next(); + } + else { + break; + } + } + addSection(sections, len, inserted < a.i ? a.ins : 0); + if (insert && inserted < a.i) + addInsert(insert, sections, a.text); + inserted = a.i; + a.forward(a.len - left); + } + else if (a.done && b.done) { + return insert ? ChangeSet.createSet(sections, insert) : ChangeDesc.create(sections); + } + else { + throw new Error("Mismatched change set lengths"); + } + } +} +function composeSets(setA, setB, mkSet = false) { + let sections = []; + let insert = mkSet ? [] : null; + let a = new SectionIter(setA), b = new SectionIter(setB); + for (let open = false;;) { + if (a.done && b.done) { + return insert ? ChangeSet.createSet(sections, insert) : ChangeDesc.create(sections); + } + else if (a.ins == 0) { // Deletion in A + addSection(sections, a.len, 0, open); + a.next(); + } + else if (b.len == 0 && !b.done) { // Insertion in B + addSection(sections, 0, b.ins, open); + if (insert) + addInsert(insert, sections, b.text); + b.next(); + } + else if (a.done || b.done) { + throw new Error("Mismatched change set lengths"); + } + else { + let len = Math.min(a.len2, b.len), sectionLen = sections.length; + if (a.ins == -1) { + let insB = b.ins == -1 ? -1 : b.off ? 0 : b.ins; + addSection(sections, len, insB, open); + if (insert && insB) + addInsert(insert, sections, b.text); + } + else if (b.ins == -1) { + addSection(sections, a.off ? 0 : a.len, len, open); + if (insert) + addInsert(insert, sections, a.textBit(len)); + } + else { + addSection(sections, a.off ? 0 : a.len, b.off ? 0 : b.ins, open); + if (insert && !b.off) + addInsert(insert, sections, b.text); + } + open = (a.ins > len || b.ins >= 0 && b.len > len) && (open || sections.length > sectionLen); + a.forward2(len); + b.forward(len); + } + } +} +class SectionIter { + constructor(set) { + this.set = set; + this.i = 0; + this.next(); + } + next() { + let { sections } = this.set; + if (this.i < sections.length) { + this.len = sections[this.i++]; + this.ins = sections[this.i++]; + } + else { + this.len = 0; + this.ins = -2; + } + this.off = 0; + } + get done() { return this.ins == -2; } + get len2() { return this.ins < 0 ? this.len : this.ins; } + get text() { + let { inserted } = this.set, index = (this.i - 2) >> 1; + return index >= inserted.length ? Text.empty : inserted[index]; + } + textBit(len) { + let { inserted } = this.set, index = (this.i - 2) >> 1; + return index >= inserted.length && !len ? Text.empty + : inserted[index].slice(this.off, len == null ? undefined : this.off + len); + } + forward(len) { + if (len == this.len) + this.next(); + else { + this.len -= len; + this.off += len; + } + } + forward2(len) { + if (this.ins == -1) + this.forward(len); + else if (len == this.ins) + this.next(); + else { + this.ins -= len; + this.off += len; + } + } +} + +/** +A single selection range. When +[`allowMultipleSelections`](https://codemirror.net/6/docs/ref/#state.EditorState^allowMultipleSelections) +is enabled, a [selection](https://codemirror.net/6/docs/ref/#state.EditorSelection) may hold +multiple ranges. By default, selections hold exactly one range. +*/ +class SelectionRange { + constructor( + /** + The lower boundary of the range. + */ + from, + /** + The upper boundary of the range. + */ + to, flags) { + this.from = from; + this.to = to; + this.flags = flags; + } + /** + The anchor of the range—the side that doesn't move when you + extend it. + */ + get anchor() { return this.flags & 32 /* RangeFlag.Inverted */ ? this.to : this.from; } + /** + The head of the range, which is moved when the range is + [extended](https://codemirror.net/6/docs/ref/#state.SelectionRange.extend). + */ + get head() { return this.flags & 32 /* RangeFlag.Inverted */ ? this.from : this.to; } + /** + True when `anchor` and `head` are at the same position. + */ + get empty() { return this.from == this.to; } + /** + If this is a cursor that is explicitly associated with the + character on one of its sides, this returns the side. -1 means + the character before its position, 1 the character after, and 0 + means no association. + */ + get assoc() { return this.flags & 8 /* RangeFlag.AssocBefore */ ? -1 : this.flags & 16 /* RangeFlag.AssocAfter */ ? 1 : 0; } + /** + The bidirectional text level associated with this cursor, if + any. + */ + get bidiLevel() { + let level = this.flags & 7 /* RangeFlag.BidiLevelMask */; + return level == 7 ? null : level; + } + /** + The goal column (stored vertical offset) associated with a + cursor. This is used to preserve the vertical position when + [moving](https://codemirror.net/6/docs/ref/#view.EditorView.moveVertically) across + lines of different length. + */ + get goalColumn() { + let value = this.flags >> 6 /* RangeFlag.GoalColumnOffset */; + return value == 16777215 /* RangeFlag.NoGoalColumn */ ? undefined : value; + } + /** + Map this range through a change, producing a valid range in the + updated document. + */ + map(change, assoc = -1) { + let from, to; + if (this.empty) { + from = to = change.mapPos(this.from, assoc); + } + else { + from = change.mapPos(this.from, 1); + to = change.mapPos(this.to, -1); + } + return from == this.from && to == this.to ? this : new SelectionRange(from, to, this.flags); + } + /** + Extend this range to cover at least `from` to `to`. + */ + extend(from, to = from) { + if (from <= this.anchor && to >= this.anchor) + return EditorSelection.range(from, to); + let head = Math.abs(from - this.anchor) > Math.abs(to - this.anchor) ? from : to; + return EditorSelection.range(this.anchor, head); + } + /** + Compare this range to another range. + */ + eq(other, includeAssoc = false) { + return this.anchor == other.anchor && this.head == other.head && + (!includeAssoc || !this.empty || this.assoc == other.assoc); + } + /** + Return a JSON-serializable object representing the range. + */ + toJSON() { return { anchor: this.anchor, head: this.head }; } + /** + Convert a JSON representation of a range to a `SelectionRange` + instance. + */ + static fromJSON(json) { + if (!json || typeof json.anchor != "number" || typeof json.head != "number") + throw new RangeError("Invalid JSON representation for SelectionRange"); + return EditorSelection.range(json.anchor, json.head); + } + /** + @internal + */ + static create(from, to, flags) { + return new SelectionRange(from, to, flags); + } +} +/** +An editor selection holds one or more selection ranges. +*/ +class EditorSelection { + constructor( + /** + The ranges in the selection, sorted by position. Ranges cannot + overlap (but they may touch, if they aren't empty). + */ + ranges, + /** + The index of the _main_ range in the selection (which is + usually the range that was added last). + */ + mainIndex) { + this.ranges = ranges; + this.mainIndex = mainIndex; + } + /** + Map a selection through a change. Used to adjust the selection + position for changes. + */ + map(change, assoc = -1) { + if (change.empty) + return this; + return EditorSelection.create(this.ranges.map(r => r.map(change, assoc)), this.mainIndex); + } + /** + Compare this selection to another selection. By default, ranges + are compared only by position. When `includeAssoc` is true, + cursor ranges must also have the same + [`assoc`](https://codemirror.net/6/docs/ref/#state.SelectionRange.assoc) value. + */ + eq(other, includeAssoc = false) { + if (this.ranges.length != other.ranges.length || + this.mainIndex != other.mainIndex) + return false; + for (let i = 0; i < this.ranges.length; i++) + if (!this.ranges[i].eq(other.ranges[i], includeAssoc)) + return false; + return true; + } + /** + Get the primary selection range. Usually, you should make sure + your code applies to _all_ ranges, by using methods like + [`changeByRange`](https://codemirror.net/6/docs/ref/#state.EditorState.changeByRange). + */ + get main() { return this.ranges[this.mainIndex]; } + /** + Make sure the selection only has one range. Returns a selection + holding only the main range from this selection. + */ + asSingle() { + return this.ranges.length == 1 ? this : new EditorSelection([this.main], 0); + } + /** + Extend this selection with an extra range. + */ + addRange(range, main = true) { + return EditorSelection.create([range].concat(this.ranges), main ? 0 : this.mainIndex + 1); + } + /** + Replace a given range with another range, and then normalize the + selection to merge and sort ranges if necessary. + */ + replaceRange(range, which = this.mainIndex) { + let ranges = this.ranges.slice(); + ranges[which] = range; + return EditorSelection.create(ranges, this.mainIndex); + } + /** + Convert this selection to an object that can be serialized to + JSON. + */ + toJSON() { + return { ranges: this.ranges.map(r => r.toJSON()), main: this.mainIndex }; + } + /** + Create a selection from a JSON representation. + */ + static fromJSON(json) { + if (!json || !Array.isArray(json.ranges) || typeof json.main != "number" || json.main >= json.ranges.length) + throw new RangeError("Invalid JSON representation for EditorSelection"); + return new EditorSelection(json.ranges.map((r) => SelectionRange.fromJSON(r)), json.main); + } + /** + Create a selection holding a single range. + */ + static single(anchor, head = anchor) { + return new EditorSelection([EditorSelection.range(anchor, head)], 0); + } + /** + Sort and merge the given set of ranges, creating a valid + selection. + */ + static create(ranges, mainIndex = 0) { + if (ranges.length == 0) + throw new RangeError("A selection needs at least one range"); + for (let pos = 0, i = 0; i < ranges.length; i++) { + let range = ranges[i]; + if (range.empty ? range.from <= pos : range.from < pos) + return EditorSelection.normalized(ranges.slice(), mainIndex); + pos = range.to; + } + return new EditorSelection(ranges, mainIndex); + } + /** + Create a cursor selection range at the given position. You can + safely ignore the optional arguments in most situations. + */ + static cursor(pos, assoc = 0, bidiLevel, goalColumn) { + return SelectionRange.create(pos, pos, (assoc == 0 ? 0 : assoc < 0 ? 8 /* RangeFlag.AssocBefore */ : 16 /* RangeFlag.AssocAfter */) | + (bidiLevel == null ? 7 : Math.min(6, bidiLevel)) | + ((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 16777215 /* RangeFlag.NoGoalColumn */) << 6 /* RangeFlag.GoalColumnOffset */)); + } + /** + Create a selection range. + */ + static range(anchor, head, goalColumn, bidiLevel) { + let flags = ((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 16777215 /* RangeFlag.NoGoalColumn */) << 6 /* RangeFlag.GoalColumnOffset */) | + (bidiLevel == null ? 7 : Math.min(6, bidiLevel)); + return head < anchor ? SelectionRange.create(head, anchor, 32 /* RangeFlag.Inverted */ | 16 /* RangeFlag.AssocAfter */ | flags) + : SelectionRange.create(anchor, head, (head > anchor ? 8 /* RangeFlag.AssocBefore */ : 0) | flags); + } + /** + @internal + */ + static normalized(ranges, mainIndex = 0) { + let main = ranges[mainIndex]; + ranges.sort((a, b) => a.from - b.from); + mainIndex = ranges.indexOf(main); + for (let i = 1; i < ranges.length; i++) { + let range = ranges[i], prev = ranges[i - 1]; + if (range.empty ? range.from <= prev.to : range.from < prev.to) { + let from = prev.from, to = Math.max(range.to, prev.to); + if (i <= mainIndex) + mainIndex--; + ranges.splice(--i, 2, range.anchor > range.head ? EditorSelection.range(to, from) : EditorSelection.range(from, to)); + } + } + return new EditorSelection(ranges, mainIndex); + } +} +function checkSelection(selection, docLength) { + for (let range of selection.ranges) + if (range.to > docLength) + throw new RangeError("Selection points outside of document"); +} + +let nextID = 0; +/** +A facet is a labeled value that is associated with an editor +state. It takes inputs from any number of extensions, and combines +those into a single output value. + +Examples of uses of facets are the [tab +size](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize), [editor +attributes](https://codemirror.net/6/docs/ref/#view.EditorView^editorAttributes), and [update +listeners](https://codemirror.net/6/docs/ref/#view.EditorView^updateListener). + +Note that `Facet` instances can be used anywhere where +[`FacetReader`](https://codemirror.net/6/docs/ref/#state.FacetReader) is expected. +*/ +class Facet { + constructor( + /** + @internal + */ + combine, + /** + @internal + */ + compareInput, + /** + @internal + */ + compare, isStatic, enables) { + this.combine = combine; + this.compareInput = compareInput; + this.compare = compare; + this.isStatic = isStatic; + /** + @internal + */ + this.id = nextID++; + this.default = combine([]); + this.extensions = typeof enables == "function" ? enables(this) : enables; + } + /** + Returns a facet reader for this facet, which can be used to + [read](https://codemirror.net/6/docs/ref/#state.EditorState.facet) it but not to define values for it. + */ + get reader() { return this; } + /** + Define a new facet. + */ + static define(config = {}) { + return new Facet(config.combine || ((a) => a), config.compareInput || ((a, b) => a === b), config.compare || (!config.combine ? sameArray : (a, b) => a === b), !!config.static, config.enables); + } + /** + Returns an extension that adds the given value to this facet. + */ + of(value) { + return new FacetProvider([], this, 0 /* Provider.Static */, value); + } + /** + Create an extension that computes a value for the facet from a + state. You must take care to declare the parts of the state that + this value depends on, since your function is only called again + for a new state when one of those parts changed. + + In cases where your value depends only on a single field, you'll + want to use the [`from`](https://codemirror.net/6/docs/ref/#state.Facet.from) method instead. + */ + compute(deps, get) { + if (this.isStatic) + throw new Error("Can't compute a static facet"); + return new FacetProvider(deps, this, 1 /* Provider.Single */, get); + } + /** + Create an extension that computes zero or more values for this + facet from a state. + */ + computeN(deps, get) { + if (this.isStatic) + throw new Error("Can't compute a static facet"); + return new FacetProvider(deps, this, 2 /* Provider.Multi */, get); + } + from(field, get) { + if (!get) + get = x => x; + return this.compute([field], state => get(state.field(field))); + } +} +function sameArray(a, b) { + return a == b || a.length == b.length && a.every((e, i) => e === b[i]); +} +class FacetProvider { + constructor(dependencies, facet, type, value) { + this.dependencies = dependencies; + this.facet = facet; + this.type = type; + this.value = value; + this.id = nextID++; + } + dynamicSlot(addresses) { + var _a; + let getter = this.value; + let compare = this.facet.compareInput; + let id = this.id, idx = addresses[id] >> 1, multi = this.type == 2 /* Provider.Multi */; + let depDoc = false, depSel = false, depAddrs = []; + for (let dep of this.dependencies) { + if (dep == "doc") + depDoc = true; + else if (dep == "selection") + depSel = true; + else if ((((_a = addresses[dep.id]) !== null && _a !== void 0 ? _a : 1) & 1) == 0) + depAddrs.push(addresses[dep.id]); + } + return { + create(state) { + state.values[idx] = getter(state); + return 1 /* SlotStatus.Changed */; + }, + update(state, tr) { + if ((depDoc && tr.docChanged) || (depSel && (tr.docChanged || tr.selection)) || ensureAll(state, depAddrs)) { + let newVal = getter(state); + if (multi ? !compareArray(newVal, state.values[idx], compare) : !compare(newVal, state.values[idx])) { + state.values[idx] = newVal; + return 1 /* SlotStatus.Changed */; + } + } + return 0; + }, + reconfigure: (state, oldState) => { + let newVal, oldAddr = oldState.config.address[id]; + if (oldAddr != null) { + let oldVal = getAddr(oldState, oldAddr); + if (this.dependencies.every(dep => { + return dep instanceof Facet ? oldState.facet(dep) === state.facet(dep) : + dep instanceof StateField ? oldState.field(dep, false) == state.field(dep, false) : true; + }) || (multi ? compareArray(newVal = getter(state), oldVal, compare) : compare(newVal = getter(state), oldVal))) { + state.values[idx] = oldVal; + return 0; + } + } + else { + newVal = getter(state); + } + state.values[idx] = newVal; + return 1 /* SlotStatus.Changed */; + } + }; + } +} +function compareArray(a, b, compare) { + if (a.length != b.length) + return false; + for (let i = 0; i < a.length; i++) + if (!compare(a[i], b[i])) + return false; + return true; +} +function ensureAll(state, addrs) { + let changed = false; + for (let addr of addrs) + if (ensureAddr(state, addr) & 1 /* SlotStatus.Changed */) + changed = true; + return changed; +} +function dynamicFacetSlot(addresses, facet, providers) { + let providerAddrs = providers.map(p => addresses[p.id]); + let providerTypes = providers.map(p => p.type); + let dynamic = providerAddrs.filter(p => !(p & 1)); + let idx = addresses[facet.id] >> 1; + function get(state) { + let values = []; + for (let i = 0; i < providerAddrs.length; i++) { + let value = getAddr(state, providerAddrs[i]); + if (providerTypes[i] == 2 /* Provider.Multi */) + for (let val of value) + values.push(val); + else + values.push(value); + } + return facet.combine(values); + } + return { + create(state) { + for (let addr of providerAddrs) + ensureAddr(state, addr); + state.values[idx] = get(state); + return 1 /* SlotStatus.Changed */; + }, + update(state, tr) { + if (!ensureAll(state, dynamic)) + return 0; + let value = get(state); + if (facet.compare(value, state.values[idx])) + return 0; + state.values[idx] = value; + return 1 /* SlotStatus.Changed */; + }, + reconfigure(state, oldState) { + let depChanged = ensureAll(state, providerAddrs); + let oldProviders = oldState.config.facets[facet.id], oldValue = oldState.facet(facet); + if (oldProviders && !depChanged && sameArray(providers, oldProviders)) { + state.values[idx] = oldValue; + return 0; + } + let value = get(state); + if (facet.compare(value, oldValue)) { + state.values[idx] = oldValue; + return 0; + } + state.values[idx] = value; + return 1 /* SlotStatus.Changed */; + } + }; +} +const initField = /*@__PURE__*/Facet.define({ static: true }); +/** +Fields can store additional information in an editor state, and +keep it in sync with the rest of the state. +*/ +class StateField { + constructor( + /** + @internal + */ + id, createF, updateF, compareF, + /** + @internal + */ + spec) { + this.id = id; + this.createF = createF; + this.updateF = updateF; + this.compareF = compareF; + this.spec = spec; + /** + @internal + */ + this.provides = undefined; + } + /** + Define a state field. + */ + static define(config) { + let field = new StateField(nextID++, config.create, config.update, config.compare || ((a, b) => a === b), config); + if (config.provide) + field.provides = config.provide(field); + return field; + } + create(state) { + let init = state.facet(initField).find(i => i.field == this); + return ((init === null || init === void 0 ? void 0 : init.create) || this.createF)(state); + } + /** + @internal + */ + slot(addresses) { + let idx = addresses[this.id] >> 1; + return { + create: (state) => { + state.values[idx] = this.create(state); + return 1 /* SlotStatus.Changed */; + }, + update: (state, tr) => { + let oldVal = state.values[idx]; + let value = this.updateF(oldVal, tr); + if (this.compareF(oldVal, value)) + return 0; + state.values[idx] = value; + return 1 /* SlotStatus.Changed */; + }, + reconfigure: (state, oldState) => { + if (oldState.config.address[this.id] != null) { + state.values[idx] = oldState.field(this); + return 0; + } + state.values[idx] = this.create(state); + return 1 /* SlotStatus.Changed */; + } + }; + } + /** + Returns an extension that enables this field and overrides the + way it is initialized. Can be useful when you need to provide a + non-default starting value for the field. + */ + init(create) { + return [this, initField.of({ field: this, create })]; + } + /** + State field instances can be used as + [`Extension`](https://codemirror.net/6/docs/ref/#state.Extension) values to enable the field in a + given state. + */ + get extension() { return this; } +} +const Prec_ = { lowest: 4, low: 3, default: 2, high: 1, highest: 0 }; +function prec(value) { + return (ext) => new PrecExtension(ext, value); +} +/** +By default extensions are registered in the order they are found +in the flattened form of nested array that was provided. +Individual extension values can be assigned a precedence to +override this. Extensions that do not have a precedence set get +the precedence of the nearest parent with a precedence, or +[`default`](https://codemirror.net/6/docs/ref/#state.Prec.default) if there is no such parent. The +final ordering of extensions is determined by first sorting by +precedence and then by order within each precedence. +*/ +const Prec = { + /** + The highest precedence level, for extensions that should end up + near the start of the precedence ordering. + */ + highest: /*@__PURE__*/prec(Prec_.highest), + /** + A higher-than-default precedence, for extensions that should + come before those with default precedence. + */ + high: /*@__PURE__*/prec(Prec_.high), + /** + The default precedence, which is also used for extensions + without an explicit precedence. + */ + default: /*@__PURE__*/prec(Prec_.default), + /** + A lower-than-default precedence. + */ + low: /*@__PURE__*/prec(Prec_.low), + /** + The lowest precedence level. Meant for things that should end up + near the end of the extension order. + */ + lowest: /*@__PURE__*/prec(Prec_.lowest) +}; +class PrecExtension { + constructor(inner, prec) { + this.inner = inner; + this.prec = prec; + } +} +/** +Extension compartments can be used to make a configuration +dynamic. By [wrapping](https://codemirror.net/6/docs/ref/#state.Compartment.of) part of your +configuration in a compartment, you can later +[replace](https://codemirror.net/6/docs/ref/#state.Compartment.reconfigure) that part through a +transaction. +*/ +class Compartment { + /** + Create an instance of this compartment to add to your [state + configuration](https://codemirror.net/6/docs/ref/#state.EditorStateConfig.extensions). + */ + of(ext) { return new CompartmentInstance(this, ext); } + /** + Create an [effect](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) that + reconfigures this compartment. + */ + reconfigure(content) { + return Compartment.reconfigure.of({ compartment: this, extension: content }); + } + /** + Get the current content of the compartment in the state, or + `undefined` if it isn't present. + */ + get(state) { + return state.config.compartments.get(this); + } +} +class CompartmentInstance { + constructor(compartment, inner) { + this.compartment = compartment; + this.inner = inner; + } +} +class Configuration { + constructor(base, compartments, dynamicSlots, address, staticValues, facets) { + this.base = base; + this.compartments = compartments; + this.dynamicSlots = dynamicSlots; + this.address = address; + this.staticValues = staticValues; + this.facets = facets; + this.statusTemplate = []; + while (this.statusTemplate.length < dynamicSlots.length) + this.statusTemplate.push(0 /* SlotStatus.Unresolved */); + } + staticFacet(facet) { + let addr = this.address[facet.id]; + return addr == null ? facet.default : this.staticValues[addr >> 1]; + } + static resolve(base, compartments, oldState) { + let fields = []; + let facets = Object.create(null); + let newCompartments = new Map(); + for (let ext of flatten(base, compartments, newCompartments)) { + if (ext instanceof StateField) + fields.push(ext); + else + (facets[ext.facet.id] || (facets[ext.facet.id] = [])).push(ext); + } + let address = Object.create(null); + let staticValues = []; + let dynamicSlots = []; + for (let field of fields) { + address[field.id] = dynamicSlots.length << 1; + dynamicSlots.push(a => field.slot(a)); + } + let oldFacets = oldState === null || oldState === void 0 ? void 0 : oldState.config.facets; + for (let id in facets) { + let providers = facets[id], facet = providers[0].facet; + let oldProviders = oldFacets && oldFacets[id] || []; + if (providers.every(p => p.type == 0 /* Provider.Static */)) { + address[facet.id] = (staticValues.length << 1) | 1; + if (sameArray(oldProviders, providers)) { + staticValues.push(oldState.facet(facet)); + } + else { + let value = facet.combine(providers.map(p => p.value)); + staticValues.push(oldState && facet.compare(value, oldState.facet(facet)) ? oldState.facet(facet) : value); + } + } + else { + for (let p of providers) { + if (p.type == 0 /* Provider.Static */) { + address[p.id] = (staticValues.length << 1) | 1; + staticValues.push(p.value); + } + else { + address[p.id] = dynamicSlots.length << 1; + dynamicSlots.push(a => p.dynamicSlot(a)); + } + } + address[facet.id] = dynamicSlots.length << 1; + dynamicSlots.push(a => dynamicFacetSlot(a, facet, providers)); + } + } + let dynamic = dynamicSlots.map(f => f(address)); + return new Configuration(base, newCompartments, dynamic, address, staticValues, facets); + } +} +function flatten(extension, compartments, newCompartments) { + let result = [[], [], [], [], []]; + let seen = new Map(); + function inner(ext, prec) { + let known = seen.get(ext); + if (known != null) { + if (known <= prec) + return; + let found = result[known].indexOf(ext); + if (found > -1) + result[known].splice(found, 1); + if (ext instanceof CompartmentInstance) + newCompartments.delete(ext.compartment); + } + seen.set(ext, prec); + if (Array.isArray(ext)) { + for (let e of ext) + inner(e, prec); + } + else if (ext instanceof CompartmentInstance) { + if (newCompartments.has(ext.compartment)) + throw new RangeError(`Duplicate use of compartment in extensions`); + let content = compartments.get(ext.compartment) || ext.inner; + newCompartments.set(ext.compartment, content); + inner(content, prec); + } + else if (ext instanceof PrecExtension) { + inner(ext.inner, ext.prec); + } + else if (ext instanceof StateField) { + result[prec].push(ext); + if (ext.provides) + inner(ext.provides, prec); + } + else if (ext instanceof FacetProvider) { + result[prec].push(ext); + if (ext.facet.extensions) + inner(ext.facet.extensions, Prec_.default); + } + else { + let content = ext.extension; + if (!content) + throw new Error(`Unrecognized extension value in extension set (${ext}). This sometimes happens because multiple instances of @codemirror/state are loaded, breaking instanceof checks.`); + inner(content, prec); + } + } + inner(extension, Prec_.default); + return result.reduce((a, b) => a.concat(b)); +} +function ensureAddr(state, addr) { + if (addr & 1) + return 2 /* SlotStatus.Computed */; + let idx = addr >> 1; + let status = state.status[idx]; + if (status == 4 /* SlotStatus.Computing */) + throw new Error("Cyclic dependency between fields and/or facets"); + if (status & 2 /* SlotStatus.Computed */) + return status; + state.status[idx] = 4 /* SlotStatus.Computing */; + let changed = state.computeSlot(state, state.config.dynamicSlots[idx]); + return state.status[idx] = 2 /* SlotStatus.Computed */ | changed; +} +function getAddr(state, addr) { + return addr & 1 ? state.config.staticValues[addr >> 1] : state.values[addr >> 1]; +} + +const languageData = /*@__PURE__*/Facet.define(); +const allowMultipleSelections = /*@__PURE__*/Facet.define({ + combine: values => values.some(v => v), + static: true +}); +const lineSeparator = /*@__PURE__*/Facet.define({ + combine: values => values.length ? values[0] : undefined, + static: true +}); +const changeFilter = /*@__PURE__*/Facet.define(); +const transactionFilter = /*@__PURE__*/Facet.define(); +const transactionExtender = /*@__PURE__*/Facet.define(); +const readOnly = /*@__PURE__*/Facet.define({ + combine: values => values.length ? values[0] : false +}); + +/** +Annotations are tagged values that are used to add metadata to +transactions in an extensible way. They should be used to model +things that effect the entire transaction (such as its [time +stamp](https://codemirror.net/6/docs/ref/#state.Transaction^time) or information about its +[origin](https://codemirror.net/6/docs/ref/#state.Transaction^userEvent)). For effects that happen +_alongside_ the other changes made by the transaction, [state +effects](https://codemirror.net/6/docs/ref/#state.StateEffect) are more appropriate. +*/ +class Annotation { + /** + @internal + */ + constructor( + /** + The annotation type. + */ + type, + /** + The value of this annotation. + */ + value) { + this.type = type; + this.value = value; + } + /** + Define a new type of annotation. + */ + static define() { return new AnnotationType(); } +} +/** +Marker that identifies a type of [annotation](https://codemirror.net/6/docs/ref/#state.Annotation). +*/ +class AnnotationType { + /** + Create an instance of this annotation. + */ + of(value) { return new Annotation(this, value); } +} +/** +Representation of a type of state effect. Defined with +[`StateEffect.define`](https://codemirror.net/6/docs/ref/#state.StateEffect^define). +*/ +class StateEffectType { + /** + @internal + */ + constructor( + // The `any` types in these function types are there to work + // around TypeScript issue #37631, where the type guard on + // `StateEffect.is` mysteriously stops working when these properly + // have type `Value`. + /** + @internal + */ + map) { + this.map = map; + } + /** + Create a [state effect](https://codemirror.net/6/docs/ref/#state.StateEffect) instance of this + type. + */ + of(value) { return new StateEffect(this, value); } +} +/** +State effects can be used to represent additional effects +associated with a [transaction](https://codemirror.net/6/docs/ref/#state.Transaction.effects). They +are often useful to model changes to custom [state +fields](https://codemirror.net/6/docs/ref/#state.StateField), when those changes aren't implicit in +document or selection changes. +*/ +class StateEffect { + /** + @internal + */ + constructor( + /** + @internal + */ + type, + /** + The value of this effect. + */ + value) { + this.type = type; + this.value = value; + } + /** + Map this effect through a position mapping. Will return + `undefined` when that ends up deleting the effect. + */ + map(mapping) { + let mapped = this.type.map(this.value, mapping); + return mapped === undefined ? undefined : mapped == this.value ? this : new StateEffect(this.type, mapped); + } + /** + Tells you whether this effect object is of a given + [type](https://codemirror.net/6/docs/ref/#state.StateEffectType). + */ + is(type) { return this.type == type; } + /** + Define a new effect type. The type parameter indicates the type + of values that his effect holds. It should be a type that + doesn't include `undefined`, since that is used in + [mapping](https://codemirror.net/6/docs/ref/#state.StateEffect.map) to indicate that an effect is + removed. + */ + static define(spec = {}) { + return new StateEffectType(spec.map || (v => v)); + } + /** + Map an array of effects through a change set. + */ + static mapEffects(effects, mapping) { + if (!effects.length) + return effects; + let result = []; + for (let effect of effects) { + let mapped = effect.map(mapping); + if (mapped) + result.push(mapped); + } + return result; + } +} +/** +This effect can be used to reconfigure the root extensions of +the editor. Doing this will discard any extensions +[appended](https://codemirror.net/6/docs/ref/#state.StateEffect^appendConfig), but does not reset +the content of [reconfigured](https://codemirror.net/6/docs/ref/#state.Compartment.reconfigure) +compartments. +*/ +StateEffect.reconfigure = /*@__PURE__*/StateEffect.define(); +/** +Append extensions to the top-level configuration of the editor. +*/ +StateEffect.appendConfig = /*@__PURE__*/StateEffect.define(); +/** +Changes to the editor state are grouped into transactions. +Typically, a user action creates a single transaction, which may +contain any number of document changes, may change the selection, +or have other effects. Create a transaction by calling +[`EditorState.update`](https://codemirror.net/6/docs/ref/#state.EditorState.update), or immediately +dispatch one by calling +[`EditorView.dispatch`](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch). +*/ +class Transaction { + constructor( + /** + The state from which the transaction starts. + */ + startState, + /** + The document changes made by this transaction. + */ + changes, + /** + The selection set by this transaction, or undefined if it + doesn't explicitly set a selection. + */ + selection, + /** + The effects added to the transaction. + */ + effects, + /** + @internal + */ + annotations, + /** + Whether the selection should be scrolled into view after this + transaction is dispatched. + */ + scrollIntoView) { + this.startState = startState; + this.changes = changes; + this.selection = selection; + this.effects = effects; + this.annotations = annotations; + this.scrollIntoView = scrollIntoView; + /** + @internal + */ + this._doc = null; + /** + @internal + */ + this._state = null; + if (selection) + checkSelection(selection, changes.newLength); + if (!annotations.some((a) => a.type == Transaction.time)) + this.annotations = annotations.concat(Transaction.time.of(Date.now())); + } + /** + @internal + */ + static create(startState, changes, selection, effects, annotations, scrollIntoView) { + return new Transaction(startState, changes, selection, effects, annotations, scrollIntoView); + } + /** + The new document produced by the transaction. Contrary to + [`.state`](https://codemirror.net/6/docs/ref/#state.Transaction.state)`.doc`, accessing this won't + force the entire new state to be computed right away, so it is + recommended that [transaction + filters](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter) use this getter + when they need to look at the new document. + */ + get newDoc() { + return this._doc || (this._doc = this.changes.apply(this.startState.doc)); + } + /** + The new selection produced by the transaction. If + [`this.selection`](https://codemirror.net/6/docs/ref/#state.Transaction.selection) is undefined, + this will [map](https://codemirror.net/6/docs/ref/#state.EditorSelection.map) the start state's + current selection through the changes made by the transaction. + */ + get newSelection() { + return this.selection || this.startState.selection.map(this.changes); + } + /** + The new state created by the transaction. Computed on demand + (but retained for subsequent access), so it is recommended not to + access it in [transaction + filters](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter) when possible. + */ + get state() { + if (!this._state) + this.startState.applyTransaction(this); + return this._state; + } + /** + Get the value of the given annotation type, if any. + */ + annotation(type) { + for (let ann of this.annotations) + if (ann.type == type) + return ann.value; + return undefined; + } + /** + Indicates whether the transaction changed the document. + */ + get docChanged() { return !this.changes.empty; } + /** + Indicates whether this transaction reconfigures the state + (through a [configuration compartment](https://codemirror.net/6/docs/ref/#state.Compartment) or + with a top-level configuration + [effect](https://codemirror.net/6/docs/ref/#state.StateEffect^reconfigure). + */ + get reconfigured() { return this.startState.config != this.state.config; } + /** + Returns true if the transaction has a [user + event](https://codemirror.net/6/docs/ref/#state.Transaction^userEvent) annotation that is equal to + or more specific than `event`. For example, if the transaction + has `"select.pointer"` as user event, `"select"` and + `"select.pointer"` will match it. + */ + isUserEvent(event) { + let e = this.annotation(Transaction.userEvent); + return !!(e && (e == event || e.length > event.length && e.slice(0, event.length) == event && e[event.length] == ".")); + } +} +/** +Annotation used to store transaction timestamps. Automatically +added to every transaction, holding `Date.now()`. +*/ +Transaction.time = /*@__PURE__*/Annotation.define(); +/** +Annotation used to associate a transaction with a user interface +event. Holds a string identifying the event, using a +dot-separated format to support attaching more specific +information. The events used by the core libraries are: + + - `"input"` when content is entered + - `"input.type"` for typed input + - `"input.type.compose"` for composition + - `"input.paste"` for pasted input + - `"input.drop"` when adding content with drag-and-drop + - `"input.complete"` when autocompleting + - `"delete"` when the user deletes content + - `"delete.selection"` when deleting the selection + - `"delete.forward"` when deleting forward from the selection + - `"delete.backward"` when deleting backward from the selection + - `"delete.cut"` when cutting to the clipboard + - `"move"` when content is moved + - `"move.drop"` when content is moved within the editor through drag-and-drop + - `"select"` when explicitly changing the selection + - `"select.pointer"` when selecting with a mouse or other pointing device + - `"undo"` and `"redo"` for history actions + +Use [`isUserEvent`](https://codemirror.net/6/docs/ref/#state.Transaction.isUserEvent) to check +whether the annotation matches a given event. +*/ +Transaction.userEvent = /*@__PURE__*/Annotation.define(); +/** +Annotation indicating whether a transaction should be added to +the undo history or not. +*/ +Transaction.addToHistory = /*@__PURE__*/Annotation.define(); +/** +Annotation indicating (when present and true) that a transaction +represents a change made by some other actor, not the user. This +is used, for example, to tag other people's changes in +collaborative editing. +*/ +Transaction.remote = /*@__PURE__*/Annotation.define(); +function joinRanges(a, b) { + let result = []; + for (let iA = 0, iB = 0;;) { + let from, to; + if (iA < a.length && (iB == b.length || b[iB] >= a[iA])) { + from = a[iA++]; + to = a[iA++]; + } + else if (iB < b.length) { + from = b[iB++]; + to = b[iB++]; + } + else + return result; + if (!result.length || result[result.length - 1] < from) + result.push(from, to); + else if (result[result.length - 1] < to) + result[result.length - 1] = to; + } +} +function mergeTransaction(a, b, sequential) { + var _a; + let mapForA, mapForB, changes; + if (sequential) { + mapForA = b.changes; + mapForB = ChangeSet.empty(b.changes.length); + changes = a.changes.compose(b.changes); + } + else { + mapForA = b.changes.map(a.changes); + mapForB = a.changes.mapDesc(b.changes, true); + changes = a.changes.compose(mapForA); + } + return { + changes, + selection: b.selection ? b.selection.map(mapForB) : (_a = a.selection) === null || _a === void 0 ? void 0 : _a.map(mapForA), + effects: StateEffect.mapEffects(a.effects, mapForA).concat(StateEffect.mapEffects(b.effects, mapForB)), + annotations: a.annotations.length ? a.annotations.concat(b.annotations) : b.annotations, + scrollIntoView: a.scrollIntoView || b.scrollIntoView + }; +} +function resolveTransactionInner(state, spec, docSize) { + let sel = spec.selection, annotations = asArray(spec.annotations); + if (spec.userEvent) + annotations = annotations.concat(Transaction.userEvent.of(spec.userEvent)); + return { + changes: spec.changes instanceof ChangeSet ? spec.changes + : ChangeSet.of(spec.changes || [], docSize, state.facet(lineSeparator)), + selection: sel && (sel instanceof EditorSelection ? sel : EditorSelection.single(sel.anchor, sel.head)), + effects: asArray(spec.effects), + annotations, + scrollIntoView: !!spec.scrollIntoView + }; +} +function resolveTransaction(state, specs, filter) { + let s = resolveTransactionInner(state, specs.length ? specs[0] : {}, state.doc.length); + if (specs.length && specs[0].filter === false) + filter = false; + for (let i = 1; i < specs.length; i++) { + if (specs[i].filter === false) + filter = false; + let seq = !!specs[i].sequential; + s = mergeTransaction(s, resolveTransactionInner(state, specs[i], seq ? s.changes.newLength : state.doc.length), seq); + } + let tr = Transaction.create(state, s.changes, s.selection, s.effects, s.annotations, s.scrollIntoView); + return extendTransaction(filter ? filterTransaction(tr) : tr); +} +// Finish a transaction by applying filters if necessary. +function filterTransaction(tr) { + let state = tr.startState; + // Change filters + let result = true; + for (let filter of state.facet(changeFilter)) { + let value = filter(tr); + if (value === false) { + result = false; + break; + } + if (Array.isArray(value)) + result = result === true ? value : joinRanges(result, value); + } + if (result !== true) { + let changes, back; + if (result === false) { + back = tr.changes.invertedDesc; + changes = ChangeSet.empty(state.doc.length); + } + else { + let filtered = tr.changes.filter(result); + changes = filtered.changes; + back = filtered.filtered.mapDesc(filtered.changes).invertedDesc; + } + tr = Transaction.create(state, changes, tr.selection && tr.selection.map(back), StateEffect.mapEffects(tr.effects, back), tr.annotations, tr.scrollIntoView); + } + // Transaction filters + let filters = state.facet(transactionFilter); + for (let i = filters.length - 1; i >= 0; i--) { + let filtered = filters[i](tr); + if (filtered instanceof Transaction) + tr = filtered; + else if (Array.isArray(filtered) && filtered.length == 1 && filtered[0] instanceof Transaction) + tr = filtered[0]; + else + tr = resolveTransaction(state, asArray(filtered), false); + } + return tr; +} +function extendTransaction(tr) { + let state = tr.startState, extenders = state.facet(transactionExtender), spec = tr; + for (let i = extenders.length - 1; i >= 0; i--) { + let extension = extenders[i](tr); + if (extension && Object.keys(extension).length) + spec = mergeTransaction(spec, resolveTransactionInner(state, extension, tr.changes.newLength), true); + } + return spec == tr ? tr : Transaction.create(state, tr.changes, tr.selection, spec.effects, spec.annotations, spec.scrollIntoView); +} +const none = []; +function asArray(value) { + return value == null ? none : Array.isArray(value) ? value : [value]; +} + +/** +The categories produced by a [character +categorizer](https://codemirror.net/6/docs/ref/#state.EditorState.charCategorizer). These are used +do things like selecting by word. +*/ +var CharCategory = /*@__PURE__*/(function (CharCategory) { + /** + Word characters. + */ + CharCategory[CharCategory["Word"] = 0] = "Word"; + /** + Whitespace. + */ + CharCategory[CharCategory["Space"] = 1] = "Space"; + /** + Anything else. + */ + CharCategory[CharCategory["Other"] = 2] = "Other"; +return CharCategory})(CharCategory || (CharCategory = {})); +const nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; +let wordChar; +try { + wordChar = /*@__PURE__*/new RegExp("[\\p{Alphabetic}\\p{Number}_]", "u"); +} +catch (_) { } +function hasWordChar(str) { + if (wordChar) + return wordChar.test(str); + for (let i = 0; i < str.length; i++) { + let ch = str[i]; + if (/\w/.test(ch) || ch > "\x80" && (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))) + return true; + } + return false; +} +function makeCategorizer(wordChars) { + return (char) => { + if (!/\S/.test(char)) + return CharCategory.Space; + if (hasWordChar(char)) + return CharCategory.Word; + for (let i = 0; i < wordChars.length; i++) + if (char.indexOf(wordChars[i]) > -1) + return CharCategory.Word; + return CharCategory.Other; + }; +} + +/** +The editor state class is a persistent (immutable) data structure. +To update a state, you [create](https://codemirror.net/6/docs/ref/#state.EditorState.update) a +[transaction](https://codemirror.net/6/docs/ref/#state.Transaction), which produces a _new_ state +instance, without modifying the original object. + +As such, _never_ mutate properties of a state directly. That'll +just break things. +*/ +class EditorState { + constructor( + /** + @internal + */ + config, + /** + The current document. + */ + doc, + /** + The current selection. + */ + selection, + /** + @internal + */ + values, computeSlot, tr) { + this.config = config; + this.doc = doc; + this.selection = selection; + this.values = values; + this.status = config.statusTemplate.slice(); + this.computeSlot = computeSlot; + // Fill in the computed state immediately, so that further queries + // for it made during the update return this state + if (tr) + tr._state = this; + for (let i = 0; i < this.config.dynamicSlots.length; i++) + ensureAddr(this, i << 1); + this.computeSlot = null; + } + field(field, require = true) { + let addr = this.config.address[field.id]; + if (addr == null) { + if (require) + throw new RangeError("Field is not present in this state"); + return undefined; + } + ensureAddr(this, addr); + return getAddr(this, addr); + } + /** + Create a [transaction](https://codemirror.net/6/docs/ref/#state.Transaction) that updates this + state. Any number of [transaction specs](https://codemirror.net/6/docs/ref/#state.TransactionSpec) + can be passed. Unless + [`sequential`](https://codemirror.net/6/docs/ref/#state.TransactionSpec.sequential) is set, the + [changes](https://codemirror.net/6/docs/ref/#state.TransactionSpec.changes) (if any) of each spec + are assumed to start in the _current_ document (not the document + produced by previous specs), and its + [selection](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection) and + [effects](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) are assumed to refer + to the document created by its _own_ changes. The resulting + transaction contains the combined effect of all the different + specs. For [selection](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection), later + specs take precedence over earlier ones. + */ + update(...specs) { + return resolveTransaction(this, specs, true); + } + /** + @internal + */ + applyTransaction(tr) { + let conf = this.config, { base, compartments } = conf; + for (let effect of tr.effects) { + if (effect.is(Compartment.reconfigure)) { + if (conf) { + compartments = new Map; + conf.compartments.forEach((val, key) => compartments.set(key, val)); + conf = null; + } + compartments.set(effect.value.compartment, effect.value.extension); + } + else if (effect.is(StateEffect.reconfigure)) { + conf = null; + base = effect.value; + } + else if (effect.is(StateEffect.appendConfig)) { + conf = null; + base = asArray(base).concat(effect.value); + } + } + let startValues; + if (!conf) { + conf = Configuration.resolve(base, compartments, this); + let intermediateState = new EditorState(conf, this.doc, this.selection, conf.dynamicSlots.map(() => null), (state, slot) => slot.reconfigure(state, this), null); + startValues = intermediateState.values; + } + else { + startValues = tr.startState.values.slice(); + } + let selection = tr.startState.facet(allowMultipleSelections) ? tr.newSelection : tr.newSelection.asSingle(); + new EditorState(conf, tr.newDoc, selection, startValues, (state, slot) => slot.update(state, tr), tr); + } + /** + Create a [transaction spec](https://codemirror.net/6/docs/ref/#state.TransactionSpec) that + replaces every selection range with the given content. + */ + replaceSelection(text) { + if (typeof text == "string") + text = this.toText(text); + return this.changeByRange(range => ({ changes: { from: range.from, to: range.to, insert: text }, + range: EditorSelection.cursor(range.from + text.length) })); + } + /** + Create a set of changes and a new selection by running the given + function for each range in the active selection. The function + can return an optional set of changes (in the coordinate space + of the start document), plus an updated range (in the coordinate + space of the document produced by the call's own changes). This + method will merge all the changes and ranges into a single + changeset and selection, and return it as a [transaction + spec](https://codemirror.net/6/docs/ref/#state.TransactionSpec), which can be passed to + [`update`](https://codemirror.net/6/docs/ref/#state.EditorState.update). + */ + changeByRange(f) { + let sel = this.selection; + let result1 = f(sel.ranges[0]); + let changes = this.changes(result1.changes), ranges = [result1.range]; + let effects = asArray(result1.effects); + for (let i = 1; i < sel.ranges.length; i++) { + let result = f(sel.ranges[i]); + let newChanges = this.changes(result.changes), newMapped = newChanges.map(changes); + for (let j = 0; j < i; j++) + ranges[j] = ranges[j].map(newMapped); + let mapBy = changes.mapDesc(newChanges, true); + ranges.push(result.range.map(mapBy)); + changes = changes.compose(newMapped); + effects = StateEffect.mapEffects(effects, newMapped).concat(StateEffect.mapEffects(asArray(result.effects), mapBy)); + } + return { + changes, + selection: EditorSelection.create(ranges, sel.mainIndex), + effects + }; + } + /** + Create a [change set](https://codemirror.net/6/docs/ref/#state.ChangeSet) from the given change + description, taking the state's document length and line + separator into account. + */ + changes(spec = []) { + if (spec instanceof ChangeSet) + return spec; + return ChangeSet.of(spec, this.doc.length, this.facet(EditorState.lineSeparator)); + } + /** + Using the state's [line + separator](https://codemirror.net/6/docs/ref/#state.EditorState^lineSeparator), create a + [`Text`](https://codemirror.net/6/docs/ref/#state.Text) instance from the given string. + */ + toText(string) { + return Text.of(string.split(this.facet(EditorState.lineSeparator) || DefaultSplit)); + } + /** + Return the given range of the document as a string. + */ + sliceDoc(from = 0, to = this.doc.length) { + return this.doc.sliceString(from, to, this.lineBreak); + } + /** + Get the value of a state [facet](https://codemirror.net/6/docs/ref/#state.Facet). + */ + facet(facet) { + let addr = this.config.address[facet.id]; + if (addr == null) + return facet.default; + ensureAddr(this, addr); + return getAddr(this, addr); + } + /** + Convert this state to a JSON-serializable object. When custom + fields should be serialized, you can pass them in as an object + mapping property names (in the resulting object, which should + not use `doc` or `selection`) to fields. + */ + toJSON(fields) { + let result = { + doc: this.sliceDoc(), + selection: this.selection.toJSON() + }; + if (fields) + for (let prop in fields) { + let value = fields[prop]; + if (value instanceof StateField && this.config.address[value.id] != null) + result[prop] = value.spec.toJSON(this.field(fields[prop]), this); + } + return result; + } + /** + Deserialize a state from its JSON representation. When custom + fields should be deserialized, pass the same object you passed + to [`toJSON`](https://codemirror.net/6/docs/ref/#state.EditorState.toJSON) when serializing as + third argument. + */ + static fromJSON(json, config = {}, fields) { + if (!json || typeof json.doc != "string") + throw new RangeError("Invalid JSON representation for EditorState"); + let fieldInit = []; + if (fields) + for (let prop in fields) { + if (Object.prototype.hasOwnProperty.call(json, prop)) { + let field = fields[prop], value = json[prop]; + fieldInit.push(field.init(state => field.spec.fromJSON(value, state))); + } + } + return EditorState.create({ + doc: json.doc, + selection: EditorSelection.fromJSON(json.selection), + extensions: config.extensions ? fieldInit.concat([config.extensions]) : fieldInit + }); + } + /** + Create a new state. You'll usually only need this when + initializing an editor—updated states are created by applying + transactions. + */ + static create(config = {}) { + let configuration = Configuration.resolve(config.extensions || [], new Map); + let doc = config.doc instanceof Text ? config.doc + : Text.of((config.doc || "").split(configuration.staticFacet(EditorState.lineSeparator) || DefaultSplit)); + let selection = !config.selection ? EditorSelection.single(0) + : config.selection instanceof EditorSelection ? config.selection + : EditorSelection.single(config.selection.anchor, config.selection.head); + checkSelection(selection, doc.length); + if (!configuration.staticFacet(allowMultipleSelections)) + selection = selection.asSingle(); + return new EditorState(configuration, doc, selection, configuration.dynamicSlots.map(() => null), (state, slot) => slot.create(state), null); + } + /** + The size (in columns) of a tab in the document, determined by + the [`tabSize`](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize) facet. + */ + get tabSize() { return this.facet(EditorState.tabSize); } + /** + Get the proper [line-break](https://codemirror.net/6/docs/ref/#state.EditorState^lineSeparator) + string for this state. + */ + get lineBreak() { return this.facet(EditorState.lineSeparator) || "\n"; } + /** + Returns true when the editor is + [configured](https://codemirror.net/6/docs/ref/#state.EditorState^readOnly) to be read-only. + */ + get readOnly() { return this.facet(readOnly); } + /** + Look up a translation for the given phrase (via the + [`phrases`](https://codemirror.net/6/docs/ref/#state.EditorState^phrases) facet), or return the + original string if no translation is found. + + If additional arguments are passed, they will be inserted in + place of markers like `$1` (for the first value) and `$2`, etc. + A single `$` is equivalent to `$1`, and `$$` will produce a + literal dollar sign. + */ + phrase(phrase, ...insert) { + for (let map of this.facet(EditorState.phrases)) + if (Object.prototype.hasOwnProperty.call(map, phrase)) { + phrase = map[phrase]; + break; + } + if (insert.length) + phrase = phrase.replace(/\$(\$|\d*)/g, (m, i) => { + if (i == "$") + return "$"; + let n = +(i || 1); + return !n || n > insert.length ? m : insert[n - 1]; + }); + return phrase; + } + /** + Find the values for a given language data field, provided by the + the [`languageData`](https://codemirror.net/6/docs/ref/#state.EditorState^languageData) facet. + + Examples of language data fields are... + + - [`"commentTokens"`](https://codemirror.net/6/docs/ref/#commands.CommentTokens) for specifying + comment syntax. + - [`"autocomplete"`](https://codemirror.net/6/docs/ref/#autocomplete.autocompletion^config.override) + for providing language-specific completion sources. + - [`"wordChars"`](https://codemirror.net/6/docs/ref/#state.EditorState.charCategorizer) for adding + characters that should be considered part of words in this + language. + - [`"closeBrackets"`](https://codemirror.net/6/docs/ref/#autocomplete.CloseBracketConfig) controls + bracket closing behavior. + */ + languageDataAt(name, pos, side = -1) { + let values = []; + for (let provider of this.facet(languageData)) { + for (let result of provider(this, pos, side)) { + if (Object.prototype.hasOwnProperty.call(result, name)) + values.push(result[name]); + } + } + return values; + } + /** + Return a function that can categorize strings (expected to + represent a single [grapheme cluster](https://codemirror.net/6/docs/ref/#state.findClusterBreak)) + into one of: + + - Word (contains an alphanumeric character or a character + explicitly listed in the local language's `"wordChars"` + language data, which should be a string) + - Space (contains only whitespace) + - Other (anything else) + */ + charCategorizer(at) { + return makeCategorizer(this.languageDataAt("wordChars", at).join("")); + } + /** + Find the word at the given position, meaning the range + containing all [word](https://codemirror.net/6/docs/ref/#state.CharCategory.Word) characters + around it. If no word characters are adjacent to the position, + this returns null. + */ + wordAt(pos) { + let { text, from, length } = this.doc.lineAt(pos); + let cat = this.charCategorizer(pos); + let start = pos - from, end = pos - from; + while (start > 0) { + let prev = findClusterBreak(text, start, false); + if (cat(text.slice(prev, start)) != CharCategory.Word) + break; + start = prev; + } + while (end < length) { + let next = findClusterBreak(text, end); + if (cat(text.slice(end, next)) != CharCategory.Word) + break; + end = next; + } + return start == end ? null : EditorSelection.range(start + from, end + from); + } +} +/** +A facet that, when enabled, causes the editor to allow multiple +ranges to be selected. Be careful though, because by default the +editor relies on the native DOM selection, which cannot handle +multiple selections. An extension like +[`drawSelection`](https://codemirror.net/6/docs/ref/#view.drawSelection) can be used to make +secondary selections visible to the user. +*/ +EditorState.allowMultipleSelections = allowMultipleSelections; +/** +Configures the tab size to use in this state. The first +(highest-precedence) value of the facet is used. If no value is +given, this defaults to 4. +*/ +EditorState.tabSize = /*@__PURE__*/Facet.define({ + combine: values => values.length ? values[0] : 4 +}); +/** +The line separator to use. By default, any of `"\n"`, `"\r\n"` +and `"\r"` is treated as a separator when splitting lines, and +lines are joined with `"\n"`. + +When you configure a value here, only that precise separator +will be used, allowing you to round-trip documents through the +editor without normalizing line separators. +*/ +EditorState.lineSeparator = lineSeparator; +/** +This facet controls the value of the +[`readOnly`](https://codemirror.net/6/docs/ref/#state.EditorState.readOnly) getter, which is +consulted by commands and extensions that implement editing +functionality to determine whether they should apply. It +defaults to false, but when its highest-precedence value is +`true`, such functionality disables itself. + +Not to be confused with +[`EditorView.editable`](https://codemirror.net/6/docs/ref/#view.EditorView^editable), which +controls whether the editor's DOM is set to be editable (and +thus focusable). +*/ +EditorState.readOnly = readOnly; +/** +Registers translation phrases. The +[`phrase`](https://codemirror.net/6/docs/ref/#state.EditorState.phrase) method will look through +all objects registered with this facet to find translations for +its argument. +*/ +EditorState.phrases = /*@__PURE__*/Facet.define({ + compare(a, b) { + let kA = Object.keys(a), kB = Object.keys(b); + return kA.length == kB.length && kA.every(k => a[k] == b[k]); + } +}); +/** +A facet used to register [language +data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) providers. +*/ +EditorState.languageData = languageData; +/** +Facet used to register change filters, which are called for each +transaction (unless explicitly +[disabled](https://codemirror.net/6/docs/ref/#state.TransactionSpec.filter)), and can suppress +part of the transaction's changes. + +Such a function can return `true` to indicate that it doesn't +want to do anything, `false` to completely stop the changes in +the transaction, or a set of ranges in which changes should be +suppressed. Such ranges are represented as an array of numbers, +with each pair of two numbers indicating the start and end of a +range. So for example `[10, 20, 100, 110]` suppresses changes +between 10 and 20, and between 100 and 110. +*/ +EditorState.changeFilter = changeFilter; +/** +Facet used to register a hook that gets a chance to update or +replace transaction specs before they are applied. This will +only be applied for transactions that don't have +[`filter`](https://codemirror.net/6/docs/ref/#state.TransactionSpec.filter) set to `false`. You +can either return a single transaction spec (possibly the input +transaction), or an array of specs (which will be combined in +the same way as the arguments to +[`EditorState.update`](https://codemirror.net/6/docs/ref/#state.EditorState.update)). + +When possible, it is recommended to avoid accessing +[`Transaction.state`](https://codemirror.net/6/docs/ref/#state.Transaction.state) in a filter, +since it will force creation of a state that will then be +discarded again, if the transaction is actually filtered. + +(This functionality should be used with care. Indiscriminately +modifying transaction is likely to break something or degrade +the user experience.) +*/ +EditorState.transactionFilter = transactionFilter; +/** +This is a more limited form of +[`transactionFilter`](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter), +which can only add +[annotations](https://codemirror.net/6/docs/ref/#state.TransactionSpec.annotations) and +[effects](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects). _But_, this type +of filter runs even if the transaction has disabled regular +[filtering](https://codemirror.net/6/docs/ref/#state.TransactionSpec.filter), making it suitable +for effects that don't need to touch the changes or selection, +but do want to process every transaction. + +Extenders run _after_ filters, when both are present. +*/ +EditorState.transactionExtender = transactionExtender; +Compartment.reconfigure = /*@__PURE__*/StateEffect.define(); + +/** +Utility function for combining behaviors to fill in a config +object from an array of provided configs. `defaults` should hold +default values for all optional fields in `Config`. + +The function will, by default, error +when a field gets two values that aren't `===`-equal, but you can +provide combine functions per field to do something else. +*/ +function combineConfig(configs, defaults, // Should hold only the optional properties of Config, but I haven't managed to express that +combine = {}) { + let result = {}; + for (let config of configs) + for (let key of Object.keys(config)) { + let value = config[key], current = result[key]; + if (current === undefined) + result[key] = value; + else if (current === value || value === undefined) ; // No conflict + else if (Object.hasOwnProperty.call(combine, key)) + result[key] = combine[key](current, value); + else + throw new Error("Config merge conflict for field " + key); + } + for (let key in defaults) + if (result[key] === undefined) + result[key] = defaults[key]; + return result; +} + +/** +Each range is associated with a value, which must inherit from +this class. +*/ +class RangeValue { + /** + Compare this value with another value. Used when comparing + rangesets. The default implementation compares by identity. + Unless you are only creating a fixed number of unique instances + of your value type, it is a good idea to implement this + properly. + */ + eq(other) { return this == other; } + /** + Create a [range](https://codemirror.net/6/docs/ref/#state.Range) with this value. + */ + range(from, to = from) { return Range.create(from, to, this); } +} +RangeValue.prototype.startSide = RangeValue.prototype.endSide = 0; +RangeValue.prototype.point = false; +RangeValue.prototype.mapMode = MapMode.TrackDel; +/** +A range associates a value with a range of positions. +*/ +class Range { + constructor( + /** + The range's start position. + */ + from, + /** + Its end position. + */ + to, + /** + The value associated with this range. + */ + value) { + this.from = from; + this.to = to; + this.value = value; + } + /** + @internal + */ + static create(from, to, value) { + return new Range(from, to, value); + } +} +function cmpRange(a, b) { + return a.from - b.from || a.value.startSide - b.value.startSide; +} +class Chunk { + constructor(from, to, value, + // Chunks are marked with the largest point that occurs + // in them (or -1 for no points), so that scans that are + // only interested in points (such as the + // heightmap-related logic) can skip range-only chunks. + maxPoint) { + this.from = from; + this.to = to; + this.value = value; + this.maxPoint = maxPoint; + } + get length() { return this.to[this.to.length - 1]; } + // Find the index of the given position and side. Use the ranges' + // `from` pos when `end == false`, `to` when `end == true`. + findIndex(pos, side, end, startAt = 0) { + let arr = end ? this.to : this.from; + for (let lo = startAt, hi = arr.length;;) { + if (lo == hi) + return lo; + let mid = (lo + hi) >> 1; + let diff = arr[mid] - pos || (end ? this.value[mid].endSide : this.value[mid].startSide) - side; + if (mid == lo) + return diff >= 0 ? lo : hi; + if (diff >= 0) + hi = mid; + else + lo = mid + 1; + } + } + between(offset, from, to, f) { + for (let i = this.findIndex(from, -1000000000 /* C.Far */, true), e = this.findIndex(to, 1000000000 /* C.Far */, false, i); i < e; i++) + if (f(this.from[i] + offset, this.to[i] + offset, this.value[i]) === false) + return false; + } + map(offset, changes) { + let value = [], from = [], to = [], newPos = -1, maxPoint = -1; + for (let i = 0; i < this.value.length; i++) { + let val = this.value[i], curFrom = this.from[i] + offset, curTo = this.to[i] + offset, newFrom, newTo; + if (curFrom == curTo) { + let mapped = changes.mapPos(curFrom, val.startSide, val.mapMode); + if (mapped == null) + continue; + newFrom = newTo = mapped; + if (val.startSide != val.endSide) { + newTo = changes.mapPos(curFrom, val.endSide); + if (newTo < newFrom) + continue; + } + } + else { + newFrom = changes.mapPos(curFrom, val.startSide); + newTo = changes.mapPos(curTo, val.endSide); + if (newFrom > newTo || newFrom == newTo && val.startSide > 0 && val.endSide <= 0) + continue; + } + if ((newTo - newFrom || val.endSide - val.startSide) < 0) + continue; + if (newPos < 0) + newPos = newFrom; + if (val.point) + maxPoint = Math.max(maxPoint, newTo - newFrom); + value.push(val); + from.push(newFrom - newPos); + to.push(newTo - newPos); + } + return { mapped: value.length ? new Chunk(from, to, value, maxPoint) : null, pos: newPos }; + } +} +/** +A range set stores a collection of [ranges](https://codemirror.net/6/docs/ref/#state.Range) in a +way that makes them efficient to [map](https://codemirror.net/6/docs/ref/#state.RangeSet.map) and +[update](https://codemirror.net/6/docs/ref/#state.RangeSet.update). This is an immutable data +structure. +*/ +class RangeSet { + constructor( + /** + @internal + */ + chunkPos, + /** + @internal + */ + chunk, + /** + @internal + */ + nextLayer, + /** + @internal + */ + maxPoint) { + this.chunkPos = chunkPos; + this.chunk = chunk; + this.nextLayer = nextLayer; + this.maxPoint = maxPoint; + } + /** + @internal + */ + static create(chunkPos, chunk, nextLayer, maxPoint) { + return new RangeSet(chunkPos, chunk, nextLayer, maxPoint); + } + /** + @internal + */ + get length() { + let last = this.chunk.length - 1; + return last < 0 ? 0 : Math.max(this.chunkEnd(last), this.nextLayer.length); + } + /** + The number of ranges in the set. + */ + get size() { + if (this.isEmpty) + return 0; + let size = this.nextLayer.size; + for (let chunk of this.chunk) + size += chunk.value.length; + return size; + } + /** + @internal + */ + chunkEnd(index) { + return this.chunkPos[index] + this.chunk[index].length; + } + /** + Update the range set, optionally adding new ranges or filtering + out existing ones. + + (Note: The type parameter is just there as a kludge to work + around TypeScript variance issues that prevented `RangeSet` + from being a subtype of `RangeSet` when `X` is a subtype of + `Y`.) + */ + update(updateSpec) { + let { add = [], sort = false, filterFrom = 0, filterTo = this.length } = updateSpec; + let filter = updateSpec.filter; + if (add.length == 0 && !filter) + return this; + if (sort) + add = add.slice().sort(cmpRange); + if (this.isEmpty) + return add.length ? RangeSet.of(add) : this; + let cur = new LayerCursor(this, null, -1).goto(0), i = 0, spill = []; + let builder = new RangeSetBuilder(); + while (cur.value || i < add.length) { + if (i < add.length && (cur.from - add[i].from || cur.startSide - add[i].value.startSide) >= 0) { + let range = add[i++]; + if (!builder.addInner(range.from, range.to, range.value)) + spill.push(range); + } + else if (cur.rangeIndex == 1 && cur.chunkIndex < this.chunk.length && + (i == add.length || this.chunkEnd(cur.chunkIndex) < add[i].from) && + (!filter || filterFrom > this.chunkEnd(cur.chunkIndex) || filterTo < this.chunkPos[cur.chunkIndex]) && + builder.addChunk(this.chunkPos[cur.chunkIndex], this.chunk[cur.chunkIndex])) { + cur.nextChunk(); + } + else { + if (!filter || filterFrom > cur.to || filterTo < cur.from || filter(cur.from, cur.to, cur.value)) { + if (!builder.addInner(cur.from, cur.to, cur.value)) + spill.push(Range.create(cur.from, cur.to, cur.value)); + } + cur.next(); + } + } + return builder.finishInner(this.nextLayer.isEmpty && !spill.length ? RangeSet.empty + : this.nextLayer.update({ add: spill, filter, filterFrom, filterTo })); + } + /** + Map this range set through a set of changes, return the new set. + */ + map(changes) { + if (changes.empty || this.isEmpty) + return this; + let chunks = [], chunkPos = [], maxPoint = -1; + for (let i = 0; i < this.chunk.length; i++) { + let start = this.chunkPos[i], chunk = this.chunk[i]; + let touch = changes.touchesRange(start, start + chunk.length); + if (touch === false) { + maxPoint = Math.max(maxPoint, chunk.maxPoint); + chunks.push(chunk); + chunkPos.push(changes.mapPos(start)); + } + else if (touch === true) { + let { mapped, pos } = chunk.map(start, changes); + if (mapped) { + maxPoint = Math.max(maxPoint, mapped.maxPoint); + chunks.push(mapped); + chunkPos.push(pos); + } + } + } + let next = this.nextLayer.map(changes); + return chunks.length == 0 ? next : new RangeSet(chunkPos, chunks, next || RangeSet.empty, maxPoint); + } + /** + Iterate over the ranges that touch the region `from` to `to`, + calling `f` for each. There is no guarantee that the ranges will + be reported in any specific order. When the callback returns + `false`, iteration stops. + */ + between(from, to, f) { + if (this.isEmpty) + return; + for (let i = 0; i < this.chunk.length; i++) { + let start = this.chunkPos[i], chunk = this.chunk[i]; + if (to >= start && from <= start + chunk.length && + chunk.between(start, from - start, to - start, f) === false) + return; + } + this.nextLayer.between(from, to, f); + } + /** + Iterate over the ranges in this set, in order, including all + ranges that end at or after `from`. + */ + iter(from = 0) { + return HeapCursor.from([this]).goto(from); + } + /** + @internal + */ + get isEmpty() { return this.nextLayer == this; } + /** + Iterate over the ranges in a collection of sets, in order, + starting from `from`. + */ + static iter(sets, from = 0) { + return HeapCursor.from(sets).goto(from); + } + /** + Iterate over two groups of sets, calling methods on `comparator` + to notify it of possible differences. + */ + static compare(oldSets, newSets, + /** + This indicates how the underlying data changed between these + ranges, and is needed to synchronize the iteration. + */ + textDiff, comparator, + /** + Can be used to ignore all non-point ranges, and points below + the given size. When -1, all ranges are compared. + */ + minPointSize = -1) { + let a = oldSets.filter(set => set.maxPoint > 0 || !set.isEmpty && set.maxPoint >= minPointSize); + let b = newSets.filter(set => set.maxPoint > 0 || !set.isEmpty && set.maxPoint >= minPointSize); + let sharedChunks = findSharedChunks(a, b, textDiff); + let sideA = new SpanCursor(a, sharedChunks, minPointSize); + let sideB = new SpanCursor(b, sharedChunks, minPointSize); + textDiff.iterGaps((fromA, fromB, length) => compare(sideA, fromA, sideB, fromB, length, comparator)); + if (textDiff.empty && textDiff.length == 0) + compare(sideA, 0, sideB, 0, 0, comparator); + } + /** + Compare the contents of two groups of range sets, returning true + if they are equivalent in the given range. + */ + static eq(oldSets, newSets, from = 0, to) { + if (to == null) + to = 1000000000 /* C.Far */ - 1; + let a = oldSets.filter(set => !set.isEmpty && newSets.indexOf(set) < 0); + let b = newSets.filter(set => !set.isEmpty && oldSets.indexOf(set) < 0); + if (a.length != b.length) + return false; + if (!a.length) + return true; + let sharedChunks = findSharedChunks(a, b); + let sideA = new SpanCursor(a, sharedChunks, 0).goto(from), sideB = new SpanCursor(b, sharedChunks, 0).goto(from); + for (;;) { + if (sideA.to != sideB.to || + !sameValues(sideA.active, sideB.active) || + sideA.point && (!sideB.point || !sideA.point.eq(sideB.point))) + return false; + if (sideA.to > to) + return true; + sideA.next(); + sideB.next(); + } + } + /** + Iterate over a group of range sets at the same time, notifying + the iterator about the ranges covering every given piece of + content. Returns the open count (see + [`SpanIterator.span`](https://codemirror.net/6/docs/ref/#state.SpanIterator.span)) at the end + of the iteration. + */ + static spans(sets, from, to, iterator, + /** + When given and greater than -1, only points of at least this + size are taken into account. + */ + minPointSize = -1) { + let cursor = new SpanCursor(sets, null, minPointSize).goto(from), pos = from; + let openRanges = cursor.openStart; + for (;;) { + let curTo = Math.min(cursor.to, to); + if (cursor.point) { + let active = cursor.activeForPoint(cursor.to); + let openCount = cursor.pointFrom < from ? active.length + 1 + : cursor.point.startSide < 0 ? active.length + : Math.min(active.length, openRanges); + iterator.point(pos, curTo, cursor.point, active, openCount, cursor.pointRank); + openRanges = Math.min(cursor.openEnd(curTo), active.length); + } + else if (curTo > pos) { + iterator.span(pos, curTo, cursor.active, openRanges); + openRanges = cursor.openEnd(curTo); + } + if (cursor.to > to) + return openRanges + (cursor.point && cursor.to > to ? 1 : 0); + pos = cursor.to; + cursor.next(); + } + } + /** + Create a range set for the given range or array of ranges. By + default, this expects the ranges to be _sorted_ (by start + position and, if two start at the same position, + `value.startSide`). You can pass `true` as second argument to + cause the method to sort them. + */ + static of(ranges, sort = false) { + let build = new RangeSetBuilder(); + for (let range of ranges instanceof Range ? [ranges] : sort ? lazySort(ranges) : ranges) + build.add(range.from, range.to, range.value); + return build.finish(); + } + /** + Join an array of range sets into a single set. + */ + static join(sets) { + if (!sets.length) + return RangeSet.empty; + let result = sets[sets.length - 1]; + for (let i = sets.length - 2; i >= 0; i--) { + for (let layer = sets[i]; layer != RangeSet.empty; layer = layer.nextLayer) + result = new RangeSet(layer.chunkPos, layer.chunk, result, Math.max(layer.maxPoint, result.maxPoint)); + } + return result; + } +} +/** +The empty set of ranges. +*/ +RangeSet.empty = /*@__PURE__*/new RangeSet([], [], null, -1); +function lazySort(ranges) { + if (ranges.length > 1) + for (let prev = ranges[0], i = 1; i < ranges.length; i++) { + let cur = ranges[i]; + if (cmpRange(prev, cur) > 0) + return ranges.slice().sort(cmpRange); + prev = cur; + } + return ranges; +} +RangeSet.empty.nextLayer = RangeSet.empty; +/** +A range set builder is a data structure that helps build up a +[range set](https://codemirror.net/6/docs/ref/#state.RangeSet) directly, without first allocating +an array of [`Range`](https://codemirror.net/6/docs/ref/#state.Range) objects. +*/ +class RangeSetBuilder { + finishChunk(newArrays) { + this.chunks.push(new Chunk(this.from, this.to, this.value, this.maxPoint)); + this.chunkPos.push(this.chunkStart); + this.chunkStart = -1; + this.setMaxPoint = Math.max(this.setMaxPoint, this.maxPoint); + this.maxPoint = -1; + if (newArrays) { + this.from = []; + this.to = []; + this.value = []; + } + } + /** + Create an empty builder. + */ + constructor() { + this.chunks = []; + this.chunkPos = []; + this.chunkStart = -1; + this.last = null; + this.lastFrom = -1000000000 /* C.Far */; + this.lastTo = -1000000000 /* C.Far */; + this.from = []; + this.to = []; + this.value = []; + this.maxPoint = -1; + this.setMaxPoint = -1; + this.nextLayer = null; + } + /** + Add a range. Ranges should be added in sorted (by `from` and + `value.startSide`) order. + */ + add(from, to, value) { + if (!this.addInner(from, to, value)) + (this.nextLayer || (this.nextLayer = new RangeSetBuilder)).add(from, to, value); + } + /** + @internal + */ + addInner(from, to, value) { + let diff = from - this.lastTo || value.startSide - this.last.endSide; + if (diff <= 0 && (from - this.lastFrom || value.startSide - this.last.startSide) < 0) + throw new Error("Ranges must be added sorted by `from` position and `startSide`"); + if (diff < 0) + return false; + if (this.from.length == 250 /* C.ChunkSize */) + this.finishChunk(true); + if (this.chunkStart < 0) + this.chunkStart = from; + this.from.push(from - this.chunkStart); + this.to.push(to - this.chunkStart); + this.last = value; + this.lastFrom = from; + this.lastTo = to; + this.value.push(value); + if (value.point) + this.maxPoint = Math.max(this.maxPoint, to - from); + return true; + } + /** + @internal + */ + addChunk(from, chunk) { + if ((from - this.lastTo || chunk.value[0].startSide - this.last.endSide) < 0) + return false; + if (this.from.length) + this.finishChunk(true); + this.setMaxPoint = Math.max(this.setMaxPoint, chunk.maxPoint); + this.chunks.push(chunk); + this.chunkPos.push(from); + let last = chunk.value.length - 1; + this.last = chunk.value[last]; + this.lastFrom = chunk.from[last] + from; + this.lastTo = chunk.to[last] + from; + return true; + } + /** + Finish the range set. Returns the new set. The builder can't be + used anymore after this has been called. + */ + finish() { return this.finishInner(RangeSet.empty); } + /** + @internal + */ + finishInner(next) { + if (this.from.length) + this.finishChunk(false); + if (this.chunks.length == 0) + return next; + let result = RangeSet.create(this.chunkPos, this.chunks, this.nextLayer ? this.nextLayer.finishInner(next) : next, this.setMaxPoint); + this.from = null; // Make sure further `add` calls produce errors + return result; + } +} +function findSharedChunks(a, b, textDiff) { + let inA = new Map(); + for (let set of a) + for (let i = 0; i < set.chunk.length; i++) + if (set.chunk[i].maxPoint <= 0) + inA.set(set.chunk[i], set.chunkPos[i]); + let shared = new Set(); + for (let set of b) + for (let i = 0; i < set.chunk.length; i++) { + let known = inA.get(set.chunk[i]); + if (known != null && (textDiff ? textDiff.mapPos(known) : known) == set.chunkPos[i] && + !(textDiff === null || textDiff === void 0 ? void 0 : textDiff.touchesRange(known, known + set.chunk[i].length))) + shared.add(set.chunk[i]); + } + return shared; +} +class LayerCursor { + constructor(layer, skip, minPoint, rank = 0) { + this.layer = layer; + this.skip = skip; + this.minPoint = minPoint; + this.rank = rank; + } + get startSide() { return this.value ? this.value.startSide : 0; } + get endSide() { return this.value ? this.value.endSide : 0; } + goto(pos, side = -1000000000 /* C.Far */) { + this.chunkIndex = this.rangeIndex = 0; + this.gotoInner(pos, side, false); + return this; + } + gotoInner(pos, side, forward) { + while (this.chunkIndex < this.layer.chunk.length) { + let next = this.layer.chunk[this.chunkIndex]; + if (!(this.skip && this.skip.has(next) || + this.layer.chunkEnd(this.chunkIndex) < pos || + next.maxPoint < this.minPoint)) + break; + this.chunkIndex++; + forward = false; + } + if (this.chunkIndex < this.layer.chunk.length) { + let rangeIndex = this.layer.chunk[this.chunkIndex].findIndex(pos - this.layer.chunkPos[this.chunkIndex], side, true); + if (!forward || this.rangeIndex < rangeIndex) + this.setRangeIndex(rangeIndex); + } + this.next(); + } + forward(pos, side) { + if ((this.to - pos || this.endSide - side) < 0) + this.gotoInner(pos, side, true); + } + next() { + for (;;) { + if (this.chunkIndex == this.layer.chunk.length) { + this.from = this.to = 1000000000 /* C.Far */; + this.value = null; + break; + } + else { + let chunkPos = this.layer.chunkPos[this.chunkIndex], chunk = this.layer.chunk[this.chunkIndex]; + let from = chunkPos + chunk.from[this.rangeIndex]; + this.from = from; + this.to = chunkPos + chunk.to[this.rangeIndex]; + this.value = chunk.value[this.rangeIndex]; + this.setRangeIndex(this.rangeIndex + 1); + if (this.minPoint < 0 || this.value.point && this.to - this.from >= this.minPoint) + break; + } + } + } + setRangeIndex(index) { + if (index == this.layer.chunk[this.chunkIndex].value.length) { + this.chunkIndex++; + if (this.skip) { + while (this.chunkIndex < this.layer.chunk.length && this.skip.has(this.layer.chunk[this.chunkIndex])) + this.chunkIndex++; + } + this.rangeIndex = 0; + } + else { + this.rangeIndex = index; + } + } + nextChunk() { + this.chunkIndex++; + this.rangeIndex = 0; + this.next(); + } + compare(other) { + return this.from - other.from || this.startSide - other.startSide || this.rank - other.rank || + this.to - other.to || this.endSide - other.endSide; + } +} +class HeapCursor { + constructor(heap) { + this.heap = heap; + } + static from(sets, skip = null, minPoint = -1) { + let heap = []; + for (let i = 0; i < sets.length; i++) { + for (let cur = sets[i]; !cur.isEmpty; cur = cur.nextLayer) { + if (cur.maxPoint >= minPoint) + heap.push(new LayerCursor(cur, skip, minPoint, i)); + } + } + return heap.length == 1 ? heap[0] : new HeapCursor(heap); + } + get startSide() { return this.value ? this.value.startSide : 0; } + goto(pos, side = -1000000000 /* C.Far */) { + for (let cur of this.heap) + cur.goto(pos, side); + for (let i = this.heap.length >> 1; i >= 0; i--) + heapBubble(this.heap, i); + this.next(); + return this; + } + forward(pos, side) { + for (let cur of this.heap) + cur.forward(pos, side); + for (let i = this.heap.length >> 1; i >= 0; i--) + heapBubble(this.heap, i); + if ((this.to - pos || this.value.endSide - side) < 0) + this.next(); + } + next() { + if (this.heap.length == 0) { + this.from = this.to = 1000000000 /* C.Far */; + this.value = null; + this.rank = -1; + } + else { + let top = this.heap[0]; + this.from = top.from; + this.to = top.to; + this.value = top.value; + this.rank = top.rank; + if (top.value) + top.next(); + heapBubble(this.heap, 0); + } + } +} +function heapBubble(heap, index) { + for (let cur = heap[index];;) { + let childIndex = (index << 1) + 1; + if (childIndex >= heap.length) + break; + let child = heap[childIndex]; + if (childIndex + 1 < heap.length && child.compare(heap[childIndex + 1]) >= 0) { + child = heap[childIndex + 1]; + childIndex++; + } + if (cur.compare(child) < 0) + break; + heap[childIndex] = cur; + heap[index] = child; + index = childIndex; + } +} +class SpanCursor { + constructor(sets, skip, minPoint) { + this.minPoint = minPoint; + this.active = []; + this.activeTo = []; + this.activeRank = []; + this.minActive = -1; + // A currently active point range, if any + this.point = null; + this.pointFrom = 0; + this.pointRank = 0; + this.to = -1000000000 /* C.Far */; + this.endSide = 0; + // The amount of open active ranges at the start of the iterator. + // Not including points. + this.openStart = -1; + this.cursor = HeapCursor.from(sets, skip, minPoint); + } + goto(pos, side = -1000000000 /* C.Far */) { + this.cursor.goto(pos, side); + this.active.length = this.activeTo.length = this.activeRank.length = 0; + this.minActive = -1; + this.to = pos; + this.endSide = side; + this.openStart = -1; + this.next(); + return this; + } + forward(pos, side) { + while (this.minActive > -1 && (this.activeTo[this.minActive] - pos || this.active[this.minActive].endSide - side) < 0) + this.removeActive(this.minActive); + this.cursor.forward(pos, side); + } + removeActive(index) { + remove(this.active, index); + remove(this.activeTo, index); + remove(this.activeRank, index); + this.minActive = findMinIndex(this.active, this.activeTo); + } + addActive(trackOpen) { + let i = 0, { value, to, rank } = this.cursor; + // Organize active marks by rank first, then by size + while (i < this.activeRank.length && (rank - this.activeRank[i] || to - this.activeTo[i]) > 0) + i++; + insert(this.active, i, value); + insert(this.activeTo, i, to); + insert(this.activeRank, i, rank); + if (trackOpen) + insert(trackOpen, i, this.cursor.from); + this.minActive = findMinIndex(this.active, this.activeTo); + } + // After calling this, if `this.point` != null, the next range is a + // point. Otherwise, it's a regular range, covered by `this.active`. + next() { + let from = this.to, wasPoint = this.point; + this.point = null; + let trackOpen = this.openStart < 0 ? [] : null; + for (;;) { + let a = this.minActive; + if (a > -1 && (this.activeTo[a] - this.cursor.from || this.active[a].endSide - this.cursor.startSide) < 0) { + if (this.activeTo[a] > from) { + this.to = this.activeTo[a]; + this.endSide = this.active[a].endSide; + break; + } + this.removeActive(a); + if (trackOpen) + remove(trackOpen, a); + } + else if (!this.cursor.value) { + this.to = this.endSide = 1000000000 /* C.Far */; + break; + } + else if (this.cursor.from > from) { + this.to = this.cursor.from; + this.endSide = this.cursor.startSide; + break; + } + else { + let nextVal = this.cursor.value; + if (!nextVal.point) { // Opening a range + this.addActive(trackOpen); + this.cursor.next(); + } + else if (wasPoint && this.cursor.to == this.to && this.cursor.from < this.cursor.to) { + // Ignore any non-empty points that end precisely at the end of the prev point + this.cursor.next(); + } + else { // New point + this.point = nextVal; + this.pointFrom = this.cursor.from; + this.pointRank = this.cursor.rank; + this.to = this.cursor.to; + this.endSide = nextVal.endSide; + this.cursor.next(); + this.forward(this.to, this.endSide); + break; + } + } + } + if (trackOpen) { + this.openStart = 0; + for (let i = trackOpen.length - 1; i >= 0 && trackOpen[i] < from; i--) + this.openStart++; + } + } + activeForPoint(to) { + if (!this.active.length) + return this.active; + let active = []; + for (let i = this.active.length - 1; i >= 0; i--) { + if (this.activeRank[i] < this.pointRank) + break; + if (this.activeTo[i] > to || this.activeTo[i] == to && this.active[i].endSide >= this.point.endSide) + active.push(this.active[i]); + } + return active.reverse(); + } + openEnd(to) { + let open = 0; + for (let i = this.activeTo.length - 1; i >= 0 && this.activeTo[i] > to; i--) + open++; + return open; + } +} +function compare(a, startA, b, startB, length, comparator) { + a.goto(startA); + b.goto(startB); + let endB = startB + length; + let pos = startB, dPos = startB - startA; + for (;;) { + let dEnd = (a.to + dPos) - b.to, diff = dEnd || a.endSide - b.endSide; + let end = diff < 0 ? a.to + dPos : b.to, clipEnd = Math.min(end, endB); + if (a.point || b.point) { + if (!(a.point && b.point && (a.point == b.point || a.point.eq(b.point)) && + sameValues(a.activeForPoint(a.to), b.activeForPoint(b.to)))) + comparator.comparePoint(pos, clipEnd, a.point, b.point); + } + else { + if (clipEnd > pos && !sameValues(a.active, b.active)) + comparator.compareRange(pos, clipEnd, a.active, b.active); + } + if (end > endB) + break; + if ((dEnd || a.openEnd != b.openEnd) && comparator.boundChange) + comparator.boundChange(end); + pos = end; + if (diff <= 0) + a.next(); + if (diff >= 0) + b.next(); + } +} +function sameValues(a, b) { + if (a.length != b.length) + return false; + for (let i = 0; i < a.length; i++) + if (a[i] != b[i] && !a[i].eq(b[i])) + return false; + return true; +} +function remove(array, index) { + for (let i = index, e = array.length - 1; i < e; i++) + array[i] = array[i + 1]; + array.pop(); +} +function insert(array, index, value) { + for (let i = array.length - 1; i >= index; i--) + array[i + 1] = array[i]; + array[index] = value; +} +function findMinIndex(value, array) { + let found = -1, foundPos = 1000000000 /* C.Far */; + for (let i = 0; i < array.length; i++) + if ((array[i] - foundPos || value[i].endSide - value[found].endSide) < 0) { + found = i; + foundPos = array[i]; + } + return found; +} + +/** +Count the column position at the given offset into the string, +taking extending characters and tab size into account. +*/ +function countColumn(string, tabSize, to = string.length) { + let n = 0; + for (let i = 0; i < to;) { + if (string.charCodeAt(i) == 9) { + n += tabSize - (n % tabSize); + i++; + } + else { + n++; + i = findClusterBreak(string, i); + } + } + return n; +} +/** +Find the offset that corresponds to the given column position in a +string, taking extending characters and tab size into account. By +default, the string length is returned when it is too short to +reach the column. Pass `strict` true to make it return -1 in that +situation. +*/ +function findColumn(string, col, tabSize, strict) { + for (let i = 0, n = 0;;) { + if (n >= col) + return i; + if (i == string.length) + break; + n += string.charCodeAt(i) == 9 ? tabSize - (n % tabSize) : 1; + i = findClusterBreak(string, i); + } + return strict === true ? -1 : string.length; +} + +export { Annotation, AnnotationType, ChangeDesc, ChangeSet, CharCategory, Compartment, EditorSelection, EditorState, Facet, Line, MapMode, Prec, Range, RangeSet, RangeSetBuilder, RangeValue, SelectionRange, StateEffect, StateEffectType, StateField, Text, Transaction, codePointAt, codePointSize, combineConfig, countColumn, findClusterBreak, findColumn, fromCodePoint }; diff --git a/node_modules/@codemirror/state/package.json b/node_modules/@codemirror/state/package.json new file mode 100644 index 0000000..b0895d1 --- /dev/null +++ b/node_modules/@codemirror/state/package.json @@ -0,0 +1,38 @@ +{ + "name": "@codemirror/state", + "version": "6.5.0", + "description": "Editor state data structures for the CodeMirror code editor", + "scripts": { + "test": "cm-runtests", + "prepare": "cm-buildhelper src/index.ts" + }, + "keywords": [ + "editor", + "code" + ], + "author": { + "name": "Marijn Haverbeke", + "email": "marijn@haverbeke.berlin", + "url": "http://marijnhaverbeke.nl" + }, + "type": "module", + "main": "dist/index.cjs", + "exports": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + }, + "types": "dist/index.d.ts", + "module": "dist/index.js", + "sideEffects": false, + "license": "MIT", + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + }, + "devDependencies": { + "@codemirror/buildhelper": "^1.0.0" + }, + "repository": { + "type": "git", + "url": "https://github.com/codemirror/state.git" + } +} diff --git a/node_modules/@codemirror/view/.github/workflows/dispatch.yml b/node_modules/@codemirror/view/.github/workflows/dispatch.yml new file mode 100644 index 0000000..d050072 --- /dev/null +++ b/node_modules/@codemirror/view/.github/workflows/dispatch.yml @@ -0,0 +1,16 @@ +name: Trigger CI +on: push + +jobs: + build: + name: Dispatch to main repo + runs-on: ubuntu-latest + steps: + - name: Emit repository_dispatch + uses: mvasigh/dispatch-action@main + with: + # You should create a personal access token and store it in your repository + token: ${{ secrets.DISPATCH_AUTH }} + repo: dev + owner: codemirror + event_type: push diff --git a/node_modules/@codemirror/view/CHANGELOG.md b/node_modules/@codemirror/view/CHANGELOG.md new file mode 100644 index 0000000..42b3852 --- /dev/null +++ b/node_modules/@codemirror/view/CHANGELOG.md @@ -0,0 +1,2084 @@ +## 6.38.6 (2025-10-13) + +### Bug fixes + +Work around a regression in Safari 26 that causes fragments of old selections to remain visible. + +## 6.38.5 (2025-10-07) + +### Bug fixes + +Avoid firing text changes that cover unchanged text on Android. + +Fix an issue where the editor could, in some circumstances, insert a stray newline when typing over a document that ended in a block widget. + +Work around an issue in Safari 26 that causes inappropriate scrolling on focus in some circumstances. + +## 6.38.4 (2025-09-28) + +### Bug fixes + +Work around a Chrome Android issue where the browser doesn't properly fire composition end events, leaving CodeMirror to believe the user was still composing. + +## 6.38.3 (2025-09-22) + +### Bug fixes + +Work around a rendering bug in Mobile Safari by completely hiding empty layers. + +Fix vertical cursor motion in Chrome around decorations with bottom borders or margins. + +Fix an issue that caused mark decorations longer than 512 characters to needlessly be split. + +Move the cursor out of atomic ranges when text input happens. + +## 6.38.2 (2025-09-01) + +### Bug fixes + +Re-enable falling dispatching keys by key code for Cmd-Alt- combinations on macOS. + +Make sure all pointer selections skip atomic ranges. + +## 6.38.1 (2025-07-15) + +### Bug fixes + +Make the keymap not dispatch Alt key combos on macOS by key code, because those are generally used to type special characters. + +Fix a layout bug that could occur with very narrow editors. + +## 6.38.0 (2025-06-27) + +### New features + +Gutters can now specify that they should be displayed after the content (which would be to the right in a left-to-right layout). + +## 6.37.2 (2025-06-12) + +### Bug fixes + +Fix an issue where moving the cursor vertically from the one-but-last character on a line would sometimes move incorrectly on Safari. + +Fix an issue causing coordinates between lines of text to sometimes be inappropriately placed at the end of the line by `posAtCoords`. + +## 6.37.1 (2025-05-30) + +### Bug fixes + +Properly add `crelt` as a dependency. + +## 6.37.0 (2025-05-29) + +### New features + +View plugins can now take an argument, in which case they must be instantiated with their `of` method in order to be added to a configuration. + +The new `showDialog` function makes it easy to show a notification or prompt using a CodeMirror panel. + +## 6.36.8 (2025-05-12) + +### Bug fixes + +Make `logException` log errors to the console when `onerror` returns a falsy value. + +Fix an issue in `MatchDecorator` causing `updateDeco` to sometimes not do the right thing for deletions. + +## 6.36.7 (2025-05-02) + +### Bug fixes + +Use the `aria-placeholder` attribute to communicate the placeholder text to screen readers. + +Fix a crash when `EditorView.composing` or `.compositionStarted` are accessed during view initialization. + +## 6.36.6 (2025-04-24) + +### Bug fixes + +Fix an issue where `drawSelection` would draw selections starting at a block widget not at a line break in an odd way. + +Fix an issue where the editor would inappropriately scroll when editing near the bottom of the document with line wrapping enabled, in some cases. + +Fix an issue that caused unnecessary transactions on focus change. + +## 6.36.5 (2025-03-29) + +### Bug fixes + +Fix an issue where some browsers wouldn't enable context menu paste when clicking on placeholder text. + +Fix an issue where cursor height would unnecessarily be based on a placeholder node's dimensions, and thus be off from the text height. + +## 6.36.4 (2025-03-03) + +### Bug fixes + +Fix an issue where scrolling down to a range higher than the viewport could in some situations fail to scroll to the proper position. + +## 6.36.3 (2025-02-18) + +### Bug fixes + +Make sure event handlers registered with `domEventHandlers` are not called during view updates, to avoid triggering nested update errors. + +Don't include the window scrollbars in the space available for displaying tooltips. + +Work around an issue with Chrome's `EditContext` that shows up when using autocompletion while composing with Samsung's virtual Android keyboard. + +## 6.36.2 (2025-01-09) + +### Bug fixes + +Fix an issue where some kinds of relayouts could put the editor in a state where it believed it wasn't in window, preventing relayout, though it in fact was. + +Make sure macOS double-space-to-period conversions are properly suppressed. + +Fix an issue where native selection changes, such as mobile spacebar-drag, weren't being picked up in edit context mode. + +## 6.36.1 (2024-12-19) + +### Bug fixes + +Fix a crash in MatchDecorator when updating matches at the end of the document. + +## 6.36.0 (2024-12-17) + +### Bug fixes + +Make selection rectangles verticaly align precisely, rather than introducing a slight overlap. + +Fix an issue in `MatchDecorator` that caused it to fully rebuild its decorations on normal edits. + +### New features + +View updates now have a `viewportMoved` flag that is only true when a viewport change originated from something other than mapping the viewport over a document change. + +## 6.35.3 (2024-12-09) + +### Bug fixes + +Fix an issue where mark decorations that got merged or split weren't properly redrawn. + +Avoid spurious focus events by not updating the DOM selection when the editor is unfocused but focusable. + +Disable `writingsuggestions` for the editable element, to opt out of Safari's new intelligence completions (which mess up in the editor). + +## 6.35.2 (2024-12-07) + +### Bug fixes + +Fix an issue on Chrome where typing at the end of the document would insert a character after the cursor. + +## 6.35.1 (2024-12-06) + +### Bug fixes + +Work around another crash caused by incorrect composition positions reported by `EditContext`. + +Stop disabling custom cursors on Safari version 11.4 and up, which support `caret-color`. + +Fix an issue where a tooltip with wrapped content could, in some circumstances, fail to find a stable position due to a cyclic dependency between its width and its position. + +## 6.35.0 (2024-11-21) + +### New features + +Tooltips can now use the `clip` option to control whether they are hidden when outside the visible editor content. + +## 6.34.3 (2024-11-15) + +### Bug fixes + +Make sure positions covered by a gutter or a panel aren't treated as visible for the purpose of displaying tooltips. + +Properly include the tooltip arrow height when checking whether a tooltip fits in its preferred above/below position. + +Fix an issue with compositions on Chrome inserting their content in the wrong position when another document change came in during composition. + +## 6.34.2 (2024-11-05) + +### Bug fixes + +Fix the default cursor color for dark themes, which was way too dark. + +## 6.34.1 (2024-09-27) + +### Bug fixes + +Avoid a stack overflow that could happen when updating a line with a lot of text tokens. + +Improve the way enormously long (non-wrapped) lines are displayed by making sure they stay shorter than the maximal pixel size the browser's CSS engine can handle. + +## 6.34.0 (2024-09-25) + +### Bug fixes + +Fix an issue where the dots past the wrapping point were displayed incorrectly when using `highlightWhitespace` with a wrapped sequence of spaces. + +Improve performance of documents displaying lots of highlighted spaces by using a CSS background instead of pseudo-element. + +### New features + +`placeholder` now allows a function that constructs the placedholder DOM to be passed in, and uses `cloneNode` when a raw element is passed in, to prevent adding the same element to multiple editors. + +## 6.33.1 (2024-08-30) + +### Bug fixes + +Work around odd behavior in Chrome's newly supported `caretPositionFromPoint` method, which could cause CodeMirror to crash with a null dereference. + +## 6.33.0 (2024-08-24) + +### Bug fixes + +Make it easier to move the pointer over a hover tooltip with an arrow by not closing the tooltip when the pointer is moving over the gap for the arrow. + +### New features + +The new `EditorView.clipboardInputFilter` and `clipboardOutputFilter` facets allow you to register filter functions that change text taken from or sent to the clipboard. + +## 6.32.0 (2024-08-12) + +### Bug fixes + +Fix a bug where the editor could draw way too big a viewport when not managing its own scrollbar. + +### New features + +The new `gutterWidgetClass` facet makes it possible to add a class to gutter elements next to widgets. + +## 6.31.0 (2024-08-11) + +### Bug fixes + +Avoid the editor's geometry measurements becoming incorrect when fonts finish loading by scheduling a measure on `document.fonts.ready`. + +Avoid an issue where Chrome would incorrectly scroll the window when deleting lines in the editor. + +Fix an issue where in some layouts editor content would be drawn on top of panel elements. + +Fix an issue where `coordsAtPos` would return null when querying a position in a block widget. + +### New features + +The new `lineNumberWidgetMarker` facet makes it possible to insert markers into the line number gutter for widgets. + +## 6.30.0 (2024-08-05) + +### Bug fixes + +Make spell check corrections work again on `EditContext`-enabled Chrome versions. + +### New features + +The value returned by `hoverTooltip` now has an `active` property providing the state field used to store the open tooltips. + +## 6.29.1 (2024-07-29) + +### Bug fixes + +Fix a crash on old Safari browsers that don't support `MediaQueryList.addEventListener`. + +Fix an issue where `EditorView.viewportLineBlocks` (and thus other things like the gutter) might be out of date after some kinds of decoration changes. + +## 6.29.0 (2024-07-25) + +### Bug fixes + +Fix an issue that caused typing into an editor marked read-only to cause document changes when using `EditContext`. + +Associate a cursor created by clicking above the end of the text on a wrap point with the line before it. + +### New features + +The package now exports the type of hover tooltip sources as `HoverTooltipSource`. + +## 6.28.6 (2024-07-19) + +### Bug fixes + +Fix an issue where the editor got confused about the position of inserted text when using Chrome's `EditContext` and canceling transactions for typed text. + +## 6.28.5 (2024-07-17) + +### Bug fixes + +Fix a bug that broke drag scrolling along one axis when the innermost scrollable element around the editor was only scrollable along the other axis. + +Work around a memory leak in Chrome's EditContext implementation. + +## 6.28.4 (2024-07-03) + +### Bug fixes + +Fix a bug where EditContext-based editing could corrupt the document in some situations. + +## 6.28.3 (2024-07-01) + +### Bug fixes + +Fix an issue causing the IME interface to appear in the wrong spot on Chrome Windows. + +## 6.28.2 (2024-06-21) + +### Bug fixes + +Only use `EditContext` on Chrome versions that support passing it an inverted selection range. + +Fix an issue that prevented non-inclusive block widgets from having their `updateDOM` method called when changed. + +Re-enable `EditContext` use on Chrome 126 and up. + +## 6.28.1 (2024-06-12) + +### Bug fixes + +Disable `EditContext` by default again, to work around a regression where Chrome's implementation doesn't support inverted selections. + +Make sure `EditorView.editable` is respected when `EditContext` is used. + +## 6.28.0 (2024-06-10) + +### Bug fixes + +Fix an issue where long lines broken up by block widgets were sometimes only partially rendered. + +### New features + +The editor will now, when available (which is only on Chrome for the foreseeable future) use the [`EditContext`](https://developer.mozilla.org/en-US/docs/Web/API/EditContext) API to capture text input. + +## 6.27.0 (2024-06-04) + +### New features + +The new `setTabFocusMode` method can be used to control whether the editor disables key bindings for Tab and Shift-Tab. + +## 6.26.4 (2024-06-04) + +### Bug fixes + +Fix an issue where commands with an optional second argument would get the keyboard event in that argument when called from a keymap. + +Fix an issue that could cause the cursor to be rendered on the wrong side of a zero-length block widget. + +Fix an issue where `drawSelection` got confused by block widgets in line-wrapped editors in some situations. + +Don't hide the native selection in widgets that have focus. + +Make sure that clicking an unfocusable editor still remove focus from any other focused elements. + +Fix a crash when loading the package in a non-browser environment. + +Stop mouse selection when the user types. + +## 6.26.3 (2024-04-12) + +### Bug fixes + +Fix an issue where dispatching an update to an editor before it measured itself for the first time could cause the scroll position to incorrectly move. + +Fix a crash when multiple tooltips with arrows are shown. + +## 6.26.2 (2024-04-09) + +### Bug fixes + +Improve behavior of `scrollPastEnd` in a scaled editor. + +When available, use `Selection.getComposedRanges` on Safari to find the selection inside a shadow DOM. + +Remove the workaround that avoided inappropriate styling on composed text after a decoration again, since it breaks the stock Android virtual keyboard. + +## 6.26.1 (2024-03-28) + +### Bug fixes + +Fix the editor getting stuck in composition when Safari fails to fire a compositionend event for a dead key composition. + +Fix an issue where, with IME systems that kept the cursor at the start of the composed text, the editor misidentified the target node and disrupted composition. + +Fix a bug where in a line-wrapped editor, with some content, the initial scroll position would be off from the top of the document. + +## 6.26.0 (2024-03-14) + +### Bug fixes + +Avoid the editor getting confused when iOS autocorrects on pressing Enter and does the correction and the break insertion in two different events. + +Fix the pasting of copied URIs in iOS. + +Fix a bug where a scaled editor could keep performing unnecessary updates due to tiny differences in geometry values returned by the browser. + +Fix a bug where, on iOS with a physical keyboard, the modifiers for some keys weren't being passed to the keymaps. + +Work around the fact that Mobile Safari makes DOM changes before firing a key event when typing ctrl-d on an external keyboard. + +Fix an issue where some commands didn't properly scroll the cursor into view on Mobile Safari. + +Re-measure the document when print settings are changed on Chrome. + +### New features + +The `EditorView.scrollHandler` facet can be used to override or extend the behavior of the editor when things are scrolled into view. + +## 6.25.1 (2024-03-06) + +### Bug fixes + +Fix accidental non-optional field in layer config objects. + +## 6.25.0 (2024-03-04) + +### Bug fixes + +Properly recognize Android GBoard enter presses that strip a space at the end of the line as enter. + +Fix a bug that caused the gutter to have the wrong height when the editor was scaled after construction. + +When starting a composition after a non-inclusive mark decoration, temporarily insert a widget that prevents the composed text from inheriting that mark's styles. + +Make sure the selection is repositioned when a transaction changes decorations without changing the document. + +### New features + +View plugins can now provide a `docViewUpdate` method that is called whenever the document view is updated. + +Layers now take a `updateOnDocUpdate` option that controls whether they are automatically updated when the document view changes. + +## 6.24.1 (2024-02-19) + +### Bug fixes + +Fix a crash that happens when hover tooltips are active during changes, introduced in 6.24.0. + +## 6.24.0 (2024-02-09) + +### Bug fixes + +Fix an issue that broke context-menu select-all on Chrome when the viewport didn't cover the whole document. + +Make sure tooltips are ordered by extension precedence in the DOM. + +### New features + +Hover tooltip sources may now return multiple tooltips. + +## 6.23.1 (2024-01-24) + +### Bug fixes + +Fix a bug that caused `Tooltip.above` to not take effect for tooltips that were already present when the tooltip plugin is initialized. + +Automatically reposition tooltips when their size changes. + +## 6.23.0 (2023-12-28) + +### Bug fixes + +Work around odd iOS Safari behavior when doing select all. + +Fix a composition interruption when an widget is inserted next to the cursor. + +Fix a crash in bidirectional cursor motion. + +Simplify visual motion through bidirectional text, fix several corner cases where it would work badly. + +Fix a bug that broke some bidi isolates not on the first line of the document. + +### New features + +`EditorView.bidiIsolatedRanges` now supports automatically determining the direction of the range if not provided by the decoration. + +`EditorView.visualLineSide` can be used to find the visual end or start of a line with bidirectional text. + +The new `EditorView.outerDecorations` facet can be used to provide decorations that should always be at the bottom of the precedence stack. + +## 6.22.3 (2023-12-13) + +### Bug fixes + +Fix a bug that could cause tooltips to be unnecessarily be positioned absolutely. + +Make sure that, when an editor creates tooltips immediately on initialization, the editor is attached to the document when their `mount` callback is called. + +## 6.22.2 (2023-12-08) + +### Bug fixes + +Fix an issue in the bidirectional motion that could cause the cursor to get stuck in a loop when a zero-width non-joiner char was placed on a direction boundary. + +Fix a bug that corrupts the editor's internal view tree data structure on some types of edits, putting the editor in a broken state. + +## 6.22.1 (2023-11-27) + +### Bug fixes + +Call widget `destroy` methods when the entire editor is destroyed or reset. + +Work around an issue on Safari on macOS Sonoma that made the native cursor visible even when `drawSelection` is enabled. + +Fix an issue where, on some browsers, the screenreader announced text ended up in the printed document. + +Fix a bug where a hover tooltip could stick around even though the pointer was no longer on the editor when it was moved out over the tooltip. + +Fix an issue where hover tooltips could close when moving the mouse onto them due to mouse position rounding issues. + +## 6.22.0 (2023-11-03) + +### Bug fixes + +Exceptions raised by update listeners are now routed to the configured exception sink, if any. + +Fix an issue where passing large scroll margins to `scrollIntoView` would cause the measure loop to fail to terminate. + +Widgets that are draggable (and allow drag events through in their `ignoreEvent` implementation) can now use the editor's built-in drag/drop behavior. + +### New features + +The new `scrollTo` option to `EditorView` allows an initial scroll position to be provided. + +The new `EditorView.scrollSnapshot` method returns an effect that can be used to reset to a previous scroll position. + +## 6.21.4 (2023-10-24) + +### Bug fixes + +Support the `offset`, `getCoords`, `overlap`, and `resize` properties on hover tooltips, as long as they aren't given conflicting values when there are multiple active hover tooltips. + +Fix a bug that caused tooltips in the default configuration to be positioned incorrectly on Chrome when the editor was transformed. + +## 6.21.3 (2023-10-06) + +### Bug fixes + +Fix an issue that caused `coordsForChar` to return the wrong rectangle for characters after a line wrap in Safari. + +Make the context menu work when clicking below the content in a fixed-height editor. + +Tooltips that have been put below/above their target position because there is no room on their default side now stay there on further updates. + +## 6.21.2 (2023-10-02) + +### Bug fixes + +Fix a regression that broke dragging text from inside the editor. + +## 6.21.1 (2023-10-02) + +### Bug fixes + +Fix a bug that could corrupt the DOM view for specific changes involving newlines and mark decorations. + +## 6.21.0 (2023-09-29) + +### Bug fixes + +Fix a bug that could cause zero-length widgets at the start of a line to be left in the view even after they were removed. + +### New features + +`RectangleMarker`'s dimension properties are now public. + +## 6.20.2 (2023-09-25) + +### Bug fixes + +Fix an issue in the way the DOM selection is being read that could break backspacing of widgets on Android. + +Fix a bug where the editor could incorrectly computate its transform scale when it was small. + +## 6.20.1 (2023-09-22) + +### Bug fixes + +Fix a crash in plugin event handlers after dynamic reconfiguration. + +Fix an issue where, on Chrome, tooltips would no longer use fixed positioning. + +## 6.20.0 (2023-09-20) + +### Bug fixes + +Fix an issue that caused `repositionTooltips` to crash when it was called on an editor without tooltips. + +Fix an issue that caused the tooltip system to leave empty nodes in the DOM when an editor using the `parent` option to `tooltips` is destroyed. + +Fix a bug that regression mouse interaction with the area of a fixed-size editor that isn't covered by the content. + +Fix some issues with the way `moveVertically` behaved for positions on line wrap points. + +Fix a bug that could cause the document DOM to be incorrectly updated on some types of viewport changes. + +### New features + +The new `getDrawSelectionConfig` function returns the `drawSelection` configuration for a given state. + +## 6.19.0 (2023-09-14) + +### Bug fixes + +Make sure the drop cursor is properly cleaned up even when another extension handles the drop event. + +Fix a crash related to non-inclusive replacing block decorations. + +### New features + +The new `EditorView.domEventObservers` (and the corresponding option to view plugins) allows you to register functions that are always called for an event, regardless of whether other handlers handled it. + +## 6.18.1 (2023-09-11) + +### Bug fixes + +Fix an issue where the editor duplicated text when the browser moved content into the focused text node on composition. + +Make sure `widgetMarker` is called for gutters on lines covered by a block replace decoration. + +Fix an issue where the cursor could be shown in a position that doesn't allow a cursor when the selection is in a block widget. + +## 6.18.0 (2023-09-05) + +### New features + +The new `EditorView.scaleX` and `scaleY` properties return the CSS-transformed scale of the editor (or 1 when not scaled). + +The editor now supports being scaled with CSS. + +## 6.17.1 (2023-08-31) + +### Bug fixes + +Don't close the hover tooltip when the pointer moves over empty space caused by line breaks within the hovered range. + +Fix a bug where on Chrome Android, if a virtual keyboard was slow to apply a change, the editor could end up dropping it. + +Work around an issue where line-wise copy/cut didn't work in Firefox because the browser wasn't firing those events when nothing was selected. + +Fix a crash triggered by the way some Android IME systems update the DOM. + +Fix a bug that caused replacing a word by an emoji on Chrome Android to be treated as a backspace press. + +## 6.17.0 (2023-08-28) + +### Bug fixes + +Fix a bug that broke hover tooltips when hovering over a widget. + +### New features + +The new `EditorView.cspNonce` facet can be used to provide a Content Security Policy nonce for the library's generated CSS. + +The new `EditorView.bidiIsolatedRanges` can be used to inform the editor about ranges styled as Unicode bidirection isolates, so that it can compute the character order correctly. + +`EditorView.dispatch` now also accepts an array of transactions to be applied together in a single view update. + +The new `dispatchTransactions` option to `new EditorView` now replaces the old (deprecated but still supported) `dispatch` option in a way that allows multiple transactions to be applied in one update. + +Input handlers are now passed an additional argument that they can use to retrieve the default transaction that would be applied for the insertion. + +## 6.16.0 (2023-07-31) + +### Bug fixes + +Fix an issue that made the gutter not stick in place when the editor was in a right-to-left context. + +### New features + +The new `EditorView.coordsForChar` method returns the client rectangle for a given character in the editor. + +## 6.15.3 (2023-07-18) + +### Bug fixes + +Fix another crash regression for compositions before line breaks. + +## 6.15.2 (2023-07-18) + +### Bug fixes + +Fix the check that made sure compositions are dropped when the selection is moved. + +## 6.15.1 (2023-07-18) + +### Bug fixes + +Fix a regression that could cause the composition content to be drawn incorrectly. + +## 6.15.0 (2023-07-17) + +### Bug fixes + +Fix dragging a selection from inside the current selection on macOS. + +Fix an issue that could cause the scroll position to jump wildly + +Don't try to scroll fixed-positioned elements into view by scrolling their parent elements. + +Fix a bug that caused the cursor to be hidden when showing a placeholder that consisted of the empty string. + +Resolve some issues where composition could incorrectly affect nearby replaced content. + +### New features + +Key bindings can now set a `stopPropagation` field to cause the view to stop the key event propagation when it considers the event handled. + +## 6.14.1 (2023-07-06) + +### Bug fixes + +Fix an issue where scrolling up through line-wrapped text would sometimes cause the scroll position to pop down. + +Fix an issue where clicking wouldn't focus the editor on Firefox when it was in an iframe and already the active element of the frame. + +Fix a bug that could cause compositions to be disrupted because their surrounding DOM was repurposed for some other piece of content. + +Fix a bug where adding content to the editor could inappropriately move the scroll position. + +Extend detection of Enter presses on Android to `beforeInput` events with an `"insertLineBreak"` type. + +## 6.14.0 (2023-06-23) + +### Bug fixes + +When dragging text inside the editor, look at the state of Ctrl (or Alt on macOS) at the time of the drop, not the start of drag, to determine whether to move or copy the text. + +Fix an issue where having a bunch of padding on lines could cause vertical cursor motion and `posAtCoords` to jump over lines. + +### New features + +Block widget decorations can now be given an `inlineOrder` option to make them appear in the same ordering as surrounding inline widgets. + +## 6.13.2 (2023-06-13) + +### Bug fixes + +Fix an issue in scroll position stabilization for changes above the visible, where Chrome already does this natively and we ended up compensating twice. + +## 6.13.1 (2023-06-12) + +### Bug fixes + +Fix a bug where the cursor would in some circumstances be drawn on the wrong side of an inline widget. + +Fix an issue where `scrollPastEnd` could cause the scroll position of editors that weren't in view to be changed unnecessarily. + +## 6.13.0 (2023-06-05) + +### Bug fixes + +Forbid widget decoration side values bigger than 10000, to prevent them from breaking range ordering invariants. + +Fix a bug where differences between widgets' estimated and actual heights could cause the editor to inappropriately move the scroll position. + +Avoid another situation in which composition that inserts line breaks could corrupt the editor DOM. + +### New features + +Inline widgets may now introduce line breaks, if they report this through the `WidgetType.lineBreaks` property. + +## 6.12.0 (2023-05-18) + +### Bug fixes + +Remove an accidentally included `console.log`. + +### New features + +`EditorViewConfig.dispatch` is now passed the view object as a second argument. + +## 6.11.3 (2023-05-17) + +### Bug fixes + +Make sure pointer selection respects `EditorView.atomicRanges`. + +Preserve DOM widgets when their decoration type changes but they otherwise stay in the same place. + +Fix a bug in `drawSelection` that could lead to invisible or incorrect selections for a blank line below a block widget. + +## 6.11.2 (2023-05-13) + +### Bug fixes + +Fix a bug where the `crosshairCursor` extension could, when non-native key events were fired, trigger disruptive and needless view updates. + +Fix an Android issue where backspacing at the front of a line with widget decorations could replace those decorations with their text content. + +Respect scroll margins when scrolling the target of drag-selection into view. + +Validate selection offsets reported by the browser, to work around Safari giving us invalid values in some cases. + +## 6.11.1 (2023-05-09) + +### Bug fixes + +Don't preserve the DOM around a composition that spans multiple lines. +## 6.11.0 (2023-05-03) + +### New features + +Gutters now support a `widgetMarker` option that can be used to add markers next to block widgets. + +## 6.10.1 (2023-05-01) + +### Bug fixes + +Limit cursor height in front of custom placeholder DOM elements. + +## 6.10.0 (2023-04-25) + +### Bug fixes + +Fix a crash in `drawSelection` when a measured position falls on a position that doesn't have corresponding screen coordinates. + +Work around unhelpful interaction observer behavior that could cause the editor to not notice it was visible. + +Give the cursor next to a line-wrapped placeholder a single-line height. + +Make sure drop events below the editable element in a fixed-height editor get handled properly. + +### New features + +Widget decorations can now define custom `coordsAtPos` methods to control the way the editor computes screen positions at or in the widget. + +## 6.9.6 (2023-04-21) + +### Bug fixes + +Fix an issue where, when escape was pressed followed by a key that the editor handled, followed by tab, the tab would still move focus. + +Fix an issue where, in some circumstances, the editor would ignore text changes at the end of a composition. + +Allow inline widgets to be updated to a different length via `updateDOM`. + +## 6.9.5 (2023-04-17) + +### Bug fixes + +Avoid disrupting the composition in specific cases where Safari invasively changes the DOM structure in the middle of a composition. + +Fix a bug that prevented `destroy` being called on hover tooltips. + +Fix a bug where the editor could take focus when content changes required it to restore the DOM selection. + +Fix height layout corruption caused by a division by zero. + +Make sure styles targeting the editor's focus status are specific enough to not cause them to apply to editors nested inside another focused editor. This will require themes to adjust their selection background styles to match the new specificity. + +## 6.9.4 (2023-04-11) + +### Bug fixes + +Make the editor scroll while dragging a selection near its sides, even if the cursor isn't outside the scrollable element. + +Fix a bug that interrupted composition after widgets in some circumstances on Firefox. + +Make sure the last change in a composition has its user event set to `input.type.compose`, even if the `compositionend` event fires before the changes are applied. + +Make it possible to remove additional selection ranges by clicking on them with ctrl/cmd held, even if they aren't cursors. + +Keep widget buffers between widgets and compositions, since removing them confuses IME on macOS Firefox. + +Fix a bug where, for DOM changes that put the selection in the middle of the changed range, the editor incorrectly set its selection state. + +Fix a bug where `coordsAtPos` could return a coordinates before the line break when querying a line-wrapped position with a positive `side`. + +## 6.9.3 (2023-03-21) + +### Bug fixes + +Work around a Firefox issue that caused `coordsAtPos` to return rectangles with the full line height on empty lines. + +Opening a context menu by clicking below the content element but inside the editor now properly shows the browser's menu for editable elements. + +Fix an issue that broke composition (especially of Chinese IME) after widget decorations. + +Fix an issue that would cause the cursor to jump around during compositions inside nested mark decorations. + +## 6.9.2 (2023-03-08) + +### Bug fixes + +Work around a Firefox CSS bug that caused cursors to stop blinking in a scrolled editor. + +Fix an issue in `drawSelection` where the selection extended into the editor's padding. + +Fix pasting of links copied from iOS share sheet. + +## 6.9.1 (2023-02-17) + +### Bug fixes + +Improve the way `posAtCoords` picks the side of a widget to return by comparing the coordinates the center of the widget. + +Fix an issue where transactions created for the `focusChangeEffect` facet were sometimes not dispatched. + +## 6.9.0 (2023-02-15) + +### Bug fixes + +Fix an issue where inaccurate estimated vertical positions could cause the viewport to not converge in line-wrapped editors. + +Don't suppress double-space to period conversion when autocorrect is enabled. + +Make sure the measuring code notices when the scaling of the editor is changed, and does a full measure in that case. + +### New features + +The new `EditorView.focusChangeEffect` facet can be used to dispatch a state effect when the editor is focused or blurred. + +## 6.8.1 (2023-02-08) + +### Bug fixes + +Fix an issue where tooltips that have their height reduced have their height flicker when scrolling or otherwise interacting with the editor. + +## 6.8.0 (2023-02-07) + +### Bug fixes + +Fix a regression that caused clicking on the scrollbar to move the selection. + +Fix an issue where focus or blur event handlers that dispatched editor transactions could corrupt the mouse selection state. + +Fix a CSS regression that prevented the drop cursor from being positioned properly. + +### New features + +`WidgetType.updateDOM` is now passed the editor view object. + +## 6.7.3 (2023-01-12) + +### Bug fixes + +Fix a bug in `posAtCoords` that could cause incorrect results for positions to the left of a wrapped line. + +## 6.7.2 (2023-01-04) + +### Bug fixes + +Fix a regression where the cursor didn't restart its blink cycle when moving it with the pointer. + +Even without a `key` property, measure request objects that are already scheduled will not be scheduled again by `requestMeasure`. + +Fix an issue where keymaps incorrectly interpreted key events that used Ctrl+Alt modifiers to simulate AltGr on Windows. + +Fix a bug where line decorations with a different `class` property would be treated as equal. + +Fix a bug that caused `drawSelection` to not notice when it was reconfigured. + +Fix a crash in the gutter extension caused by sharing of mutable arrays. + +Fix a regression that caused touch selection on mobile platforms to not work in an uneditable editor. + +Fix a bug where DOM events on the boundary between lines could get assigned to the wrong line. + +## 6.7.1 (2022-12-12) + +### Bug fixes + +Make the editor properly scroll when moving the pointer out of it during drag selection. + +Fix a regression where clicking below the content element in an editor with its own height didn't focus the editor. + +## 6.7.0 (2022-12-07) + +### Bug fixes + +Make the editor notice widget height changes to automatically adjust its height information. + +Fix an issue where widget buffers could be incorrectly omitted after empty lines. + +Fix an issue in content redrawing that could cause `coordsAtPos` to return incorrect results. + +### New features + +The static `RectangleMarker.forRange` method exposes the logic used by the editor to draw rectangles covering a selection range. + +Layers can now provide a `destroy` function to be called when the layer is removed. + +The new `highlightWhitespace` extension makes spaces and tabs in the editor visible. + +The `highlightTrailingWhitespace` extension can be used to make trailing whitespace stand out. + +## 6.6.0 (2022-11-24) + +### New features + +The `layer` function can now be used to define extensions that draw DOM elements over or below the document text. + +Tooltips that are bigger than the available vertical space for them will now have their height set so that they don't stick out of the window. The new `resize` property on `TooltipView` can be used to opt out of this behavior. + +## 6.5.1 (2022-11-15) + +### Bug fixes + +Fix a bug that caused marked unnecessary splitting of mark decoration DOM elements in some cases. + +## 6.5.0 (2022-11-14) + +### Bug fixes + +Fix an issue where key bindings were activated for the wrong key in some situations with non-US keyboards. + +### New features + +A tooltip's `positioned` callback is now passed the available space for tooltips. + +## 6.4.2 (2022-11-10) + +### Bug fixes + +Typing into a read-only editor no longer moves the cursor. + +Fix an issue where hover tooltips were closed when the mouse was moved over them if they had a custom parent element. + +Fix an issue where the editor could end up displaying incorrect height measurements (typically after initializing). + +## 6.4.1 (2022-11-07) + +### Bug fixes + +Fix an issue where coordinates next to replaced widgets were returned incorrectly, causing the cursor to be drawn in the wrong place. + +Update the `crosshairCursor` state on every mousemove event. + +Avoid an issue in the way that the editor enforces cursor associativity that could cause the cursor to get stuck on single-character wrapped lines. + +## 6.4.0 (2022-10-18) + +### Bug fixes + +Avoid an issue where `scrollPastEnd` makes a single-line editor have a vertical scrollbar. + +Work around a Chrome bug where it inserts a newline when you press space at the start of a wrapped line. + +Align `rectangularSelection`'s behavior with other popular editors by making it create cursors at the end of lines that are too short to touch the rectangle. + +Fix an issue where coordinates on mark decoration boundaries were sometimes taken from the wrong side of the position. + +Prevent scrolling artifacts caused by attempts to scroll stuff into view when the editor isn't being displayed. + +### New features + +`TooltipView` objects can now provide a `destroy` method to be called when the tooltip is removed. + +## 6.3.1 (2022-10-10) + +### Bug fixes + +Fix a crash when trying to scroll something into view in an editor that wasn't in the visible DOM. + +Fix an issue where `coordsAtPos` returned the coordinates on the wrong side of a widget decoration wrapped in a mark decoration. + +Fix an issue where content on long wrapped lines could fail to properly scroll into view. + +Fix an issue where DOM change reading on Chrome Android could get confused when a transaction came in right after a beforeinput event for backspace, enter, or delete. + +## 6.3.0 (2022-09-28) + +### Bug fixes + +Reduce the amount of wrap-point jittering when scrolling through a very long wrapped line. + +Fix an issue where scrolling to content that wasn't currently drawn due to being on a very long line would often fail to scroll to the right position. + +Suppress double-space-adds-period behavior on Chrome Mac when it behaves weirdly next to widget. + +### New features + +Key binding objects with an `any` property will now add handlers that are called for any key, within the ordering of the keybindings. + +## 6.2.5 (2022-09-24) + +### Bug fixes + +Don't override double/triple tap behavior on touch screen devices, so that the mobile selection menu pops up properly. + +Fix an issue where updating the selection could crash on Safari when the editor was hidden. + +## 6.2.4 (2022-09-16) + +### Bug fixes + +Highlight the active line even when there is a selection. Prevent the active line background from obscuring the selection backdrop. + +Fix an issue where elements with negative margins would confuse the editor's scrolling-into-view logic. + +Fix scrolling to a specific position in an editor that has not been in view yet. + +## 6.2.3 (2022-09-08) + +### Bug fixes + +Fix a bug where cursor motion, when starting from a non-empty selection range, could get stuck on atomic ranges in some circumstances. + +Avoid triggering Chrome Android's text-duplication issue when a period is typed in the middle of a word. + +## 6.2.2 (2022-08-31) + +### Bug fixes + +Don't reset the selection for selection change events that were suppressed by a node view. + +## 6.2.1 (2022-08-25) + +### Bug fixes + +Don't use the global `document` variable to track focus, since that doesn't work in another window/frame. + +Fix an issue where key handlers that didn't return true were sometimes called twice for the same keypress. + +Avoid editing glitches when using deletion keys like ctrl-d on iOS. + +Properly treat characters from the 'Arabic Presentation Forms-A' Unicode block as right-to-left. + +Work around a Firefox bug that inserts text at the wrong point for specific cross-line selections. + +## 6.2.0 (2022-08-05) + +### Bug fixes + +Fix a bug where `posAtCoords` would return the wrong results for positions to the right of wrapped lines. + +### New features + +The new `EditorView.setRoot` method can be used when an editor view is moved to a new document or shadow root. + +## 6.1.4 (2022-08-04) + +### Bug fixes + +Make selection-restoration on focus more reliable. + +## 6.1.3 (2022-08-03) + +### Bug fixes + +Fix a bug where a document that contains only non-printing characters would lead to bogus text measurements (and, from those, to crashing). + +Make sure differences between estimated and actual block heights don't cause visible scroll glitches. + +## 6.1.2 (2022-07-27) + +### Bug fixes + +Fix an issue where double tapping enter to confirm IME input and insert a newline on iOS would sometimes insert two newlines. + +Fix an issue on iOS where a composition could get aborted if the editor scrolled on backspace. + +## 6.1.1 (2022-07-25) + +### Bug fixes + +Make `highlightSpecialChars` replace directional isolate characters by default. + +The editor will now try to suppress browsers' native behavior of resetting the selection in the editable content when the editable element is focused (programmatically, with tab, etc). + +Fix a CSS issue that made it possible, when the gutters were wide enough, for them to overlap with the content. + +## 6.1.0 (2022-07-19) + +### New features + +`MatchDecorator` now supports a `decorate` option that can be used to customize the way decorations are added for each match. + +## 6.0.3 (2022-07-08) + +### Bug fixes + +Fix a problem where `posAtCoords` could incorrectly return the start of the next line when querying positions between lines. + +Fix an issue where registering a high-precedence keymap made keymap handling take precedence over other keydown event handlers. + +Ctrl/Cmd-clicking can now remove ranges from a multi-range selection. + +## 6.0.2 (2022-06-23) + +### Bug fixes + +Fix a CSS issue that broke horizontal scroll width stabilization. + +Fix a bug where `defaultLineHeight` could get an incorrect value in very narrow editors. + +## 6.0.1 (2022-06-17) + +### Bug fixes + +Avoid DOM selection corruption when the editor doesn't have focus but has selection and updates its content. + +Fall back to dispatching by key code when a key event produces a non-ASCII character (so that Cyrillic and Arabic keyboards can still use bindings specified with Latin characters). + +## 6.0.0 (2022-06-08) + +### New features + +The new static `EditorView.findFromDOM` method can be used to retrieve an editor instance from its DOM structure. + +Instead of passing a constructed state to the `EditorView` constructor, it is now also possible to inline the configuration options to the state in the view config object. + +## 0.20.7 (2022-05-30) + +### Bug fixes + +Fix an issue on Chrome Android where the DOM could fail to display the actual document after backspace. + +Avoid an issue on Chrome Android where DOM changes were sometimes inappropriately replace by a backspace key effect due to spurious beforeinput events. + +Fix a problem where the content element's width didn't cover the width of the actual content. + +Work around a bug in Chrome 102 which caused wheel scrolling of the editor to be interrupted every few lines. + +## 0.20.6 (2022-05-20) + +### Bug fixes + +Make sure the editor re-measures itself when its attributes are updated. + +## 0.20.5 (2022-05-18) + +### Bug fixes + +Fix an issue where gutter elements without any markers in them would not get the `cm-gutterElement` class assigned. + +Fix an issue where DOM event handlers registered by plugins were retained indefinitely, even after the editor was reconfigured. + +## 0.20.4 (2022-05-03) + +### Bug fixes + +Prevent Mac-style behavior of inserting a period when the user inserts two spaces. + +Fix an issue where the editor would sometimes not restore the DOM selection when refocused with a selection identical to the one it held when it lost focus. + +## 0.20.3 (2022-04-27) + +### Bug fixes + +Fix a bug where the input handling could crash on repeated (or held) backspace presses on Chrome Android. + +## 0.20.2 (2022-04-22) + +### New features + +The new `hideOn` option to `hoverTooltip` allows more fine-grained control over when the tooltip should hide. + +## 0.20.1 (2022-04-20) + +### Bug fixes + +Remove debug statements that accidentally made it into 0.20.0. + +Fix a regression in `moveVertically`. + +## 0.20.0 (2022-04-20) + +### Breaking changes + +The deprecated interfaces `blockAtHeight`, `visualLineAtHeight`, `viewportLines`, `visualLineAt`, `scrollPosIntoView`, `scrollTo`, and `centerOn` were removed from the library. + +All decorations are now provided through `EditorView.decorations`, and are part of a single precedence ordering. Decoration sources that need access to the view are provided as functions. + +Atomic ranges are now specified through a facet (`EditorView.atomicRanges`). + +Scroll margins are now specified through a facet (`EditorView.scrollMargins`). + +Plugin fields no longer exist in the library (and are replaced by facets holding function values). + +This package no longer re-exports the Range type from @codemirror/state. + +### Bug fixes + +Fix a bug where zero-length block widgets could cause `viewportLineBlocks` to contain overlapping ranges. + +### New features + +The new `perLineTextDirection` facet configures whether the editor reads text direction per line, or uses a single direction for the entire editor. `EditorView.textDirectionAt` returns the direction around a given position. + +`rectangularSelection` and `crosshairCursor` from @codemirror/rectangular-selection were merged into this package. + +This package now exports the tooltip functionality that used to live in @codemirror/tooltip. + +The exports from the old @codemirror/panel package are now available from this package. + +The exports from the old @codemirror/gutter package are now available from this package. + +## 0.19.48 (2022-03-30) + +### Bug fixes + +Fix an issue where DOM syncing could crash when a DOM node was moved from a parent to a child node (via widgets reusing existing nodes). + +To avoid interfering with things like a vim mode too much, the editor will now only activate the tab-to-move-focus escape hatch after an escape press that wasn't handled by an event handler. + +Make sure the view measures itself before the page is printed. + +Tweak types of view plugin defining functions to avoid TypeScript errors when the plugin value doesn't have any of the interface's properties. + +## 0.19.47 (2022-03-08) + +### Bug fixes + +Fix an issue where block widgets at the start of the viewport could break height computations. + +## 0.19.46 (2022-03-03) + +### Bug fixes + +Fix a bug where block widgets on the edges of viewports could cause the positioning of content to misalign with the gutter and height computations. + +Improve cursor height next to widgets. + +Fix a bug where mapping positions to screen coordinates could return incorred coordinates during composition. + +## 0.19.45 (2022-02-23) + +### Bug fixes + +Fix an issue where the library failed to call `WidgetType.destroy` on the old widget when replacing a widget with a different widget of the same type. + +Fix an issue where the editor would compute DOM positions inside composition contexts incorrectly in some cases, causing the selection to be put in the wrong place and needlessly interrupting compositions. + +Fix leaking of resize event handlers. + +## 0.19.44 (2022-02-17) + +### Bug fixes + +Fix a crash that occasionally occurred when drag-selecting in a way that scrolled the editor. + +### New features + +The new `EditorView.compositionStarted` property indicates whether a composition is starting. + +## 0.19.43 (2022-02-16) + +### Bug fixes + +Fix several issues where editing or composition went wrong due to our zero-width space kludge characters ending up in unexpected places. + +Make sure the editor re-measures its dimensions whenever its theme changes. + +Fix an issue where some keys on Android phones could leave the editor DOM unsynced with the actual document. + +## 0.19.42 (2022-02-05) + +### Bug fixes + +Fix a regression in cursor position determination after making an edit next to a widget. + +## 0.19.41 (2022-02-04) + +### Bug fixes + +Fix an issue where the editor's view of its content height could go out of sync with the DOM when a line-wrapping editor had its width changed, causing wrapping to change. + +Fix a bug that caused the editor to draw way too much content when scrolling to a position in an editor (much) taller than the window. + +Report an error when a replace decoration from a plugin crosses a line break, rather than silently ignoring it. + +Fix an issue where reading DOM changes was broken when `lineSeparator` contained more than one character. + +Make ordering of replace and mark decorations with the same extent and inclusivness more predictable by giving replace decorations precedence. + +Fix a bug where, on Chrome, replacement across line boundaries and next to widgets could cause bogus zero-width characters to appear in the content. + +## 0.19.40 (2022-01-19) + +### Bug fixes + +Make composition input properly appear at secondary cursors (except when those are in the DOM node with the composition, in which case the browser won't allow us to intervene without aborting the composition). + +Fix a bug that cause the editor to get confused about which content was visible after scrolling something into view. + +Fix a bug where the dummy elements rendered around widgets could end up in a separate set of wrapping marks, and thus become visible. + +`EditorView.moveVertically` now preserves the `assoc` property of the input range. + +Get rid of gaps between selection elements drawn by `drawSelection`. + +Fix an issue where replacing text next to a widget might leak bogus zero-width spaces into the document. + +Avoid browser selection mishandling when a focused view has `setState` called by eagerly refocusing it. + +## 0.19.39 (2022-01-06) + +### Bug fixes + +Make sure the editor signals a `geometryChanged` update when its width changes. + +### New features + +`EditorView.darkTheme` can now be queried to figure out whether the editor is using a dark theme. + +## 0.19.38 (2022-01-05) + +### Bug fixes + +Fix a bug that caused line decorations with a `class` property to suppress all other line decorations for that line. + +Fix a bug that caused scroll effects to be corrupted when further updates came in before they were applied. + +Fix an issue where, depending on which way a floating point rounding error fell, `posAtCoords` (and thus vertical cursor motion) for positions outside of the vertical range of the document might or might not return the start/end of the document. + +## 0.19.37 (2021-12-22) + +### Bug fixes + +Fix regression where plugin replacing decorations that span to the end of the line are ignored. + +## 0.19.36 (2021-12-22) + +### Bug fixes + +Fix a crash in `posAtCoords` when the position lies in a block widget that is rendered but scrolled out of view. + +Adding block decorations from a plugin now raises an error. Replacing decorations that cross lines are ignored, when provided by a plugin. + +Fix inverted interpretation of the `precise` argument to `posAtCoords`. + +## 0.19.35 (2021-12-20) + +### Bug fixes + +The editor will now handle double-taps as if they are double-clicks, rather than letting the browser's native behavior happen (because the latter often does the wrong thing). + +Fix an issue where backspacing out a selection on Chrome Android would sometimes only delete the last character due to event order issues. + +`posAtCoords`, without second argument, will no longer return null for positions below or above the document. + +## 0.19.34 (2021-12-17) + +### Bug fixes + +Fix a bug where content line elements would in some cases lose their `cm-line` class. + +## 0.19.33 (2021-12-16) + +### Breaking changes + +`EditorView.scrollTo` and `EditorView.centerOn` are deprecated in favor of `EditorView.scrollIntoView`, and will be removed in the next breaking release. + +### Bug fixes + +Fix an issue that could cause the editor to unnecessarily interfere with composition (especially visible on macOS Chrome). + +A composition started with multiple lines selected will no longer be interruptd by the editor. + +### New features + +The new `EditorView.scrollIntoView` function allows you to do more fine-grained scrolling. + +## 0.19.32 (2021-12-15) + +### Bug fixes + +Fix a bug where CodeMirror's own event handers would run even after a user-supplied handler called `preventDefault` on an event. + +Properly draw selections when negative text-indent is used for soft wrapping. + +Fix an issue where `viewportLineBlocks` could hold inaccurate height information when the vertical scaling changed. + +Fixes drop cursor positioning when the document is scrolled. Force a content measure when the editor comes into view + +Fix a bug that could cause the editor to not measure its layout the first time it came into view. + +## 0.19.31 (2021-12-13) + +### New features + +The package now exports a `dropCursor` extension that draws a cursor at the current drop position when dragging content over the editor. + +## 0.19.30 (2021-12-13) + +### Bug fixes + +Refine Android key event handling to work properly in a GBoard corner case where pressing Enter fires a bunch of spurious deleteContentBackward events. + +Fix a crash in `drawSelection` for some kinds of selections. + +Prevent a possibility where some content updates causes duplicate text to remain in DOM. + +### New features + +Support a `maxLength` option to `MatchDecorator` that allows user code to control how far it scans into hidden parts of viewport lines. + +## 0.19.29 (2021-12-09) + +### Bug fixes + +Fix a bug that could cause out-of-view editors to get a nonsensical viewport and fail to scroll into view when asked to. + +Fix a bug where would return 0 when clicking below the content if the last line was replaced with a block widget decoration. + +Fix an issue where clicking at the position of the previous cursor in a blurred editor would cause the selection to reset to the start of the document. + +Fix an issue where composition could be interrupted if the browser created a new node inside a mark decoration node. + +## 0.19.28 (2021-12-08) + +### Bug fixes + +Fix an issue where pressing Enter on Chrome Android during composition did not fire key handlers for Enter. + +Avoid a Chrome bug where the virtual keyboard closes when pressing backspace after a widget. + +Fix an issue where the editor could show a horizontal scroll bar even after all lines that caused it had been deleted or changed. + +## 0.19.27 (2021-12-06) + +### Bug fixes + +Fix a bug that could cause `EditorView.plugin` to inappropriately return `null` during plugin initialization. + +Fix a bug where a block widget without `estimatedHeight` at the end of the document could fail to be drawn + +## 0.19.26 (2021-12-03) + +### New features + +Widgets can now define a `destroy` method that is called when they are removed from the view. + +## 0.19.25 (2021-12-02) + +### Bug fixes + +Widgets around replaced ranges are now visible when their side does not point towards the replaced range. + +A replaced line with a line decoration no longer creates an extra empty line block in the editor. + +The `scrollPastEnd` extension will now over-reserve space at the bottom of the editor on startup, to prevent restored scroll positions from being clipped. + +### New features + +`EditorView.editorAttributes` and `contentAttributes` may now hold functions that produce the attributes. + +## 0.19.24 (2021-12-01) + +### Bug fixes + +Fix a bug where `lineBlockAt`, for queries inside the viewport, would always return the first line in the viewport. + +## 0.19.23 (2021-11-30) + +### Bug fixes + +Fix an issue where after some kinds of changes, `EditorView.viewportLineBlocks` held an out-of-date set of blocks. + +### New features + +Export `EditorView.documentPadding`, with information about the vertical padding of the document. + +## 0.19.22 (2021-11-30) + +### Bug fixes + +Fix an issue where editors with large vertical padding (for example via `scrollPastEnd`) could sometimes lose their scroll position on Chrome. + +Avoid some unnecessary DOM measuring work by more carefully checking whether it is needed. + +### New features + +The new `elementAtHeight`, `lineBlockAtHeight`, and `lineBlockAt` methods provide a simpler and more efficient replacement for the (now deprecated) `blockAtHeight`, `visualLineAtHeight`, and `visualLineAt` methods. + +The editor view now exports a `documentTop` getter that gives you the vertical position of the top of the document. All height info is queried and reported relative to this top. + +The editor view's new `viewportLineBlocks` property provides an array of in-viewport line blocks, and replaces the (now deprecated) `viewportLines` method. + +## 0.19.21 (2021-11-26) + +### Bug fixes + +Fix a problem where the DOM update would unnecessarily trigger browser relayouts. + +## 0.19.20 (2021-11-19) + +### Bug fixes + +Run a measure cycle when the editor's size spontaneously changes. + +## 0.19.19 (2021-11-17) + +### Bug fixes + +Fix a bug that caused the precedence of `editorAttributes` and `contentAttributes` to be inverted, making lower-precedence extensions override higher-precedence ones. + +## 0.19.18 (2021-11-16) + +### Bug fixes + +Fix an issue where the editor wasn't aware it was line-wrapping with its own `lineWrapping` extension enabled. + +## 0.19.17 (2021-11-16) + +### Bug fixes + +Avoid an issue where stretches of whitespace on line wrap points could cause the cursor to be placed outside of the content. + +## 0.19.16 (2021-11-11) + +### Breaking changes + +Block replacement decorations now default to inclusive, because non-inclusive block decorations are rarely what you need. + +### Bug fixes + +Fix an issue that caused block widgets to always have a large side value, making it impossible to show them between to replacement decorations. + +Fix a crash that could happen after some types of viewport changes, due to a bug in the block widget view data structure. + +## 0.19.15 (2021-11-09) + +### Bug fixes + +Fix a bug where the editor would think it was invisible when the document body was given screen height and scroll behavior. + +Fix selection reading inside a shadow root on iOS. + +## 0.19.14 (2021-11-07) + +### Bug fixes + +Fix an issue where typing into a read-only editor would move the selection. + +Fix slowness when backspace is held down on iOS. + +## 0.19.13 (2021-11-06) + +### Bug fixes + +Fix a bug where backspace, enter, and delete would get applied twice on iOS. + +## 0.19.12 (2021-11-04) + +### Bug fixes + +Make sure the workaround for the lost virtual keyboard on Chrome Android also works on slower phones. Slight style change in beforeinput handler + +Avoid failure cases in viewport-based rendering of very long lines. + +## 0.19.11 (2021-11-03) + +### Breaking changes + +`EditorView.scrollPosIntoView` has been deprecated. Use the `EditorView.scrollTo` effect instead. + +### New features + +The new `EditorView.centerOn` effect can be used to scroll a given range to the center of the view. + +## 0.19.10 (2021-11-02) + +### Bug fixes + +Don't crash when `IntersectionObserver` fires its callback without any records. Try to handle some backspace issues on Chrome Android + +Using backspace near uneditable widgets on Chrome Android should now be more reliable. + +Work around a number of browser bugs by always rendering zero-width spaces around in-content widgets, so that browsers will treat the positions near them as valid cursor positions and not try to run composition across widget boundaries. + +Work around bogus composition changes created by Chrome Android after handled backspace presses. + +Work around an issue where tapping on an uneditable node in the editor would sometimes fail to show the virtual keyboard on Chrome Android. + +Prevent translation services from translating the editor content. Show direction override characters as special chars by default + +`specialChars` will now, by default, replace direction override chars, to mitigate https://trojansource.codes/ attacks. + +### New features + +The editor view will, if `parent` is given but `root` is not, derive the root from the parent element. + +Line decorations now accept a `class` property to directly add DOM classes to the line. + +## 0.19.9 (2021-10-01) + +### Bug fixes + +Fix an issue where some kinds of reflows in the surrounding document could move unrendered parts of the editor into view without the editor noticing and updating its viewport. + +Fix an occasional crash in the selection drawing extension. + +## 0.19.8 (2021-09-26) + +### Bug fixes + +Fix a bug that could, on DOM changes near block widgets, insert superfluous line breaks. + +Make interacting with a destroyed editor view do nothing, rather than crash, to avoid tripping people up with pending timeouts and such. + +Make sure `ViewUpdate.viewportChanged` is true whenever `visibleRanges` changes, so that plugins acting only on visible ranges can use it to check when to update. + +Fix line-wise cut on empty lines. + +## 0.19.7 (2021-09-13) + +### Bug fixes + +The view is now aware of the new `EditorState.readOnly` property, and suppresses events that modify the document when it is true. + +## 0.19.6 (2021-09-10) + +### Bug fixes + +Remove a `console.log` that slipped into the previous release. + +## 0.19.5 (2021-09-09) + +### New features + +The new `EditorView.scrollTo` effect can be used to scroll a given range into view. + +## 0.19.4 (2021-09-01) + +### Bug fixes + +Fix an issue where lines containing just a widget decoration wrapped in a mark decoration could be displayed with 0 height. + +## 0.19.3 (2021-08-25) + +### Bug fixes + +Fix a view corruption that could happen in situations involving overlapping mark decorations. + +## 0.19.2 (2021-08-23) + +### New features + +The package now exports a `scrollPastEnd` function, which returns an extension that adds space below the document to allow the last line to be scrolled to the top of the editor. + +## 0.19.1 (2021-08-11) + +### Breaking changes + +The view now emits new-style user event annotations for the transactions it generates. + +### Bug fixes + +Fix a bug where `coordsAtPos` would allow the passed `side` argument to override widget sides, producing incorrect cursor positions. + +Fix a bug that could cause content lines to be misaligned in certain situations involving widgets at the end of lines. + +Fix an issue where, if the browser decided to modify DOM attributes in the content in response to some editing action, the view failed to reset those again. + +## 0.18.19 (2021-07-12) + +### Bug fixes + +Fix a regression where `EditorView.editable.of(false)` didn't disable editing on Webkit-based browsers. + +## 0.18.18 (2021-07-06) + +### Bug fixes + +Fix a bug that caused `EditorView.moveVertically` to only move by one line, even when given a custom distance, in some cases. + +Hide Safari's native bold/italic/underline controls for the content. + +Fix a CSS problem that prevented Safari from breaking words longer than the line in line-wrapping mode. + +Avoid a problem where composition would be inappropriately abored on Safari. + +Fix drag-selection that scrolls the content by dragging past the visible viewport. + +### New features + +`posAtCoords` now has an imprecise mode where it'll return an approximate position even for parts of the document that aren't currently rendered. + +## 0.18.17 (2021-06-14) + +### Bug fixes + +Make `drawSelection` behave properly when lines are split by block widgets. + +Make sure drawn selections that span a single line break don't leave a gap between the lines. + +## 0.18.16 (2021-06-03) + +### Bug fixes + +Fix a crash that could occur when the document changed during mouse selection. + +Fix a bug where composition inside styled content would sometimes be inappropriately aborted by editor DOM updates. + +### New features + +`MouseSelectionStyle.update` may now return true to indicate it should be queried for a new selection after the update. + +## 0.18.15 (2021-06-01) + +### Bug fixes + +Fix a bug that would, in very specific circumstances, cause `posAtCoords` to go into an infinite loop in Safari. + +Fix a bug where some types of IME input on Mobile Safari would drop text. + +## 0.18.14 (2021-05-28) + +### Bug fixes + +Fix an issue where the DOM selection was sometimes not properly updated when next to a widget. + +Invert the order in which overlapping decorations are drawn so that higher-precedence decorations are nested inside lower-precedence ones (and thus override their styling). + +Fix a but in `posAtCoords` where it would in some situations return -1 instead of `null`. + +### New features + +A new plugin field, `PluginField.atomicRanges`, can be used to cause cursor motion to skip past some ranges of the document. + +## 0.18.13 (2021-05-20) + +### Bug fixes + +Fix a bug that would cause the content DOM update to crash in specific circumstances. + +Work around an issue where, after some types of changes, Mobile Safari would ignore Enter presses. + +Make iOS enter and backspace handling more robust, so that platform bugs are less likely to break those keys in the editor. + +Fix a regression where Esc + Tab no longer allowed the user to exit the editor. + +### New features + +You can now drop text files into the editor. + +## 0.18.12 (2021-05-10) + +### Bug fixes + +Work around a Mobile Safari bug where, after backspacing out the last character on a line, Enter didn't work anymore. + +Work around a problem in Mobile Safari where you couldn't tap to put the cursor at the end of a line that ended in a widget. + +## 0.18.11 (2021-04-30) + +### Bug fixes + +Add an attribute to prevent the Grammarly browser extension from messing with the editor content. + +Fix more issues around selection handling a Shadow DOM in Safari. + +## 0.18.10 (2021-04-27) + +### Bug fixes + +Fix a bug where some types of updates wouldn't properly cause marks around the changes to be joined in the DOM. + +Fix an issue where the content and gutters in a fixed-height editor could be smaller than the editor height. + +Fix a crash on Safari when initializing an editor in an unfocused window. + +Fix a bug where the editor would incorrectly conclude it was out of view in some types of absolutely positioned parent elements. + +## 0.18.9 (2021-04-23) + +### Bug fixes + +Fix a crash that occurred when determining DOM coordinates in some specific situations. + +Fix a crash when a DOM change that ended at a zero-width view element (widget) removed that element from the DOM. + +Disable autocorrect and autocapitalize by default, since in most code-editor contexts they get in the way. You can use `EditorView.contentAttributes` to override this. + +Fix a bug that interfered with native touch selection handling on Android. + +Fix an unnecessary DOM update after composition that would disrupt touch selection on Android. + +Add a workaround for Safari's broken selection reporting when the editor is in a shadow DOM tree. + +Fix select-all from the context menu on Safari. + +## 0.18.8 (2021-04-19) + +### Bug fixes + +Handle selection replacements where the inserted text matches the start/end of the replaced text better. + +Fix an issue where the editor would miss scroll events when it was placed in a DOM component slot. + +## 0.18.7 (2021-04-13) + +### Bug fixes + +Fix a crash when drag-selecting out of the editor with editable turned off. + +Backspace and delete now largely work in an editor without a keymap. + +Pressing backspace on iOS should now properly update the virtual keyboard's capitalize and autocorrect state. + +Prevent random line-wrapping in (non-wrapping) editors on Mobile Safari. +## 0.18.6 (2021-04-08) + +### Bug fixes + +Fix an issue in the compiled output that would break the code when minified with terser. + +## 0.18.5 (2021-04-07) + +### Bug fixes + +Improve handling of bidi text with brackets (conforming to Unicode 13's bidi algorithm). + +Fix the position where `drawSelection` displays the cursor on bidi boundaries. + +## 0.18.4 (2021-04-07) + +### Bug fixes + +Fix an issue where the default focus ring gets obscured by the gutters and active line. + +Fix an issue where the editor believed Chrome Android didn't support the CSS `tab-size` style. + +Don't style active lines when there are non-empty selection ranges, so that the active line background doesn't obscure the selection. + +Make iOS autocapitalize update properly when you press Enter. + +## 0.18.3 (2021-03-19) + +### Breaking changes + +The outer DOM element now has class `cm-editor` instead of `cm-wrap` (`cm-wrap` will be present as well until 0.19). + +### Bug fixes + +Improve behavior of `posAtCoords` when the position is near text but not in any character's actual box. + +## 0.18.2 (2021-03-19) + +### Bug fixes + +Triple-clicking now selects the line break after the clicked line (if any). + +Fix an issue where the `drawSelection` plugin would fail to draw the top line of the selection when it started in an empty line. + +Fix an issue where, at the end of a specific type of composition on iOS, the editor read the DOM before the browser was done updating it. + +## 0.18.1 (2021-03-05) + +### Bug fixes + +Fix an issue where, on iOS, some types of IME would cause the composed content to be deleted when confirming a composition. + +## 0.18.0 (2021-03-03) + +### Breaking changes + +The `themeClass` function and ``-style selectors in themes are no longer supported (prefixing with `cm-` should be done manually now). + +Themes must now use `&` (instead of an extra `$`) to target the editor wrapper element. + +The editor no longer adds `cm-light` or `cm-dark` classes. Targeting light or dark configurations in base themes should now be done by using a `&light` or `&dark` top-level selector. + +## 0.17.13 (2021-03-03) + +### Bug fixes + +Work around a Firefox bug where it won't draw the cursor when it is between uneditable elements. + +Fix a bug that broke built-in mouse event handling. + +## 0.17.12 (2021-03-02) + +### Bug fixes + +Avoid interfering with touch events, to allow native selection behavior. + +Fix a bug that broke sub-selectors with multiple `&` placeholders in themes. + +## 0.17.11 (2021-02-25) + +### Bug fixes + +Fix vertical cursor motion on Safari with a larger line-height. + +Fix incorrect selection drawing (with `drawSelection`) when the selection spans to just after a soft wrap point. + +Fix an issue where compositions on Safari were inappropriately aborted in some circumstances. + +The view will now redraw when the `EditorView.phrases` facet changes, to make sure translated text is properly updated. + +## 0.17.10 (2021-02-22) + +### Bug fixes + +Long words without spaces, when line-wrapping is enabled, are now properly broken. + +Fix the horizontal position of selections drawn by `drawSelection` in right-to-left editors with a scrollbar. + +## 0.17.9 (2021-02-18) + +### Bug fixes + +Fix an issue where pasting linewise at the start of a line left the cursor before the inserted content. + +## 0.17.8 (2021-02-16) + +### Bug fixes + +Fix a problem where the DOM selection and the editor state could get out of sync in non-editable mode. + +Fix a crash when the editor was hidden on Safari, due to `getClientRects` returning an empty list. + +Prevent Firefox from making the scrollable element keyboard-focusable. + +## 0.17.7 (2021-01-25) + +### New features + +Add an `EditorView.announce` state effect that can be used to conveniently provide screen reader announcements. + +## 0.17.6 (2021-01-22) + +### Bug fixes + +Avoid creating very high scroll containers for large documents so that we don't overflow the DOM's fixed-precision numbers. + +## 0.17.5 (2021-01-15) + +### Bug fixes + +Fix a bug that would create space-filling placeholders with incorrect height when document is very large. + +## 0.17.4 (2021-01-14) + +### Bug fixes + +The `drawSelection` extension will now reuse cursor DOM nodes when the number of cursors stays the same, allowing some degree of cursor transition animation. + +Makes highlighted special characters styleable (``) and fix their default look in dark themes to have appropriate contrast. + +### New features + +Adds a new `MatchDecorator` helper class which can be used to easily maintain decorations on content that matches a regular expression. + +## 0.17.3 (2021-01-06) + +### New features + +The package now also exports a CommonJS module. + +## 0.17.2 (2021-01-04) + +### Bug fixes + +Work around Chrome problem where the native shift-enter behavior inserts two line breaks. + +Make bracket closing and bracket pair removing more reliable on Android. + +Fix bad cursor position and superfluous change transactions after pressing enter when in a composition on Android. + +Fix issue where the wrong character was deleted when backspacing out a character before an identical copy of that character on Android. + +## 0.17.1 (2020-12-30) + +### Bug fixes + +Fix a bug that prevented `ViewUpdate.focusChanged` from ever being true. + +## 0.17.0 (2020-12-29) + +### Breaking changes + +First numbered release. + diff --git a/node_modules/@codemirror/view/LICENSE b/node_modules/@codemirror/view/LICENSE new file mode 100644 index 0000000..9a91f48 --- /dev/null +++ b/node_modules/@codemirror/view/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (C) 2018-2021 by Marijn Haverbeke and others + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/node_modules/@codemirror/view/README.md b/node_modules/@codemirror/view/README.md new file mode 100644 index 0000000..c92c232 --- /dev/null +++ b/node_modules/@codemirror/view/README.md @@ -0,0 +1,37 @@ +# @codemirror/view [![NPM version](https://img.shields.io/npm/v/@codemirror/view.svg)](https://www.npmjs.org/package/@codemirror/view) + +[ [**WEBSITE**](https://codemirror.net/) | [**DOCS**](https://codemirror.net/docs/ref/#view) | [**ISSUES**](https://github.com/codemirror/dev/issues) | [**FORUM**](https://discuss.codemirror.net/c/next/) | [**CHANGELOG**](https://github.com/codemirror/view/blob/main/CHANGELOG.md) ] + +This package implements the DOM view component for the +[CodeMirror](https://codemirror.net/) code editor. + +The [project page](https://codemirror.net/) has more information, a +number of [examples](https://codemirror.net/examples/) and the +[documentation](https://codemirror.net/docs/). + +This code is released under an +[MIT license](https://github.com/codemirror/view/tree/main/LICENSE). + +We aim to be an inclusive, welcoming community. To make that explicit, +we have a [code of +conduct](http://contributor-covenant.org/version/1/1/0/) that applies +to communication around the project. + +## Usage + +```javascript +import {EditorView} from "@codemirror/view" +import {basicSetup} from "codemirror" + +const view = new EditorView({ + parent: document.querySelector("#some-node"), + doc: "Content text", + extensions: [basicSetup /* ... */] +}) +``` + +Add additional extensions, such as a [language +mode](https://codemirror.net/#languages), to configure the editor. +Call +[`view.dispatch`](https://codemirror.net/docs/ref/#view.EditorView.dispatch) +to update the editor's state. diff --git a/node_modules/@codemirror/view/dist/index.cjs b/node_modules/@codemirror/view/dist/index.cjs new file mode 100644 index 0000000..17c2179 --- /dev/null +++ b/node_modules/@codemirror/view/dist/index.cjs @@ -0,0 +1,11496 @@ +'use strict'; + +var state = require('@codemirror/state'); +var styleMod = require('style-mod'); +var w3cKeyname = require('w3c-keyname'); +var elt = require('crelt'); + +let nav = typeof navigator != "undefined" ? navigator : { userAgent: "", vendor: "", platform: "" }; +let doc = typeof document != "undefined" ? document : { documentElement: { style: {} } }; +const ie_edge = /Edge\/(\d+)/.exec(nav.userAgent); +const ie_upto10 = /MSIE \d/.test(nav.userAgent); +const ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(nav.userAgent); +const ie = !!(ie_upto10 || ie_11up || ie_edge); +const gecko = !ie && /gecko\/(\d+)/i.test(nav.userAgent); +const chrome = !ie && /Chrome\/(\d+)/.exec(nav.userAgent); +const webkit = "webkitFontSmoothing" in doc.documentElement.style; +const safari = !ie && /Apple Computer/.test(nav.vendor); +const ios = safari && (/Mobile\/\w+/.test(nav.userAgent) || nav.maxTouchPoints > 2); +var browser = { + mac: ios || /Mac/.test(nav.platform), + windows: /Win/.test(nav.platform), + linux: /Linux|X11/.test(nav.platform), + ie, + ie_version: ie_upto10 ? doc.documentMode || 6 : ie_11up ? +ie_11up[1] : ie_edge ? +ie_edge[1] : 0, + gecko, + gecko_version: gecko ? +(/Firefox\/(\d+)/.exec(nav.userAgent) || [0, 0])[1] : 0, + chrome: !!chrome, + chrome_version: chrome ? +chrome[1] : 0, + ios, + android: /Android\b/.test(nav.userAgent), + webkit, + webkit_version: webkit ? +(/\bAppleWebKit\/(\d+)/.exec(nav.userAgent) || [0, 0])[1] : 0, + safari, + safari_version: safari ? +(/\bVersion\/(\d+(\.\d+)?)/.exec(nav.userAgent) || [0, 0])[1] : 0, + tabSize: doc.documentElement.style.tabSize != null ? "tab-size" : "-moz-tab-size" +}; + +function getSelection(root) { + let target; + // Browsers differ on whether shadow roots have a getSelection + // method. If it exists, use that, otherwise, call it on the + // document. + if (root.nodeType == 11) { // Shadow root + target = root.getSelection ? root : root.ownerDocument; + } + else { + target = root; + } + return target.getSelection(); +} +function contains(dom, node) { + return node ? dom == node || dom.contains(node.nodeType != 1 ? node.parentNode : node) : false; +} +function hasSelection(dom, selection) { + if (!selection.anchorNode) + return false; + try { + // Firefox will raise 'permission denied' errors when accessing + // properties of `sel.anchorNode` when it's in a generated CSS + // element. + return contains(dom, selection.anchorNode); + } + catch (_) { + return false; + } +} +function clientRectsFor(dom) { + if (dom.nodeType == 3) + return textRange(dom, 0, dom.nodeValue.length).getClientRects(); + else if (dom.nodeType == 1) + return dom.getClientRects(); + else + return []; +} +// Scans forward and backward through DOM positions equivalent to the +// given one to see if the two are in the same place (i.e. after a +// text node vs at the end of that text node) +function isEquivalentPosition(node, off, targetNode, targetOff) { + return targetNode ? (scanFor(node, off, targetNode, targetOff, -1) || + scanFor(node, off, targetNode, targetOff, 1)) : false; +} +function domIndex(node) { + for (var index = 0;; index++) { + node = node.previousSibling; + if (!node) + return index; + } +} +function isBlockElement(node) { + return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName); +} +function scanFor(node, off, targetNode, targetOff, dir) { + for (;;) { + if (node == targetNode && off == targetOff) + return true; + if (off == (dir < 0 ? 0 : maxOffset(node))) { + if (node.nodeName == "DIV") + return false; + let parent = node.parentNode; + if (!parent || parent.nodeType != 1) + return false; + off = domIndex(node) + (dir < 0 ? 0 : 1); + node = parent; + } + else if (node.nodeType == 1) { + node = node.childNodes[off + (dir < 0 ? -1 : 0)]; + if (node.nodeType == 1 && node.contentEditable == "false") + return false; + off = dir < 0 ? maxOffset(node) : 0; + } + else { + return false; + } + } +} +function maxOffset(node) { + return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length; +} +function flattenRect(rect, left) { + let x = left ? rect.left : rect.right; + return { left: x, right: x, top: rect.top, bottom: rect.bottom }; +} +function windowRect(win) { + let vp = win.visualViewport; + if (vp) + return { + left: 0, right: vp.width, + top: 0, bottom: vp.height + }; + return { left: 0, right: win.innerWidth, + top: 0, bottom: win.innerHeight }; +} +function getScale(elt, rect) { + let scaleX = rect.width / elt.offsetWidth; + let scaleY = rect.height / elt.offsetHeight; + if (scaleX > 0.995 && scaleX < 1.005 || !isFinite(scaleX) || Math.abs(rect.width - elt.offsetWidth) < 1) + scaleX = 1; + if (scaleY > 0.995 && scaleY < 1.005 || !isFinite(scaleY) || Math.abs(rect.height - elt.offsetHeight) < 1) + scaleY = 1; + return { scaleX, scaleY }; +} +function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) { + let doc = dom.ownerDocument, win = doc.defaultView || window; + for (let cur = dom, stop = false; cur && !stop;) { + if (cur.nodeType == 1) { // Element + let bounding, top = cur == doc.body; + let scaleX = 1, scaleY = 1; + if (top) { + bounding = windowRect(win); + } + else { + if (/^(fixed|sticky)$/.test(getComputedStyle(cur).position)) + stop = true; + if (cur.scrollHeight <= cur.clientHeight && cur.scrollWidth <= cur.clientWidth) { + cur = cur.assignedSlot || cur.parentNode; + continue; + } + let rect = cur.getBoundingClientRect(); + ({ scaleX, scaleY } = getScale(cur, rect)); + // Make sure scrollbar width isn't included in the rectangle + bounding = { left: rect.left, right: rect.left + cur.clientWidth * scaleX, + top: rect.top, bottom: rect.top + cur.clientHeight * scaleY }; + } + let moveX = 0, moveY = 0; + if (y == "nearest") { + if (rect.top < bounding.top) { + moveY = rect.top - (bounding.top + yMargin); + if (side > 0 && rect.bottom > bounding.bottom + moveY) + moveY = rect.bottom - bounding.bottom + yMargin; + } + else if (rect.bottom > bounding.bottom) { + moveY = rect.bottom - bounding.bottom + yMargin; + if (side < 0 && (rect.top - moveY) < bounding.top) + moveY = rect.top - (bounding.top + yMargin); + } + } + else { + let rectHeight = rect.bottom - rect.top, boundingHeight = bounding.bottom - bounding.top; + let targetTop = y == "center" && rectHeight <= boundingHeight ? rect.top + rectHeight / 2 - boundingHeight / 2 : + y == "start" || y == "center" && side < 0 ? rect.top - yMargin : + rect.bottom - boundingHeight + yMargin; + moveY = targetTop - bounding.top; + } + if (x == "nearest") { + if (rect.left < bounding.left) { + moveX = rect.left - (bounding.left + xMargin); + if (side > 0 && rect.right > bounding.right + moveX) + moveX = rect.right - bounding.right + xMargin; + } + else if (rect.right > bounding.right) { + moveX = rect.right - bounding.right + xMargin; + if (side < 0 && rect.left < bounding.left + moveX) + moveX = rect.left - (bounding.left + xMargin); + } + } + else { + let targetLeft = x == "center" ? rect.left + (rect.right - rect.left) / 2 - (bounding.right - bounding.left) / 2 : + (x == "start") == ltr ? rect.left - xMargin : + rect.right - (bounding.right - bounding.left) + xMargin; + moveX = targetLeft - bounding.left; + } + if (moveX || moveY) { + if (top) { + win.scrollBy(moveX, moveY); + } + else { + let movedX = 0, movedY = 0; + if (moveY) { + let start = cur.scrollTop; + cur.scrollTop += moveY / scaleY; + movedY = (cur.scrollTop - start) * scaleY; + } + if (moveX) { + let start = cur.scrollLeft; + cur.scrollLeft += moveX / scaleX; + movedX = (cur.scrollLeft - start) * scaleX; + } + rect = { left: rect.left - movedX, top: rect.top - movedY, + right: rect.right - movedX, bottom: rect.bottom - movedY }; + if (movedX && Math.abs(movedX - moveX) < 1) + x = "nearest"; + if (movedY && Math.abs(movedY - moveY) < 1) + y = "nearest"; + } + } + if (top) + break; + if (rect.top < bounding.top || rect.bottom > bounding.bottom || + rect.left < bounding.left || rect.right > bounding.right) + rect = { left: Math.max(rect.left, bounding.left), right: Math.min(rect.right, bounding.right), + top: Math.max(rect.top, bounding.top), bottom: Math.min(rect.bottom, bounding.bottom) }; + cur = cur.assignedSlot || cur.parentNode; + } + else if (cur.nodeType == 11) { // A shadow root + cur = cur.host; + } + else { + break; + } + } +} +function scrollableParents(dom) { + let doc = dom.ownerDocument, x, y; + for (let cur = dom.parentNode; cur;) { + if (cur == doc.body || (x && y)) { + break; + } + else if (cur.nodeType == 1) { + if (!y && cur.scrollHeight > cur.clientHeight) + y = cur; + if (!x && cur.scrollWidth > cur.clientWidth) + x = cur; + cur = cur.assignedSlot || cur.parentNode; + } + else if (cur.nodeType == 11) { + cur = cur.host; + } + else { + break; + } + } + return { x, y }; +} +class DOMSelectionState { + constructor() { + this.anchorNode = null; + this.anchorOffset = 0; + this.focusNode = null; + this.focusOffset = 0; + } + eq(domSel) { + return this.anchorNode == domSel.anchorNode && this.anchorOffset == domSel.anchorOffset && + this.focusNode == domSel.focusNode && this.focusOffset == domSel.focusOffset; + } + setRange(range) { + let { anchorNode, focusNode } = range; + // Clip offsets to node size to avoid crashes when Safari reports bogus offsets (#1152) + this.set(anchorNode, Math.min(range.anchorOffset, anchorNode ? maxOffset(anchorNode) : 0), focusNode, Math.min(range.focusOffset, focusNode ? maxOffset(focusNode) : 0)); + } + set(anchorNode, anchorOffset, focusNode, focusOffset) { + this.anchorNode = anchorNode; + this.anchorOffset = anchorOffset; + this.focusNode = focusNode; + this.focusOffset = focusOffset; + } +} +let preventScrollSupported = null; +// Safari 26 breaks preventScroll support +if (browser.safari && browser.safari_version >= 26) + preventScrollSupported = false; +// Feature-detects support for .focus({preventScroll: true}), and uses +// a fallback kludge when not supported. +function focusPreventScroll(dom) { + if (dom.setActive) + return dom.setActive(); // in IE + if (preventScrollSupported) + return dom.focus(preventScrollSupported); + let stack = []; + for (let cur = dom; cur; cur = cur.parentNode) { + stack.push(cur, cur.scrollTop, cur.scrollLeft); + if (cur == cur.ownerDocument) + break; + } + dom.focus(preventScrollSupported == null ? { + get preventScroll() { + preventScrollSupported = { preventScroll: true }; + return true; + } + } : undefined); + if (!preventScrollSupported) { + preventScrollSupported = false; + for (let i = 0; i < stack.length;) { + let elt = stack[i++], top = stack[i++], left = stack[i++]; + if (elt.scrollTop != top) + elt.scrollTop = top; + if (elt.scrollLeft != left) + elt.scrollLeft = left; + } + } +} +let scratchRange; +function textRange(node, from, to = from) { + let range = scratchRange || (scratchRange = document.createRange()); + range.setEnd(node, to); + range.setStart(node, from); + return range; +} +function dispatchKey(elt, name, code, mods) { + let options = { key: name, code: name, keyCode: code, which: code, cancelable: true }; + if (mods) + ({ altKey: options.altKey, ctrlKey: options.ctrlKey, shiftKey: options.shiftKey, metaKey: options.metaKey } = mods); + let down = new KeyboardEvent("keydown", options); + down.synthetic = true; + elt.dispatchEvent(down); + let up = new KeyboardEvent("keyup", options); + up.synthetic = true; + elt.dispatchEvent(up); + return down.defaultPrevented || up.defaultPrevented; +} +function getRoot(node) { + while (node) { + if (node && (node.nodeType == 9 || node.nodeType == 11 && node.host)) + return node; + node = node.assignedSlot || node.parentNode; + } + return null; +} +function clearAttributes(node) { + while (node.attributes.length) + node.removeAttributeNode(node.attributes[0]); +} +function atElementStart(doc, selection) { + let node = selection.focusNode, offset = selection.focusOffset; + if (!node || selection.anchorNode != node || selection.anchorOffset != offset) + return false; + // Safari can report bogus offsets (#1152) + offset = Math.min(offset, maxOffset(node)); + for (;;) { + if (offset) { + if (node.nodeType != 1) + return false; + let prev = node.childNodes[offset - 1]; + if (prev.contentEditable == "false") + offset--; + else { + node = prev; + offset = maxOffset(node); + } + } + else if (node == doc) { + return true; + } + else { + offset = domIndex(node); + node = node.parentNode; + } + } +} +function isScrolledToBottom(elt) { + return elt.scrollTop > Math.max(1, elt.scrollHeight - elt.clientHeight - 4); +} +function textNodeBefore(startNode, startOffset) { + for (let node = startNode, offset = startOffset;;) { + if (node.nodeType == 3 && offset > 0) { + return { node: node, offset: offset }; + } + else if (node.nodeType == 1 && offset > 0) { + if (node.contentEditable == "false") + return null; + node = node.childNodes[offset - 1]; + offset = maxOffset(node); + } + else if (node.parentNode && !isBlockElement(node)) { + offset = domIndex(node); + node = node.parentNode; + } + else { + return null; + } + } +} +function textNodeAfter(startNode, startOffset) { + for (let node = startNode, offset = startOffset;;) { + if (node.nodeType == 3 && offset < node.nodeValue.length) { + return { node: node, offset: offset }; + } + else if (node.nodeType == 1 && offset < node.childNodes.length) { + if (node.contentEditable == "false") + return null; + node = node.childNodes[offset]; + offset = 0; + } + else if (node.parentNode && !isBlockElement(node)) { + offset = domIndex(node) + 1; + node = node.parentNode; + } + else { + return null; + } + } +} + +class DOMPos { + constructor(node, offset, precise = true) { + this.node = node; + this.offset = offset; + this.precise = precise; + } + static before(dom, precise) { return new DOMPos(dom.parentNode, domIndex(dom), precise); } + static after(dom, precise) { return new DOMPos(dom.parentNode, domIndex(dom) + 1, precise); } +} +const noChildren = []; +class ContentView { + constructor() { + this.parent = null; + this.dom = null; + this.flags = 2 /* ViewFlag.NodeDirty */; + } + get overrideDOMText() { return null; } + get posAtStart() { + return this.parent ? this.parent.posBefore(this) : 0; + } + get posAtEnd() { + return this.posAtStart + this.length; + } + posBefore(view) { + let pos = this.posAtStart; + for (let child of this.children) { + if (child == view) + return pos; + pos += child.length + child.breakAfter; + } + throw new RangeError("Invalid child in posBefore"); + } + posAfter(view) { + return this.posBefore(view) + view.length; + } + sync(view, track) { + if (this.flags & 2 /* ViewFlag.NodeDirty */) { + let parent = this.dom; + let prev = null, next; + for (let child of this.children) { + if (child.flags & 7 /* ViewFlag.Dirty */) { + if (!child.dom && (next = prev ? prev.nextSibling : parent.firstChild)) { + let contentView = ContentView.get(next); + if (!contentView || !contentView.parent && contentView.canReuseDOM(child)) + child.reuseDOM(next); + } + child.sync(view, track); + child.flags &= ~7 /* ViewFlag.Dirty */; + } + next = prev ? prev.nextSibling : parent.firstChild; + if (track && !track.written && track.node == parent && next != child.dom) + track.written = true; + if (child.dom.parentNode == parent) { + while (next && next != child.dom) + next = rm$1(next); + } + else { + parent.insertBefore(child.dom, next); + } + prev = child.dom; + } + next = prev ? prev.nextSibling : parent.firstChild; + if (next && track && track.node == parent) + track.written = true; + while (next) + next = rm$1(next); + } + else if (this.flags & 1 /* ViewFlag.ChildDirty */) { + for (let child of this.children) + if (child.flags & 7 /* ViewFlag.Dirty */) { + child.sync(view, track); + child.flags &= ~7 /* ViewFlag.Dirty */; + } + } + } + reuseDOM(_dom) { } + localPosFromDOM(node, offset) { + let after; + if (node == this.dom) { + after = this.dom.childNodes[offset]; + } + else { + let bias = maxOffset(node) == 0 ? 0 : offset == 0 ? -1 : 1; + for (;;) { + let parent = node.parentNode; + if (parent == this.dom) + break; + if (bias == 0 && parent.firstChild != parent.lastChild) { + if (node == parent.firstChild) + bias = -1; + else + bias = 1; + } + node = parent; + } + if (bias < 0) + after = node; + else + after = node.nextSibling; + } + if (after == this.dom.firstChild) + return 0; + while (after && !ContentView.get(after)) + after = after.nextSibling; + if (!after) + return this.length; + for (let i = 0, pos = 0;; i++) { + let child = this.children[i]; + if (child.dom == after) + return pos; + pos += child.length + child.breakAfter; + } + } + domBoundsAround(from, to, offset = 0) { + let fromI = -1, fromStart = -1, toI = -1, toEnd = -1; + for (let i = 0, pos = offset, prevEnd = offset; i < this.children.length; i++) { + let child = this.children[i], end = pos + child.length; + if (pos < from && end > to) + return child.domBoundsAround(from, to, pos); + if (end >= from && fromI == -1) { + fromI = i; + fromStart = pos; + } + if (pos > to && child.dom.parentNode == this.dom) { + toI = i; + toEnd = prevEnd; + break; + } + prevEnd = end; + pos = end + child.breakAfter; + } + return { from: fromStart, to: toEnd < 0 ? offset + this.length : toEnd, + startDOM: (fromI ? this.children[fromI - 1].dom.nextSibling : null) || this.dom.firstChild, + endDOM: toI < this.children.length && toI >= 0 ? this.children[toI].dom : null }; + } + markDirty(andParent = false) { + this.flags |= 2 /* ViewFlag.NodeDirty */; + this.markParentsDirty(andParent); + } + markParentsDirty(childList) { + for (let parent = this.parent; parent; parent = parent.parent) { + if (childList) + parent.flags |= 2 /* ViewFlag.NodeDirty */; + if (parent.flags & 1 /* ViewFlag.ChildDirty */) + return; + parent.flags |= 1 /* ViewFlag.ChildDirty */; + childList = false; + } + } + setParent(parent) { + if (this.parent != parent) { + this.parent = parent; + if (this.flags & 7 /* ViewFlag.Dirty */) + this.markParentsDirty(true); + } + } + setDOM(dom) { + if (this.dom == dom) + return; + if (this.dom) + this.dom.cmView = null; + this.dom = dom; + dom.cmView = this; + } + get rootView() { + for (let v = this;;) { + let parent = v.parent; + if (!parent) + return v; + v = parent; + } + } + replaceChildren(from, to, children = noChildren) { + this.markDirty(); + for (let i = from; i < to; i++) { + let child = this.children[i]; + if (child.parent == this && children.indexOf(child) < 0) + child.destroy(); + } + if (children.length < 250) + this.children.splice(from, to - from, ...children); + else + this.children = [].concat(this.children.slice(0, from), children, this.children.slice(to)); + for (let i = 0; i < children.length; i++) + children[i].setParent(this); + } + ignoreMutation(_rec) { return false; } + ignoreEvent(_event) { return false; } + childCursor(pos = this.length) { + return new ChildCursor(this.children, pos, this.children.length); + } + childPos(pos, bias = 1) { + return this.childCursor().findPos(pos, bias); + } + toString() { + let name = this.constructor.name.replace("View", ""); + return name + (this.children.length ? "(" + this.children.join() + ")" : + this.length ? "[" + (name == "Text" ? this.text : this.length) + "]" : "") + + (this.breakAfter ? "#" : ""); + } + static get(node) { return node.cmView; } + get isEditable() { return true; } + get isWidget() { return false; } + get isHidden() { return false; } + merge(from, to, source, hasStart, openStart, openEnd) { + return false; + } + become(other) { return false; } + canReuseDOM(other) { + return other.constructor == this.constructor && !((this.flags | other.flags) & 8 /* ViewFlag.Composition */); + } + // When this is a zero-length view with a side, this should return a + // number <= 0 to indicate it is before its position, or a + // number > 0 when after its position. + getSide() { return 0; } + destroy() { + for (let child of this.children) + if (child.parent == this) + child.destroy(); + this.parent = null; + } +} +ContentView.prototype.breakAfter = 0; +// Remove a DOM node and return its next sibling. +function rm$1(dom) { + let next = dom.nextSibling; + dom.parentNode.removeChild(dom); + return next; +} +class ChildCursor { + constructor(children, pos, i) { + this.children = children; + this.pos = pos; + this.i = i; + this.off = 0; + } + findPos(pos, bias = 1) { + for (;;) { + if (pos > this.pos || pos == this.pos && + (bias > 0 || this.i == 0 || this.children[this.i - 1].breakAfter)) { + this.off = pos - this.pos; + return this; + } + let next = this.children[--this.i]; + this.pos -= next.length + next.breakAfter; + } + } +} +function replaceRange(parent, fromI, fromOff, toI, toOff, insert, breakAtStart, openStart, openEnd) { + let { children } = parent; + let before = children.length ? children[fromI] : null; + let last = insert.length ? insert[insert.length - 1] : null; + let breakAtEnd = last ? last.breakAfter : breakAtStart; + // Change within a single child + if (fromI == toI && before && !breakAtStart && !breakAtEnd && insert.length < 2 && + before.merge(fromOff, toOff, insert.length ? last : null, fromOff == 0, openStart, openEnd)) + return; + if (toI < children.length) { + let after = children[toI]; + // Make sure the end of the child after the update is preserved in `after` + if (after && (toOff < after.length || after.breakAfter && (last === null || last === void 0 ? void 0 : last.breakAfter))) { + // If we're splitting a child, separate part of it to avoid that + // being mangled when updating the child before the update. + if (fromI == toI) { + after = after.split(toOff); + toOff = 0; + } + // If the element after the replacement should be merged with + // the last replacing element, update `content` + if (!breakAtEnd && last && after.merge(0, toOff, last, true, 0, openEnd)) { + insert[insert.length - 1] = after; + } + else { + // Remove the start of the after element, if necessary, and + // add it to `content`. + if (toOff || after.children.length && !after.children[0].length) + after.merge(0, toOff, null, false, 0, openEnd); + insert.push(after); + } + } + else if (after === null || after === void 0 ? void 0 : after.breakAfter) { + // The element at `toI` is entirely covered by this range. + // Preserve its line break, if any. + if (last) + last.breakAfter = 1; + else + breakAtStart = 1; + } + // Since we've handled the next element from the current elements + // now, make sure `toI` points after that. + toI++; + } + if (before) { + before.breakAfter = breakAtStart; + if (fromOff > 0) { + if (!breakAtStart && insert.length && before.merge(fromOff, before.length, insert[0], false, openStart, 0)) { + before.breakAfter = insert.shift().breakAfter; + } + else if (fromOff < before.length || before.children.length && before.children[before.children.length - 1].length == 0) { + before.merge(fromOff, before.length, null, false, openStart, 0); + } + fromI++; + } + } + // Try to merge widgets on the boundaries of the replacement + while (fromI < toI && insert.length) { + if (children[toI - 1].become(insert[insert.length - 1])) { + toI--; + insert.pop(); + openEnd = insert.length ? 0 : openStart; + } + else if (children[fromI].become(insert[0])) { + fromI++; + insert.shift(); + openStart = insert.length ? 0 : openEnd; + } + else { + break; + } + } + if (!insert.length && fromI && toI < children.length && !children[fromI - 1].breakAfter && + children[toI].merge(0, 0, children[fromI - 1], false, openStart, openEnd)) + fromI--; + if (fromI < toI || insert.length) + parent.replaceChildren(fromI, toI, insert); +} +function mergeChildrenInto(parent, from, to, insert, openStart, openEnd) { + let cur = parent.childCursor(); + let { i: toI, off: toOff } = cur.findPos(to, 1); + let { i: fromI, off: fromOff } = cur.findPos(from, -1); + let dLen = from - to; + for (let view of insert) + dLen += view.length; + parent.length += dLen; + replaceRange(parent, fromI, fromOff, toI, toOff, insert, 0, openStart, openEnd); +} + +const MaxJoinLen = 256; +class TextView extends ContentView { + constructor(text) { + super(); + this.text = text; + } + get length() { return this.text.length; } + createDOM(textDOM) { + this.setDOM(textDOM || document.createTextNode(this.text)); + } + sync(view, track) { + if (!this.dom) + this.createDOM(); + if (this.dom.nodeValue != this.text) { + if (track && track.node == this.dom) + track.written = true; + this.dom.nodeValue = this.text; + } + } + reuseDOM(dom) { + if (dom.nodeType == 3) + this.createDOM(dom); + } + merge(from, to, source) { + if ((this.flags & 8 /* ViewFlag.Composition */) || + source && (!(source instanceof TextView) || + this.length - (to - from) + source.length > MaxJoinLen || + (source.flags & 8 /* ViewFlag.Composition */))) + return false; + this.text = this.text.slice(0, from) + (source ? source.text : "") + this.text.slice(to); + this.markDirty(); + return true; + } + split(from) { + let result = new TextView(this.text.slice(from)); + this.text = this.text.slice(0, from); + this.markDirty(); + result.flags |= this.flags & 8 /* ViewFlag.Composition */; + return result; + } + localPosFromDOM(node, offset) { + return node == this.dom ? offset : offset ? this.text.length : 0; + } + domAtPos(pos) { return new DOMPos(this.dom, pos); } + domBoundsAround(_from, _to, offset) { + return { from: offset, to: offset + this.length, startDOM: this.dom, endDOM: this.dom.nextSibling }; + } + coordsAt(pos, side) { + return textCoords(this.dom, pos, side); + } +} +class MarkView extends ContentView { + constructor(mark, children = [], length = 0) { + super(); + this.mark = mark; + this.children = children; + this.length = length; + for (let ch of children) + ch.setParent(this); + } + setAttrs(dom) { + clearAttributes(dom); + if (this.mark.class) + dom.className = this.mark.class; + if (this.mark.attrs) + for (let name in this.mark.attrs) + dom.setAttribute(name, this.mark.attrs[name]); + return dom; + } + canReuseDOM(other) { + return super.canReuseDOM(other) && !((this.flags | other.flags) & 8 /* ViewFlag.Composition */); + } + reuseDOM(node) { + if (node.nodeName == this.mark.tagName.toUpperCase()) { + this.setDOM(node); + this.flags |= 4 /* ViewFlag.AttrsDirty */ | 2 /* ViewFlag.NodeDirty */; + } + } + sync(view, track) { + if (!this.dom) + this.setDOM(this.setAttrs(document.createElement(this.mark.tagName))); + else if (this.flags & 4 /* ViewFlag.AttrsDirty */) + this.setAttrs(this.dom); + super.sync(view, track); + } + merge(from, to, source, _hasStart, openStart, openEnd) { + if (source && (!(source instanceof MarkView && source.mark.eq(this.mark)) || + (from && openStart <= 0) || (to < this.length && openEnd <= 0))) + return false; + mergeChildrenInto(this, from, to, source ? source.children.slice() : [], openStart - 1, openEnd - 1); + this.markDirty(); + return true; + } + split(from) { + let result = [], off = 0, detachFrom = -1, i = 0; + for (let elt of this.children) { + let end = off + elt.length; + if (end > from) + result.push(off < from ? elt.split(from - off) : elt); + if (detachFrom < 0 && off >= from) + detachFrom = i; + off = end; + i++; + } + let length = this.length - from; + this.length = from; + if (detachFrom > -1) { + this.children.length = detachFrom; + this.markDirty(); + } + return new MarkView(this.mark, result, length); + } + domAtPos(pos) { + return inlineDOMAtPos(this, pos); + } + coordsAt(pos, side) { + return coordsInChildren(this, pos, side); + } +} +function textCoords(text, pos, side) { + let length = text.nodeValue.length; + if (pos > length) + pos = length; + let from = pos, to = pos, flatten = 0; + if (pos == 0 && side < 0 || pos == length && side >= 0) { + if (!(browser.chrome || browser.gecko)) { // These browsers reliably return valid rectangles for empty ranges + if (pos) { + from--; + flatten = 1; + } // FIXME this is wrong in RTL text + else if (to < length) { + to++; + flatten = -1; + } + } + } + else { + if (side < 0) + from--; + else if (to < length) + to++; + } + let rects = textRange(text, from, to).getClientRects(); + if (!rects.length) + return null; + let rect = rects[(flatten ? flatten < 0 : side >= 0) ? 0 : rects.length - 1]; + if (browser.safari && !flatten && rect.width == 0) + rect = Array.prototype.find.call(rects, r => r.width) || rect; + return flatten ? flattenRect(rect, flatten < 0) : rect || null; +} +// Also used for collapsed ranges that don't have a placeholder widget! +class WidgetView extends ContentView { + static create(widget, length, side) { + return new WidgetView(widget, length, side); + } + constructor(widget, length, side) { + super(); + this.widget = widget; + this.length = length; + this.side = side; + this.prevWidget = null; + } + split(from) { + let result = WidgetView.create(this.widget, this.length - from, this.side); + this.length -= from; + return result; + } + sync(view) { + if (!this.dom || !this.widget.updateDOM(this.dom, view)) { + if (this.dom && this.prevWidget) + this.prevWidget.destroy(this.dom); + this.prevWidget = null; + this.setDOM(this.widget.toDOM(view)); + if (!this.widget.editable) + this.dom.contentEditable = "false"; + } + } + getSide() { return this.side; } + merge(from, to, source, hasStart, openStart, openEnd) { + if (source && (!(source instanceof WidgetView) || !this.widget.compare(source.widget) || + from > 0 && openStart <= 0 || to < this.length && openEnd <= 0)) + return false; + this.length = from + (source ? source.length : 0) + (this.length - to); + return true; + } + become(other) { + if (other instanceof WidgetView && other.side == this.side && + this.widget.constructor == other.widget.constructor) { + if (!this.widget.compare(other.widget)) + this.markDirty(true); + if (this.dom && !this.prevWidget) + this.prevWidget = this.widget; + this.widget = other.widget; + this.length = other.length; + return true; + } + return false; + } + ignoreMutation() { return true; } + ignoreEvent(event) { return this.widget.ignoreEvent(event); } + get overrideDOMText() { + if (this.length == 0) + return state.Text.empty; + let top = this; + while (top.parent) + top = top.parent; + let { view } = top, text = view && view.state.doc, start = this.posAtStart; + return text ? text.slice(start, start + this.length) : state.Text.empty; + } + domAtPos(pos) { + return (this.length ? pos == 0 : this.side > 0) + ? DOMPos.before(this.dom) + : DOMPos.after(this.dom, pos == this.length); + } + domBoundsAround() { return null; } + coordsAt(pos, side) { + let custom = this.widget.coordsAt(this.dom, pos, side); + if (custom) + return custom; + let rects = this.dom.getClientRects(), rect = null; + if (!rects.length) + return null; + let fromBack = this.side ? this.side < 0 : pos > 0; + for (let i = fromBack ? rects.length - 1 : 0;; i += (fromBack ? -1 : 1)) { + rect = rects[i]; + if (pos > 0 ? i == 0 : i == rects.length - 1 || rect.top < rect.bottom) + break; + } + return flattenRect(rect, !fromBack); + } + get isEditable() { return false; } + get isWidget() { return true; } + get isHidden() { return this.widget.isHidden; } + destroy() { + super.destroy(); + if (this.dom) + this.widget.destroy(this.dom); + } +} +// These are drawn around uneditable widgets to avoid a number of +// browser bugs that show up when the cursor is directly next to +// uneditable inline content. +class WidgetBufferView extends ContentView { + constructor(side) { + super(); + this.side = side; + } + get length() { return 0; } + merge() { return false; } + become(other) { + return other instanceof WidgetBufferView && other.side == this.side; + } + split() { return new WidgetBufferView(this.side); } + sync() { + if (!this.dom) { + let dom = document.createElement("img"); + dom.className = "cm-widgetBuffer"; + dom.setAttribute("aria-hidden", "true"); + this.setDOM(dom); + } + } + getSide() { return this.side; } + domAtPos(pos) { return this.side > 0 ? DOMPos.before(this.dom) : DOMPos.after(this.dom); } + localPosFromDOM() { return 0; } + domBoundsAround() { return null; } + coordsAt(pos) { + return this.dom.getBoundingClientRect(); + } + get overrideDOMText() { + return state.Text.empty; + } + get isHidden() { return true; } +} +TextView.prototype.children = WidgetView.prototype.children = WidgetBufferView.prototype.children = noChildren; +function inlineDOMAtPos(parent, pos) { + let dom = parent.dom, { children } = parent, i = 0; + for (let off = 0; i < children.length; i++) { + let child = children[i], end = off + child.length; + if (end == off && child.getSide() <= 0) + continue; + if (pos > off && pos < end && child.dom.parentNode == dom) + return child.domAtPos(pos - off); + if (pos <= off) + break; + off = end; + } + for (let j = i; j > 0; j--) { + let prev = children[j - 1]; + if (prev.dom.parentNode == dom) + return prev.domAtPos(prev.length); + } + for (let j = i; j < children.length; j++) { + let next = children[j]; + if (next.dom.parentNode == dom) + return next.domAtPos(0); + } + return new DOMPos(dom, 0); +} +// Assumes `view`, if a mark view, has precisely 1 child. +function joinInlineInto(parent, view, open) { + let last, { children } = parent; + if (open > 0 && view instanceof MarkView && children.length && + (last = children[children.length - 1]) instanceof MarkView && last.mark.eq(view.mark)) { + joinInlineInto(last, view.children[0], open - 1); + } + else { + children.push(view); + view.setParent(parent); + } + parent.length += view.length; +} +function coordsInChildren(view, pos, side) { + let before = null, beforePos = -1, after = null, afterPos = -1; + function scan(view, pos) { + for (let i = 0, off = 0; i < view.children.length && off <= pos; i++) { + let child = view.children[i], end = off + child.length; + if (end >= pos) { + if (child.children.length) { + scan(child, pos - off); + } + else if ((!after || after.isHidden && (side > 0 || onSameLine(after, child))) && + (end > pos || off == end && child.getSide() > 0)) { + after = child; + afterPos = pos - off; + } + else if (off < pos || (off == end && child.getSide() < 0) && !child.isHidden) { + before = child; + beforePos = pos - off; + } + } + off = end; + } + } + scan(view, pos); + let target = (side < 0 ? before : after) || before || after; + if (target) + return target.coordsAt(Math.max(0, target == before ? beforePos : afterPos), side); + return fallbackRect(view); +} +function fallbackRect(view) { + let last = view.dom.lastChild; + if (!last) + return view.dom.getBoundingClientRect(); + let rects = clientRectsFor(last); + return rects[rects.length - 1] || null; +} +function onSameLine(a, b) { + let posA = a.coordsAt(0, 1), posB = b.coordsAt(0, 1); + return posA && posB && posB.top < posA.bottom; +} + +function combineAttrs(source, target) { + for (let name in source) { + if (name == "class" && target.class) + target.class += " " + source.class; + else if (name == "style" && target.style) + target.style += ";" + source.style; + else + target[name] = source[name]; + } + return target; +} +const noAttrs = Object.create(null); +function attrsEq(a, b, ignore) { + if (a == b) + return true; + if (!a) + a = noAttrs; + if (!b) + b = noAttrs; + let keysA = Object.keys(a), keysB = Object.keys(b); + if (keysA.length - (ignore && keysA.indexOf(ignore) > -1 ? 1 : 0) != + keysB.length - (ignore && keysB.indexOf(ignore) > -1 ? 1 : 0)) + return false; + for (let key of keysA) { + if (key != ignore && (keysB.indexOf(key) == -1 || a[key] !== b[key])) + return false; + } + return true; +} +function updateAttrs(dom, prev, attrs) { + let changed = false; + if (prev) + for (let name in prev) + if (!(attrs && name in attrs)) { + changed = true; + if (name == "style") + dom.style.cssText = ""; + else + dom.removeAttribute(name); + } + if (attrs) + for (let name in attrs) + if (!(prev && prev[name] == attrs[name])) { + changed = true; + if (name == "style") + dom.style.cssText = attrs[name]; + else + dom.setAttribute(name, attrs[name]); + } + return changed; +} +function getAttrs(dom) { + let attrs = Object.create(null); + for (let i = 0; i < dom.attributes.length; i++) { + let attr = dom.attributes[i]; + attrs[attr.name] = attr.value; + } + return attrs; +} + +/** +Widgets added to the content are described by subclasses of this +class. Using a description object like that makes it possible to +delay creating of the DOM structure for a widget until it is +needed, and to avoid redrawing widgets even if the decorations +that define them are recreated. +*/ +class WidgetType { + /** + Compare this instance to another instance of the same type. + (TypeScript can't express this, but only instances of the same + specific class will be passed to this method.) This is used to + avoid redrawing widgets when they are replaced by a new + decoration of the same type. The default implementation just + returns `false`, which will cause new instances of the widget to + always be redrawn. + */ + eq(widget) { return false; } + /** + Update a DOM element created by a widget of the same type (but + different, non-`eq` content) to reflect this widget. May return + true to indicate that it could update, false to indicate it + couldn't (in which case the widget will be redrawn). The default + implementation just returns false. + */ + updateDOM(dom, view) { return false; } + /** + @internal + */ + compare(other) { + return this == other || this.constructor == other.constructor && this.eq(other); + } + /** + The estimated height this widget will have, to be used when + estimating the height of content that hasn't been drawn. May + return -1 to indicate you don't know. The default implementation + returns -1. + */ + get estimatedHeight() { return -1; } + /** + For inline widgets that are displayed inline (as opposed to + `inline-block`) and introduce line breaks (through `
` tags + or textual newlines), this must indicate the amount of line + breaks they introduce. Defaults to 0. + */ + get lineBreaks() { return 0; } + /** + Can be used to configure which kinds of events inside the widget + should be ignored by the editor. The default is to ignore all + events. + */ + ignoreEvent(event) { return true; } + /** + Override the way screen coordinates for positions at/in the + widget are found. `pos` will be the offset into the widget, and + `side` the side of the position that is being queried—less than + zero for before, greater than zero for after, and zero for + directly at that position. + */ + coordsAt(dom, pos, side) { return null; } + /** + @internal + */ + get isHidden() { return false; } + /** + @internal + */ + get editable() { return false; } + /** + This is called when the an instance of the widget is removed + from the editor view. + */ + destroy(dom) { } +} +/** +The different types of blocks that can occur in an editor view. +*/ +exports.BlockType = void 0; +(function (BlockType) { + /** + A line of text. + */ + BlockType[BlockType["Text"] = 0] = "Text"; + /** + A block widget associated with the position after it. + */ + BlockType[BlockType["WidgetBefore"] = 1] = "WidgetBefore"; + /** + A block widget associated with the position before it. + */ + BlockType[BlockType["WidgetAfter"] = 2] = "WidgetAfter"; + /** + A block widget [replacing](https://codemirror.net/6/docs/ref/#view.Decoration^replace) a range of content. + */ + BlockType[BlockType["WidgetRange"] = 3] = "WidgetRange"; +})(exports.BlockType || (exports.BlockType = {})); +/** +A decoration provides information on how to draw or style a piece +of content. You'll usually use it wrapped in a +[`Range`](https://codemirror.net/6/docs/ref/#state.Range), which adds a start and end position. +@nonabstract +*/ +class Decoration extends state.RangeValue { + constructor( + /** + @internal + */ + startSide, + /** + @internal + */ + endSide, + /** + @internal + */ + widget, + /** + The config object used to create this decoration. You can + include additional properties in there to store metadata about + your decoration. + */ + spec) { + super(); + this.startSide = startSide; + this.endSide = endSide; + this.widget = widget; + this.spec = spec; + } + /** + @internal + */ + get heightRelevant() { return false; } + /** + Create a mark decoration, which influences the styling of the + content in its range. Nested mark decorations will cause nested + DOM elements to be created. Nesting order is determined by + precedence of the [facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations), with + the higher-precedence decorations creating the inner DOM nodes. + Such elements are split on line boundaries and on the boundaries + of lower-precedence decorations. + */ + static mark(spec) { + return new MarkDecoration(spec); + } + /** + Create a widget decoration, which displays a DOM element at the + given position. + */ + static widget(spec) { + let side = Math.max(-10000, Math.min(10000, spec.side || 0)), block = !!spec.block; + side += (block && !spec.inlineOrder) + ? (side > 0 ? 300000000 /* Side.BlockAfter */ : -400000000 /* Side.BlockBefore */) + : (side > 0 ? 100000000 /* Side.InlineAfter */ : -100000000 /* Side.InlineBefore */); + return new PointDecoration(spec, side, side, block, spec.widget || null, false); + } + /** + Create a replace decoration which replaces the given range with + a widget, or simply hides it. + */ + static replace(spec) { + let block = !!spec.block, startSide, endSide; + if (spec.isBlockGap) { + startSide = -500000000 /* Side.GapStart */; + endSide = 400000000 /* Side.GapEnd */; + } + else { + let { start, end } = getInclusive(spec, block); + startSide = (start ? (block ? -300000000 /* Side.BlockIncStart */ : -1 /* Side.InlineIncStart */) : 500000000 /* Side.NonIncStart */) - 1; + endSide = (end ? (block ? 200000000 /* Side.BlockIncEnd */ : 1 /* Side.InlineIncEnd */) : -600000000 /* Side.NonIncEnd */) + 1; + } + return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true); + } + /** + Create a line decoration, which can add DOM attributes to the + line starting at the given position. + */ + static line(spec) { + return new LineDecoration(spec); + } + /** + Build a [`DecorationSet`](https://codemirror.net/6/docs/ref/#view.DecorationSet) from the given + decorated range or ranges. If the ranges aren't already sorted, + pass `true` for `sort` to make the library sort them for you. + */ + static set(of, sort = false) { + return state.RangeSet.of(of, sort); + } + /** + @internal + */ + hasHeight() { return this.widget ? this.widget.estimatedHeight > -1 : false; } +} +/** +The empty set of decorations. +*/ +Decoration.none = state.RangeSet.empty; +class MarkDecoration extends Decoration { + constructor(spec) { + let { start, end } = getInclusive(spec); + super(start ? -1 /* Side.InlineIncStart */ : 500000000 /* Side.NonIncStart */, end ? 1 /* Side.InlineIncEnd */ : -600000000 /* Side.NonIncEnd */, null, spec); + this.tagName = spec.tagName || "span"; + this.class = spec.class || ""; + this.attrs = spec.attributes || null; + } + eq(other) { + var _a, _b; + return this == other || + other instanceof MarkDecoration && + this.tagName == other.tagName && + (this.class || ((_a = this.attrs) === null || _a === void 0 ? void 0 : _a.class)) == (other.class || ((_b = other.attrs) === null || _b === void 0 ? void 0 : _b.class)) && + attrsEq(this.attrs, other.attrs, "class"); + } + range(from, to = from) { + if (from >= to) + throw new RangeError("Mark decorations may not be empty"); + return super.range(from, to); + } +} +MarkDecoration.prototype.point = false; +class LineDecoration extends Decoration { + constructor(spec) { + super(-200000000 /* Side.Line */, -200000000 /* Side.Line */, null, spec); + } + eq(other) { + return other instanceof LineDecoration && + this.spec.class == other.spec.class && + attrsEq(this.spec.attributes, other.spec.attributes); + } + range(from, to = from) { + if (to != from) + throw new RangeError("Line decoration ranges must be zero-length"); + return super.range(from, to); + } +} +LineDecoration.prototype.mapMode = state.MapMode.TrackBefore; +LineDecoration.prototype.point = true; +class PointDecoration extends Decoration { + constructor(spec, startSide, endSide, block, widget, isReplace) { + super(startSide, endSide, widget, spec); + this.block = block; + this.isReplace = isReplace; + this.mapMode = !block ? state.MapMode.TrackDel : startSide <= 0 ? state.MapMode.TrackBefore : state.MapMode.TrackAfter; + } + // Only relevant when this.block == true + get type() { + return this.startSide != this.endSide ? exports.BlockType.WidgetRange + : this.startSide <= 0 ? exports.BlockType.WidgetBefore : exports.BlockType.WidgetAfter; + } + get heightRelevant() { + return this.block || !!this.widget && (this.widget.estimatedHeight >= 5 || this.widget.lineBreaks > 0); + } + eq(other) { + return other instanceof PointDecoration && + widgetsEq(this.widget, other.widget) && + this.block == other.block && + this.startSide == other.startSide && this.endSide == other.endSide; + } + range(from, to = from) { + if (this.isReplace && (from > to || (from == to && this.startSide > 0 && this.endSide <= 0))) + throw new RangeError("Invalid range for replacement decoration"); + if (!this.isReplace && to != from) + throw new RangeError("Widget decorations can only have zero-length ranges"); + return super.range(from, to); + } +} +PointDecoration.prototype.point = true; +function getInclusive(spec, block = false) { + let { inclusiveStart: start, inclusiveEnd: end } = spec; + if (start == null) + start = spec.inclusive; + if (end == null) + end = spec.inclusive; + return { start: start !== null && start !== void 0 ? start : block, end: end !== null && end !== void 0 ? end : block }; +} +function widgetsEq(a, b) { + return a == b || !!(a && b && a.compare(b)); +} +function addRange(from, to, ranges, margin = 0) { + let last = ranges.length - 1; + if (last >= 0 && ranges[last] + margin >= from) + ranges[last] = Math.max(ranges[last], to); + else + ranges.push(from, to); +} + +class LineView extends ContentView { + constructor() { + super(...arguments); + this.children = []; + this.length = 0; + this.prevAttrs = undefined; + this.attrs = null; + this.breakAfter = 0; + } + // Consumes source + merge(from, to, source, hasStart, openStart, openEnd) { + if (source) { + if (!(source instanceof LineView)) + return false; + if (!this.dom) + source.transferDOM(this); // Reuse source.dom when appropriate + } + if (hasStart) + this.setDeco(source ? source.attrs : null); + mergeChildrenInto(this, from, to, source ? source.children.slice() : [], openStart, openEnd); + return true; + } + split(at) { + let end = new LineView; + end.breakAfter = this.breakAfter; + if (this.length == 0) + return end; + let { i, off } = this.childPos(at); + if (off) { + end.append(this.children[i].split(off), 0); + this.children[i].merge(off, this.children[i].length, null, false, 0, 0); + i++; + } + for (let j = i; j < this.children.length; j++) + end.append(this.children[j], 0); + while (i > 0 && this.children[i - 1].length == 0) + this.children[--i].destroy(); + this.children.length = i; + this.markDirty(); + this.length = at; + return end; + } + transferDOM(other) { + if (!this.dom) + return; + this.markDirty(); + other.setDOM(this.dom); + other.prevAttrs = this.prevAttrs === undefined ? this.attrs : this.prevAttrs; + this.prevAttrs = undefined; + this.dom = null; + } + setDeco(attrs) { + if (!attrsEq(this.attrs, attrs)) { + if (this.dom) { + this.prevAttrs = this.attrs; + this.markDirty(); + } + this.attrs = attrs; + } + } + append(child, openStart) { + joinInlineInto(this, child, openStart); + } + // Only called when building a line view in ContentBuilder + addLineDeco(deco) { + let attrs = deco.spec.attributes, cls = deco.spec.class; + if (attrs) + this.attrs = combineAttrs(attrs, this.attrs || {}); + if (cls) + this.attrs = combineAttrs({ class: cls }, this.attrs || {}); + } + domAtPos(pos) { + return inlineDOMAtPos(this, pos); + } + reuseDOM(node) { + if (node.nodeName == "DIV") { + this.setDOM(node); + this.flags |= 4 /* ViewFlag.AttrsDirty */ | 2 /* ViewFlag.NodeDirty */; + } + } + sync(view, track) { + var _a; + if (!this.dom) { + this.setDOM(document.createElement("div")); + this.dom.className = "cm-line"; + this.prevAttrs = this.attrs ? null : undefined; + } + else if (this.flags & 4 /* ViewFlag.AttrsDirty */) { + clearAttributes(this.dom); + this.dom.className = "cm-line"; + this.prevAttrs = this.attrs ? null : undefined; + } + if (this.prevAttrs !== undefined) { + updateAttrs(this.dom, this.prevAttrs, this.attrs); + this.dom.classList.add("cm-line"); + this.prevAttrs = undefined; + } + super.sync(view, track); + let last = this.dom.lastChild; + while (last && ContentView.get(last) instanceof MarkView) + last = last.lastChild; + if (!last || !this.length || + last.nodeName != "BR" && ((_a = ContentView.get(last)) === null || _a === void 0 ? void 0 : _a.isEditable) == false && + (!browser.ios || !this.children.some(ch => ch instanceof TextView))) { + let hack = document.createElement("BR"); + hack.cmIgnore = true; + this.dom.appendChild(hack); + } + } + measureTextSize() { + if (this.children.length == 0 || this.length > 20) + return null; + let totalWidth = 0, textHeight; + for (let child of this.children) { + if (!(child instanceof TextView) || /[^ -~]/.test(child.text)) + return null; + let rects = clientRectsFor(child.dom); + if (rects.length != 1) + return null; + totalWidth += rects[0].width; + textHeight = rects[0].height; + } + return !totalWidth ? null : { + lineHeight: this.dom.getBoundingClientRect().height, + charWidth: totalWidth / this.length, + textHeight + }; + } + coordsAt(pos, side) { + let rect = coordsInChildren(this, pos, side); + // Correct rectangle height for empty lines when the returned + // height is larger than the text height. + if (!this.children.length && rect && this.parent) { + let { heightOracle } = this.parent.view.viewState, height = rect.bottom - rect.top; + if (Math.abs(height - heightOracle.lineHeight) < 2 && heightOracle.textHeight < height) { + let dist = (height - heightOracle.textHeight) / 2; + return { top: rect.top + dist, bottom: rect.bottom - dist, left: rect.left, right: rect.left }; + } + } + return rect; + } + become(other) { + return other instanceof LineView && this.children.length == 0 && other.children.length == 0 && + attrsEq(this.attrs, other.attrs) && this.breakAfter == other.breakAfter; + } + covers() { return true; } + static find(docView, pos) { + for (let i = 0, off = 0; i < docView.children.length; i++) { + let block = docView.children[i], end = off + block.length; + if (end >= pos) { + if (block instanceof LineView) + return block; + if (end > pos) + break; + } + off = end + block.breakAfter; + } + return null; + } +} +class BlockWidgetView extends ContentView { + constructor(widget, length, deco) { + super(); + this.widget = widget; + this.length = length; + this.deco = deco; + this.breakAfter = 0; + this.prevWidget = null; + } + merge(from, to, source, _takeDeco, openStart, openEnd) { + if (source && (!(source instanceof BlockWidgetView) || !this.widget.compare(source.widget) || + from > 0 && openStart <= 0 || to < this.length && openEnd <= 0)) + return false; + this.length = from + (source ? source.length : 0) + (this.length - to); + return true; + } + domAtPos(pos) { + return pos == 0 ? DOMPos.before(this.dom) : DOMPos.after(this.dom, pos == this.length); + } + split(at) { + let len = this.length - at; + this.length = at; + let end = new BlockWidgetView(this.widget, len, this.deco); + end.breakAfter = this.breakAfter; + return end; + } + get children() { return noChildren; } + sync(view) { + if (!this.dom || !this.widget.updateDOM(this.dom, view)) { + if (this.dom && this.prevWidget) + this.prevWidget.destroy(this.dom); + this.prevWidget = null; + this.setDOM(this.widget.toDOM(view)); + if (!this.widget.editable) + this.dom.contentEditable = "false"; + } + } + get overrideDOMText() { + return this.parent ? this.parent.view.state.doc.slice(this.posAtStart, this.posAtEnd) : state.Text.empty; + } + domBoundsAround() { return null; } + become(other) { + if (other instanceof BlockWidgetView && + other.widget.constructor == this.widget.constructor) { + if (!other.widget.compare(this.widget)) + this.markDirty(true); + if (this.dom && !this.prevWidget) + this.prevWidget = this.widget; + this.widget = other.widget; + this.length = other.length; + this.deco = other.deco; + this.breakAfter = other.breakAfter; + return true; + } + return false; + } + ignoreMutation() { return true; } + ignoreEvent(event) { return this.widget.ignoreEvent(event); } + get isEditable() { return false; } + get isWidget() { return true; } + coordsAt(pos, side) { + let custom = this.widget.coordsAt(this.dom, pos, side); + if (custom) + return custom; + if (this.widget instanceof BlockGapWidget) + return null; + return flattenRect(this.dom.getBoundingClientRect(), this.length ? pos == 0 : side <= 0); + } + destroy() { + super.destroy(); + if (this.dom) + this.widget.destroy(this.dom); + } + covers(side) { + let { startSide, endSide } = this.deco; + return startSide == endSide ? false : side < 0 ? startSide < 0 : endSide > 0; + } +} +class BlockGapWidget extends WidgetType { + constructor(height) { + super(); + this.height = height; + } + toDOM() { + let elt = document.createElement("div"); + elt.className = "cm-gap"; + this.updateDOM(elt); + return elt; + } + eq(other) { return other.height == this.height; } + updateDOM(elt) { + elt.style.height = this.height + "px"; + return true; + } + get editable() { return true; } + get estimatedHeight() { return this.height; } + ignoreEvent() { return false; } +} + +class ContentBuilder { + constructor(doc, pos, end, disallowBlockEffectsFor) { + this.doc = doc; + this.pos = pos; + this.end = end; + this.disallowBlockEffectsFor = disallowBlockEffectsFor; + this.content = []; + this.curLine = null; + this.breakAtStart = 0; + this.pendingBuffer = 0 /* Buf.No */; + this.bufferMarks = []; + // Set to false directly after a widget that covers the position after it + this.atCursorPos = true; + this.openStart = -1; + this.openEnd = -1; + this.text = ""; + this.textOff = 0; + this.cursor = doc.iter(); + this.skip = pos; + } + posCovered() { + if (this.content.length == 0) + return !this.breakAtStart && this.doc.lineAt(this.pos).from != this.pos; + let last = this.content[this.content.length - 1]; + return !(last.breakAfter || last instanceof BlockWidgetView && last.deco.endSide < 0); + } + getLine() { + if (!this.curLine) { + this.content.push(this.curLine = new LineView); + this.atCursorPos = true; + } + return this.curLine; + } + flushBuffer(active = this.bufferMarks) { + if (this.pendingBuffer) { + this.curLine.append(wrapMarks(new WidgetBufferView(-1), active), active.length); + this.pendingBuffer = 0 /* Buf.No */; + } + } + addBlockWidget(view) { + this.flushBuffer(); + this.curLine = null; + this.content.push(view); + } + finish(openEnd) { + if (this.pendingBuffer && openEnd <= this.bufferMarks.length) + this.flushBuffer(); + else + this.pendingBuffer = 0 /* Buf.No */; + if (!this.posCovered() && + !(openEnd && this.content.length && this.content[this.content.length - 1] instanceof BlockWidgetView)) + this.getLine(); + } + buildText(length, active, openStart) { + while (length > 0) { + if (this.textOff == this.text.length) { + let { value, lineBreak, done } = this.cursor.next(this.skip); + this.skip = 0; + if (done) + throw new Error("Ran out of text content when drawing inline views"); + if (lineBreak) { + if (!this.posCovered()) + this.getLine(); + if (this.content.length) + this.content[this.content.length - 1].breakAfter = 1; + else + this.breakAtStart = 1; + this.flushBuffer(); + this.curLine = null; + this.atCursorPos = true; + length--; + continue; + } + else { + this.text = value; + this.textOff = 0; + } + } + let remaining = Math.min(this.text.length - this.textOff, length); + let take = Math.min(remaining, 512 /* T.Chunk */); + this.flushBuffer(active.slice(active.length - openStart)); + this.getLine().append(wrapMarks(new TextView(this.text.slice(this.textOff, this.textOff + take)), active), openStart); + this.atCursorPos = true; + this.textOff += take; + length -= take; + openStart = remaining <= take ? 0 : active.length; + } + } + span(from, to, active, openStart) { + this.buildText(to - from, active, openStart); + this.pos = to; + if (this.openStart < 0) + this.openStart = openStart; + } + point(from, to, deco, active, openStart, index) { + if (this.disallowBlockEffectsFor[index] && deco instanceof PointDecoration) { + if (deco.block) + throw new RangeError("Block decorations may not be specified via plugins"); + if (to > this.doc.lineAt(this.pos).to) + throw new RangeError("Decorations that replace line breaks may not be specified via plugins"); + } + let len = to - from; + if (deco instanceof PointDecoration) { + if (deco.block) { + if (deco.startSide > 0 && !this.posCovered()) + this.getLine(); + this.addBlockWidget(new BlockWidgetView(deco.widget || NullWidget.block, len, deco)); + } + else { + let view = WidgetView.create(deco.widget || NullWidget.inline, len, len ? 0 : deco.startSide); + let cursorBefore = this.atCursorPos && !view.isEditable && openStart <= active.length && + (from < to || deco.startSide > 0); + let cursorAfter = !view.isEditable && (from < to || openStart > active.length || deco.startSide <= 0); + let line = this.getLine(); + if (this.pendingBuffer == 2 /* Buf.IfCursor */ && !cursorBefore && !view.isEditable) + this.pendingBuffer = 0 /* Buf.No */; + this.flushBuffer(active); + if (cursorBefore) { + line.append(wrapMarks(new WidgetBufferView(1), active), openStart); + openStart = active.length + Math.max(0, openStart - active.length); + } + line.append(wrapMarks(view, active), openStart); + this.atCursorPos = cursorAfter; + this.pendingBuffer = !cursorAfter ? 0 /* Buf.No */ : from < to || openStart > active.length ? 1 /* Buf.Yes */ : 2 /* Buf.IfCursor */; + if (this.pendingBuffer) + this.bufferMarks = active.slice(); + } + } + else if (this.doc.lineAt(this.pos).from == this.pos) { // Line decoration + this.getLine().addLineDeco(deco); + } + if (len) { + // Advance the iterator past the replaced content + if (this.textOff + len <= this.text.length) { + this.textOff += len; + } + else { + this.skip += len - (this.text.length - this.textOff); + this.text = ""; + this.textOff = 0; + } + this.pos = to; + } + if (this.openStart < 0) + this.openStart = openStart; + } + static build(text, from, to, decorations, dynamicDecorationMap) { + let builder = new ContentBuilder(text, from, to, dynamicDecorationMap); + builder.openEnd = state.RangeSet.spans(decorations, from, to, builder); + if (builder.openStart < 0) + builder.openStart = builder.openEnd; + builder.finish(builder.openEnd); + return builder; + } +} +function wrapMarks(view, active) { + for (let mark of active) + view = new MarkView(mark, [view], view.length); + return view; +} +class NullWidget extends WidgetType { + constructor(tag) { + super(); + this.tag = tag; + } + eq(other) { return other.tag == this.tag; } + toDOM() { return document.createElement(this.tag); } + updateDOM(elt) { return elt.nodeName.toLowerCase() == this.tag; } + get isHidden() { return true; } +} +NullWidget.inline = new NullWidget("span"); +NullWidget.block = new NullWidget("div"); + +/** +Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection). +*/ +exports.Direction = void 0; +(function (Direction) { + // (These are chosen to match the base levels, in bidi algorithm + // terms, of spans in that direction.) + /** + Left-to-right. + */ + Direction[Direction["LTR"] = 0] = "LTR"; + /** + Right-to-left. + */ + Direction[Direction["RTL"] = 1] = "RTL"; +})(exports.Direction || (exports.Direction = {})); +const LTR = exports.Direction.LTR, RTL = exports.Direction.RTL; +// Decode a string with each type encoded as log2(type) +function dec(str) { + let result = []; + for (let i = 0; i < str.length; i++) + result.push(1 << +str[i]); + return result; +} +// Character types for codepoints 0 to 0xf8 +const LowTypes = dec("88888888888888888888888888888888888666888888787833333333337888888000000000000000000000000008888880000000000000000000000000088888888888888888888888888888888888887866668888088888663380888308888800000000000000000000000800000000000000000000000000000008"); +// Character types for codepoints 0x600 to 0x6f9 +const ArabicTypes = dec("4444448826627288999999999992222222222222222222222222222222222222222222222229999999999999999999994444444444644222822222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222999999949999999229989999223333333333"); +const Brackets = Object.create(null), BracketStack = []; +// There's a lot more in +// https://www.unicode.org/Public/UCD/latest/ucd/BidiBrackets.txt, +// which are left out to keep code size down. +for (let p of ["()", "[]", "{}"]) { + let l = p.charCodeAt(0), r = p.charCodeAt(1); + Brackets[l] = r; + Brackets[r] = -l; +} +function charType(ch) { + return ch <= 0xf7 ? LowTypes[ch] : + 0x590 <= ch && ch <= 0x5f4 ? 2 /* T.R */ : + 0x600 <= ch && ch <= 0x6f9 ? ArabicTypes[ch - 0x600] : + 0x6ee <= ch && ch <= 0x8ac ? 4 /* T.AL */ : + 0x2000 <= ch && ch <= 0x200c ? 256 /* T.NI */ : + 0xfb50 <= ch && ch <= 0xfdff ? 4 /* T.AL */ : 1 /* T.L */; +} +const BidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac\ufb50-\ufdff]/; +/** +Represents a contiguous range of text that has a single direction +(as in left-to-right or right-to-left). +*/ +class BidiSpan { + /** + The direction of this span. + */ + get dir() { return this.level % 2 ? RTL : LTR; } + /** + @internal + */ + constructor( + /** + The start of the span (relative to the start of the line). + */ + from, + /** + The end of the span. + */ + to, + /** + The ["bidi + level"](https://unicode.org/reports/tr9/#Basic_Display_Algorithm) + of the span (in this context, 0 means + left-to-right, 1 means right-to-left, 2 means left-to-right + number inside right-to-left text). + */ + level) { + this.from = from; + this.to = to; + this.level = level; + } + /** + @internal + */ + side(end, dir) { return (this.dir == dir) == end ? this.to : this.from; } + /** + @internal + */ + forward(forward, dir) { return forward == (this.dir == dir); } + /** + @internal + */ + static find(order, index, level, assoc) { + let maybe = -1; + for (let i = 0; i < order.length; i++) { + let span = order[i]; + if (span.from <= index && span.to >= index) { + if (span.level == level) + return i; + // When multiple spans match, if assoc != 0, take the one that + // covers that side, otherwise take the one with the minimum + // level. + if (maybe < 0 || (assoc != 0 ? (assoc < 0 ? span.from < index : span.to > index) : order[maybe].level > span.level)) + maybe = i; + } + } + if (maybe < 0) + throw new RangeError("Index out of range"); + return maybe; + } +} +function isolatesEq(a, b) { + if (a.length != b.length) + return false; + for (let i = 0; i < a.length; i++) { + let iA = a[i], iB = b[i]; + if (iA.from != iB.from || iA.to != iB.to || iA.direction != iB.direction || !isolatesEq(iA.inner, iB.inner)) + return false; + } + return true; +} +// Reused array of character types +const types = []; +// Fill in the character types (in `types`) from `from` to `to` and +// apply W normalization rules. +function computeCharTypes(line, rFrom, rTo, isolates, outerType) { + for (let iI = 0; iI <= isolates.length; iI++) { + let from = iI ? isolates[iI - 1].to : rFrom, to = iI < isolates.length ? isolates[iI].from : rTo; + let prevType = iI ? 256 /* T.NI */ : outerType; + // W1. Examine each non-spacing mark (NSM) in the level run, and + // change the type of the NSM to the type of the previous + // character. If the NSM is at the start of the level run, it will + // get the type of sor. + // W2. Search backwards from each instance of a European number + // until the first strong type (R, L, AL, or sor) is found. If an + // AL is found, change the type of the European number to Arabic + // number. + // W3. Change all ALs to R. + // (Left after this: L, R, EN, AN, ET, CS, NI) + for (let i = from, prev = prevType, prevStrong = prevType; i < to; i++) { + let type = charType(line.charCodeAt(i)); + if (type == 512 /* T.NSM */) + type = prev; + else if (type == 8 /* T.EN */ && prevStrong == 4 /* T.AL */) + type = 16 /* T.AN */; + types[i] = type == 4 /* T.AL */ ? 2 /* T.R */ : type; + if (type & 7 /* T.Strong */) + prevStrong = type; + prev = type; + } + // W5. A sequence of European terminators adjacent to European + // numbers changes to all European numbers. + // W6. Otherwise, separators and terminators change to Other + // Neutral. + // W7. Search backwards from each instance of a European number + // until the first strong type (R, L, or sor) is found. If an L is + // found, then change the type of the European number to L. + // (Left after this: L, R, EN+AN, NI) + for (let i = from, prev = prevType, prevStrong = prevType; i < to; i++) { + let type = types[i]; + if (type == 128 /* T.CS */) { + if (i < to - 1 && prev == types[i + 1] && (prev & 24 /* T.Num */)) + type = types[i] = prev; + else + types[i] = 256 /* T.NI */; + } + else if (type == 64 /* T.ET */) { + let end = i + 1; + while (end < to && types[end] == 64 /* T.ET */) + end++; + let replace = (i && prev == 8 /* T.EN */) || (end < rTo && types[end] == 8 /* T.EN */) ? (prevStrong == 1 /* T.L */ ? 1 /* T.L */ : 8 /* T.EN */) : 256 /* T.NI */; + for (let j = i; j < end; j++) + types[j] = replace; + i = end - 1; + } + else if (type == 8 /* T.EN */ && prevStrong == 1 /* T.L */) { + types[i] = 1 /* T.L */; + } + prev = type; + if (type & 7 /* T.Strong */) + prevStrong = type; + } + } +} +// Process brackets throughout a run sequence. +function processBracketPairs(line, rFrom, rTo, isolates, outerType) { + let oppositeType = outerType == 1 /* T.L */ ? 2 /* T.R */ : 1 /* T.L */; + for (let iI = 0, sI = 0, context = 0; iI <= isolates.length; iI++) { + let from = iI ? isolates[iI - 1].to : rFrom, to = iI < isolates.length ? isolates[iI].from : rTo; + // N0. Process bracket pairs in an isolating run sequence + // sequentially in the logical order of the text positions of the + // opening paired brackets using the logic given below. Within this + // scope, bidirectional types EN and AN are treated as R. + for (let i = from, ch, br, type; i < to; i++) { + // Keeps [startIndex, type, strongSeen] triples for each open + // bracket on BracketStack. + if (br = Brackets[ch = line.charCodeAt(i)]) { + if (br < 0) { // Closing bracket + for (let sJ = sI - 3; sJ >= 0; sJ -= 3) { + if (BracketStack[sJ + 1] == -br) { + let flags = BracketStack[sJ + 2]; + let type = (flags & 2 /* Bracketed.EmbedInside */) ? outerType : + !(flags & 4 /* Bracketed.OppositeInside */) ? 0 : + (flags & 1 /* Bracketed.OppositeBefore */) ? oppositeType : outerType; + if (type) + types[i] = types[BracketStack[sJ]] = type; + sI = sJ; + break; + } + } + } + else if (BracketStack.length == 189 /* Bracketed.MaxDepth */) { + break; + } + else { + BracketStack[sI++] = i; + BracketStack[sI++] = ch; + BracketStack[sI++] = context; + } + } + else if ((type = types[i]) == 2 /* T.R */ || type == 1 /* T.L */) { + let embed = type == outerType; + context = embed ? 0 : 1 /* Bracketed.OppositeBefore */; + for (let sJ = sI - 3; sJ >= 0; sJ -= 3) { + let cur = BracketStack[sJ + 2]; + if (cur & 2 /* Bracketed.EmbedInside */) + break; + if (embed) { + BracketStack[sJ + 2] |= 2 /* Bracketed.EmbedInside */; + } + else { + if (cur & 4 /* Bracketed.OppositeInside */) + break; + BracketStack[sJ + 2] |= 4 /* Bracketed.OppositeInside */; + } + } + } + } + } +} +function processNeutrals(rFrom, rTo, isolates, outerType) { + for (let iI = 0, prev = outerType; iI <= isolates.length; iI++) { + let from = iI ? isolates[iI - 1].to : rFrom, to = iI < isolates.length ? isolates[iI].from : rTo; + // N1. A sequence of neutrals takes the direction of the + // surrounding strong text if the text on both sides has the same + // direction. European and Arabic numbers act as if they were R in + // terms of their influence on neutrals. Start-of-level-run (sor) + // and end-of-level-run (eor) are used at level run boundaries. + // N2. Any remaining neutrals take the embedding direction. + // (Left after this: L, R, EN+AN) + for (let i = from; i < to;) { + let type = types[i]; + if (type == 256 /* T.NI */) { + let end = i + 1; + for (;;) { + if (end == to) { + if (iI == isolates.length) + break; + end = isolates[iI++].to; + to = iI < isolates.length ? isolates[iI].from : rTo; + } + else if (types[end] == 256 /* T.NI */) { + end++; + } + else { + break; + } + } + let beforeL = prev == 1 /* T.L */; + let afterL = (end < rTo ? types[end] : outerType) == 1 /* T.L */; + let replace = beforeL == afterL ? (beforeL ? 1 /* T.L */ : 2 /* T.R */) : outerType; + for (let j = end, jI = iI, fromJ = jI ? isolates[jI - 1].to : rFrom; j > i;) { + if (j == fromJ) { + j = isolates[--jI].from; + fromJ = jI ? isolates[jI - 1].to : rFrom; + } + types[--j] = replace; + } + i = end; + } + else { + prev = type; + i++; + } + } + } +} +// Find the contiguous ranges of character types in a given range, and +// emit spans for them. Flip the order of the spans as appropriate +// based on the level, and call through to compute the spans for +// isolates at the proper point. +function emitSpans(line, from, to, level, baseLevel, isolates, order) { + let ourType = level % 2 ? 2 /* T.R */ : 1 /* T.L */; + if ((level % 2) == (baseLevel % 2)) { // Same dir as base direction, don't flip + for (let iCh = from, iI = 0; iCh < to;) { + // Scan a section of characters in direction ourType, unless + // there's another type of char right after iCh, in which case + // we scan a section of other characters (which, if ourType == + // T.L, may contain both T.R and T.AN chars). + let sameDir = true, isNum = false; + if (iI == isolates.length || iCh < isolates[iI].from) { + let next = types[iCh]; + if (next != ourType) { + sameDir = false; + isNum = next == 16 /* T.AN */; + } + } + // Holds an array of isolates to pass to a recursive call if we + // must recurse (to distinguish T.AN inside an RTL section in + // LTR text), null if we can emit directly + let recurse = !sameDir && ourType == 1 /* T.L */ ? [] : null; + let localLevel = sameDir ? level : level + 1; + let iScan = iCh; + run: for (;;) { + if (iI < isolates.length && iScan == isolates[iI].from) { + if (isNum) + break run; + let iso = isolates[iI]; + // Scan ahead to verify that there is another char in this dir after the isolate(s) + if (!sameDir) + for (let upto = iso.to, jI = iI + 1;;) { + if (upto == to) + break run; + if (jI < isolates.length && isolates[jI].from == upto) + upto = isolates[jI++].to; + else if (types[upto] == ourType) + break run; + else + break; + } + iI++; + if (recurse) { + recurse.push(iso); + } + else { + if (iso.from > iCh) + order.push(new BidiSpan(iCh, iso.from, localLevel)); + let dirSwap = (iso.direction == LTR) != !(localLevel % 2); + computeSectionOrder(line, dirSwap ? level + 1 : level, baseLevel, iso.inner, iso.from, iso.to, order); + iCh = iso.to; + } + iScan = iso.to; + } + else if (iScan == to || (sameDir ? types[iScan] != ourType : types[iScan] == ourType)) { + break; + } + else { + iScan++; + } + } + if (recurse) + emitSpans(line, iCh, iScan, level + 1, baseLevel, recurse, order); + else if (iCh < iScan) + order.push(new BidiSpan(iCh, iScan, localLevel)); + iCh = iScan; + } + } + else { + // Iterate in reverse to flip the span order. Same code again, but + // going from the back of the section to the front + for (let iCh = to, iI = isolates.length; iCh > from;) { + let sameDir = true, isNum = false; + if (!iI || iCh > isolates[iI - 1].to) { + let next = types[iCh - 1]; + if (next != ourType) { + sameDir = false; + isNum = next == 16 /* T.AN */; + } + } + let recurse = !sameDir && ourType == 1 /* T.L */ ? [] : null; + let localLevel = sameDir ? level : level + 1; + let iScan = iCh; + run: for (;;) { + if (iI && iScan == isolates[iI - 1].to) { + if (isNum) + break run; + let iso = isolates[--iI]; + // Scan ahead to verify that there is another char in this dir after the isolate(s) + if (!sameDir) + for (let upto = iso.from, jI = iI;;) { + if (upto == from) + break run; + if (jI && isolates[jI - 1].to == upto) + upto = isolates[--jI].from; + else if (types[upto - 1] == ourType) + break run; + else + break; + } + if (recurse) { + recurse.push(iso); + } + else { + if (iso.to < iCh) + order.push(new BidiSpan(iso.to, iCh, localLevel)); + let dirSwap = (iso.direction == LTR) != !(localLevel % 2); + computeSectionOrder(line, dirSwap ? level + 1 : level, baseLevel, iso.inner, iso.from, iso.to, order); + iCh = iso.from; + } + iScan = iso.from; + } + else if (iScan == from || (sameDir ? types[iScan - 1] != ourType : types[iScan - 1] == ourType)) { + break; + } + else { + iScan--; + } + } + if (recurse) + emitSpans(line, iScan, iCh, level + 1, baseLevel, recurse, order); + else if (iScan < iCh) + order.push(new BidiSpan(iScan, iCh, localLevel)); + iCh = iScan; + } + } +} +function computeSectionOrder(line, level, baseLevel, isolates, from, to, order) { + let outerType = (level % 2 ? 2 /* T.R */ : 1 /* T.L */); + computeCharTypes(line, from, to, isolates, outerType); + processBracketPairs(line, from, to, isolates, outerType); + processNeutrals(from, to, isolates, outerType); + emitSpans(line, from, to, level, baseLevel, isolates, order); +} +function computeOrder(line, direction, isolates) { + if (!line) + return [new BidiSpan(0, 0, direction == RTL ? 1 : 0)]; + if (direction == LTR && !isolates.length && !BidiRE.test(line)) + return trivialOrder(line.length); + if (isolates.length) + while (line.length > types.length) + types[types.length] = 256 /* T.NI */; // Make sure types array has no gaps + let order = [], level = direction == LTR ? 0 : 1; + computeSectionOrder(line, level, level, isolates, 0, line.length, order); + return order; +} +function trivialOrder(length) { + return [new BidiSpan(0, length, 0)]; +} +let movedOver = ""; +// This implementation moves strictly visually, without concern for a +// traversal visiting every logical position in the string. It will +// still do so for simple input, but situations like multiple isolates +// with the same level next to each other, or text going against the +// main dir at the end of the line, will make some positions +// unreachable with this motion. Each visible cursor position will +// correspond to the lower-level bidi span that touches it. +// +// The alternative would be to solve an order globally for a given +// line, making sure that it includes every position, but that would +// require associating non-canonical (higher bidi span level) +// positions with a given visual position, which is likely to confuse +// people. (And would generally be a lot more complicated.) +function moveVisually(line, order, dir, start, forward) { + var _a; + let startIndex = start.head - line.from; + let spanI = BidiSpan.find(order, startIndex, (_a = start.bidiLevel) !== null && _a !== void 0 ? _a : -1, start.assoc); + let span = order[spanI], spanEnd = span.side(forward, dir); + // End of span + if (startIndex == spanEnd) { + let nextI = spanI += forward ? 1 : -1; + if (nextI < 0 || nextI >= order.length) + return null; + span = order[spanI = nextI]; + startIndex = span.side(!forward, dir); + spanEnd = span.side(forward, dir); + } + let nextIndex = state.findClusterBreak(line.text, startIndex, span.forward(forward, dir)); + if (nextIndex < span.from || nextIndex > span.to) + nextIndex = spanEnd; + movedOver = line.text.slice(Math.min(startIndex, nextIndex), Math.max(startIndex, nextIndex)); + let nextSpan = spanI == (forward ? order.length - 1 : 0) ? null : order[spanI + (forward ? 1 : -1)]; + if (nextSpan && nextIndex == spanEnd && nextSpan.level + (forward ? 0 : 1) < span.level) + return state.EditorSelection.cursor(nextSpan.side(!forward, dir) + line.from, nextSpan.forward(forward, dir) ? 1 : -1, nextSpan.level); + return state.EditorSelection.cursor(nextIndex + line.from, span.forward(forward, dir) ? -1 : 1, span.level); +} +function autoDirection(text, from, to) { + for (let i = from; i < to; i++) { + let type = charType(text.charCodeAt(i)); + if (type == 1 /* T.L */) + return LTR; + if (type == 2 /* T.R */ || type == 4 /* T.AL */) + return RTL; + } + return LTR; +} + +const clickAddsSelectionRange = state.Facet.define(); +const dragMovesSelection$1 = state.Facet.define(); +const mouseSelectionStyle = state.Facet.define(); +const exceptionSink = state.Facet.define(); +const updateListener = state.Facet.define(); +const inputHandler = state.Facet.define(); +const focusChangeEffect = state.Facet.define(); +const clipboardInputFilter = state.Facet.define(); +const clipboardOutputFilter = state.Facet.define(); +const perLineTextDirection = state.Facet.define({ + combine: values => values.some(x => x) +}); +const nativeSelectionHidden = state.Facet.define({ + combine: values => values.some(x => x) +}); +const scrollHandler = state.Facet.define(); +class ScrollTarget { + constructor(range, y = "nearest", x = "nearest", yMargin = 5, xMargin = 5, + // This data structure is abused to also store precise scroll + // snapshots, instead of a `scrollIntoView` request. When this + // flag is `true`, `range` points at a position in the reference + // line, `yMargin` holds the difference between the top of that + // line and the top of the editor, and `xMargin` holds the + // editor's `scrollLeft`. + isSnapshot = false) { + this.range = range; + this.y = y; + this.x = x; + this.yMargin = yMargin; + this.xMargin = xMargin; + this.isSnapshot = isSnapshot; + } + map(changes) { + return changes.empty ? this : + new ScrollTarget(this.range.map(changes), this.y, this.x, this.yMargin, this.xMargin, this.isSnapshot); + } + clip(state$1) { + return this.range.to <= state$1.doc.length ? this : + new ScrollTarget(state.EditorSelection.cursor(state$1.doc.length), this.y, this.x, this.yMargin, this.xMargin, this.isSnapshot); + } +} +const scrollIntoView = state.StateEffect.define({ map: (t, ch) => t.map(ch) }); +const setEditContextFormatting = state.StateEffect.define(); +/** +Log or report an unhandled exception in client code. Should +probably only be used by extension code that allows client code to +provide functions, and calls those functions in a context where an +exception can't be propagated to calling code in a reasonable way +(for example when in an event handler). + +Either calls a handler registered with +[`EditorView.exceptionSink`](https://codemirror.net/6/docs/ref/#view.EditorView^exceptionSink), +`window.onerror`, if defined, or `console.error` (in which case +it'll pass `context`, when given, as first argument). +*/ +function logException(state, exception, context) { + let handler = state.facet(exceptionSink); + if (handler.length) + handler[0](exception); + else if (window.onerror && window.onerror(String(exception), context, undefined, undefined, exception)) ; + else if (context) + console.error(context + ":", exception); + else + console.error(exception); +} +const editable = state.Facet.define({ combine: values => values.length ? values[0] : true }); +let nextPluginID = 0; +const viewPlugin = state.Facet.define({ + combine(plugins) { + return plugins.filter((p, i) => { + for (let j = 0; j < i; j++) + if (plugins[j].plugin == p.plugin) + return false; + return true; + }); + } +}); +/** +View plugins associate stateful values with a view. They can +influence the way the content is drawn, and are notified of things +that happen in the view. They optionally take an argument, in +which case you need to call [`of`](https://codemirror.net/6/docs/ref/#view.ViewPlugin.of) to create +an extension for the plugin. When the argument type is undefined, +you can use the plugin instance as an extension directly. +*/ +class ViewPlugin { + constructor( + /** + @internal + */ + id, + /** + @internal + */ + create, + /** + @internal + */ + domEventHandlers, + /** + @internal + */ + domEventObservers, buildExtensions) { + this.id = id; + this.create = create; + this.domEventHandlers = domEventHandlers; + this.domEventObservers = domEventObservers; + this.baseExtensions = buildExtensions(this); + this.extension = this.baseExtensions.concat(viewPlugin.of({ plugin: this, arg: undefined })); + } + /** + Create an extension for this plugin with the given argument. + */ + of(arg) { + return this.baseExtensions.concat(viewPlugin.of({ plugin: this, arg })); + } + /** + Define a plugin from a constructor function that creates the + plugin's value, given an editor view. + */ + static define(create, spec) { + const { eventHandlers, eventObservers, provide, decorations: deco } = spec || {}; + return new ViewPlugin(nextPluginID++, create, eventHandlers, eventObservers, plugin => { + let ext = []; + if (deco) + ext.push(decorations.of(view => { + let pluginInst = view.plugin(plugin); + return pluginInst ? deco(pluginInst) : Decoration.none; + })); + if (provide) + ext.push(provide(plugin)); + return ext; + }); + } + /** + Create a plugin for a class whose constructor takes a single + editor view as argument. + */ + static fromClass(cls, spec) { + return ViewPlugin.define((view, arg) => new cls(view, arg), spec); + } +} +class PluginInstance { + constructor(spec) { + this.spec = spec; + // When starting an update, all plugins have this field set to the + // update object, indicating they need to be updated. When finished + // updating, it is set to `null`. Retrieving a plugin that needs to + // be updated with `view.plugin` forces an eager update. + this.mustUpdate = null; + // This is null when the plugin is initially created, but + // initialized on the first update. + this.value = null; + } + get plugin() { return this.spec && this.spec.plugin; } + update(view) { + if (!this.value) { + if (this.spec) { + try { + this.value = this.spec.plugin.create(view, this.spec.arg); + } + catch (e) { + logException(view.state, e, "CodeMirror plugin crashed"); + this.deactivate(); + } + } + } + else if (this.mustUpdate) { + let update = this.mustUpdate; + this.mustUpdate = null; + if (this.value.update) { + try { + this.value.update(update); + } + catch (e) { + logException(update.state, e, "CodeMirror plugin crashed"); + if (this.value.destroy) + try { + this.value.destroy(); + } + catch (_) { } + this.deactivate(); + } + } + } + return this; + } + destroy(view) { + var _a; + if ((_a = this.value) === null || _a === void 0 ? void 0 : _a.destroy) { + try { + this.value.destroy(); + } + catch (e) { + logException(view.state, e, "CodeMirror plugin crashed"); + } + } + } + deactivate() { + this.spec = this.value = null; + } +} +const editorAttributes = state.Facet.define(); +const contentAttributes = state.Facet.define(); +// Provide decorations +const decorations = state.Facet.define(); +const outerDecorations = state.Facet.define(); +const atomicRanges = state.Facet.define(); +const bidiIsolatedRanges = state.Facet.define(); +function getIsolatedRanges(view, line) { + let isolates = view.state.facet(bidiIsolatedRanges); + if (!isolates.length) + return isolates; + let sets = isolates.map(i => i instanceof Function ? i(view) : i); + let result = []; + state.RangeSet.spans(sets, line.from, line.to, { + point() { }, + span(fromDoc, toDoc, active, open) { + let from = fromDoc - line.from, to = toDoc - line.from; + let level = result; + for (let i = active.length - 1; i >= 0; i--, open--) { + let direction = active[i].spec.bidiIsolate, update; + if (direction == null) + direction = autoDirection(line.text, from, to); + if (open > 0 && level.length && + (update = level[level.length - 1]).to == from && update.direction == direction) { + update.to = to; + level = update.inner; + } + else { + let add = { from, to, direction, inner: [] }; + level.push(add); + level = add.inner; + } + } + } + }); + return result; +} +const scrollMargins = state.Facet.define(); +function getScrollMargins(view) { + let left = 0, right = 0, top = 0, bottom = 0; + for (let source of view.state.facet(scrollMargins)) { + let m = source(view); + if (m) { + if (m.left != null) + left = Math.max(left, m.left); + if (m.right != null) + right = Math.max(right, m.right); + if (m.top != null) + top = Math.max(top, m.top); + if (m.bottom != null) + bottom = Math.max(bottom, m.bottom); + } + } + return { left, right, top, bottom }; +} +const styleModule = state.Facet.define(); +class ChangedRange { + constructor(fromA, toA, fromB, toB) { + this.fromA = fromA; + this.toA = toA; + this.fromB = fromB; + this.toB = toB; + } + join(other) { + return new ChangedRange(Math.min(this.fromA, other.fromA), Math.max(this.toA, other.toA), Math.min(this.fromB, other.fromB), Math.max(this.toB, other.toB)); + } + addToSet(set) { + let i = set.length, me = this; + for (; i > 0; i--) { + let range = set[i - 1]; + if (range.fromA > me.toA) + continue; + if (range.toA < me.fromA) + break; + me = me.join(range); + set.splice(i - 1, 1); + } + set.splice(i, 0, me); + return set; + } + static extendWithRanges(diff, ranges) { + if (ranges.length == 0) + return diff; + let result = []; + for (let dI = 0, rI = 0, posA = 0, posB = 0;; dI++) { + let next = dI == diff.length ? null : diff[dI], off = posA - posB; + let end = next ? next.fromB : 1e9; + while (rI < ranges.length && ranges[rI] < end) { + let from = ranges[rI], to = ranges[rI + 1]; + let fromB = Math.max(posB, from), toB = Math.min(end, to); + if (fromB <= toB) + new ChangedRange(fromB + off, toB + off, fromB, toB).addToSet(result); + if (to > end) + break; + else + rI += 2; + } + if (!next) + return result; + new ChangedRange(next.fromA, next.toA, next.fromB, next.toB).addToSet(result); + posA = next.toA; + posB = next.toB; + } + } +} +/** +View [plugins](https://codemirror.net/6/docs/ref/#view.ViewPlugin) are given instances of this +class, which describe what happened, whenever the view is updated. +*/ +class ViewUpdate { + constructor( + /** + The editor view that the update is associated with. + */ + view, + /** + The new editor state. + */ + state$1, + /** + The transactions involved in the update. May be empty. + */ + transactions) { + this.view = view; + this.state = state$1; + this.transactions = transactions; + /** + @internal + */ + this.flags = 0; + this.startState = view.state; + this.changes = state.ChangeSet.empty(this.startState.doc.length); + for (let tr of transactions) + this.changes = this.changes.compose(tr.changes); + let changedRanges = []; + this.changes.iterChangedRanges((fromA, toA, fromB, toB) => changedRanges.push(new ChangedRange(fromA, toA, fromB, toB))); + this.changedRanges = changedRanges; + } + /** + @internal + */ + static create(view, state, transactions) { + return new ViewUpdate(view, state, transactions); + } + /** + Tells you whether the [viewport](https://codemirror.net/6/docs/ref/#view.EditorView.viewport) or + [visible ranges](https://codemirror.net/6/docs/ref/#view.EditorView.visibleRanges) changed in this + update. + */ + get viewportChanged() { + return (this.flags & 4 /* UpdateFlag.Viewport */) > 0; + } + /** + Returns true when + [`viewportChanged`](https://codemirror.net/6/docs/ref/#view.ViewUpdate.viewportChanged) is true + and the viewport change is not just the result of mapping it in + response to document changes. + */ + get viewportMoved() { + return (this.flags & 8 /* UpdateFlag.ViewportMoved */) > 0; + } + /** + Indicates whether the height of a block element in the editor + changed in this update. + */ + get heightChanged() { + return (this.flags & 2 /* UpdateFlag.Height */) > 0; + } + /** + Returns true when the document was modified or the size of the + editor, or elements within the editor, changed. + */ + get geometryChanged() { + return this.docChanged || (this.flags & (16 /* UpdateFlag.Geometry */ | 2 /* UpdateFlag.Height */)) > 0; + } + /** + True when this update indicates a focus change. + */ + get focusChanged() { + return (this.flags & 1 /* UpdateFlag.Focus */) > 0; + } + /** + Whether the document changed in this update. + */ + get docChanged() { + return !this.changes.empty; + } + /** + Whether the selection was explicitly set in this update. + */ + get selectionSet() { + return this.transactions.some(tr => tr.selection); + } + /** + @internal + */ + get empty() { return this.flags == 0 && this.transactions.length == 0; } +} + +class DocView extends ContentView { + get length() { return this.view.state.doc.length; } + constructor(view) { + super(); + this.view = view; + this.decorations = []; + this.dynamicDecorationMap = [false]; + this.domChanged = null; + this.hasComposition = null; + this.markedForComposition = new Set; + this.editContextFormatting = Decoration.none; + this.lastCompositionAfterCursor = false; + // Track a minimum width for the editor. When measuring sizes in + // measureVisibleLineHeights, this is updated to point at the width + // of a given element and its extent in the document. When a change + // happens in that range, these are reset. That way, once we've seen + // a line/element of a given length, we keep the editor wide enough + // to fit at least that element, until it is changed, at which point + // we forget it again. + this.minWidth = 0; + this.minWidthFrom = 0; + this.minWidthTo = 0; + // Track whether the DOM selection was set in a lossy way, so that + // we don't mess it up when reading it back it + this.impreciseAnchor = null; + this.impreciseHead = null; + this.forceSelection = false; + // Used by the resize observer to ignore resizes that we caused + // ourselves + this.lastUpdate = Date.now(); + this.setDOM(view.contentDOM); + this.children = [new LineView]; + this.children[0].setParent(this); + this.updateDeco(); + this.updateInner([new ChangedRange(0, 0, 0, view.state.doc.length)], 0, null); + } + // Update the document view to a given state. + update(update) { + var _a; + let changedRanges = update.changedRanges; + if (this.minWidth > 0 && changedRanges.length) { + if (!changedRanges.every(({ fromA, toA }) => toA < this.minWidthFrom || fromA > this.minWidthTo)) { + this.minWidth = this.minWidthFrom = this.minWidthTo = 0; + } + else { + this.minWidthFrom = update.changes.mapPos(this.minWidthFrom, 1); + this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1); + } + } + this.updateEditContextFormatting(update); + let readCompositionAt = -1; + if (this.view.inputState.composing >= 0 && !this.view.observer.editContext) { + if ((_a = this.domChanged) === null || _a === void 0 ? void 0 : _a.newSel) + readCompositionAt = this.domChanged.newSel.head; + else if (!touchesComposition(update.changes, this.hasComposition) && !update.selectionSet) + readCompositionAt = update.state.selection.main.head; + } + let composition = readCompositionAt > -1 ? findCompositionRange(this.view, update.changes, readCompositionAt) : null; + this.domChanged = null; + if (this.hasComposition) { + this.markedForComposition.clear(); + let { from, to } = this.hasComposition; + changedRanges = new ChangedRange(from, to, update.changes.mapPos(from, -1), update.changes.mapPos(to, 1)) + .addToSet(changedRanges.slice()); + } + this.hasComposition = composition ? { from: composition.range.fromB, to: composition.range.toB } : null; + // When the DOM nodes around the selection are moved to another + // parent, Chrome sometimes reports a different selection through + // getSelection than the one that it actually shows to the user. + // This forces a selection update when lines are joined to work + // around that. Issue #54 + if ((browser.ie || browser.chrome) && !composition && update && + update.state.doc.lines != update.startState.doc.lines) + this.forceSelection = true; + let prevDeco = this.decorations, deco = this.updateDeco(); + let decoDiff = findChangedDeco(prevDeco, deco, update.changes); + changedRanges = ChangedRange.extendWithRanges(changedRanges, decoDiff); + if (!(this.flags & 7 /* ViewFlag.Dirty */) && changedRanges.length == 0) { + return false; + } + else { + this.updateInner(changedRanges, update.startState.doc.length, composition); + if (update.transactions.length) + this.lastUpdate = Date.now(); + return true; + } + } + // Used by update and the constructor do perform the actual DOM + // update + updateInner(changes, oldLength, composition) { + this.view.viewState.mustMeasureContent = true; + this.updateChildren(changes, oldLength, composition); + let { observer } = this.view; + observer.ignore(() => { + // Lock the height during redrawing, since Chrome sometimes + // messes with the scroll position during DOM mutation (though + // no relayout is triggered and I cannot imagine how it can + // recompute the scroll position without a layout) + this.dom.style.height = this.view.viewState.contentHeight / this.view.scaleY + "px"; + this.dom.style.flexBasis = this.minWidth ? this.minWidth + "px" : ""; + // Chrome will sometimes, when DOM mutations occur directly + // around the selection, get confused and report a different + // selection from the one it displays (issue #218). This tries + // to detect that situation. + let track = browser.chrome || browser.ios ? { node: observer.selectionRange.focusNode, written: false } : undefined; + this.sync(this.view, track); + this.flags &= ~7 /* ViewFlag.Dirty */; + if (track && (track.written || observer.selectionRange.focusNode != track.node)) + this.forceSelection = true; + this.dom.style.height = ""; + }); + this.markedForComposition.forEach(cView => cView.flags &= ~8 /* ViewFlag.Composition */); + let gaps = []; + if (this.view.viewport.from || this.view.viewport.to < this.view.state.doc.length) + for (let child of this.children) + if (child instanceof BlockWidgetView && child.widget instanceof BlockGapWidget) + gaps.push(child.dom); + observer.updateGaps(gaps); + } + updateChildren(changes, oldLength, composition) { + let ranges = composition ? composition.range.addToSet(changes.slice()) : changes; + let cursor = this.childCursor(oldLength); + for (let i = ranges.length - 1;; i--) { + let next = i >= 0 ? ranges[i] : null; + if (!next) + break; + let { fromA, toA, fromB, toB } = next, content, breakAtStart, openStart, openEnd; + if (composition && composition.range.fromB < toB && composition.range.toB > fromB) { + let before = ContentBuilder.build(this.view.state.doc, fromB, composition.range.fromB, this.decorations, this.dynamicDecorationMap); + let after = ContentBuilder.build(this.view.state.doc, composition.range.toB, toB, this.decorations, this.dynamicDecorationMap); + breakAtStart = before.breakAtStart; + openStart = before.openStart; + openEnd = after.openEnd; + let compLine = this.compositionView(composition); + if (after.breakAtStart) { + compLine.breakAfter = 1; + } + else if (after.content.length && + compLine.merge(compLine.length, compLine.length, after.content[0], false, after.openStart, 0)) { + compLine.breakAfter = after.content[0].breakAfter; + after.content.shift(); + } + if (before.content.length && + compLine.merge(0, 0, before.content[before.content.length - 1], true, 0, before.openEnd)) { + before.content.pop(); + } + content = before.content.concat(compLine).concat(after.content); + } + else { + ({ content, breakAtStart, openStart, openEnd } = + ContentBuilder.build(this.view.state.doc, fromB, toB, this.decorations, this.dynamicDecorationMap)); + } + let { i: toI, off: toOff } = cursor.findPos(toA, 1); + let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1); + replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd); + } + if (composition) + this.fixCompositionDOM(composition); + } + updateEditContextFormatting(update) { + this.editContextFormatting = this.editContextFormatting.map(update.changes); + for (let tr of update.transactions) + for (let effect of tr.effects) + if (effect.is(setEditContextFormatting)) { + this.editContextFormatting = effect.value; + } + } + compositionView(composition) { + let cur = new TextView(composition.text.nodeValue); + cur.flags |= 8 /* ViewFlag.Composition */; + for (let { deco } of composition.marks) + cur = new MarkView(deco, [cur], cur.length); + let line = new LineView; + line.append(cur, 0); + return line; + } + fixCompositionDOM(composition) { + let fix = (dom, cView) => { + cView.flags |= 8 /* ViewFlag.Composition */ | (cView.children.some(c => c.flags & 7 /* ViewFlag.Dirty */) ? 1 /* ViewFlag.ChildDirty */ : 0); + this.markedForComposition.add(cView); + let prev = ContentView.get(dom); + if (prev && prev != cView) + prev.dom = null; + cView.setDOM(dom); + }; + let pos = this.childPos(composition.range.fromB, 1); + let cView = this.children[pos.i]; + fix(composition.line, cView); + for (let i = composition.marks.length - 1; i >= -1; i--) { + pos = cView.childPos(pos.off, 1); + cView = cView.children[pos.i]; + fix(i >= 0 ? composition.marks[i].node : composition.text, cView); + } + } + // Sync the DOM selection to this.state.selection + updateSelection(mustRead = false, fromPointer = false) { + if (mustRead || !this.view.observer.selectionRange.focusNode) + this.view.observer.readSelectionRange(); + let activeElt = this.view.root.activeElement, focused = activeElt == this.dom; + let selectionNotFocus = !focused && !(this.view.state.facet(editable) || this.dom.tabIndex > -1) && + hasSelection(this.dom, this.view.observer.selectionRange) && !(activeElt && this.dom.contains(activeElt)); + if (!(focused || fromPointer || selectionNotFocus)) + return; + let force = this.forceSelection; + this.forceSelection = false; + let main = this.view.state.selection.main; + let anchor = this.moveToLine(this.domAtPos(main.anchor)); + let head = main.empty ? anchor : this.moveToLine(this.domAtPos(main.head)); + // Always reset on Firefox when next to an uneditable node to + // avoid invisible cursor bugs (#111) + if (browser.gecko && main.empty && !this.hasComposition && betweenUneditable(anchor)) { + let dummy = document.createTextNode(""); + this.view.observer.ignore(() => anchor.node.insertBefore(dummy, anchor.node.childNodes[anchor.offset] || null)); + anchor = head = new DOMPos(dummy, 0); + force = true; + } + let domSel = this.view.observer.selectionRange; + // If the selection is already here, or in an equivalent position, don't touch it + if (force || !domSel.focusNode || (!isEquivalentPosition(anchor.node, anchor.offset, domSel.anchorNode, domSel.anchorOffset) || + !isEquivalentPosition(head.node, head.offset, domSel.focusNode, domSel.focusOffset)) && !this.suppressWidgetCursorChange(domSel, main)) { + this.view.observer.ignore(() => { + // Chrome Android will hide the virtual keyboard when tapping + // inside an uneditable node, and not bring it back when we + // move the cursor to its proper position. This tries to + // restore the keyboard by cycling focus. + if (browser.android && browser.chrome && this.dom.contains(domSel.focusNode) && + inUneditable(domSel.focusNode, this.dom)) { + this.dom.blur(); + this.dom.focus({ preventScroll: true }); + } + let rawSel = getSelection(this.view.root); + if (!rawSel) ; + else if (main.empty) { + // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=1612076 + if (browser.gecko) { + let nextTo = nextToUneditable(anchor.node, anchor.offset); + if (nextTo && nextTo != (1 /* NextTo.Before */ | 2 /* NextTo.After */)) { + let text = (nextTo == 1 /* NextTo.Before */ ? textNodeBefore : textNodeAfter)(anchor.node, anchor.offset); + if (text) + anchor = new DOMPos(text.node, text.offset); + } + } + rawSel.collapse(anchor.node, anchor.offset); + if (main.bidiLevel != null && rawSel.caretBidiLevel !== undefined) + rawSel.caretBidiLevel = main.bidiLevel; + } + else if (rawSel.extend) { + // Selection.extend can be used to create an 'inverted' selection + // (one where the focus is before the anchor), but not all + // browsers support it yet. + rawSel.collapse(anchor.node, anchor.offset); + // Safari will ignore the call above when the editor is + // hidden, and then raise an error on the call to extend + // (#940). + try { + rawSel.extend(head.node, head.offset); + } + catch (_) { } + } + else { + // Primitive (IE) way + let range = document.createRange(); + if (main.anchor > main.head) + [anchor, head] = [head, anchor]; + range.setEnd(head.node, head.offset); + range.setStart(anchor.node, anchor.offset); + rawSel.removeAllRanges(); + rawSel.addRange(range); + } + if (selectionNotFocus && this.view.root.activeElement == this.dom) { + this.dom.blur(); + if (activeElt) + activeElt.focus(); + } + }); + this.view.observer.setSelectionRange(anchor, head); + } + this.impreciseAnchor = anchor.precise ? null : new DOMPos(domSel.anchorNode, domSel.anchorOffset); + this.impreciseHead = head.precise ? null : new DOMPos(domSel.focusNode, domSel.focusOffset); + } + // If a zero-length widget is inserted next to the cursor during + // composition, avoid moving it across it and disrupting the + // composition. + suppressWidgetCursorChange(sel, cursor) { + return this.hasComposition && cursor.empty && + isEquivalentPosition(sel.focusNode, sel.focusOffset, sel.anchorNode, sel.anchorOffset) && + this.posFromDOM(sel.focusNode, sel.focusOffset) == cursor.head; + } + enforceCursorAssoc() { + if (this.hasComposition) + return; + let { view } = this, cursor = view.state.selection.main; + let sel = getSelection(view.root); + let { anchorNode, anchorOffset } = view.observer.selectionRange; + if (!sel || !cursor.empty || !cursor.assoc || !sel.modify) + return; + let line = LineView.find(this, cursor.head); + if (!line) + return; + let lineStart = line.posAtStart; + if (cursor.head == lineStart || cursor.head == lineStart + line.length) + return; + let before = this.coordsAt(cursor.head, -1), after = this.coordsAt(cursor.head, 1); + if (!before || !after || before.bottom > after.top) + return; + let dom = this.domAtPos(cursor.head + cursor.assoc); + sel.collapse(dom.node, dom.offset); + sel.modify("move", cursor.assoc < 0 ? "forward" : "backward", "lineboundary"); + // This can go wrong in corner cases like single-character lines, + // so check and reset if necessary. + view.observer.readSelectionRange(); + let newRange = view.observer.selectionRange; + if (view.docView.posFromDOM(newRange.anchorNode, newRange.anchorOffset) != cursor.from) + sel.collapse(anchorNode, anchorOffset); + } + // If a position is in/near a block widget, move it to a nearby text + // line, since we don't want the cursor inside a block widget. + moveToLine(pos) { + // Block widgets will return positions before/after them, which + // are thus directly in the document DOM element. + let dom = this.dom, newPos; + if (pos.node != dom) + return pos; + for (let i = pos.offset; !newPos && i < dom.childNodes.length; i++) { + let view = ContentView.get(dom.childNodes[i]); + if (view instanceof LineView) + newPos = view.domAtPos(0); + } + for (let i = pos.offset - 1; !newPos && i >= 0; i--) { + let view = ContentView.get(dom.childNodes[i]); + if (view instanceof LineView) + newPos = view.domAtPos(view.length); + } + return newPos ? new DOMPos(newPos.node, newPos.offset, true) : pos; + } + nearest(dom) { + for (let cur = dom; cur;) { + let domView = ContentView.get(cur); + if (domView && domView.rootView == this) + return domView; + cur = cur.parentNode; + } + return null; + } + posFromDOM(node, offset) { + let view = this.nearest(node); + if (!view) + throw new RangeError("Trying to find position for a DOM position outside of the document"); + return view.localPosFromDOM(node, offset) + view.posAtStart; + } + domAtPos(pos) { + let { i, off } = this.childCursor().findPos(pos, -1); + for (; i < this.children.length - 1;) { + let child = this.children[i]; + if (off < child.length || child instanceof LineView) + break; + i++; + off = 0; + } + return this.children[i].domAtPos(off); + } + coordsAt(pos, side) { + let best = null, bestPos = 0; + for (let off = this.length, i = this.children.length - 1; i >= 0; i--) { + let child = this.children[i], end = off - child.breakAfter, start = end - child.length; + if (end < pos) + break; + if (start <= pos && (start < pos || child.covers(-1)) && (end > pos || child.covers(1)) && + (!best || child instanceof LineView && !(best instanceof LineView && side >= 0))) { + best = child; + bestPos = start; + } + else if (best && start == pos && end == pos && child instanceof BlockWidgetView && Math.abs(side) < 2) { + if (child.deco.startSide < 0) + break; + else if (i) + best = null; + } + off = start; + } + return best ? best.coordsAt(pos - bestPos, side) : null; + } + coordsForChar(pos) { + let { i, off } = this.childPos(pos, 1), child = this.children[i]; + if (!(child instanceof LineView)) + return null; + while (child.children.length) { + let { i, off: childOff } = child.childPos(off, 1); + for (;; i++) { + if (i == child.children.length) + return null; + if ((child = child.children[i]).length) + break; + } + off = childOff; + } + if (!(child instanceof TextView)) + return null; + let end = state.findClusterBreak(child.text, off); + if (end == off) + return null; + let rects = textRange(child.dom, off, end).getClientRects(); + for (let i = 0; i < rects.length; i++) { + let rect = rects[i]; + if (i == rects.length - 1 || rect.top < rect.bottom && rect.left < rect.right) + return rect; + } + return null; + } + measureVisibleLineHeights(viewport) { + let result = [], { from, to } = viewport; + let contentWidth = this.view.contentDOM.clientWidth; + let isWider = contentWidth > Math.max(this.view.scrollDOM.clientWidth, this.minWidth) + 1; + let widest = -1, ltr = this.view.textDirection == exports.Direction.LTR; + for (let pos = 0, i = 0; i < this.children.length; i++) { + let child = this.children[i], end = pos + child.length; + if (end > to) + break; + if (pos >= from) { + let childRect = child.dom.getBoundingClientRect(); + result.push(childRect.height); + if (isWider) { + let last = child.dom.lastChild; + let rects = last ? clientRectsFor(last) : []; + if (rects.length) { + let rect = rects[rects.length - 1]; + let width = ltr ? rect.right - childRect.left : childRect.right - rect.left; + if (width > widest) { + widest = width; + this.minWidth = contentWidth; + this.minWidthFrom = pos; + this.minWidthTo = end; + } + } + } + } + pos = end + child.breakAfter; + } + return result; + } + textDirectionAt(pos) { + let { i } = this.childPos(pos, 1); + return getComputedStyle(this.children[i].dom).direction == "rtl" ? exports.Direction.RTL : exports.Direction.LTR; + } + measureTextSize() { + for (let child of this.children) { + if (child instanceof LineView) { + let measure = child.measureTextSize(); + if (measure) + return measure; + } + } + // If no workable line exists, force a layout of a measurable element + let dummy = document.createElement("div"), lineHeight, charWidth, textHeight; + dummy.className = "cm-line"; + dummy.style.width = "99999px"; + dummy.style.position = "absolute"; + dummy.textContent = "abc def ghi jkl mno pqr stu"; + this.view.observer.ignore(() => { + this.dom.appendChild(dummy); + let rect = clientRectsFor(dummy.firstChild)[0]; + lineHeight = dummy.getBoundingClientRect().height; + charWidth = rect ? rect.width / 27 : 7; + textHeight = rect ? rect.height : lineHeight; + dummy.remove(); + }); + return { lineHeight, charWidth, textHeight }; + } + childCursor(pos = this.length) { + // Move back to start of last element when possible, so that + // `ChildCursor.findPos` doesn't have to deal with the edge case + // of being after the last element. + let i = this.children.length; + if (i) + pos -= this.children[--i].length; + return new ChildCursor(this.children, pos, i); + } + computeBlockGapDeco() { + let deco = [], vs = this.view.viewState; + for (let pos = 0, i = 0;; i++) { + let next = i == vs.viewports.length ? null : vs.viewports[i]; + let end = next ? next.from - 1 : this.length; + if (end > pos) { + let height = (vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top) / this.view.scaleY; + deco.push(Decoration.replace({ + widget: new BlockGapWidget(height), + block: true, + inclusive: true, + isBlockGap: true, + }).range(pos, end)); + } + if (!next) + break; + pos = next.to + 1; + } + return Decoration.set(deco); + } + updateDeco() { + let i = 1; + let allDeco = this.view.state.facet(decorations).map(d => { + let dynamic = this.dynamicDecorationMap[i++] = typeof d == "function"; + return dynamic ? d(this.view) : d; + }); + let dynamicOuter = false, outerDeco = this.view.state.facet(outerDecorations).map((d, i) => { + let dynamic = typeof d == "function"; + if (dynamic) + dynamicOuter = true; + return dynamic ? d(this.view) : d; + }); + if (outerDeco.length) { + this.dynamicDecorationMap[i++] = dynamicOuter; + allDeco.push(state.RangeSet.join(outerDeco)); + } + this.decorations = [ + this.editContextFormatting, + ...allDeco, + this.computeBlockGapDeco(), + this.view.viewState.lineGapDeco + ]; + while (i < this.decorations.length) + this.dynamicDecorationMap[i++] = false; + return this.decorations; + } + scrollIntoView(target) { + if (target.isSnapshot) { + let ref = this.view.viewState.lineBlockAt(target.range.head); + this.view.scrollDOM.scrollTop = ref.top - target.yMargin; + this.view.scrollDOM.scrollLeft = target.xMargin; + return; + } + for (let handler of this.view.state.facet(scrollHandler)) { + try { + if (handler(this.view, target.range, target)) + return true; + } + catch (e) { + logException(this.view.state, e, "scroll handler"); + } + } + let { range } = target; + let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other; + if (!rect) + return; + if (!range.empty && (other = this.coordsAt(range.anchor, range.anchor > range.head ? -1 : 1))) + rect = { left: Math.min(rect.left, other.left), top: Math.min(rect.top, other.top), + right: Math.max(rect.right, other.right), bottom: Math.max(rect.bottom, other.bottom) }; + let margins = getScrollMargins(this.view); + let targetRect = { + left: rect.left - margins.left, top: rect.top - margins.top, + right: rect.right + margins.right, bottom: rect.bottom + margins.bottom + }; + let { offsetWidth, offsetHeight } = this.view.scrollDOM; + scrollRectIntoView(this.view.scrollDOM, targetRect, range.head < range.anchor ? -1 : 1, target.x, target.y, Math.max(Math.min(target.xMargin, offsetWidth), -offsetWidth), Math.max(Math.min(target.yMargin, offsetHeight), -offsetHeight), this.view.textDirection == exports.Direction.LTR); + } +} +function betweenUneditable(pos) { + return pos.node.nodeType == 1 && pos.node.firstChild && + (pos.offset == 0 || pos.node.childNodes[pos.offset - 1].contentEditable == "false") && + (pos.offset == pos.node.childNodes.length || pos.node.childNodes[pos.offset].contentEditable == "false"); +} +function findCompositionNode(view, headPos) { + let sel = view.observer.selectionRange; + if (!sel.focusNode) + return null; + let textBefore = textNodeBefore(sel.focusNode, sel.focusOffset); + let textAfter = textNodeAfter(sel.focusNode, sel.focusOffset); + let textNode = textBefore || textAfter; + if (textAfter && textBefore && textAfter.node != textBefore.node) { + let descAfter = ContentView.get(textAfter.node); + if (!descAfter || descAfter instanceof TextView && descAfter.text != textAfter.node.nodeValue) { + textNode = textAfter; + } + else if (view.docView.lastCompositionAfterCursor) { + let descBefore = ContentView.get(textBefore.node); + if (!(!descBefore || descBefore instanceof TextView && descBefore.text != textBefore.node.nodeValue)) + textNode = textAfter; + } + } + view.docView.lastCompositionAfterCursor = textNode != textBefore; + if (!textNode) + return null; + let from = headPos - textNode.offset; + return { from, to: from + textNode.node.nodeValue.length, node: textNode.node }; +} +function findCompositionRange(view, changes, headPos) { + let found = findCompositionNode(view, headPos); + if (!found) + return null; + let { node: textNode, from, to } = found, text = textNode.nodeValue; + // Don't try to preserve multi-line compositions + if (/[\n\r]/.test(text)) + return null; + if (view.state.doc.sliceString(found.from, found.to) != text) + return null; + let inv = changes.invertedDesc; + let range = new ChangedRange(inv.mapPos(from), inv.mapPos(to), from, to); + let marks = []; + for (let parent = textNode.parentNode;; parent = parent.parentNode) { + let parentView = ContentView.get(parent); + if (parentView instanceof MarkView) + marks.push({ node: parent, deco: parentView.mark }); + else if (parentView instanceof LineView || parent.nodeName == "DIV" && parent.parentNode == view.contentDOM) + return { range, text: textNode, marks, line: parent }; + else if (parent != view.contentDOM) + marks.push({ node: parent, deco: new MarkDecoration({ + inclusive: true, + attributes: getAttrs(parent), + tagName: parent.tagName.toLowerCase() + }) }); + else + return null; + } +} +function nextToUneditable(node, offset) { + if (node.nodeType != 1) + return 0; + return (offset && node.childNodes[offset - 1].contentEditable == "false" ? 1 /* NextTo.Before */ : 0) | + (offset < node.childNodes.length && node.childNodes[offset].contentEditable == "false" ? 2 /* NextTo.After */ : 0); +} +let DecorationComparator$1 = class DecorationComparator { + constructor() { + this.changes = []; + } + compareRange(from, to) { addRange(from, to, this.changes); } + comparePoint(from, to) { addRange(from, to, this.changes); } + boundChange(pos) { addRange(pos, pos, this.changes); } +}; +function findChangedDeco(a, b, diff) { + let comp = new DecorationComparator$1; + state.RangeSet.compare(a, b, diff, comp); + return comp.changes; +} +function inUneditable(node, inside) { + for (let cur = node; cur && cur != inside; cur = cur.assignedSlot || cur.parentNode) { + if (cur.nodeType == 1 && cur.contentEditable == 'false') { + return true; + } + } + return false; +} +function touchesComposition(changes, composition) { + let touched = false; + if (composition) + changes.iterChangedRanges((from, to) => { + if (from < composition.to && to > composition.from) + touched = true; + }); + return touched; +} + +function groupAt(state$1, pos, bias = 1) { + let categorize = state$1.charCategorizer(pos); + let line = state$1.doc.lineAt(pos), linePos = pos - line.from; + if (line.length == 0) + return state.EditorSelection.cursor(pos); + if (linePos == 0) + bias = 1; + else if (linePos == line.length) + bias = -1; + let from = linePos, to = linePos; + if (bias < 0) + from = state.findClusterBreak(line.text, linePos, false); + else + to = state.findClusterBreak(line.text, linePos); + let cat = categorize(line.text.slice(from, to)); + while (from > 0) { + let prev = state.findClusterBreak(line.text, from, false); + if (categorize(line.text.slice(prev, from)) != cat) + break; + from = prev; + } + while (to < line.length) { + let next = state.findClusterBreak(line.text, to); + if (categorize(line.text.slice(to, next)) != cat) + break; + to = next; + } + return state.EditorSelection.range(from + line.from, to + line.from); +} +// Search the DOM for the {node, offset} position closest to the given +// coordinates. Very inefficient and crude, but can usually be avoided +// by calling caret(Position|Range)FromPoint instead. +function getdx(x, rect) { + return rect.left > x ? rect.left - x : Math.max(0, x - rect.right); +} +function getdy(y, rect) { + return rect.top > y ? rect.top - y : Math.max(0, y - rect.bottom); +} +function yOverlap(a, b) { + return a.top < b.bottom - 1 && a.bottom > b.top + 1; +} +function upTop(rect, top) { + return top < rect.top ? { top, left: rect.left, right: rect.right, bottom: rect.bottom } : rect; +} +function upBot(rect, bottom) { + return bottom > rect.bottom ? { top: rect.top, left: rect.left, right: rect.right, bottom } : rect; +} +function domPosAtCoords(parent, x, y) { + let closest, closestRect, closestX, closestY, closestOverlap = false; + let above, below, aboveRect, belowRect; + for (let child = parent.firstChild; child; child = child.nextSibling) { + let rects = clientRectsFor(child); + for (let i = 0; i < rects.length; i++) { + let rect = rects[i]; + if (closestRect && yOverlap(closestRect, rect)) + rect = upTop(upBot(rect, closestRect.bottom), closestRect.top); + let dx = getdx(x, rect), dy = getdy(y, rect); + if (dx == 0 && dy == 0) + return child.nodeType == 3 ? domPosInText(child, x, y) : domPosAtCoords(child, x, y); + if (!closest || closestY > dy || closestY == dy && closestX > dx) { + closest = child; + closestRect = rect; + closestX = dx; + closestY = dy; + closestOverlap = !dx ? true : x < rect.left ? i > 0 : i < rects.length - 1; + } + if (dx == 0) { + if (y > rect.bottom && (!aboveRect || aboveRect.bottom < rect.bottom)) { + above = child; + aboveRect = rect; + } + else if (y < rect.top && (!belowRect || belowRect.top > rect.top)) { + below = child; + belowRect = rect; + } + } + else if (aboveRect && yOverlap(aboveRect, rect)) { + aboveRect = upBot(aboveRect, rect.bottom); + } + else if (belowRect && yOverlap(belowRect, rect)) { + belowRect = upTop(belowRect, rect.top); + } + } + } + if (aboveRect && aboveRect.bottom >= y) { + closest = above; + closestRect = aboveRect; + } + else if (belowRect && belowRect.top <= y) { + closest = below; + closestRect = belowRect; + } + if (!closest) + return { node: parent, offset: 0 }; + let clipX = Math.max(closestRect.left, Math.min(closestRect.right, x)); + if (closest.nodeType == 3) + return domPosInText(closest, clipX, y); + if (closestOverlap && closest.contentEditable != "false") + return domPosAtCoords(closest, clipX, y); + let offset = Array.prototype.indexOf.call(parent.childNodes, closest) + + (x >= (closestRect.left + closestRect.right) / 2 ? 1 : 0); + return { node: parent, offset }; +} +function domPosInText(node, x, y) { + let len = node.nodeValue.length; + let closestOffset = -1, closestDY = 1e9, generalSide = 0; + for (let i = 0; i < len; i++) { + let rects = textRange(node, i, i + 1).getClientRects(); + for (let j = 0; j < rects.length; j++) { + let rect = rects[j]; + if (rect.top == rect.bottom) + continue; + if (!generalSide) + generalSide = x - rect.left; + let dy = (rect.top > y ? rect.top - y : y - rect.bottom) - 1; + if (rect.left - 1 <= x && rect.right + 1 >= x && dy < closestDY) { + let right = x >= (rect.left + rect.right) / 2, after = right; + if (browser.chrome || browser.gecko) { + // Check for RTL on browsers that support getting client + // rects for empty ranges. + let rectBefore = textRange(node, i).getBoundingClientRect(); + if (rectBefore.left == rect.right) + after = !right; + } + if (dy <= 0) + return { node, offset: i + (after ? 1 : 0) }; + closestOffset = i + (after ? 1 : 0); + closestDY = dy; + } + } + } + return { node, offset: closestOffset > -1 ? closestOffset : generalSide > 0 ? node.nodeValue.length : 0 }; +} +function posAtCoords(view, coords, precise, bias = -1) { + var _a, _b; + let content = view.contentDOM.getBoundingClientRect(), docTop = content.top + view.viewState.paddingTop; + let block, { docHeight } = view.viewState; + let { x, y } = coords, yOffset = y - docTop; + if (yOffset < 0) + return 0; + if (yOffset > docHeight) + return view.state.doc.length; + // Scan for a text block near the queried y position + for (let halfLine = view.viewState.heightOracle.textHeight / 2, bounced = false;;) { + block = view.elementAtHeight(yOffset); + if (block.type == exports.BlockType.Text) + break; + for (;;) { + // Move the y position out of this block + yOffset = bias > 0 ? block.bottom + halfLine : block.top - halfLine; + if (yOffset >= 0 && yOffset <= docHeight) + break; + // If the document consists entirely of replaced widgets, we + // won't find a text block, so return 0 + if (bounced) + return precise ? null : 0; + bounced = true; + bias = -bias; + } + } + y = docTop + yOffset; + let lineStart = block.from; + // If this is outside of the rendered viewport, we can't determine a position + if (lineStart < view.viewport.from) + return view.viewport.from == 0 ? 0 : precise ? null : posAtCoordsImprecise(view, content, block, x, y); + if (lineStart > view.viewport.to) + return view.viewport.to == view.state.doc.length ? view.state.doc.length : + precise ? null : posAtCoordsImprecise(view, content, block, x, y); + // Prefer ShadowRootOrDocument.elementFromPoint if present, fall back to document if not + let doc = view.dom.ownerDocument; + let root = view.root.elementFromPoint ? view.root : doc; + let element = root.elementFromPoint(x, y); + if (element && !view.contentDOM.contains(element)) + element = null; + // If the element is unexpected, clip x at the sides of the content area and try again + if (!element) { + x = Math.max(content.left + 1, Math.min(content.right - 1, x)); + element = root.elementFromPoint(x, y); + if (element && !view.contentDOM.contains(element)) + element = null; + } + // There's visible editor content under the point, so we can try + // using caret(Position|Range)FromPoint as a shortcut + let node, offset = -1; + if (element && ((_a = view.docView.nearest(element)) === null || _a === void 0 ? void 0 : _a.isEditable) != false) { + if (doc.caretPositionFromPoint) { + let pos = doc.caretPositionFromPoint(x, y); + if (pos) + ({ offsetNode: node, offset } = pos); + } + else if (doc.caretRangeFromPoint) { + let range = doc.caretRangeFromPoint(x, y); + if (range) + ({ startContainer: node, startOffset: offset } = range); + } + if (node && (!view.contentDOM.contains(node) || + browser.safari && isSuspiciousSafariCaretResult(node, offset, x) || + browser.chrome && isSuspiciousChromeCaretResult(node, offset, x))) + node = undefined; + // Chrome will return offsets into elements without child + // nodes, which will lead to a null deref below, so clip the + // offset to the node size. + if (node) + offset = Math.min(maxOffset(node), offset); + } + // No luck, do our own (potentially expensive) search + if (!node || !view.docView.dom.contains(node)) { + let line = LineView.find(view.docView, lineStart); + if (!line) + return yOffset > block.top + block.height / 2 ? block.to : block.from; + ({ node, offset } = domPosAtCoords(line.dom, x, y)); + } + let nearest = view.docView.nearest(node); + if (!nearest) + return null; + if (nearest.isWidget && ((_b = nearest.dom) === null || _b === void 0 ? void 0 : _b.nodeType) == 1) { + let rect = nearest.dom.getBoundingClientRect(); + return coords.y < rect.top || coords.y <= rect.bottom && coords.x <= (rect.left + rect.right) / 2 + ? nearest.posAtStart : nearest.posAtEnd; + } + else { + return nearest.localPosFromDOM(node, offset) + nearest.posAtStart; + } +} +function posAtCoordsImprecise(view, contentRect, block, x, y) { + let into = Math.round((x - contentRect.left) * view.defaultCharacterWidth); + if (view.lineWrapping && block.height > view.defaultLineHeight * 1.5) { + let textHeight = view.viewState.heightOracle.textHeight; + let line = Math.floor((y - block.top - (view.defaultLineHeight - textHeight) * 0.5) / textHeight); + into += line * view.viewState.heightOracle.lineLength; + } + let content = view.state.sliceDoc(block.from, block.to); + return block.from + state.findColumn(content, into, view.state.tabSize); +} +function isEndOfLineBefore(node, offset, x) { + let len, scan = node; + if (node.nodeType != 3 || offset != (len = node.nodeValue.length)) + return false; + for (;;) { // Check that there is no content after this node + let next = scan.nextSibling; + if (next) { + if (next.nodeName == "BR") + break; + return false; + } + else { + let parent = scan.parentNode; + if (!parent || parent.nodeName == "DIV") + break; + scan = parent; + } + } + return textRange(node, len - 1, len).getBoundingClientRect().right > x; +} +// In case of a high line height, Safari's caretRangeFromPoint treats +// the space between lines as belonging to the last character of the +// line before. This is used to detect such a result so that it can be +// ignored (issue #401). +function isSuspiciousSafariCaretResult(node, offset, x) { + return isEndOfLineBefore(node, offset, x); +} +// Chrome will move positions between lines to the start of the next line +function isSuspiciousChromeCaretResult(node, offset, x) { + if (offset != 0) + return isEndOfLineBefore(node, offset, x); + for (let cur = node;;) { + let parent = cur.parentNode; + if (!parent || parent.nodeType != 1 || parent.firstChild != cur) + return false; + if (parent.classList.contains("cm-line")) + break; + cur = parent; + } + let rect = node.nodeType == 1 ? node.getBoundingClientRect() + : textRange(node, 0, Math.max(node.nodeValue.length, 1)).getBoundingClientRect(); + return x - rect.left > 5; +} +function blockAt(view, pos, side) { + let line = view.lineBlockAt(pos); + if (Array.isArray(line.type)) { + let best; + for (let l of line.type) { + if (l.from > pos) + break; + if (l.to < pos) + continue; + if (l.from < pos && l.to > pos) + return l; + if (!best || (l.type == exports.BlockType.Text && (best.type != l.type || (side < 0 ? l.from < pos : l.to > pos)))) + best = l; + } + return best || line; + } + return line; +} +function moveToLineBoundary(view, start, forward, includeWrap) { + let line = blockAt(view, start.head, start.assoc || -1); + let coords = !includeWrap || line.type != exports.BlockType.Text || !(view.lineWrapping || line.widgetLineBreaks) ? null + : view.coordsAtPos(start.assoc < 0 && start.head > line.from ? start.head - 1 : start.head); + if (coords) { + let editorRect = view.dom.getBoundingClientRect(); + let direction = view.textDirectionAt(line.from); + let pos = view.posAtCoords({ x: forward == (direction == exports.Direction.LTR) ? editorRect.right - 1 : editorRect.left + 1, + y: (coords.top + coords.bottom) / 2 }); + if (pos != null) + return state.EditorSelection.cursor(pos, forward ? -1 : 1); + } + return state.EditorSelection.cursor(forward ? line.to : line.from, forward ? -1 : 1); +} +function moveByChar(view, start, forward, by) { + let line = view.state.doc.lineAt(start.head), spans = view.bidiSpans(line); + let direction = view.textDirectionAt(line.from); + for (let cur = start, check = null;;) { + let next = moveVisually(line, spans, direction, cur, forward), char = movedOver; + if (!next) { + if (line.number == (forward ? view.state.doc.lines : 1)) + return cur; + char = "\n"; + line = view.state.doc.line(line.number + (forward ? 1 : -1)); + spans = view.bidiSpans(line); + next = view.visualLineSide(line, !forward); + } + if (!check) { + if (!by) + return next; + check = by(char); + } + else if (!check(char)) { + return cur; + } + cur = next; + } +} +function byGroup(view, pos, start) { + let categorize = view.state.charCategorizer(pos); + let cat = categorize(start); + return (next) => { + let nextCat = categorize(next); + if (cat == state.CharCategory.Space) + cat = nextCat; + return cat == nextCat; + }; +} +function moveVertically(view, start, forward, distance) { + let startPos = start.head, dir = forward ? 1 : -1; + if (startPos == (forward ? view.state.doc.length : 0)) + return state.EditorSelection.cursor(startPos, start.assoc); + let goal = start.goalColumn, startY; + let rect = view.contentDOM.getBoundingClientRect(); + let startCoords = view.coordsAtPos(startPos, start.assoc || -1), docTop = view.documentTop; + if (startCoords) { + if (goal == null) + goal = startCoords.left - rect.left; + startY = dir < 0 ? startCoords.top : startCoords.bottom; + } + else { + let line = view.viewState.lineBlockAt(startPos); + if (goal == null) + goal = Math.min(rect.right - rect.left, view.defaultCharacterWidth * (startPos - line.from)); + startY = (dir < 0 ? line.top : line.bottom) + docTop; + } + let resolvedGoal = rect.left + goal; + let dist = distance !== null && distance !== void 0 ? distance : (view.viewState.heightOracle.textHeight >> 1); + for (let extra = 0;; extra += 10) { + let curY = startY + (dist + extra) * dir; + let pos = posAtCoords(view, { x: resolvedGoal, y: curY }, false, dir); + if (curY < rect.top || curY > rect.bottom || (dir < 0 ? pos < startPos : pos > startPos)) { + let charRect = view.docView.coordsForChar(pos); + let assoc = !charRect || curY < charRect.top ? -1 : 1; + return state.EditorSelection.cursor(pos, assoc, undefined, goal); + } + } +} +function skipAtomicRanges(atoms, pos, bias) { + for (;;) { + let moved = 0; + for (let set of atoms) { + set.between(pos - 1, pos + 1, (from, to, value) => { + if (pos > from && pos < to) { + let side = moved || bias || (pos - from < to - pos ? -1 : 1); + pos = side < 0 ? from : to; + moved = side; + } + }); + } + if (!moved) + return pos; + } +} +function skipAtomsForSelection(atoms, sel) { + let ranges = null; + for (let i = 0; i < sel.ranges.length; i++) { + let range = sel.ranges[i], updated = null; + if (range.empty) { + let pos = skipAtomicRanges(atoms, range.from, 0); + if (pos != range.from) + updated = state.EditorSelection.cursor(pos, -1); + } + else { + let from = skipAtomicRanges(atoms, range.from, -1); + let to = skipAtomicRanges(atoms, range.to, 1); + if (from != range.from || to != range.to) + updated = state.EditorSelection.range(range.from == range.anchor ? from : to, range.from == range.head ? from : to); + } + if (updated) { + if (!ranges) + ranges = sel.ranges.slice(); + ranges[i] = updated; + } + } + return ranges ? state.EditorSelection.create(ranges, sel.mainIndex) : sel; +} +function skipAtoms(view, oldPos, pos) { + let newPos = skipAtomicRanges(view.state.facet(atomicRanges).map(f => f(view)), pos.from, oldPos.head > pos.from ? -1 : 1); + return newPos == pos.from ? pos : state.EditorSelection.cursor(newPos, newPos < pos.from ? 1 : -1); +} + +const LineBreakPlaceholder = "\uffff"; +class DOMReader { + constructor(points, state$1) { + this.points = points; + this.text = ""; + this.lineSeparator = state$1.facet(state.EditorState.lineSeparator); + } + append(text) { + this.text += text; + } + lineBreak() { + this.text += LineBreakPlaceholder; + } + readRange(start, end) { + if (!start) + return this; + let parent = start.parentNode; + for (let cur = start;;) { + this.findPointBefore(parent, cur); + let oldLen = this.text.length; + this.readNode(cur); + let next = cur.nextSibling; + if (next == end) + break; + let view = ContentView.get(cur), nextView = ContentView.get(next); + if ((view && nextView ? view.breakAfter : + (view ? view.breakAfter : isBlockElement(cur)) || + (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen)) && + !isEmptyToEnd(next, end)) + this.lineBreak(); + cur = next; + } + this.findPointBefore(parent, end); + return this; + } + readTextNode(node) { + let text = node.nodeValue; + for (let point of this.points) + if (point.node == node) + point.pos = this.text.length + Math.min(point.offset, text.length); + for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) { + let nextBreak = -1, breakSize = 1, m; + if (this.lineSeparator) { + nextBreak = text.indexOf(this.lineSeparator, off); + breakSize = this.lineSeparator.length; + } + else if (m = re.exec(text)) { + nextBreak = m.index; + breakSize = m[0].length; + } + this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak)); + if (nextBreak < 0) + break; + this.lineBreak(); + if (breakSize > 1) + for (let point of this.points) + if (point.node == node && point.pos > this.text.length) + point.pos -= breakSize - 1; + off = nextBreak + breakSize; + } + } + readNode(node) { + if (node.cmIgnore) + return; + let view = ContentView.get(node); + let fromView = view && view.overrideDOMText; + if (fromView != null) { + this.findPointInside(node, fromView.length); + for (let i = fromView.iter(); !i.next().done;) { + if (i.lineBreak) + this.lineBreak(); + else + this.append(i.value); + } + } + else if (node.nodeType == 3) { + this.readTextNode(node); + } + else if (node.nodeName == "BR") { + if (node.nextSibling) + this.lineBreak(); + } + else if (node.nodeType == 1) { + this.readRange(node.firstChild, null); + } + } + findPointBefore(node, next) { + for (let point of this.points) + if (point.node == node && node.childNodes[point.offset] == next) + point.pos = this.text.length; + } + findPointInside(node, length) { + for (let point of this.points) + if (node.nodeType == 3 ? point.node == node : node.contains(point.node)) + point.pos = this.text.length + (isAtEnd(node, point.node, point.offset) ? length : 0); + } +} +function isAtEnd(parent, node, offset) { + for (;;) { + if (!node || offset < maxOffset(node)) + return false; + if (node == parent) + return true; + offset = domIndex(node) + 1; + node = node.parentNode; + } +} +function isEmptyToEnd(node, end) { + let widgets; + for (;; node = node.nextSibling) { + if (node == end || !node) + break; + let view = ContentView.get(node); + if (!((view === null || view === void 0 ? void 0 : view.isWidget) || node.cmIgnore)) + return false; + if (view) + (widgets || (widgets = [])).push(view); + } + if (widgets) + for (let w of widgets) { + let override = w.overrideDOMText; + if (override === null || override === void 0 ? void 0 : override.length) + return false; + } + return true; +} +class DOMPoint { + constructor(node, offset) { + this.node = node; + this.offset = offset; + this.pos = -1; + } +} + +class DOMChange { + constructor(view, start, end, typeOver) { + this.typeOver = typeOver; + this.bounds = null; + this.text = ""; + this.domChanged = start > -1; + let { impreciseHead: iHead, impreciseAnchor: iAnchor } = view.docView; + if (view.state.readOnly && start > -1) { + // Ignore changes when the editor is read-only + this.newSel = null; + } + else if (start > -1 && (this.bounds = view.docView.domBoundsAround(start, end, 0))) { + let selPoints = iHead || iAnchor ? [] : selectionPoints(view); + let reader = new DOMReader(selPoints, view.state); + reader.readRange(this.bounds.startDOM, this.bounds.endDOM); + this.text = reader.text; + this.newSel = selectionFromPoints(selPoints, this.bounds.from); + } + else { + let domSel = view.observer.selectionRange; + let head = iHead && iHead.node == domSel.focusNode && iHead.offset == domSel.focusOffset || + !contains(view.contentDOM, domSel.focusNode) + ? view.state.selection.main.head + : view.docView.posFromDOM(domSel.focusNode, domSel.focusOffset); + let anchor = iAnchor && iAnchor.node == domSel.anchorNode && iAnchor.offset == domSel.anchorOffset || + !contains(view.contentDOM, domSel.anchorNode) + ? view.state.selection.main.anchor + : view.docView.posFromDOM(domSel.anchorNode, domSel.anchorOffset); + // iOS will refuse to select the block gaps when doing + // select-all. + // Chrome will put the selection *inside* them, confusing + // posFromDOM + let vp = view.viewport; + if ((browser.ios || browser.chrome) && view.state.selection.main.empty && head != anchor && + (vp.from > 0 || vp.to < view.state.doc.length)) { + let from = Math.min(head, anchor), to = Math.max(head, anchor); + let offFrom = vp.from - from, offTo = vp.to - to; + if ((offFrom == 0 || offFrom == 1 || from == 0) && (offTo == 0 || offTo == -1 || to == view.state.doc.length)) { + head = 0; + anchor = view.state.doc.length; + } + } + this.newSel = state.EditorSelection.single(anchor, head); + } + } +} +function applyDOMChange(view, domChange) { + let change; + let { newSel } = domChange, sel = view.state.selection.main; + let lastKey = view.inputState.lastKeyTime > Date.now() - 100 ? view.inputState.lastKeyCode : -1; + if (domChange.bounds) { + let { from, to } = domChange.bounds; + let preferredPos = sel.from, preferredSide = null; + // Prefer anchoring to end when Backspace is pressed (or, on + // Android, when something was deleted) + if (lastKey === 8 || browser.android && domChange.text.length < to - from) { + preferredPos = sel.to; + preferredSide = "end"; + } + let diff = findDiff(view.state.doc.sliceString(from, to, LineBreakPlaceholder), domChange.text, preferredPos - from, preferredSide); + if (diff) { + // Chrome inserts two newlines when pressing shift-enter at the + // end of a line. DomChange drops one of those. + if (browser.chrome && lastKey == 13 && + diff.toB == diff.from + 2 && domChange.text.slice(diff.from, diff.toB) == LineBreakPlaceholder + LineBreakPlaceholder) + diff.toB--; + change = { from: from + diff.from, to: from + diff.toA, + insert: state.Text.of(domChange.text.slice(diff.from, diff.toB).split(LineBreakPlaceholder)) }; + } + } + else if (newSel && (!view.hasFocus && view.state.facet(editable) || newSel.main.eq(sel))) { + newSel = null; + } + if (!change && !newSel) + return false; + if (!change && domChange.typeOver && !sel.empty && newSel && newSel.main.empty) { + // Heuristic to notice typing over a selected character + change = { from: sel.from, to: sel.to, insert: view.state.doc.slice(sel.from, sel.to) }; + } + else if ((browser.mac || browser.android) && change && change.from == change.to && change.from == sel.head - 1 && + /^\. ?$/.test(change.insert.toString()) && view.contentDOM.getAttribute("autocorrect") == "off") { + // Detect insert-period-on-double-space Mac and Android behavior, + // and transform it into a regular space insert. + if (newSel && change.insert.length == 2) + newSel = state.EditorSelection.single(newSel.main.anchor - 1, newSel.main.head - 1); + change = { from: change.from, to: change.to, insert: state.Text.of([change.insert.toString().replace(".", " ")]) }; + } + else if (change && change.from >= sel.from && change.to <= sel.to && + (change.from != sel.from || change.to != sel.to) && + (sel.to - sel.from) - (change.to - change.from) <= 4) { + // If the change is inside the selection and covers most of it, + // assume it is a selection replace (with identical characters at + // the start/end not included in the diff) + change = { + from: sel.from, to: sel.to, + insert: view.state.doc.slice(sel.from, change.from).append(change.insert).append(view.state.doc.slice(change.to, sel.to)) + }; + } + else if (browser.chrome && change && change.from == change.to && change.from == sel.head && + change.insert.toString() == "\n " && view.lineWrapping) { + // In Chrome, if you insert a space at the start of a wrapped + // line, it will actually insert a newline and a space, causing a + // bogus new line to be created in CodeMirror (#968) + if (newSel) + newSel = state.EditorSelection.single(newSel.main.anchor - 1, newSel.main.head - 1); + change = { from: sel.from, to: sel.to, insert: state.Text.of([" "]) }; + } + if (change) { + return applyDOMChangeInner(view, change, newSel, lastKey); + } + else if (newSel && !newSel.main.eq(sel)) { + let scrollIntoView = false, userEvent = "select"; + if (view.inputState.lastSelectionTime > Date.now() - 50) { + if (view.inputState.lastSelectionOrigin == "select") + scrollIntoView = true; + userEvent = view.inputState.lastSelectionOrigin; + if (userEvent == "select.pointer") + newSel = skipAtomsForSelection(view.state.facet(atomicRanges).map(f => f(view)), newSel); + } + view.dispatch({ selection: newSel, scrollIntoView, userEvent }); + return true; + } + else { + return false; + } +} +function applyDOMChangeInner(view, change, newSel, lastKey = -1) { + if (browser.ios && view.inputState.flushIOSKey(change)) + return true; + let sel = view.state.selection.main; + // Android browsers don't fire reasonable key events for enter, + // backspace, or delete. So this detects changes that look like + // they're caused by those keys, and reinterprets them as key + // events. (Some of these keys are also handled by beforeinput + // events and the pendingAndroidKey mechanism, but that's not + // reliable in all situations.) + if (browser.android && + ((change.to == sel.to && + // GBoard will sometimes remove a space it just inserted + // after a completion when you press enter + (change.from == sel.from || change.from == sel.from - 1 && view.state.sliceDoc(change.from, sel.from) == " ") && + change.insert.length == 1 && change.insert.lines == 2 && + dispatchKey(view.contentDOM, "Enter", 13)) || + ((change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 || + lastKey == 8 && change.insert.length < change.to - change.from && change.to > sel.head) && + dispatchKey(view.contentDOM, "Backspace", 8)) || + (change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 && + dispatchKey(view.contentDOM, "Delete", 46)))) + return true; + let text = change.insert.toString(); + if (view.inputState.composing >= 0) + view.inputState.composing++; + let defaultTr; + let defaultInsert = () => defaultTr || (defaultTr = applyDefaultInsert(view, change, newSel)); + if (!view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text, defaultInsert))) + view.dispatch(defaultInsert()); + return true; +} +function applyDefaultInsert(view, change, newSel) { + let tr, startState = view.state, sel = startState.selection.main, inAtomic = -1; + if (change.from == change.to && change.from < sel.from || change.from > sel.to) { + let side = change.from < sel.from ? -1 : 1, pos = side < 0 ? sel.from : sel.to; + let moved = skipAtomicRanges(startState.facet(atomicRanges).map(f => f(view)), pos, side); + if (change.from == moved) + inAtomic = moved; + } + if (inAtomic > -1) { + tr = { + changes: change, + selection: state.EditorSelection.cursor(change.from + change.insert.length, -1) + }; + } + else if (change.from >= sel.from && change.to <= sel.to && change.to - change.from >= (sel.to - sel.from) / 3 && + (!newSel || newSel.main.empty && newSel.main.from == change.from + change.insert.length) && + view.inputState.composing < 0) { + let before = sel.from < change.from ? startState.sliceDoc(sel.from, change.from) : ""; + let after = sel.to > change.to ? startState.sliceDoc(change.to, sel.to) : ""; + tr = startState.replaceSelection(view.state.toText(before + change.insert.sliceString(0, undefined, view.state.lineBreak) + after)); + } + else { + let changes = startState.changes(change); + let mainSel = newSel && newSel.main.to <= changes.newLength ? newSel.main : undefined; + // Try to apply a composition change to all cursors + if (startState.selection.ranges.length > 1 && view.inputState.composing >= 0 && + change.to <= sel.to && change.to >= sel.to - 10) { + let replaced = view.state.sliceDoc(change.from, change.to); + let compositionRange, composition = newSel && findCompositionNode(view, newSel.main.head); + if (composition) { + let dLen = change.insert.length - (change.to - change.from); + compositionRange = { from: composition.from, to: composition.to - dLen }; + } + else { + compositionRange = view.state.doc.lineAt(sel.head); + } + let offset = sel.to - change.to, size = sel.to - sel.from; + tr = startState.changeByRange(range => { + if (range.from == sel.from && range.to == sel.to) + return { changes, range: mainSel || range.map(changes) }; + let to = range.to - offset, from = to - replaced.length; + if (range.to - range.from != size || view.state.sliceDoc(from, to) != replaced || + // Unfortunately, there's no way to make multiple + // changes in the same node work without aborting + // composition, so cursors in the composition range are + // ignored. + range.to >= compositionRange.from && range.from <= compositionRange.to) + return { range }; + let rangeChanges = startState.changes({ from, to, insert: change.insert }), selOff = range.to - sel.to; + return { + changes: rangeChanges, + range: !mainSel ? range.map(rangeChanges) : + state.EditorSelection.range(Math.max(0, mainSel.anchor + selOff), Math.max(0, mainSel.head + selOff)) + }; + }); + } + else { + tr = { + changes, + selection: mainSel && startState.selection.replaceRange(mainSel) + }; + } + } + let userEvent = "input.type"; + if (view.composing || + view.inputState.compositionPendingChange && view.inputState.compositionEndedAt > Date.now() - 50) { + view.inputState.compositionPendingChange = false; + userEvent += ".compose"; + if (view.inputState.compositionFirstChange) { + userEvent += ".start"; + view.inputState.compositionFirstChange = false; + } + } + return startState.update(tr, { userEvent, scrollIntoView: true }); +} +function findDiff(a, b, preferredPos, preferredSide) { + let minLen = Math.min(a.length, b.length); + let from = 0; + while (from < minLen && a.charCodeAt(from) == b.charCodeAt(from)) + from++; + if (from == minLen && a.length == b.length) + return null; + let toA = a.length, toB = b.length; + while (toA > 0 && toB > 0 && a.charCodeAt(toA - 1) == b.charCodeAt(toB - 1)) { + toA--; + toB--; + } + if (preferredSide == "end") { + let adjust = Math.max(0, from - Math.min(toA, toB)); + preferredPos -= toA + adjust - from; + } + if (toA < from && a.length < b.length) { + let move = preferredPos <= from && preferredPos >= toA ? from - preferredPos : 0; + from -= move; + toB = from + (toB - toA); + toA = from; + } + else if (toB < from) { + let move = preferredPos <= from && preferredPos >= toB ? from - preferredPos : 0; + from -= move; + toA = from + (toA - toB); + toB = from; + } + return { from, toA, toB }; +} +function selectionPoints(view) { + let result = []; + if (view.root.activeElement != view.contentDOM) + return result; + let { anchorNode, anchorOffset, focusNode, focusOffset } = view.observer.selectionRange; + if (anchorNode) { + result.push(new DOMPoint(anchorNode, anchorOffset)); + if (focusNode != anchorNode || focusOffset != anchorOffset) + result.push(new DOMPoint(focusNode, focusOffset)); + } + return result; +} +function selectionFromPoints(points, base) { + if (points.length == 0) + return null; + let anchor = points[0].pos, head = points.length == 2 ? points[1].pos : anchor; + return anchor > -1 && head > -1 ? state.EditorSelection.single(anchor + base, head + base) : null; +} + +class InputState { + setSelectionOrigin(origin) { + this.lastSelectionOrigin = origin; + this.lastSelectionTime = Date.now(); + } + constructor(view) { + this.view = view; + this.lastKeyCode = 0; + this.lastKeyTime = 0; + this.lastTouchTime = 0; + this.lastFocusTime = 0; + this.lastScrollTop = 0; + this.lastScrollLeft = 0; + // On iOS, some keys need to have their default behavior happen + // (after which we retroactively handle them and reset the DOM) to + // avoid messing up the virtual keyboard state. + this.pendingIOSKey = undefined; + /** + When enabled (>-1), tab presses are not given to key handlers, + leaving the browser's default behavior. If >0, the mode expires + at that timestamp, and any other keypress clears it. + Esc enables temporary tab focus mode for two seconds when not + otherwise handled. + */ + this.tabFocusMode = -1; + this.lastSelectionOrigin = null; + this.lastSelectionTime = 0; + this.lastContextMenu = 0; + this.scrollHandlers = []; + this.handlers = Object.create(null); + // -1 means not in a composition. Otherwise, this counts the number + // of changes made during the composition. The count is used to + // avoid treating the start state of the composition, before any + // changes have been made, as part of the composition. + this.composing = -1; + // Tracks whether the next change should be marked as starting the + // composition (null means no composition, true means next is the + // first, false means first has already been marked for this + // composition) + this.compositionFirstChange = null; + // End time of the previous composition + this.compositionEndedAt = 0; + // Used in a kludge to detect when an Enter keypress should be + // considered part of the composition on Safari, which fires events + // in the wrong order + this.compositionPendingKey = false; + // Used to categorize changes as part of a composition, even when + // the mutation events fire shortly after the compositionend event + this.compositionPendingChange = false; + this.mouseSelection = null; + // When a drag from the editor is active, this points at the range + // being dragged. + this.draggedContent = null; + this.handleEvent = this.handleEvent.bind(this); + this.notifiedFocused = view.hasFocus; + // On Safari adding an input event handler somehow prevents an + // issue where the composition vanishes when you press enter. + if (browser.safari) + view.contentDOM.addEventListener("input", () => null); + if (browser.gecko) + firefoxCopyCutHack(view.contentDOM.ownerDocument); + } + handleEvent(event) { + if (!eventBelongsToEditor(this.view, event) || this.ignoreDuringComposition(event)) + return; + if (event.type == "keydown" && this.keydown(event)) + return; + if (this.view.updateState != 0 /* UpdateState.Idle */) + Promise.resolve().then(() => this.runHandlers(event.type, event)); + else + this.runHandlers(event.type, event); + } + runHandlers(type, event) { + let handlers = this.handlers[type]; + if (handlers) { + for (let observer of handlers.observers) + observer(this.view, event); + for (let handler of handlers.handlers) { + if (event.defaultPrevented) + break; + if (handler(this.view, event)) { + event.preventDefault(); + break; + } + } + } + } + ensureHandlers(plugins) { + let handlers = computeHandlers(plugins), prev = this.handlers, dom = this.view.contentDOM; + for (let type in handlers) + if (type != "scroll") { + let passive = !handlers[type].handlers.length; + let exists = prev[type]; + if (exists && passive != !exists.handlers.length) { + dom.removeEventListener(type, this.handleEvent); + exists = null; + } + if (!exists) + dom.addEventListener(type, this.handleEvent, { passive }); + } + for (let type in prev) + if (type != "scroll" && !handlers[type]) + dom.removeEventListener(type, this.handleEvent); + this.handlers = handlers; + } + keydown(event) { + // Must always run, even if a custom handler handled the event + this.lastKeyCode = event.keyCode; + this.lastKeyTime = Date.now(); + if (event.keyCode == 9 && this.tabFocusMode > -1 && (!this.tabFocusMode || Date.now() <= this.tabFocusMode)) + return true; + if (this.tabFocusMode > 0 && event.keyCode != 27 && modifierCodes.indexOf(event.keyCode) < 0) + this.tabFocusMode = -1; + // Chrome for Android usually doesn't fire proper key events, but + // occasionally does, usually surrounded by a bunch of complicated + // composition changes. When an enter or backspace key event is + // seen, hold off on handling DOM events for a bit, and then + // dispatch it. + if (browser.android && browser.chrome && !event.synthetic && + (event.keyCode == 13 || event.keyCode == 8)) { + this.view.observer.delayAndroidKey(event.key, event.keyCode); + return true; + } + // Preventing the default behavior of Enter on iOS makes the + // virtual keyboard get stuck in the wrong (lowercase) + // state. So we let it go through, and then, in + // applyDOMChange, notify key handlers of it and reset to + // the state they produce. + let pending; + if (browser.ios && !event.synthetic && !event.altKey && !event.metaKey && + ((pending = PendingKeys.find(key => key.keyCode == event.keyCode)) && !event.ctrlKey || + EmacsyPendingKeys.indexOf(event.key) > -1 && event.ctrlKey && !event.shiftKey)) { + this.pendingIOSKey = pending || event; + setTimeout(() => this.flushIOSKey(), 250); + return true; + } + if (event.keyCode != 229) + this.view.observer.forceFlush(); + return false; + } + flushIOSKey(change) { + let key = this.pendingIOSKey; + if (!key) + return false; + // This looks like an autocorrection before Enter + if (key.key == "Enter" && change && change.from < change.to && /^\S+$/.test(change.insert.toString())) + return false; + this.pendingIOSKey = undefined; + return dispatchKey(this.view.contentDOM, key.key, key.keyCode, key instanceof KeyboardEvent ? key : undefined); + } + ignoreDuringComposition(event) { + if (!/^key/.test(event.type) || event.synthetic) + return false; + if (this.composing > 0) + return true; + // See https://www.stum.de/2016/06/24/handling-ime-events-in-javascript/. + // On some input method editors (IMEs), the Enter key is used to + // confirm character selection. On Safari, when Enter is pressed, + // compositionend and keydown events are sometimes emitted in the + // wrong order. The key event should still be ignored, even when + // it happens after the compositionend event. + if (browser.safari && !browser.ios && this.compositionPendingKey && Date.now() - this.compositionEndedAt < 100) { + this.compositionPendingKey = false; + return true; + } + return false; + } + startMouseSelection(mouseSelection) { + if (this.mouseSelection) + this.mouseSelection.destroy(); + this.mouseSelection = mouseSelection; + } + update(update) { + this.view.observer.update(update); + if (this.mouseSelection) + this.mouseSelection.update(update); + if (this.draggedContent && update.docChanged) + this.draggedContent = this.draggedContent.map(update.changes); + if (update.transactions.length) + this.lastKeyCode = this.lastSelectionTime = 0; + } + destroy() { + if (this.mouseSelection) + this.mouseSelection.destroy(); + } +} +function bindHandler(plugin, handler) { + return (view, event) => { + try { + return handler.call(plugin, event, view); + } + catch (e) { + logException(view.state, e); + } + }; +} +function computeHandlers(plugins) { + let result = Object.create(null); + function record(type) { + return result[type] || (result[type] = { observers: [], handlers: [] }); + } + for (let plugin of plugins) { + let spec = plugin.spec, handlers = spec && spec.plugin.domEventHandlers, observers = spec && spec.plugin.domEventObservers; + if (handlers) + for (let type in handlers) { + let f = handlers[type]; + if (f) + record(type).handlers.push(bindHandler(plugin.value, f)); + } + if (observers) + for (let type in observers) { + let f = observers[type]; + if (f) + record(type).observers.push(bindHandler(plugin.value, f)); + } + } + for (let type in handlers) + record(type).handlers.push(handlers[type]); + for (let type in observers) + record(type).observers.push(observers[type]); + return result; +} +const PendingKeys = [ + { key: "Backspace", keyCode: 8, inputType: "deleteContentBackward" }, + { key: "Enter", keyCode: 13, inputType: "insertParagraph" }, + { key: "Enter", keyCode: 13, inputType: "insertLineBreak" }, + { key: "Delete", keyCode: 46, inputType: "deleteContentForward" } +]; +const EmacsyPendingKeys = "dthko"; +// Key codes for modifier keys +const modifierCodes = [16, 17, 18, 20, 91, 92, 224, 225]; +const dragScrollMargin = 6; +function dragScrollSpeed(dist) { + return Math.max(0, dist) * 0.7 + 8; +} +function dist(a, b) { + return Math.max(Math.abs(a.clientX - b.clientX), Math.abs(a.clientY - b.clientY)); +} +class MouseSelection { + constructor(view, startEvent, style, mustSelect) { + this.view = view; + this.startEvent = startEvent; + this.style = style; + this.mustSelect = mustSelect; + this.scrollSpeed = { x: 0, y: 0 }; + this.scrolling = -1; + this.lastEvent = startEvent; + this.scrollParents = scrollableParents(view.contentDOM); + this.atoms = view.state.facet(atomicRanges).map(f => f(view)); + let doc = view.contentDOM.ownerDocument; + doc.addEventListener("mousemove", this.move = this.move.bind(this)); + doc.addEventListener("mouseup", this.up = this.up.bind(this)); + this.extend = startEvent.shiftKey; + this.multiple = view.state.facet(state.EditorState.allowMultipleSelections) && addsSelectionRange(view, startEvent); + this.dragging = isInPrimarySelection(view, startEvent) && getClickType(startEvent) == 1 ? null : false; + } + start(event) { + // When clicking outside of the selection, immediately apply the + // effect of starting the selection + if (this.dragging === false) + this.select(event); + } + move(event) { + if (event.buttons == 0) + return this.destroy(); + if (this.dragging || this.dragging == null && dist(this.startEvent, event) < 10) + return; + this.select(this.lastEvent = event); + let sx = 0, sy = 0; + let left = 0, top = 0, right = this.view.win.innerWidth, bottom = this.view.win.innerHeight; + if (this.scrollParents.x) + ({ left, right } = this.scrollParents.x.getBoundingClientRect()); + if (this.scrollParents.y) + ({ top, bottom } = this.scrollParents.y.getBoundingClientRect()); + let margins = getScrollMargins(this.view); + if (event.clientX - margins.left <= left + dragScrollMargin) + sx = -dragScrollSpeed(left - event.clientX); + else if (event.clientX + margins.right >= right - dragScrollMargin) + sx = dragScrollSpeed(event.clientX - right); + if (event.clientY - margins.top <= top + dragScrollMargin) + sy = -dragScrollSpeed(top - event.clientY); + else if (event.clientY + margins.bottom >= bottom - dragScrollMargin) + sy = dragScrollSpeed(event.clientY - bottom); + this.setScrollSpeed(sx, sy); + } + up(event) { + if (this.dragging == null) + this.select(this.lastEvent); + if (!this.dragging) + event.preventDefault(); + this.destroy(); + } + destroy() { + this.setScrollSpeed(0, 0); + let doc = this.view.contentDOM.ownerDocument; + doc.removeEventListener("mousemove", this.move); + doc.removeEventListener("mouseup", this.up); + this.view.inputState.mouseSelection = this.view.inputState.draggedContent = null; + } + setScrollSpeed(sx, sy) { + this.scrollSpeed = { x: sx, y: sy }; + if (sx || sy) { + if (this.scrolling < 0) + this.scrolling = setInterval(() => this.scroll(), 50); + } + else if (this.scrolling > -1) { + clearInterval(this.scrolling); + this.scrolling = -1; + } + } + scroll() { + let { x, y } = this.scrollSpeed; + if (x && this.scrollParents.x) { + this.scrollParents.x.scrollLeft += x; + x = 0; + } + if (y && this.scrollParents.y) { + this.scrollParents.y.scrollTop += y; + y = 0; + } + if (x || y) + this.view.win.scrollBy(x, y); + if (this.dragging === false) + this.select(this.lastEvent); + } + select(event) { + let { view } = this, selection = skipAtomsForSelection(this.atoms, this.style.get(event, this.extend, this.multiple)); + if (this.mustSelect || !selection.eq(view.state.selection, this.dragging === false)) + this.view.dispatch({ + selection, + userEvent: "select.pointer" + }); + this.mustSelect = false; + } + update(update) { + if (update.transactions.some(tr => tr.isUserEvent("input.type"))) + this.destroy(); + else if (this.style.update(update)) + setTimeout(() => this.select(this.lastEvent), 20); + } +} +function addsSelectionRange(view, event) { + let facet = view.state.facet(clickAddsSelectionRange); + return facet.length ? facet[0](event) : browser.mac ? event.metaKey : event.ctrlKey; +} +function dragMovesSelection(view, event) { + let facet = view.state.facet(dragMovesSelection$1); + return facet.length ? facet[0](event) : browser.mac ? !event.altKey : !event.ctrlKey; +} +function isInPrimarySelection(view, event) { + let { main } = view.state.selection; + if (main.empty) + return false; + // On boundary clicks, check whether the coordinates are inside the + // selection's client rectangles + let sel = getSelection(view.root); + if (!sel || sel.rangeCount == 0) + return true; + let rects = sel.getRangeAt(0).getClientRects(); + for (let i = 0; i < rects.length; i++) { + let rect = rects[i]; + if (rect.left <= event.clientX && rect.right >= event.clientX && + rect.top <= event.clientY && rect.bottom >= event.clientY) + return true; + } + return false; +} +function eventBelongsToEditor(view, event) { + if (!event.bubbles) + return true; + if (event.defaultPrevented) + return false; + for (let node = event.target, cView; node != view.contentDOM; node = node.parentNode) + if (!node || node.nodeType == 11 || ((cView = ContentView.get(node)) && cView.ignoreEvent(event))) + return false; + return true; +} +const handlers = Object.create(null); +const observers = Object.create(null); +// This is very crude, but unfortunately both these browsers _pretend_ +// that they have a clipboard API—all the objects and methods are +// there, they just don't work, and they are hard to test. +const brokenClipboardAPI = (browser.ie && browser.ie_version < 15) || + (browser.ios && browser.webkit_version < 604); +function capturePaste(view) { + let parent = view.dom.parentNode; + if (!parent) + return; + let target = parent.appendChild(document.createElement("textarea")); + target.style.cssText = "position: fixed; left: -10000px; top: 10px"; + target.focus(); + setTimeout(() => { + view.focus(); + target.remove(); + doPaste(view, target.value); + }, 50); +} +function textFilter(state, facet, text) { + for (let filter of state.facet(facet)) + text = filter(text, state); + return text; +} +function doPaste(view, input) { + input = textFilter(view.state, clipboardInputFilter, input); + let { state: state$1 } = view, changes, i = 1, text = state$1.toText(input); + let byLine = text.lines == state$1.selection.ranges.length; + let linewise = lastLinewiseCopy != null && state$1.selection.ranges.every(r => r.empty) && lastLinewiseCopy == text.toString(); + if (linewise) { + let lastLine = -1; + changes = state$1.changeByRange(range => { + let line = state$1.doc.lineAt(range.from); + if (line.from == lastLine) + return { range }; + lastLine = line.from; + let insert = state$1.toText((byLine ? text.line(i++).text : input) + state$1.lineBreak); + return { changes: { from: line.from, insert }, + range: state.EditorSelection.cursor(range.from + insert.length) }; + }); + } + else if (byLine) { + changes = state$1.changeByRange(range => { + let line = text.line(i++); + return { changes: { from: range.from, to: range.to, insert: line.text }, + range: state.EditorSelection.cursor(range.from + line.length) }; + }); + } + else { + changes = state$1.replaceSelection(text); + } + view.dispatch(changes, { + userEvent: "input.paste", + scrollIntoView: true + }); +} +observers.scroll = view => { + view.inputState.lastScrollTop = view.scrollDOM.scrollTop; + view.inputState.lastScrollLeft = view.scrollDOM.scrollLeft; +}; +handlers.keydown = (view, event) => { + view.inputState.setSelectionOrigin("select"); + if (event.keyCode == 27 && view.inputState.tabFocusMode != 0) + view.inputState.tabFocusMode = Date.now() + 2000; + return false; +}; +observers.touchstart = (view, e) => { + view.inputState.lastTouchTime = Date.now(); + view.inputState.setSelectionOrigin("select.pointer"); +}; +observers.touchmove = view => { + view.inputState.setSelectionOrigin("select.pointer"); +}; +handlers.mousedown = (view, event) => { + view.observer.flush(); + if (view.inputState.lastTouchTime > Date.now() - 2000) + return false; // Ignore touch interaction + let style = null; + for (let makeStyle of view.state.facet(mouseSelectionStyle)) { + style = makeStyle(view, event); + if (style) + break; + } + if (!style && event.button == 0) + style = basicMouseSelection(view, event); + if (style) { + let mustFocus = !view.hasFocus; + view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus)); + if (mustFocus) + view.observer.ignore(() => { + focusPreventScroll(view.contentDOM); + let active = view.root.activeElement; + if (active && !active.contains(view.contentDOM)) + active.blur(); + }); + let mouseSel = view.inputState.mouseSelection; + if (mouseSel) { + mouseSel.start(event); + return mouseSel.dragging === false; + } + } + else { + view.inputState.setSelectionOrigin("select.pointer"); + } + return false; +}; +function rangeForClick(view, pos, bias, type) { + if (type == 1) { // Single click + return state.EditorSelection.cursor(pos, bias); + } + else if (type == 2) { // Double click + return groupAt(view.state, pos, bias); + } + else { // Triple click + let visual = LineView.find(view.docView, pos), line = view.state.doc.lineAt(visual ? visual.posAtEnd : pos); + let from = visual ? visual.posAtStart : line.from, to = visual ? visual.posAtEnd : line.to; + if (to < view.state.doc.length && to == line.to) + to++; + return state.EditorSelection.range(from, to); + } +} +let inside = (x, y, rect) => y >= rect.top && y <= rect.bottom && x >= rect.left && x <= rect.right; +// Try to determine, for the given coordinates, associated with the +// given position, whether they are related to the element before or +// the element after the position. +function findPositionSide(view, pos, x, y) { + let line = LineView.find(view.docView, pos); + if (!line) + return 1; + let off = pos - line.posAtStart; + // Line boundaries point into the line + if (off == 0) + return 1; + if (off == line.length) + return -1; + // Positions on top of an element point at that element + let before = line.coordsAt(off, -1); + if (before && inside(x, y, before)) + return -1; + let after = line.coordsAt(off, 1); + if (after && inside(x, y, after)) + return 1; + // This is probably a line wrap point. Pick before if the point is + // above its bottom. + return before && before.bottom >= y ? -1 : 1; +} +function queryPos(view, event) { + let pos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false); + return { pos, bias: findPositionSide(view, pos, event.clientX, event.clientY) }; +} +const BadMouseDetail = browser.ie && browser.ie_version <= 11; +let lastMouseDown = null, lastMouseDownCount = 0, lastMouseDownTime = 0; +function getClickType(event) { + if (!BadMouseDetail) + return event.detail; + let last = lastMouseDown, lastTime = lastMouseDownTime; + lastMouseDown = event; + lastMouseDownTime = Date.now(); + return lastMouseDownCount = !last || (lastTime > Date.now() - 400 && Math.abs(last.clientX - event.clientX) < 2 && + Math.abs(last.clientY - event.clientY) < 2) ? (lastMouseDownCount + 1) % 3 : 1; +} +function basicMouseSelection(view, event) { + let start = queryPos(view, event), type = getClickType(event); + let startSel = view.state.selection; + return { + update(update) { + if (update.docChanged) { + start.pos = update.changes.mapPos(start.pos); + startSel = startSel.map(update.changes); + } + }, + get(event, extend, multiple) { + let cur = queryPos(view, event), removed; + let range = rangeForClick(view, cur.pos, cur.bias, type); + if (start.pos != cur.pos && !extend) { + let startRange = rangeForClick(view, start.pos, start.bias, type); + let from = Math.min(startRange.from, range.from), to = Math.max(startRange.to, range.to); + range = from < range.from ? state.EditorSelection.range(from, to) : state.EditorSelection.range(to, from); + } + if (extend) + return startSel.replaceRange(startSel.main.extend(range.from, range.to)); + else if (multiple && type == 1 && startSel.ranges.length > 1 && (removed = removeRangeAround(startSel, cur.pos))) + return removed; + else if (multiple) + return startSel.addRange(range); + else + return state.EditorSelection.create([range]); + } + }; +} +function removeRangeAround(sel, pos) { + for (let i = 0; i < sel.ranges.length; i++) { + let { from, to } = sel.ranges[i]; + if (from <= pos && to >= pos) + return state.EditorSelection.create(sel.ranges.slice(0, i).concat(sel.ranges.slice(i + 1)), sel.mainIndex == i ? 0 : sel.mainIndex - (sel.mainIndex > i ? 1 : 0)); + } + return null; +} +handlers.dragstart = (view, event) => { + let { selection: { main: range } } = view.state; + if (event.target.draggable) { + let cView = view.docView.nearest(event.target); + if (cView && cView.isWidget) { + let from = cView.posAtStart, to = from + cView.length; + if (from >= range.to || to <= range.from) + range = state.EditorSelection.range(from, to); + } + } + let { inputState } = view; + if (inputState.mouseSelection) + inputState.mouseSelection.dragging = true; + inputState.draggedContent = range; + if (event.dataTransfer) { + event.dataTransfer.setData("Text", textFilter(view.state, clipboardOutputFilter, view.state.sliceDoc(range.from, range.to))); + event.dataTransfer.effectAllowed = "copyMove"; + } + return false; +}; +handlers.dragend = view => { + view.inputState.draggedContent = null; + return false; +}; +function dropText(view, event, text, direct) { + text = textFilter(view.state, clipboardInputFilter, text); + if (!text) + return; + let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false); + let { draggedContent } = view.inputState; + let del = direct && draggedContent && dragMovesSelection(view, event) + ? { from: draggedContent.from, to: draggedContent.to } : null; + let ins = { from: dropPos, insert: text }; + let changes = view.state.changes(del ? [del, ins] : ins); + view.focus(); + view.dispatch({ + changes, + selection: { anchor: changes.mapPos(dropPos, -1), head: changes.mapPos(dropPos, 1) }, + userEvent: del ? "move.drop" : "input.drop" + }); + view.inputState.draggedContent = null; +} +handlers.drop = (view, event) => { + if (!event.dataTransfer) + return false; + if (view.state.readOnly) + return true; + let files = event.dataTransfer.files; + if (files && files.length) { // For a file drop, read the file's text. + let text = Array(files.length), read = 0; + let finishFile = () => { + if (++read == files.length) + dropText(view, event, text.filter(s => s != null).join(view.state.lineBreak), false); + }; + for (let i = 0; i < files.length; i++) { + let reader = new FileReader; + reader.onerror = finishFile; + reader.onload = () => { + if (!/[\x00-\x08\x0e-\x1f]{2}/.test(reader.result)) + text[i] = reader.result; + finishFile(); + }; + reader.readAsText(files[i]); + } + return true; + } + else { + let text = event.dataTransfer.getData("Text"); + if (text) { + dropText(view, event, text, true); + return true; + } + } + return false; +}; +handlers.paste = (view, event) => { + if (view.state.readOnly) + return true; + view.observer.flush(); + let data = brokenClipboardAPI ? null : event.clipboardData; + if (data) { + doPaste(view, data.getData("text/plain") || data.getData("text/uri-list")); + return true; + } + else { + capturePaste(view); + return false; + } +}; +function captureCopy(view, text) { + // The extra wrapper is somehow necessary on IE/Edge to prevent the + // content from being mangled when it is put onto the clipboard + let parent = view.dom.parentNode; + if (!parent) + return; + let target = parent.appendChild(document.createElement("textarea")); + target.style.cssText = "position: fixed; left: -10000px; top: 10px"; + target.value = text; + target.focus(); + target.selectionEnd = text.length; + target.selectionStart = 0; + setTimeout(() => { + target.remove(); + view.focus(); + }, 50); +} +function copiedRange(state) { + let content = [], ranges = [], linewise = false; + for (let range of state.selection.ranges) + if (!range.empty) { + content.push(state.sliceDoc(range.from, range.to)); + ranges.push(range); + } + if (!content.length) { + // Nothing selected, do a line-wise copy + let upto = -1; + for (let { from } of state.selection.ranges) { + let line = state.doc.lineAt(from); + if (line.number > upto) { + content.push(line.text); + ranges.push({ from: line.from, to: Math.min(state.doc.length, line.to + 1) }); + } + upto = line.number; + } + linewise = true; + } + return { text: textFilter(state, clipboardOutputFilter, content.join(state.lineBreak)), ranges, linewise }; +} +let lastLinewiseCopy = null; +handlers.copy = handlers.cut = (view, event) => { + let { text, ranges, linewise } = copiedRange(view.state); + if (!text && !linewise) + return false; + lastLinewiseCopy = linewise ? text : null; + if (event.type == "cut" && !view.state.readOnly) + view.dispatch({ + changes: ranges, + scrollIntoView: true, + userEvent: "delete.cut" + }); + let data = brokenClipboardAPI ? null : event.clipboardData; + if (data) { + data.clearData(); + data.setData("text/plain", text); + return true; + } + else { + captureCopy(view, text); + return false; + } +}; +const isFocusChange = state.Annotation.define(); +function focusChangeTransaction(state, focus) { + let effects = []; + for (let getEffect of state.facet(focusChangeEffect)) { + let effect = getEffect(state, focus); + if (effect) + effects.push(effect); + } + return effects.length ? state.update({ effects, annotations: isFocusChange.of(true) }) : null; +} +function updateForFocusChange(view) { + setTimeout(() => { + let focus = view.hasFocus; + if (focus != view.inputState.notifiedFocused) { + let tr = focusChangeTransaction(view.state, focus); + if (tr) + view.dispatch(tr); + else + view.update([]); + } + }, 10); +} +observers.focus = view => { + view.inputState.lastFocusTime = Date.now(); + // When focusing reset the scroll position, move it back to where it was + if (!view.scrollDOM.scrollTop && (view.inputState.lastScrollTop || view.inputState.lastScrollLeft)) { + view.scrollDOM.scrollTop = view.inputState.lastScrollTop; + view.scrollDOM.scrollLeft = view.inputState.lastScrollLeft; + } + updateForFocusChange(view); +}; +observers.blur = view => { + view.observer.clearSelectionRange(); + updateForFocusChange(view); +}; +observers.compositionstart = observers.compositionupdate = view => { + if (view.observer.editContext) + return; // Composition handled by edit context + if (view.inputState.compositionFirstChange == null) + view.inputState.compositionFirstChange = true; + if (view.inputState.composing < 0) { + // FIXME possibly set a timeout to clear it again on Android + view.inputState.composing = 0; + } +}; +observers.compositionend = view => { + if (view.observer.editContext) + return; // Composition handled by edit context + view.inputState.composing = -1; + view.inputState.compositionEndedAt = Date.now(); + view.inputState.compositionPendingKey = true; + view.inputState.compositionPendingChange = view.observer.pendingRecords().length > 0; + view.inputState.compositionFirstChange = null; + if (browser.chrome && browser.android) { + // Delay flushing for a bit on Android because it'll often fire a + // bunch of contradictory changes in a row at end of compositon + view.observer.flushSoon(); + } + else if (view.inputState.compositionPendingChange) { + // If we found pending records, schedule a flush. + Promise.resolve().then(() => view.observer.flush()); + } + else { + // Otherwise, make sure that, if no changes come in soon, the + // composition view is cleared. + setTimeout(() => { + if (view.inputState.composing < 0 && view.docView.hasComposition) + view.update([]); + }, 50); + } +}; +observers.contextmenu = view => { + view.inputState.lastContextMenu = Date.now(); +}; +handlers.beforeinput = (view, event) => { + var _a, _b; + // In EditContext mode, we must handle insertReplacementText events + // directly, to make spell checking corrections work + if (event.inputType == "insertReplacementText" && view.observer.editContext) { + let text = (_a = event.dataTransfer) === null || _a === void 0 ? void 0 : _a.getData("text/plain"), ranges = event.getTargetRanges(); + if (text && ranges.length) { + let r = ranges[0]; + let from = view.posAtDOM(r.startContainer, r.startOffset), to = view.posAtDOM(r.endContainer, r.endOffset); + applyDOMChangeInner(view, { from, to, insert: view.state.toText(text) }, null); + return true; + } + } + // Because Chrome Android doesn't fire useful key events, use + // beforeinput to detect backspace (and possibly enter and delete, + // but those usually don't even seem to fire beforeinput events at + // the moment) and fake a key event for it. + // + // (preventDefault on beforeinput, though supported in the spec, + // seems to do nothing at all on Chrome). + let pending; + if (browser.chrome && browser.android && (pending = PendingKeys.find(key => key.inputType == event.inputType))) { + view.observer.delayAndroidKey(pending.key, pending.keyCode); + if (pending.key == "Backspace" || pending.key == "Delete") { + let startViewHeight = ((_b = window.visualViewport) === null || _b === void 0 ? void 0 : _b.height) || 0; + setTimeout(() => { + var _a; + // Backspacing near uneditable nodes on Chrome Android sometimes + // closes the virtual keyboard. This tries to crudely detect + // that and refocus to get it back. + if ((((_a = window.visualViewport) === null || _a === void 0 ? void 0 : _a.height) || 0) > startViewHeight + 10 && view.hasFocus) { + view.contentDOM.blur(); + view.focus(); + } + }, 100); + } + } + if (browser.ios && event.inputType == "deleteContentForward") { + // For some reason, DOM changes (and beforeinput) happen _before_ + // the key event for ctrl-d on iOS when using an external + // keyboard. + view.observer.flushSoon(); + } + // Safari will occasionally forget to fire compositionend at the end of a dead-key composition + if (browser.safari && event.inputType == "insertText" && view.inputState.composing >= 0) { + setTimeout(() => observers.compositionend(view, event), 20); + } + return false; +}; +const appliedFirefoxHack = new Set; +// In Firefox, when cut/copy handlers are added to the document, that +// somehow avoids a bug where those events aren't fired when the +// selection is empty. See https://github.com/codemirror/dev/issues/1082 +// and https://bugzilla.mozilla.org/show_bug.cgi?id=995961 +function firefoxCopyCutHack(doc) { + if (!appliedFirefoxHack.has(doc)) { + appliedFirefoxHack.add(doc); + doc.addEventListener("copy", () => { }); + doc.addEventListener("cut", () => { }); + } +} + +const wrappingWhiteSpace = ["pre-wrap", "normal", "pre-line", "break-spaces"]; +// Used to track, during updateHeight, if any actual heights changed +let heightChangeFlag = false; +function clearHeightChangeFlag() { heightChangeFlag = false; } +class HeightOracle { + constructor(lineWrapping) { + this.lineWrapping = lineWrapping; + this.doc = state.Text.empty; + this.heightSamples = {}; + this.lineHeight = 14; // The height of an entire line (line-height) + this.charWidth = 7; + this.textHeight = 14; // The height of the actual font (font-size) + this.lineLength = 30; + } + heightForGap(from, to) { + let lines = this.doc.lineAt(to).number - this.doc.lineAt(from).number + 1; + if (this.lineWrapping) + lines += Math.max(0, Math.ceil(((to - from) - (lines * this.lineLength * 0.5)) / this.lineLength)); + return this.lineHeight * lines; + } + heightForLine(length) { + if (!this.lineWrapping) + return this.lineHeight; + let lines = 1 + Math.max(0, Math.ceil((length - this.lineLength) / Math.max(1, this.lineLength - 5))); + return lines * this.lineHeight; + } + setDoc(doc) { this.doc = doc; return this; } + mustRefreshForWrapping(whiteSpace) { + return (wrappingWhiteSpace.indexOf(whiteSpace) > -1) != this.lineWrapping; + } + mustRefreshForHeights(lineHeights) { + let newHeight = false; + for (let i = 0; i < lineHeights.length; i++) { + let h = lineHeights[i]; + if (h < 0) { + i++; + } + else if (!this.heightSamples[Math.floor(h * 10)]) { // Round to .1 pixels + newHeight = true; + this.heightSamples[Math.floor(h * 10)] = true; + } + } + return newHeight; + } + refresh(whiteSpace, lineHeight, charWidth, textHeight, lineLength, knownHeights) { + let lineWrapping = wrappingWhiteSpace.indexOf(whiteSpace) > -1; + let changed = Math.round(lineHeight) != Math.round(this.lineHeight) || this.lineWrapping != lineWrapping; + this.lineWrapping = lineWrapping; + this.lineHeight = lineHeight; + this.charWidth = charWidth; + this.textHeight = textHeight; + this.lineLength = lineLength; + if (changed) { + this.heightSamples = {}; + for (let i = 0; i < knownHeights.length; i++) { + let h = knownHeights[i]; + if (h < 0) + i++; + else + this.heightSamples[Math.floor(h * 10)] = true; + } + } + return changed; + } +} +// This object is used by `updateHeight` to make DOM measurements +// arrive at the right nides. The `heights` array is a sequence of +// block heights, starting from position `from`. +class MeasuredHeights { + constructor(from, heights) { + this.from = from; + this.heights = heights; + this.index = 0; + } + get more() { return this.index < this.heights.length; } +} +/** +Record used to represent information about a block-level element +in the editor view. +*/ +class BlockInfo { + /** + @internal + */ + constructor( + /** + The start of the element in the document. + */ + from, + /** + The length of the element. + */ + length, + /** + The top position of the element (relative to the top of the + document). + */ + top, + /** + Its height. + */ + height, + /** + @internal Weird packed field that holds an array of children + for composite blocks, a decoration for block widgets, and a + number indicating the amount of widget-create line breaks for + text blocks. + */ + _content) { + this.from = from; + this.length = length; + this.top = top; + this.height = height; + this._content = _content; + } + /** + The type of element this is. When querying lines, this may be + an array of all the blocks that make up the line. + */ + get type() { + return typeof this._content == "number" ? exports.BlockType.Text : + Array.isArray(this._content) ? this._content : this._content.type; + } + /** + The end of the element as a document position. + */ + get to() { return this.from + this.length; } + /** + The bottom position of the element. + */ + get bottom() { return this.top + this.height; } + /** + If this is a widget block, this will return the widget + associated with it. + */ + get widget() { + return this._content instanceof PointDecoration ? this._content.widget : null; + } + /** + If this is a textblock, this holds the number of line breaks + that appear in widgets inside the block. + */ + get widgetLineBreaks() { + return typeof this._content == "number" ? this._content : 0; + } + /** + @internal + */ + join(other) { + let content = (Array.isArray(this._content) ? this._content : [this]) + .concat(Array.isArray(other._content) ? other._content : [other]); + return new BlockInfo(this.from, this.length + other.length, this.top, this.height + other.height, content); + } +} +var QueryType; +(function (QueryType) { + QueryType[QueryType["ByPos"] = 0] = "ByPos"; + QueryType[QueryType["ByHeight"] = 1] = "ByHeight"; + QueryType[QueryType["ByPosNoHeight"] = 2] = "ByPosNoHeight"; +})(QueryType || (QueryType = {})); +const Epsilon = 1e-3; +class HeightMap { + constructor(length, // The number of characters covered + height, // Height of this part of the document + flags = 2 /* Flag.Outdated */) { + this.length = length; + this.height = height; + this.flags = flags; + } + get outdated() { return (this.flags & 2 /* Flag.Outdated */) > 0; } + set outdated(value) { this.flags = (value ? 2 /* Flag.Outdated */ : 0) | (this.flags & ~2 /* Flag.Outdated */); } + setHeight(height) { + if (this.height != height) { + if (Math.abs(this.height - height) > Epsilon) + heightChangeFlag = true; + this.height = height; + } + } + // Base case is to replace a leaf node, which simply builds a tree + // from the new nodes and returns that (HeightMapBranch and + // HeightMapGap override this to actually use from/to) + replace(_from, _to, nodes) { + return HeightMap.of(nodes); + } + // Again, these are base cases, and are overridden for branch and gap nodes. + decomposeLeft(_to, result) { result.push(this); } + decomposeRight(_from, result) { result.push(this); } + applyChanges(decorations, oldDoc, oracle, changes) { + let me = this, doc = oracle.doc; + for (let i = changes.length - 1; i >= 0; i--) { + let { fromA, toA, fromB, toB } = changes[i]; + let start = me.lineAt(fromA, QueryType.ByPosNoHeight, oracle.setDoc(oldDoc), 0, 0); + let end = start.to >= toA ? start : me.lineAt(toA, QueryType.ByPosNoHeight, oracle, 0, 0); + toB += end.to - toA; + toA = end.to; + while (i > 0 && start.from <= changes[i - 1].toA) { + fromA = changes[i - 1].fromA; + fromB = changes[i - 1].fromB; + i--; + if (fromA < start.from) + start = me.lineAt(fromA, QueryType.ByPosNoHeight, oracle, 0, 0); + } + fromB += start.from - fromA; + fromA = start.from; + let nodes = NodeBuilder.build(oracle.setDoc(doc), decorations, fromB, toB); + me = replace(me, me.replace(fromA, toA, nodes)); + } + return me.updateHeight(oracle, 0); + } + static empty() { return new HeightMapText(0, 0); } + // nodes uses null values to indicate the position of line breaks. + // There are never line breaks at the start or end of the array, or + // two line breaks next to each other, and the array isn't allowed + // to be empty (same restrictions as return value from the builder). + static of(nodes) { + if (nodes.length == 1) + return nodes[0]; + let i = 0, j = nodes.length, before = 0, after = 0; + for (;;) { + if (i == j) { + if (before > after * 2) { + let split = nodes[i - 1]; + if (split.break) + nodes.splice(--i, 1, split.left, null, split.right); + else + nodes.splice(--i, 1, split.left, split.right); + j += 1 + split.break; + before -= split.size; + } + else if (after > before * 2) { + let split = nodes[j]; + if (split.break) + nodes.splice(j, 1, split.left, null, split.right); + else + nodes.splice(j, 1, split.left, split.right); + j += 2 + split.break; + after -= split.size; + } + else { + break; + } + } + else if (before < after) { + let next = nodes[i++]; + if (next) + before += next.size; + } + else { + let next = nodes[--j]; + if (next) + after += next.size; + } + } + let brk = 0; + if (nodes[i - 1] == null) { + brk = 1; + i--; + } + else if (nodes[i] == null) { + brk = 1; + j++; + } + return new HeightMapBranch(HeightMap.of(nodes.slice(0, i)), brk, HeightMap.of(nodes.slice(j))); + } +} +function replace(old, val) { + if (old == val) + return old; + if (old.constructor != val.constructor) + heightChangeFlag = true; + return val; +} +HeightMap.prototype.size = 1; +class HeightMapBlock extends HeightMap { + constructor(length, height, deco) { + super(length, height); + this.deco = deco; + } + blockAt(_height, _oracle, top, offset) { + return new BlockInfo(offset, this.length, top, this.height, this.deco || 0); + } + lineAt(_value, _type, oracle, top, offset) { + return this.blockAt(0, oracle, top, offset); + } + forEachLine(from, to, oracle, top, offset, f) { + if (from <= offset + this.length && to >= offset) + f(this.blockAt(0, oracle, top, offset)); + } + updateHeight(oracle, offset = 0, _force = false, measured) { + if (measured && measured.from <= offset && measured.more) + this.setHeight(measured.heights[measured.index++]); + this.outdated = false; + return this; + } + toString() { return `block(${this.length})`; } +} +class HeightMapText extends HeightMapBlock { + constructor(length, height) { + super(length, height, null); + this.collapsed = 0; // Amount of collapsed content in the line + this.widgetHeight = 0; // Maximum inline widget height + this.breaks = 0; // Number of widget-introduced line breaks on the line + } + blockAt(_height, _oracle, top, offset) { + return new BlockInfo(offset, this.length, top, this.height, this.breaks); + } + replace(_from, _to, nodes) { + let node = nodes[0]; + if (nodes.length == 1 && (node instanceof HeightMapText || node instanceof HeightMapGap && (node.flags & 4 /* Flag.SingleLine */)) && + Math.abs(this.length - node.length) < 10) { + if (node instanceof HeightMapGap) + node = new HeightMapText(node.length, this.height); + else + node.height = this.height; + if (!this.outdated) + node.outdated = false; + return node; + } + else { + return HeightMap.of(nodes); + } + } + updateHeight(oracle, offset = 0, force = false, measured) { + if (measured && measured.from <= offset && measured.more) + this.setHeight(measured.heights[measured.index++]); + else if (force || this.outdated) + this.setHeight(Math.max(this.widgetHeight, oracle.heightForLine(this.length - this.collapsed)) + + this.breaks * oracle.lineHeight); + this.outdated = false; + return this; + } + toString() { + return `line(${this.length}${this.collapsed ? -this.collapsed : ""}${this.widgetHeight ? ":" + this.widgetHeight : ""})`; + } +} +class HeightMapGap extends HeightMap { + constructor(length) { super(length, 0); } + heightMetrics(oracle, offset) { + let firstLine = oracle.doc.lineAt(offset).number, lastLine = oracle.doc.lineAt(offset + this.length).number; + let lines = lastLine - firstLine + 1; + let perLine, perChar = 0; + if (oracle.lineWrapping) { + let totalPerLine = Math.min(this.height, oracle.lineHeight * lines); + perLine = totalPerLine / lines; + if (this.length > lines + 1) + perChar = (this.height - totalPerLine) / (this.length - lines - 1); + } + else { + perLine = this.height / lines; + } + return { firstLine, lastLine, perLine, perChar }; + } + blockAt(height, oracle, top, offset) { + let { firstLine, lastLine, perLine, perChar } = this.heightMetrics(oracle, offset); + if (oracle.lineWrapping) { + let guess = offset + (height < oracle.lineHeight ? 0 + : Math.round(Math.max(0, Math.min(1, (height - top) / this.height)) * this.length)); + let line = oracle.doc.lineAt(guess), lineHeight = perLine + line.length * perChar; + let lineTop = Math.max(top, height - lineHeight / 2); + return new BlockInfo(line.from, line.length, lineTop, lineHeight, 0); + } + else { + let line = Math.max(0, Math.min(lastLine - firstLine, Math.floor((height - top) / perLine))); + let { from, length } = oracle.doc.line(firstLine + line); + return new BlockInfo(from, length, top + perLine * line, perLine, 0); + } + } + lineAt(value, type, oracle, top, offset) { + if (type == QueryType.ByHeight) + return this.blockAt(value, oracle, top, offset); + if (type == QueryType.ByPosNoHeight) { + let { from, to } = oracle.doc.lineAt(value); + return new BlockInfo(from, to - from, 0, 0, 0); + } + let { firstLine, perLine, perChar } = this.heightMetrics(oracle, offset); + let line = oracle.doc.lineAt(value), lineHeight = perLine + line.length * perChar; + let linesAbove = line.number - firstLine; + let lineTop = top + perLine * linesAbove + perChar * (line.from - offset - linesAbove); + return new BlockInfo(line.from, line.length, Math.max(top, Math.min(lineTop, top + this.height - lineHeight)), lineHeight, 0); + } + forEachLine(from, to, oracle, top, offset, f) { + from = Math.max(from, offset); + to = Math.min(to, offset + this.length); + let { firstLine, perLine, perChar } = this.heightMetrics(oracle, offset); + for (let pos = from, lineTop = top; pos <= to;) { + let line = oracle.doc.lineAt(pos); + if (pos == from) { + let linesAbove = line.number - firstLine; + lineTop += perLine * linesAbove + perChar * (from - offset - linesAbove); + } + let lineHeight = perLine + perChar * line.length; + f(new BlockInfo(line.from, line.length, lineTop, lineHeight, 0)); + lineTop += lineHeight; + pos = line.to + 1; + } + } + replace(from, to, nodes) { + let after = this.length - to; + if (after > 0) { + let last = nodes[nodes.length - 1]; + if (last instanceof HeightMapGap) + nodes[nodes.length - 1] = new HeightMapGap(last.length + after); + else + nodes.push(null, new HeightMapGap(after - 1)); + } + if (from > 0) { + let first = nodes[0]; + if (first instanceof HeightMapGap) + nodes[0] = new HeightMapGap(from + first.length); + else + nodes.unshift(new HeightMapGap(from - 1), null); + } + return HeightMap.of(nodes); + } + decomposeLeft(to, result) { + result.push(new HeightMapGap(to - 1), null); + } + decomposeRight(from, result) { + result.push(null, new HeightMapGap(this.length - from - 1)); + } + updateHeight(oracle, offset = 0, force = false, measured) { + let end = offset + this.length; + if (measured && measured.from <= offset + this.length && measured.more) { + // Fill in part of this gap with measured lines. We know there + // can't be widgets or collapsed ranges in those lines, because + // they would already have been added to the heightmap (gaps + // only contain plain text). + let nodes = [], pos = Math.max(offset, measured.from), singleHeight = -1; + if (measured.from > offset) + nodes.push(new HeightMapGap(measured.from - offset - 1).updateHeight(oracle, offset)); + while (pos <= end && measured.more) { + let len = oracle.doc.lineAt(pos).length; + if (nodes.length) + nodes.push(null); + let height = measured.heights[measured.index++]; + if (singleHeight == -1) + singleHeight = height; + else if (Math.abs(height - singleHeight) >= Epsilon) + singleHeight = -2; + let line = new HeightMapText(len, height); + line.outdated = false; + nodes.push(line); + pos += len + 1; + } + if (pos <= end) + nodes.push(null, new HeightMapGap(end - pos).updateHeight(oracle, pos)); + let result = HeightMap.of(nodes); + if (singleHeight < 0 || Math.abs(result.height - this.height) >= Epsilon || + Math.abs(singleHeight - this.heightMetrics(oracle, offset).perLine) >= Epsilon) + heightChangeFlag = true; + return replace(this, result); + } + else if (force || this.outdated) { + this.setHeight(oracle.heightForGap(offset, offset + this.length)); + this.outdated = false; + } + return this; + } + toString() { return `gap(${this.length})`; } +} +class HeightMapBranch extends HeightMap { + constructor(left, brk, right) { + super(left.length + brk + right.length, left.height + right.height, brk | (left.outdated || right.outdated ? 2 /* Flag.Outdated */ : 0)); + this.left = left; + this.right = right; + this.size = left.size + right.size; + } + get break() { return this.flags & 1 /* Flag.Break */; } + blockAt(height, oracle, top, offset) { + let mid = top + this.left.height; + return height < mid ? this.left.blockAt(height, oracle, top, offset) + : this.right.blockAt(height, oracle, mid, offset + this.left.length + this.break); + } + lineAt(value, type, oracle, top, offset) { + let rightTop = top + this.left.height, rightOffset = offset + this.left.length + this.break; + let left = type == QueryType.ByHeight ? value < rightTop : value < rightOffset; + let base = left ? this.left.lineAt(value, type, oracle, top, offset) + : this.right.lineAt(value, type, oracle, rightTop, rightOffset); + if (this.break || (left ? base.to < rightOffset : base.from > rightOffset)) + return base; + let subQuery = type == QueryType.ByPosNoHeight ? QueryType.ByPosNoHeight : QueryType.ByPos; + if (left) + return base.join(this.right.lineAt(rightOffset, subQuery, oracle, rightTop, rightOffset)); + else + return this.left.lineAt(rightOffset, subQuery, oracle, top, offset).join(base); + } + forEachLine(from, to, oracle, top, offset, f) { + let rightTop = top + this.left.height, rightOffset = offset + this.left.length + this.break; + if (this.break) { + if (from < rightOffset) + this.left.forEachLine(from, to, oracle, top, offset, f); + if (to >= rightOffset) + this.right.forEachLine(from, to, oracle, rightTop, rightOffset, f); + } + else { + let mid = this.lineAt(rightOffset, QueryType.ByPos, oracle, top, offset); + if (from < mid.from) + this.left.forEachLine(from, mid.from - 1, oracle, top, offset, f); + if (mid.to >= from && mid.from <= to) + f(mid); + if (to > mid.to) + this.right.forEachLine(mid.to + 1, to, oracle, rightTop, rightOffset, f); + } + } + replace(from, to, nodes) { + let rightStart = this.left.length + this.break; + if (to < rightStart) + return this.balanced(this.left.replace(from, to, nodes), this.right); + if (from > this.left.length) + return this.balanced(this.left, this.right.replace(from - rightStart, to - rightStart, nodes)); + let result = []; + if (from > 0) + this.decomposeLeft(from, result); + let left = result.length; + for (let node of nodes) + result.push(node); + if (from > 0) + mergeGaps(result, left - 1); + if (to < this.length) { + let right = result.length; + this.decomposeRight(to, result); + mergeGaps(result, right); + } + return HeightMap.of(result); + } + decomposeLeft(to, result) { + let left = this.left.length; + if (to <= left) + return this.left.decomposeLeft(to, result); + result.push(this.left); + if (this.break) { + left++; + if (to >= left) + result.push(null); + } + if (to > left) + this.right.decomposeLeft(to - left, result); + } + decomposeRight(from, result) { + let left = this.left.length, right = left + this.break; + if (from >= right) + return this.right.decomposeRight(from - right, result); + if (from < left) + this.left.decomposeRight(from, result); + if (this.break && from < right) + result.push(null); + result.push(this.right); + } + balanced(left, right) { + if (left.size > 2 * right.size || right.size > 2 * left.size) + return HeightMap.of(this.break ? [left, null, right] : [left, right]); + this.left = replace(this.left, left); + this.right = replace(this.right, right); + this.setHeight(left.height + right.height); + this.outdated = left.outdated || right.outdated; + this.size = left.size + right.size; + this.length = left.length + this.break + right.length; + return this; + } + updateHeight(oracle, offset = 0, force = false, measured) { + let { left, right } = this, rightStart = offset + left.length + this.break, rebalance = null; + if (measured && measured.from <= offset + left.length && measured.more) + rebalance = left = left.updateHeight(oracle, offset, force, measured); + else + left.updateHeight(oracle, offset, force); + if (measured && measured.from <= rightStart + right.length && measured.more) + rebalance = right = right.updateHeight(oracle, rightStart, force, measured); + else + right.updateHeight(oracle, rightStart, force); + if (rebalance) + return this.balanced(left, right); + this.height = this.left.height + this.right.height; + this.outdated = false; + return this; + } + toString() { return this.left + (this.break ? " " : "-") + this.right; } +} +function mergeGaps(nodes, around) { + let before, after; + if (nodes[around] == null && + (before = nodes[around - 1]) instanceof HeightMapGap && + (after = nodes[around + 1]) instanceof HeightMapGap) + nodes.splice(around - 1, 3, new HeightMapGap(before.length + 1 + after.length)); +} +const relevantWidgetHeight = 5; +class NodeBuilder { + constructor(pos, oracle) { + this.pos = pos; + this.oracle = oracle; + this.nodes = []; + this.lineStart = -1; + this.lineEnd = -1; + this.covering = null; + this.writtenTo = pos; + } + get isCovered() { + return this.covering && this.nodes[this.nodes.length - 1] == this.covering; + } + span(_from, to) { + if (this.lineStart > -1) { + let end = Math.min(to, this.lineEnd), last = this.nodes[this.nodes.length - 1]; + if (last instanceof HeightMapText) + last.length += end - this.pos; + else if (end > this.pos || !this.isCovered) + this.nodes.push(new HeightMapText(end - this.pos, -1)); + this.writtenTo = end; + if (to > end) { + this.nodes.push(null); + this.writtenTo++; + this.lineStart = -1; + } + } + this.pos = to; + } + point(from, to, deco) { + if (from < to || deco.heightRelevant) { + let height = deco.widget ? deco.widget.estimatedHeight : 0; + let breaks = deco.widget ? deco.widget.lineBreaks : 0; + if (height < 0) + height = this.oracle.lineHeight; + let len = to - from; + if (deco.block) { + this.addBlock(new HeightMapBlock(len, height, deco)); + } + else if (len || breaks || height >= relevantWidgetHeight) { + this.addLineDeco(height, breaks, len); + } + } + else if (to > from) { + this.span(from, to); + } + if (this.lineEnd > -1 && this.lineEnd < this.pos) + this.lineEnd = this.oracle.doc.lineAt(this.pos).to; + } + enterLine() { + if (this.lineStart > -1) + return; + let { from, to } = this.oracle.doc.lineAt(this.pos); + this.lineStart = from; + this.lineEnd = to; + if (this.writtenTo < from) { + if (this.writtenTo < from - 1 || this.nodes[this.nodes.length - 1] == null) + this.nodes.push(this.blankContent(this.writtenTo, from - 1)); + this.nodes.push(null); + } + if (this.pos > from) + this.nodes.push(new HeightMapText(this.pos - from, -1)); + this.writtenTo = this.pos; + } + blankContent(from, to) { + let gap = new HeightMapGap(to - from); + if (this.oracle.doc.lineAt(from).to == to) + gap.flags |= 4 /* Flag.SingleLine */; + return gap; + } + ensureLine() { + this.enterLine(); + let last = this.nodes.length ? this.nodes[this.nodes.length - 1] : null; + if (last instanceof HeightMapText) + return last; + let line = new HeightMapText(0, -1); + this.nodes.push(line); + return line; + } + addBlock(block) { + this.enterLine(); + let deco = block.deco; + if (deco && deco.startSide > 0 && !this.isCovered) + this.ensureLine(); + this.nodes.push(block); + this.writtenTo = this.pos = this.pos + block.length; + if (deco && deco.endSide > 0) + this.covering = block; + } + addLineDeco(height, breaks, length) { + let line = this.ensureLine(); + line.length += length; + line.collapsed += length; + line.widgetHeight = Math.max(line.widgetHeight, height); + line.breaks += breaks; + this.writtenTo = this.pos = this.pos + length; + } + finish(from) { + let last = this.nodes.length == 0 ? null : this.nodes[this.nodes.length - 1]; + if (this.lineStart > -1 && !(last instanceof HeightMapText) && !this.isCovered) + this.nodes.push(new HeightMapText(0, -1)); + else if (this.writtenTo < this.pos || last == null) + this.nodes.push(this.blankContent(this.writtenTo, this.pos)); + let pos = from; + for (let node of this.nodes) { + if (node instanceof HeightMapText) + node.updateHeight(this.oracle, pos); + pos += node ? node.length : 1; + } + return this.nodes; + } + // Always called with a region that on both sides either stretches + // to a line break or the end of the document. + // The returned array uses null to indicate line breaks, but never + // starts or ends in a line break, or has multiple line breaks next + // to each other. + static build(oracle, decorations, from, to) { + let builder = new NodeBuilder(from, oracle); + state.RangeSet.spans(decorations, from, to, builder, 0); + return builder.finish(from); + } +} +function heightRelevantDecoChanges(a, b, diff) { + let comp = new DecorationComparator; + state.RangeSet.compare(a, b, diff, comp, 0); + return comp.changes; +} +class DecorationComparator { + constructor() { + this.changes = []; + } + compareRange() { } + comparePoint(from, to, a, b) { + if (from < to || a && a.heightRelevant || b && b.heightRelevant) + addRange(from, to, this.changes, 5); + } +} + +function visiblePixelRange(dom, paddingTop) { + let rect = dom.getBoundingClientRect(); + let doc = dom.ownerDocument, win = doc.defaultView || window; + let left = Math.max(0, rect.left), right = Math.min(win.innerWidth, rect.right); + let top = Math.max(0, rect.top), bottom = Math.min(win.innerHeight, rect.bottom); + for (let parent = dom.parentNode; parent && parent != doc.body;) { + if (parent.nodeType == 1) { + let elt = parent; + let style = window.getComputedStyle(elt); + if ((elt.scrollHeight > elt.clientHeight || elt.scrollWidth > elt.clientWidth) && + style.overflow != "visible") { + let parentRect = elt.getBoundingClientRect(); + left = Math.max(left, parentRect.left); + right = Math.min(right, parentRect.right); + top = Math.max(top, parentRect.top); + bottom = Math.min(parent == dom.parentNode ? win.innerHeight : bottom, parentRect.bottom); + } + parent = style.position == "absolute" || style.position == "fixed" ? elt.offsetParent : elt.parentNode; + } + else if (parent.nodeType == 11) { // Shadow root + parent = parent.host; + } + else { + break; + } + } + return { left: left - rect.left, right: Math.max(left, right) - rect.left, + top: top - (rect.top + paddingTop), bottom: Math.max(top, bottom) - (rect.top + paddingTop) }; +} +function inWindow(elt) { + let rect = elt.getBoundingClientRect(), win = elt.ownerDocument.defaultView || window; + return rect.left < win.innerWidth && rect.right > 0 && + rect.top < win.innerHeight && rect.bottom > 0; +} +function fullPixelRange(dom, paddingTop) { + let rect = dom.getBoundingClientRect(); + return { left: 0, right: rect.right - rect.left, + top: paddingTop, bottom: rect.bottom - (rect.top + paddingTop) }; +} +// Line gaps are placeholder widgets used to hide pieces of overlong +// lines within the viewport, as a kludge to keep the editor +// responsive when a ridiculously long line is loaded into it. +class LineGap { + constructor(from, to, size, displaySize) { + this.from = from; + this.to = to; + this.size = size; + this.displaySize = displaySize; + } + static same(a, b) { + if (a.length != b.length) + return false; + for (let i = 0; i < a.length; i++) { + let gA = a[i], gB = b[i]; + if (gA.from != gB.from || gA.to != gB.to || gA.size != gB.size) + return false; + } + return true; + } + draw(viewState, wrapping) { + return Decoration.replace({ + widget: new LineGapWidget(this.displaySize * (wrapping ? viewState.scaleY : viewState.scaleX), wrapping) + }).range(this.from, this.to); + } +} +class LineGapWidget extends WidgetType { + constructor(size, vertical) { + super(); + this.size = size; + this.vertical = vertical; + } + eq(other) { return other.size == this.size && other.vertical == this.vertical; } + toDOM() { + let elt = document.createElement("div"); + if (this.vertical) { + elt.style.height = this.size + "px"; + } + else { + elt.style.width = this.size + "px"; + elt.style.height = "2px"; + elt.style.display = "inline-block"; + } + return elt; + } + get estimatedHeight() { return this.vertical ? this.size : -1; } +} +class ViewState { + constructor(state$1) { + this.state = state$1; + // These are contentDOM-local coordinates + this.pixelViewport = { left: 0, right: window.innerWidth, top: 0, bottom: 0 }; + this.inView = true; + this.paddingTop = 0; // Padding above the document, scaled + this.paddingBottom = 0; // Padding below the document, scaled + this.contentDOMWidth = 0; // contentDOM.getBoundingClientRect().width + this.contentDOMHeight = 0; // contentDOM.getBoundingClientRect().height + this.editorHeight = 0; // scrollDOM.clientHeight, unscaled + this.editorWidth = 0; // scrollDOM.clientWidth, unscaled + this.scrollTop = 0; // Last seen scrollDOM.scrollTop, scaled + this.scrolledToBottom = false; + // The CSS-transformation scale of the editor (transformed size / + // concrete size) + this.scaleX = 1; + this.scaleY = 1; + // The vertical position (document-relative) to which to anchor the + // scroll position. -1 means anchor to the end of the document. + this.scrollAnchorPos = 0; + // The height at the anchor position. Set by the DOM update phase. + // -1 means no height available. + this.scrollAnchorHeight = -1; + // See VP.MaxDOMHeight + this.scaler = IdScaler; + this.scrollTarget = null; + // Briefly set to true when printing, to disable viewport limiting + this.printing = false; + // Flag set when editor content was redrawn, so that the next + // measure stage knows it must read DOM layout + this.mustMeasureContent = true; + this.defaultTextDirection = exports.Direction.LTR; + this.visibleRanges = []; + // Cursor 'assoc' is only significant when the cursor is on a line + // wrap point, where it must stick to the character that it is + // associated with. Since browsers don't provide a reasonable + // interface to set or query this, when a selection is set that + // might cause this to be significant, this flag is set. The next + // measure phase will check whether the cursor is on a line-wrapping + // boundary and, if so, reset it to make sure it is positioned in + // the right place. + this.mustEnforceCursorAssoc = false; + let guessWrapping = state$1.facet(contentAttributes).some(v => typeof v != "function" && v.class == "cm-lineWrapping"); + this.heightOracle = new HeightOracle(guessWrapping); + this.stateDeco = state$1.facet(decorations).filter(d => typeof d != "function"); + this.heightMap = HeightMap.empty().applyChanges(this.stateDeco, state.Text.empty, this.heightOracle.setDoc(state$1.doc), [new ChangedRange(0, 0, 0, state$1.doc.length)]); + for (let i = 0; i < 2; i++) { + this.viewport = this.getViewport(0, null); + if (!this.updateForViewport()) + break; + } + this.updateViewportLines(); + this.lineGaps = this.ensureLineGaps([]); + this.lineGapDeco = Decoration.set(this.lineGaps.map(gap => gap.draw(this, false))); + this.computeVisibleRanges(); + } + updateForViewport() { + let viewports = [this.viewport], { main } = this.state.selection; + for (let i = 0; i <= 1; i++) { + let pos = i ? main.head : main.anchor; + if (!viewports.some(({ from, to }) => pos >= from && pos <= to)) { + let { from, to } = this.lineBlockAt(pos); + viewports.push(new Viewport(from, to)); + } + } + this.viewports = viewports.sort((a, b) => a.from - b.from); + return this.updateScaler(); + } + updateScaler() { + let scaler = this.scaler; + this.scaler = this.heightMap.height <= 7000000 /* VP.MaxDOMHeight */ ? IdScaler : + new BigScaler(this.heightOracle, this.heightMap, this.viewports); + return scaler.eq(this.scaler) ? 0 : 2 /* UpdateFlag.Height */; + } + updateViewportLines() { + this.viewportLines = []; + this.heightMap.forEachLine(this.viewport.from, this.viewport.to, this.heightOracle.setDoc(this.state.doc), 0, 0, block => { + this.viewportLines.push(scaleBlock(block, this.scaler)); + }); + } + update(update, scrollTarget = null) { + this.state = update.state; + let prevDeco = this.stateDeco; + this.stateDeco = this.state.facet(decorations).filter(d => typeof d != "function"); + let contentChanges = update.changedRanges; + let heightChanges = ChangedRange.extendWithRanges(contentChanges, heightRelevantDecoChanges(prevDeco, this.stateDeco, update ? update.changes : state.ChangeSet.empty(this.state.doc.length))); + let prevHeight = this.heightMap.height; + let scrollAnchor = this.scrolledToBottom ? null : this.scrollAnchorAt(this.scrollTop); + clearHeightChangeFlag(); + this.heightMap = this.heightMap.applyChanges(this.stateDeco, update.startState.doc, this.heightOracle.setDoc(this.state.doc), heightChanges); + if (this.heightMap.height != prevHeight || heightChangeFlag) + update.flags |= 2 /* UpdateFlag.Height */; + if (scrollAnchor) { + this.scrollAnchorPos = update.changes.mapPos(scrollAnchor.from, -1); + this.scrollAnchorHeight = scrollAnchor.top; + } + else { + this.scrollAnchorPos = -1; + this.scrollAnchorHeight = prevHeight; + } + let viewport = heightChanges.length ? this.mapViewport(this.viewport, update.changes) : this.viewport; + if (scrollTarget && (scrollTarget.range.head < viewport.from || scrollTarget.range.head > viewport.to) || + !this.viewportIsAppropriate(viewport)) + viewport = this.getViewport(0, scrollTarget); + let viewportChange = viewport.from != this.viewport.from || viewport.to != this.viewport.to; + this.viewport = viewport; + update.flags |= this.updateForViewport(); + if (viewportChange || !update.changes.empty || (update.flags & 2 /* UpdateFlag.Height */)) + this.updateViewportLines(); + if (this.lineGaps.length || this.viewport.to - this.viewport.from > (2000 /* LG.Margin */ << 1)) + this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps, update.changes))); + update.flags |= this.computeVisibleRanges(update.changes); + if (scrollTarget) + this.scrollTarget = scrollTarget; + if (!this.mustEnforceCursorAssoc && update.selectionSet && update.view.lineWrapping && + update.state.selection.main.empty && update.state.selection.main.assoc && + !update.state.facet(nativeSelectionHidden)) + this.mustEnforceCursorAssoc = true; + } + measure(view) { + let dom = view.contentDOM, style = window.getComputedStyle(dom); + let oracle = this.heightOracle; + let whiteSpace = style.whiteSpace; + this.defaultTextDirection = style.direction == "rtl" ? exports.Direction.RTL : exports.Direction.LTR; + let refresh = this.heightOracle.mustRefreshForWrapping(whiteSpace); + let domRect = dom.getBoundingClientRect(); + let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != domRect.height; + this.contentDOMHeight = domRect.height; + this.mustMeasureContent = false; + let result = 0, bias = 0; + if (domRect.width && domRect.height) { + let { scaleX, scaleY } = getScale(dom, domRect); + if (scaleX > .005 && Math.abs(this.scaleX - scaleX) > .005 || + scaleY > .005 && Math.abs(this.scaleY - scaleY) > .005) { + this.scaleX = scaleX; + this.scaleY = scaleY; + result |= 16 /* UpdateFlag.Geometry */; + refresh = measureContent = true; + } + } + // Vertical padding + let paddingTop = (parseInt(style.paddingTop) || 0) * this.scaleY; + let paddingBottom = (parseInt(style.paddingBottom) || 0) * this.scaleY; + if (this.paddingTop != paddingTop || this.paddingBottom != paddingBottom) { + this.paddingTop = paddingTop; + this.paddingBottom = paddingBottom; + result |= 16 /* UpdateFlag.Geometry */ | 2 /* UpdateFlag.Height */; + } + if (this.editorWidth != view.scrollDOM.clientWidth) { + if (oracle.lineWrapping) + measureContent = true; + this.editorWidth = view.scrollDOM.clientWidth; + result |= 16 /* UpdateFlag.Geometry */; + } + let scrollTop = view.scrollDOM.scrollTop * this.scaleY; + if (this.scrollTop != scrollTop) { + this.scrollAnchorHeight = -1; + this.scrollTop = scrollTop; + } + this.scrolledToBottom = isScrolledToBottom(view.scrollDOM); + // Pixel viewport + let pixelViewport = (this.printing ? fullPixelRange : visiblePixelRange)(dom, this.paddingTop); + let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom; + this.pixelViewport = pixelViewport; + let inView = this.pixelViewport.bottom > this.pixelViewport.top && this.pixelViewport.right > this.pixelViewport.left; + if (inView != this.inView) { + this.inView = inView; + if (inView) + measureContent = true; + } + if (!this.inView && !this.scrollTarget && !inWindow(view.dom)) + return 0; + let contentWidth = domRect.width; + if (this.contentDOMWidth != contentWidth || this.editorHeight != view.scrollDOM.clientHeight) { + this.contentDOMWidth = domRect.width; + this.editorHeight = view.scrollDOM.clientHeight; + result |= 16 /* UpdateFlag.Geometry */; + } + if (measureContent) { + let lineHeights = view.docView.measureVisibleLineHeights(this.viewport); + if (oracle.mustRefreshForHeights(lineHeights)) + refresh = true; + if (refresh || oracle.lineWrapping && Math.abs(contentWidth - this.contentDOMWidth) > oracle.charWidth) { + let { lineHeight, charWidth, textHeight } = view.docView.measureTextSize(); + refresh = lineHeight > 0 && oracle.refresh(whiteSpace, lineHeight, charWidth, textHeight, Math.max(5, contentWidth / charWidth), lineHeights); + if (refresh) { + view.docView.minWidth = 0; + result |= 16 /* UpdateFlag.Geometry */; + } + } + if (dTop > 0 && dBottom > 0) + bias = Math.max(dTop, dBottom); + else if (dTop < 0 && dBottom < 0) + bias = Math.min(dTop, dBottom); + clearHeightChangeFlag(); + for (let vp of this.viewports) { + let heights = vp.from == this.viewport.from ? lineHeights : view.docView.measureVisibleLineHeights(vp); + this.heightMap = (refresh ? HeightMap.empty().applyChanges(this.stateDeco, state.Text.empty, this.heightOracle, [new ChangedRange(0, 0, 0, view.state.doc.length)]) : this.heightMap).updateHeight(oracle, 0, refresh, new MeasuredHeights(vp.from, heights)); + } + if (heightChangeFlag) + result |= 2 /* UpdateFlag.Height */; + } + let viewportChange = !this.viewportIsAppropriate(this.viewport, bias) || + this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from || + this.scrollTarget.range.head > this.viewport.to); + if (viewportChange) { + if (result & 2 /* UpdateFlag.Height */) + result |= this.updateScaler(); + this.viewport = this.getViewport(bias, this.scrollTarget); + result |= this.updateForViewport(); + } + if ((result & 2 /* UpdateFlag.Height */) || viewportChange) + this.updateViewportLines(); + if (this.lineGaps.length || this.viewport.to - this.viewport.from > (2000 /* LG.Margin */ << 1)) + this.updateLineGaps(this.ensureLineGaps(refresh ? [] : this.lineGaps, view)); + result |= this.computeVisibleRanges(); + if (this.mustEnforceCursorAssoc) { + this.mustEnforceCursorAssoc = false; + // This is done in the read stage, because moving the selection + // to a line end is going to trigger a layout anyway, so it + // can't be a pure write. It should be rare that it does any + // writing. + view.docView.enforceCursorAssoc(); + } + return result; + } + get visibleTop() { return this.scaler.fromDOM(this.pixelViewport.top); } + get visibleBottom() { return this.scaler.fromDOM(this.pixelViewport.bottom); } + getViewport(bias, scrollTarget) { + // This will divide VP.Margin between the top and the + // bottom, depending on the bias (the change in viewport position + // since the last update). It'll hold a number between 0 and 1 + let marginTop = 0.5 - Math.max(-0.5, Math.min(0.5, bias / 1000 /* VP.Margin */ / 2)); + let map = this.heightMap, oracle = this.heightOracle; + let { visibleTop, visibleBottom } = this; + let viewport = new Viewport(map.lineAt(visibleTop - marginTop * 1000 /* VP.Margin */, QueryType.ByHeight, oracle, 0, 0).from, map.lineAt(visibleBottom + (1 - marginTop) * 1000 /* VP.Margin */, QueryType.ByHeight, oracle, 0, 0).to); + // If scrollTarget is given, make sure the viewport includes that position + if (scrollTarget) { + let { head } = scrollTarget.range; + if (head < viewport.from || head > viewport.to) { + let viewHeight = Math.min(this.editorHeight, this.pixelViewport.bottom - this.pixelViewport.top); + let block = map.lineAt(head, QueryType.ByPos, oracle, 0, 0), topPos; + if (scrollTarget.y == "center") + topPos = (block.top + block.bottom) / 2 - viewHeight / 2; + else if (scrollTarget.y == "start" || scrollTarget.y == "nearest" && head < viewport.from) + topPos = block.top; + else + topPos = block.bottom - viewHeight; + viewport = new Viewport(map.lineAt(topPos - 1000 /* VP.Margin */ / 2, QueryType.ByHeight, oracle, 0, 0).from, map.lineAt(topPos + viewHeight + 1000 /* VP.Margin */ / 2, QueryType.ByHeight, oracle, 0, 0).to); + } + } + return viewport; + } + mapViewport(viewport, changes) { + let from = changes.mapPos(viewport.from, -1), to = changes.mapPos(viewport.to, 1); + return new Viewport(this.heightMap.lineAt(from, QueryType.ByPos, this.heightOracle, 0, 0).from, this.heightMap.lineAt(to, QueryType.ByPos, this.heightOracle, 0, 0).to); + } + // Checks if a given viewport covers the visible part of the + // document and not too much beyond that. + viewportIsAppropriate({ from, to }, bias = 0) { + if (!this.inView) + return true; + let { top } = this.heightMap.lineAt(from, QueryType.ByPos, this.heightOracle, 0, 0); + let { bottom } = this.heightMap.lineAt(to, QueryType.ByPos, this.heightOracle, 0, 0); + let { visibleTop, visibleBottom } = this; + return (from == 0 || top <= visibleTop - Math.max(10 /* VP.MinCoverMargin */, Math.min(-bias, 250 /* VP.MaxCoverMargin */))) && + (to == this.state.doc.length || + bottom >= visibleBottom + Math.max(10 /* VP.MinCoverMargin */, Math.min(bias, 250 /* VP.MaxCoverMargin */))) && + (top > visibleTop - 2 * 1000 /* VP.Margin */ && bottom < visibleBottom + 2 * 1000 /* VP.Margin */); + } + mapLineGaps(gaps, changes) { + if (!gaps.length || changes.empty) + return gaps; + let mapped = []; + for (let gap of gaps) + if (!changes.touchesRange(gap.from, gap.to)) + mapped.push(new LineGap(changes.mapPos(gap.from), changes.mapPos(gap.to), gap.size, gap.displaySize)); + return mapped; + } + // Computes positions in the viewport where the start or end of a + // line should be hidden, trying to reuse existing line gaps when + // appropriate to avoid unneccesary redraws. + // Uses crude character-counting for the positioning and sizing, + // since actual DOM coordinates aren't always available and + // predictable. Relies on generous margins (see LG.Margin) to hide + // the artifacts this might produce from the user. + ensureLineGaps(current, mayMeasure) { + let wrapping = this.heightOracle.lineWrapping; + let margin = wrapping ? 10000 /* LG.MarginWrap */ : 2000 /* LG.Margin */, halfMargin = margin >> 1, doubleMargin = margin << 1; + // The non-wrapping logic won't work at all in predominantly right-to-left text. + if (this.defaultTextDirection != exports.Direction.LTR && !wrapping) + return []; + let gaps = []; + let addGap = (from, to, line, structure) => { + if (to - from < halfMargin) + return; + let sel = this.state.selection.main, avoid = [sel.from]; + if (!sel.empty) + avoid.push(sel.to); + for (let pos of avoid) { + if (pos > from && pos < to) { + addGap(from, pos - 10 /* LG.SelectionMargin */, line, structure); + addGap(pos + 10 /* LG.SelectionMargin */, to, line, structure); + return; + } + } + let gap = find(current, gap => gap.from >= line.from && gap.to <= line.to && + Math.abs(gap.from - from) < halfMargin && Math.abs(gap.to - to) < halfMargin && + !avoid.some(pos => gap.from < pos && gap.to > pos)); + if (!gap) { + // When scrolling down, snap gap ends to line starts to avoid shifts in wrapping + if (to < line.to && mayMeasure && wrapping && + mayMeasure.visibleRanges.some(r => r.from <= to && r.to >= to)) { + let lineStart = mayMeasure.moveToLineBoundary(state.EditorSelection.cursor(to), false, true).head; + if (lineStart > from) + to = lineStart; + } + let size = this.gapSize(line, from, to, structure); + let displaySize = wrapping || size < 2000000 /* VP.MaxHorizGap */ ? size : 2000000 /* VP.MaxHorizGap */; + gap = new LineGap(from, to, size, displaySize); + } + gaps.push(gap); + }; + let checkLine = (line) => { + if (line.length < doubleMargin || line.type != exports.BlockType.Text) + return; + let structure = lineStructure(line.from, line.to, this.stateDeco); + if (structure.total < doubleMargin) + return; + let target = this.scrollTarget ? this.scrollTarget.range.head : null; + let viewFrom, viewTo; + if (wrapping) { + let marginHeight = (margin / this.heightOracle.lineLength) * this.heightOracle.lineHeight; + let top, bot; + if (target != null) { + let targetFrac = findFraction(structure, target); + let spaceFrac = ((this.visibleBottom - this.visibleTop) / 2 + marginHeight) / line.height; + top = targetFrac - spaceFrac; + bot = targetFrac + spaceFrac; + } + else { + top = (this.visibleTop - line.top - marginHeight) / line.height; + bot = (this.visibleBottom - line.top + marginHeight) / line.height; + } + viewFrom = findPosition(structure, top); + viewTo = findPosition(structure, bot); + } + else { + let totalWidth = structure.total * this.heightOracle.charWidth; + let marginWidth = margin * this.heightOracle.charWidth; + let horizOffset = 0; + if (totalWidth > 2000000 /* VP.MaxHorizGap */) + for (let old of current) { + if (old.from >= line.from && old.from < line.to && old.size != old.displaySize && + old.from * this.heightOracle.charWidth + horizOffset < this.pixelViewport.left) + horizOffset = old.size - old.displaySize; + } + let pxLeft = this.pixelViewport.left + horizOffset, pxRight = this.pixelViewport.right + horizOffset; + let left, right; + if (target != null) { + let targetFrac = findFraction(structure, target); + let spaceFrac = ((pxRight - pxLeft) / 2 + marginWidth) / totalWidth; + left = targetFrac - spaceFrac; + right = targetFrac + spaceFrac; + } + else { + left = (pxLeft - marginWidth) / totalWidth; + right = (pxRight + marginWidth) / totalWidth; + } + viewFrom = findPosition(structure, left); + viewTo = findPosition(structure, right); + } + if (viewFrom > line.from) + addGap(line.from, viewFrom, line, structure); + if (viewTo < line.to) + addGap(viewTo, line.to, line, structure); + }; + for (let line of this.viewportLines) { + if (Array.isArray(line.type)) + line.type.forEach(checkLine); + else + checkLine(line); + } + return gaps; + } + gapSize(line, from, to, structure) { + let fraction = findFraction(structure, to) - findFraction(structure, from); + if (this.heightOracle.lineWrapping) { + return line.height * fraction; + } + else { + return structure.total * this.heightOracle.charWidth * fraction; + } + } + updateLineGaps(gaps) { + if (!LineGap.same(gaps, this.lineGaps)) { + this.lineGaps = gaps; + this.lineGapDeco = Decoration.set(gaps.map(gap => gap.draw(this, this.heightOracle.lineWrapping))); + } + } + computeVisibleRanges(changes) { + let deco = this.stateDeco; + if (this.lineGaps.length) + deco = deco.concat(this.lineGapDeco); + let ranges = []; + state.RangeSet.spans(deco, this.viewport.from, this.viewport.to, { + span(from, to) { ranges.push({ from, to }); }, + point() { } + }, 20); + let changed = 0; + if (ranges.length != this.visibleRanges.length) { + changed = 8 /* UpdateFlag.ViewportMoved */ | 4 /* UpdateFlag.Viewport */; + } + else { + for (let i = 0; i < ranges.length && !(changed & 8 /* UpdateFlag.ViewportMoved */); i++) { + let old = this.visibleRanges[i], nw = ranges[i]; + if (old.from != nw.from || old.to != nw.to) { + changed |= 4 /* UpdateFlag.Viewport */; + if (!(changes && changes.mapPos(old.from, -1) == nw.from && changes.mapPos(old.to, 1) == nw.to)) + changed |= 8 /* UpdateFlag.ViewportMoved */; + } + } + } + this.visibleRanges = ranges; + return changed; + } + lineBlockAt(pos) { + return (pos >= this.viewport.from && pos <= this.viewport.to && + this.viewportLines.find(b => b.from <= pos && b.to >= pos)) || + scaleBlock(this.heightMap.lineAt(pos, QueryType.ByPos, this.heightOracle, 0, 0), this.scaler); + } + lineBlockAtHeight(height) { + return (height >= this.viewportLines[0].top && height <= this.viewportLines[this.viewportLines.length - 1].bottom && + this.viewportLines.find(l => l.top <= height && l.bottom >= height)) || + scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.heightOracle, 0, 0), this.scaler); + } + scrollAnchorAt(scrollTop) { + let block = this.lineBlockAtHeight(scrollTop + 8); + return block.from >= this.viewport.from || this.viewportLines[0].top - scrollTop > 200 ? block : this.viewportLines[0]; + } + elementAtHeight(height) { + return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.heightOracle, 0, 0), this.scaler); + } + get docHeight() { + return this.scaler.toDOM(this.heightMap.height); + } + get contentHeight() { + return this.docHeight + this.paddingTop + this.paddingBottom; + } +} +class Viewport { + constructor(from, to) { + this.from = from; + this.to = to; + } +} +function lineStructure(from, to, stateDeco) { + let ranges = [], pos = from, total = 0; + state.RangeSet.spans(stateDeco, from, to, { + span() { }, + point(from, to) { + if (from > pos) { + ranges.push({ from: pos, to: from }); + total += from - pos; + } + pos = to; + } + }, 20); // We're only interested in collapsed ranges of a significant size + if (pos < to) { + ranges.push({ from: pos, to }); + total += to - pos; + } + return { total, ranges }; +} +function findPosition({ total, ranges }, ratio) { + if (ratio <= 0) + return ranges[0].from; + if (ratio >= 1) + return ranges[ranges.length - 1].to; + let dist = Math.floor(total * ratio); + for (let i = 0;; i++) { + let { from, to } = ranges[i], size = to - from; + if (dist <= size) + return from + dist; + dist -= size; + } +} +function findFraction(structure, pos) { + let counted = 0; + for (let { from, to } of structure.ranges) { + if (pos <= to) { + counted += pos - from; + break; + } + counted += to - from; + } + return counted / structure.total; +} +function find(array, f) { + for (let val of array) + if (f(val)) + return val; + return undefined; +} +// Don't scale when the document height is within the range of what +// the DOM can handle. +const IdScaler = { + toDOM(n) { return n; }, + fromDOM(n) { return n; }, + scale: 1, + eq(other) { return other == this; } +}; +// When the height is too big (> VP.MaxDOMHeight), scale down the +// regions outside the viewports so that the total height is +// VP.MaxDOMHeight. +class BigScaler { + constructor(oracle, heightMap, viewports) { + let vpHeight = 0, base = 0, domBase = 0; + this.viewports = viewports.map(({ from, to }) => { + let top = heightMap.lineAt(from, QueryType.ByPos, oracle, 0, 0).top; + let bottom = heightMap.lineAt(to, QueryType.ByPos, oracle, 0, 0).bottom; + vpHeight += bottom - top; + return { from, to, top, bottom, domTop: 0, domBottom: 0 }; + }); + this.scale = (7000000 /* VP.MaxDOMHeight */ - vpHeight) / (heightMap.height - vpHeight); + for (let obj of this.viewports) { + obj.domTop = domBase + (obj.top - base) * this.scale; + domBase = obj.domBottom = obj.domTop + (obj.bottom - obj.top); + base = obj.bottom; + } + } + toDOM(n) { + for (let i = 0, base = 0, domBase = 0;; i++) { + let vp = i < this.viewports.length ? this.viewports[i] : null; + if (!vp || n < vp.top) + return domBase + (n - base) * this.scale; + if (n <= vp.bottom) + return vp.domTop + (n - vp.top); + base = vp.bottom; + domBase = vp.domBottom; + } + } + fromDOM(n) { + for (let i = 0, base = 0, domBase = 0;; i++) { + let vp = i < this.viewports.length ? this.viewports[i] : null; + if (!vp || n < vp.domTop) + return base + (n - domBase) / this.scale; + if (n <= vp.domBottom) + return vp.top + (n - vp.domTop); + base = vp.bottom; + domBase = vp.domBottom; + } + } + eq(other) { + if (!(other instanceof BigScaler)) + return false; + return this.scale == other.scale && this.viewports.length == other.viewports.length && + this.viewports.every((vp, i) => vp.from == other.viewports[i].from && vp.to == other.viewports[i].to); + } +} +function scaleBlock(block, scaler) { + if (scaler.scale == 1) + return block; + let bTop = scaler.toDOM(block.top), bBottom = scaler.toDOM(block.bottom); + return new BlockInfo(block.from, block.length, bTop, bBottom - bTop, Array.isArray(block._content) ? block._content.map(b => scaleBlock(b, scaler)) : block._content); +} + +const theme = state.Facet.define({ combine: strs => strs.join(" ") }); +const darkTheme = state.Facet.define({ combine: values => values.indexOf(true) > -1 }); +const baseThemeID = styleMod.StyleModule.newName(), baseLightID = styleMod.StyleModule.newName(), baseDarkID = styleMod.StyleModule.newName(); +const lightDarkIDs = { "&light": "." + baseLightID, "&dark": "." + baseDarkID }; +function buildTheme(main, spec, scopes) { + return new styleMod.StyleModule(spec, { + finish(sel) { + return /&/.test(sel) ? sel.replace(/&\w*/, m => { + if (m == "&") + return main; + if (!scopes || !scopes[m]) + throw new RangeError(`Unsupported selector: ${m}`); + return scopes[m]; + }) : main + " " + sel; + } + }); +} +const baseTheme$1 = buildTheme("." + baseThemeID, { + "&": { + position: "relative !important", + boxSizing: "border-box", + "&.cm-focused": { + // Provide a simple default outline to make sure a focused + // editor is visually distinct. Can't leave the default behavior + // because that will apply to the content element, which is + // inside the scrollable container and doesn't include the + // gutters. We also can't use an 'auto' outline, since those + // are, for some reason, drawn behind the element content, which + // will cause things like the active line background to cover + // the outline (#297). + outline: "1px dotted #212121" + }, + display: "flex !important", + flexDirection: "column" + }, + ".cm-scroller": { + display: "flex !important", + alignItems: "flex-start !important", + fontFamily: "monospace", + lineHeight: 1.4, + height: "100%", + overflowX: "auto", + position: "relative", + zIndex: 0, + overflowAnchor: "none", + }, + ".cm-content": { + margin: 0, + flexGrow: 2, + flexShrink: 0, + display: "block", + whiteSpace: "pre", + wordWrap: "normal", // https://github.com/codemirror/dev/issues/456 + boxSizing: "border-box", + minHeight: "100%", + padding: "4px 0", + outline: "none", + "&[contenteditable=true]": { + WebkitUserModify: "read-write-plaintext-only", + } + }, + ".cm-lineWrapping": { + whiteSpace_fallback: "pre-wrap", // For IE + whiteSpace: "break-spaces", + wordBreak: "break-word", // For Safari, which doesn't support overflow-wrap: anywhere + overflowWrap: "anywhere", + flexShrink: 1 + }, + "&light .cm-content": { caretColor: "black" }, + "&dark .cm-content": { caretColor: "white" }, + ".cm-line": { + display: "block", + padding: "0 2px 0 6px" + }, + ".cm-layer": { + position: "absolute", + left: 0, + top: 0, + contain: "size style", + "& > *": { + position: "absolute" + } + }, + "&light .cm-selectionBackground": { + background: "#d9d9d9" + }, + "&dark .cm-selectionBackground": { + background: "#222" + }, + "&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": { + background: "#d7d4f0" + }, + "&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": { + background: "#233" + }, + ".cm-cursorLayer": { + pointerEvents: "none" + }, + "&.cm-focused > .cm-scroller > .cm-cursorLayer": { + animation: "steps(1) cm-blink 1.2s infinite" + }, + // Two animations defined so that we can switch between them to + // restart the animation without forcing another style + // recomputation. + "@keyframes cm-blink": { "0%": {}, "50%": { opacity: 0 }, "100%": {} }, + "@keyframes cm-blink2": { "0%": {}, "50%": { opacity: 0 }, "100%": {} }, + ".cm-cursor, .cm-dropCursor": { + borderLeft: "1.2px solid black", + marginLeft: "-0.6px", + pointerEvents: "none", + }, + ".cm-cursor": { + display: "none" + }, + "&dark .cm-cursor": { + borderLeftColor: "#ddd" + }, + ".cm-dropCursor": { + position: "absolute" + }, + "&.cm-focused > .cm-scroller > .cm-cursorLayer .cm-cursor": { + display: "block" + }, + ".cm-iso": { + unicodeBidi: "isolate" + }, + ".cm-announced": { + position: "fixed", + top: "-10000px" + }, + "@media print": { + ".cm-announced": { display: "none" } + }, + "&light .cm-activeLine": { backgroundColor: "#cceeff44" }, + "&dark .cm-activeLine": { backgroundColor: "#99eeff33" }, + "&light .cm-specialChar": { color: "red" }, + "&dark .cm-specialChar": { color: "#f78" }, + ".cm-gutters": { + flexShrink: 0, + display: "flex", + height: "100%", + boxSizing: "border-box", + zIndex: 200, + }, + ".cm-gutters-before": { insetInlineStart: 0 }, + ".cm-gutters-after": { insetInlineEnd: 0 }, + "&light .cm-gutters": { + backgroundColor: "#f5f5f5", + color: "#6c6c6c", + border: "0px solid #ddd", + "&.cm-gutters-before": { borderRightWidth: "1px" }, + "&.cm-gutters-after": { borderLeftWidth: "1px" }, + }, + "&dark .cm-gutters": { + backgroundColor: "#333338", + color: "#ccc" + }, + ".cm-gutter": { + display: "flex !important", // Necessary -- prevents margin collapsing + flexDirection: "column", + flexShrink: 0, + boxSizing: "border-box", + minHeight: "100%", + overflow: "hidden" + }, + ".cm-gutterElement": { + boxSizing: "border-box" + }, + ".cm-lineNumbers .cm-gutterElement": { + padding: "0 3px 0 5px", + minWidth: "20px", + textAlign: "right", + whiteSpace: "nowrap" + }, + "&light .cm-activeLineGutter": { + backgroundColor: "#e2f2ff" + }, + "&dark .cm-activeLineGutter": { + backgroundColor: "#222227" + }, + ".cm-panels": { + boxSizing: "border-box", + position: "sticky", + left: 0, + right: 0, + zIndex: 300 + }, + "&light .cm-panels": { + backgroundColor: "#f5f5f5", + color: "black" + }, + "&light .cm-panels-top": { + borderBottom: "1px solid #ddd" + }, + "&light .cm-panels-bottom": { + borderTop: "1px solid #ddd" + }, + "&dark .cm-panels": { + backgroundColor: "#333338", + color: "white" + }, + ".cm-dialog": { + padding: "2px 19px 4px 6px", + position: "relative", + "& label": { fontSize: "80%" }, + }, + ".cm-dialog-close": { + position: "absolute", + top: "3px", + right: "4px", + backgroundColor: "inherit", + border: "none", + font: "inherit", + fontSize: "14px", + padding: "0" + }, + ".cm-tab": { + display: "inline-block", + overflow: "hidden", + verticalAlign: "bottom" + }, + ".cm-widgetBuffer": { + verticalAlign: "text-top", + height: "1em", + width: 0, + display: "inline" + }, + ".cm-placeholder": { + color: "#888", + display: "inline-block", + verticalAlign: "top", + userSelect: "none" + }, + ".cm-highlightSpace": { + backgroundImage: "radial-gradient(circle at 50% 55%, #aaa 20%, transparent 5%)", + backgroundPosition: "center", + }, + ".cm-highlightTab": { + backgroundImage: `url('data:image/svg+xml,')`, + backgroundSize: "auto 100%", + backgroundPosition: "right 90%", + backgroundRepeat: "no-repeat" + }, + ".cm-trailingSpace": { + backgroundColor: "#ff332255" + }, + ".cm-button": { + verticalAlign: "middle", + color: "inherit", + fontSize: "70%", + padding: ".2em 1em", + borderRadius: "1px" + }, + "&light .cm-button": { + backgroundImage: "linear-gradient(#eff1f5, #d9d9df)", + border: "1px solid #888", + "&:active": { + backgroundImage: "linear-gradient(#b4b4b4, #d0d3d6)" + } + }, + "&dark .cm-button": { + backgroundImage: "linear-gradient(#393939, #111)", + border: "1px solid #888", + "&:active": { + backgroundImage: "linear-gradient(#111, #333)" + } + }, + ".cm-textfield": { + verticalAlign: "middle", + color: "inherit", + fontSize: "70%", + border: "1px solid silver", + padding: ".2em .5em" + }, + "&light .cm-textfield": { + backgroundColor: "white" + }, + "&dark .cm-textfield": { + border: "1px solid #555", + backgroundColor: "inherit" + } +}, lightDarkIDs); + +const observeOptions = { + childList: true, + characterData: true, + subtree: true, + attributes: true, + characterDataOldValue: true +}; +// IE11 has very broken mutation observers, so we also listen to +// DOMCharacterDataModified there +const useCharData = browser.ie && browser.ie_version <= 11; +class DOMObserver { + constructor(view) { + this.view = view; + this.active = false; + this.editContext = null; + // The known selection. Kept in our own object, as opposed to just + // directly accessing the selection because: + // - Safari doesn't report the right selection in shadow DOM + // - Reading from the selection forces a DOM layout + // - This way, we can ignore selectionchange events if we have + // already seen the 'new' selection + this.selectionRange = new DOMSelectionState; + // Set when a selection change is detected, cleared on flush + this.selectionChanged = false; + this.delayedFlush = -1; + this.resizeTimeout = -1; + this.queue = []; + this.delayedAndroidKey = null; + this.flushingAndroidKey = -1; + this.lastChange = 0; + this.scrollTargets = []; + this.intersection = null; + this.resizeScroll = null; + this.intersecting = false; + this.gapIntersection = null; + this.gaps = []; + this.printQuery = null; + // Timeout for scheduling check of the parents that need scroll handlers + this.parentCheck = -1; + this.dom = view.contentDOM; + this.observer = new MutationObserver(mutations => { + for (let mut of mutations) + this.queue.push(mut); + // IE11 will sometimes (on typing over a selection or + // backspacing out a single character text node) call the + // observer callback before actually updating the DOM. + // + // Unrelatedly, iOS Safari will, when ending a composition, + // sometimes first clear it, deliver the mutations, and then + // reinsert the finished text. CodeMirror's handling of the + // deletion will prevent the reinsertion from happening, + // breaking composition. + if ((browser.ie && browser.ie_version <= 11 || browser.ios && view.composing) && + mutations.some(m => m.type == "childList" && m.removedNodes.length || + m.type == "characterData" && m.oldValue.length > m.target.nodeValue.length)) + this.flushSoon(); + else + this.flush(); + }); + if (window.EditContext && browser.android && view.constructor.EDIT_CONTEXT !== false && + // Chrome <126 doesn't support inverted selections in edit context (#1392) + !(browser.chrome && browser.chrome_version < 126)) { + this.editContext = new EditContextManager(view); + if (view.state.facet(editable)) + view.contentDOM.editContext = this.editContext.editContext; + } + if (useCharData) + this.onCharData = (event) => { + this.queue.push({ target: event.target, + type: "characterData", + oldValue: event.prevValue }); + this.flushSoon(); + }; + this.onSelectionChange = this.onSelectionChange.bind(this); + this.onResize = this.onResize.bind(this); + this.onPrint = this.onPrint.bind(this); + this.onScroll = this.onScroll.bind(this); + if (window.matchMedia) + this.printQuery = window.matchMedia("print"); + if (typeof ResizeObserver == "function") { + this.resizeScroll = new ResizeObserver(() => { + var _a; + if (((_a = this.view.docView) === null || _a === void 0 ? void 0 : _a.lastUpdate) < Date.now() - 75) + this.onResize(); + }); + this.resizeScroll.observe(view.scrollDOM); + } + this.addWindowListeners(this.win = view.win); + this.start(); + if (typeof IntersectionObserver == "function") { + this.intersection = new IntersectionObserver(entries => { + if (this.parentCheck < 0) + this.parentCheck = setTimeout(this.listenForScroll.bind(this), 1000); + if (entries.length > 0 && (entries[entries.length - 1].intersectionRatio > 0) != this.intersecting) { + this.intersecting = !this.intersecting; + if (this.intersecting != this.view.inView) + this.onScrollChanged(document.createEvent("Event")); + } + }, { threshold: [0, .001] }); + this.intersection.observe(this.dom); + this.gapIntersection = new IntersectionObserver(entries => { + if (entries.length > 0 && entries[entries.length - 1].intersectionRatio > 0) + this.onScrollChanged(document.createEvent("Event")); + }, {}); + } + this.listenForScroll(); + this.readSelectionRange(); + } + onScrollChanged(e) { + this.view.inputState.runHandlers("scroll", e); + if (this.intersecting) + this.view.measure(); + } + onScroll(e) { + if (this.intersecting) + this.flush(false); + if (this.editContext) + this.view.requestMeasure(this.editContext.measureReq); + this.onScrollChanged(e); + } + onResize() { + if (this.resizeTimeout < 0) + this.resizeTimeout = setTimeout(() => { + this.resizeTimeout = -1; + this.view.requestMeasure(); + }, 50); + } + onPrint(event) { + if ((event.type == "change" || !event.type) && !event.matches) + return; + this.view.viewState.printing = true; + this.view.measure(); + setTimeout(() => { + this.view.viewState.printing = false; + this.view.requestMeasure(); + }, 500); + } + updateGaps(gaps) { + if (this.gapIntersection && (gaps.length != this.gaps.length || this.gaps.some((g, i) => g != gaps[i]))) { + this.gapIntersection.disconnect(); + for (let gap of gaps) + this.gapIntersection.observe(gap); + this.gaps = gaps; + } + } + onSelectionChange(event) { + let wasChanged = this.selectionChanged; + if (!this.readSelectionRange() || this.delayedAndroidKey) + return; + let { view } = this, sel = this.selectionRange; + if (view.state.facet(editable) ? view.root.activeElement != this.dom : !hasSelection(this.dom, sel)) + return; + let context = sel.anchorNode && view.docView.nearest(sel.anchorNode); + if (context && context.ignoreEvent(event)) { + if (!wasChanged) + this.selectionChanged = false; + return; + } + // Deletions on IE11 fire their events in the wrong order, giving + // us a selection change event before the DOM changes are + // reported. + // Chrome Android has a similar issue when backspacing out a + // selection (#645). + if ((browser.ie && browser.ie_version <= 11 || browser.android && browser.chrome) && !view.state.selection.main.empty && + // (Selection.isCollapsed isn't reliable on IE) + sel.focusNode && isEquivalentPosition(sel.focusNode, sel.focusOffset, sel.anchorNode, sel.anchorOffset)) + this.flushSoon(); + else + this.flush(false); + } + readSelectionRange() { + let { view } = this; + // The Selection object is broken in shadow roots in Safari. See + // https://github.com/codemirror/dev/issues/414 + let selection = getSelection(view.root); + if (!selection) + return false; + let range = browser.safari && view.root.nodeType == 11 && + view.root.activeElement == this.dom && + safariSelectionRangeHack(this.view, selection) || selection; + if (!range || this.selectionRange.eq(range)) + return false; + let local = hasSelection(this.dom, range); + // Detect the situation where the browser has, on focus, moved the + // selection to the start of the content element. Reset it to the + // position from the editor state. + if (local && !this.selectionChanged && + view.inputState.lastFocusTime > Date.now() - 200 && + view.inputState.lastTouchTime < Date.now() - 300 && + atElementStart(this.dom, range)) { + this.view.inputState.lastFocusTime = 0; + view.docView.updateSelection(); + return false; + } + this.selectionRange.setRange(range); + if (local) + this.selectionChanged = true; + return true; + } + setSelectionRange(anchor, head) { + this.selectionRange.set(anchor.node, anchor.offset, head.node, head.offset); + this.selectionChanged = false; + } + clearSelectionRange() { + this.selectionRange.set(null, 0, null, 0); + } + listenForScroll() { + this.parentCheck = -1; + let i = 0, changed = null; + for (let dom = this.dom; dom;) { + if (dom.nodeType == 1) { + if (!changed && i < this.scrollTargets.length && this.scrollTargets[i] == dom) + i++; + else if (!changed) + changed = this.scrollTargets.slice(0, i); + if (changed) + changed.push(dom); + dom = dom.assignedSlot || dom.parentNode; + } + else if (dom.nodeType == 11) { // Shadow root + dom = dom.host; + } + else { + break; + } + } + if (i < this.scrollTargets.length && !changed) + changed = this.scrollTargets.slice(0, i); + if (changed) { + for (let dom of this.scrollTargets) + dom.removeEventListener("scroll", this.onScroll); + for (let dom of this.scrollTargets = changed) + dom.addEventListener("scroll", this.onScroll); + } + } + ignore(f) { + if (!this.active) + return f(); + try { + this.stop(); + return f(); + } + finally { + this.start(); + this.clear(); + } + } + start() { + if (this.active) + return; + this.observer.observe(this.dom, observeOptions); + if (useCharData) + this.dom.addEventListener("DOMCharacterDataModified", this.onCharData); + this.active = true; + } + stop() { + if (!this.active) + return; + this.active = false; + this.observer.disconnect(); + if (useCharData) + this.dom.removeEventListener("DOMCharacterDataModified", this.onCharData); + } + // Throw away any pending changes + clear() { + this.processRecords(); + this.queue.length = 0; + this.selectionChanged = false; + } + // Chrome Android, especially in combination with GBoard, not only + // doesn't reliably fire regular key events, but also often + // surrounds the effect of enter or backspace with a bunch of + // composition events that, when interrupted, cause text duplication + // or other kinds of corruption. This hack makes the editor back off + // from handling DOM changes for a moment when such a key is + // detected (via beforeinput or keydown), and then tries to flush + // them or, if that has no effect, dispatches the given key. + delayAndroidKey(key, keyCode) { + var _a; + if (!this.delayedAndroidKey) { + let flush = () => { + let key = this.delayedAndroidKey; + if (key) { + this.clearDelayedAndroidKey(); + this.view.inputState.lastKeyCode = key.keyCode; + this.view.inputState.lastKeyTime = Date.now(); + let flushed = this.flush(); + if (!flushed && key.force) + dispatchKey(this.dom, key.key, key.keyCode); + } + }; + this.flushingAndroidKey = this.view.win.requestAnimationFrame(flush); + } + // Since backspace beforeinput is sometimes signalled spuriously, + // Enter always takes precedence. + if (!this.delayedAndroidKey || key == "Enter") + this.delayedAndroidKey = { + key, keyCode, + // Only run the key handler when no changes are detected if + // this isn't coming right after another change, in which case + // it is probably part of a weird chain of updates, and should + // be ignored if it returns the DOM to its previous state. + force: this.lastChange < Date.now() - 50 || !!((_a = this.delayedAndroidKey) === null || _a === void 0 ? void 0 : _a.force) + }; + } + clearDelayedAndroidKey() { + this.win.cancelAnimationFrame(this.flushingAndroidKey); + this.delayedAndroidKey = null; + this.flushingAndroidKey = -1; + } + flushSoon() { + if (this.delayedFlush < 0) + this.delayedFlush = this.view.win.requestAnimationFrame(() => { this.delayedFlush = -1; this.flush(); }); + } + forceFlush() { + if (this.delayedFlush >= 0) { + this.view.win.cancelAnimationFrame(this.delayedFlush); + this.delayedFlush = -1; + } + this.flush(); + } + pendingRecords() { + for (let mut of this.observer.takeRecords()) + this.queue.push(mut); + return this.queue; + } + processRecords() { + let records = this.pendingRecords(); + if (records.length) + this.queue = []; + let from = -1, to = -1, typeOver = false; + for (let record of records) { + let range = this.readMutation(record); + if (!range) + continue; + if (range.typeOver) + typeOver = true; + if (from == -1) { + ({ from, to } = range); + } + else { + from = Math.min(range.from, from); + to = Math.max(range.to, to); + } + } + return { from, to, typeOver }; + } + readChange() { + let { from, to, typeOver } = this.processRecords(); + let newSel = this.selectionChanged && hasSelection(this.dom, this.selectionRange); + if (from < 0 && !newSel) + return null; + if (from > -1) + this.lastChange = Date.now(); + this.view.inputState.lastFocusTime = 0; + this.selectionChanged = false; + let change = new DOMChange(this.view, from, to, typeOver); + this.view.docView.domChanged = { newSel: change.newSel ? change.newSel.main : null }; + return change; + } + // Apply pending changes, if any + flush(readSelection = true) { + // Completely hold off flushing when pending keys are set—the code + // managing those will make sure processRecords is called and the + // view is resynchronized after + if (this.delayedFlush >= 0 || this.delayedAndroidKey) + return false; + if (readSelection) + this.readSelectionRange(); + let domChange = this.readChange(); + if (!domChange) { + this.view.requestMeasure(); + return false; + } + let startState = this.view.state; + let handled = applyDOMChange(this.view, domChange); + // The view wasn't updated but DOM/selection changes were seen. Reset the view. + if (this.view.state == startState && + (domChange.domChanged || domChange.newSel && !domChange.newSel.main.eq(this.view.state.selection.main))) + this.view.update([]); + return handled; + } + readMutation(rec) { + let cView = this.view.docView.nearest(rec.target); + if (!cView || cView.ignoreMutation(rec)) + return null; + cView.markDirty(rec.type == "attributes"); + if (rec.type == "attributes") + cView.flags |= 4 /* ViewFlag.AttrsDirty */; + if (rec.type == "childList") { + let childBefore = findChild(cView, rec.previousSibling || rec.target.previousSibling, -1); + let childAfter = findChild(cView, rec.nextSibling || rec.target.nextSibling, 1); + return { from: childBefore ? cView.posAfter(childBefore) : cView.posAtStart, + to: childAfter ? cView.posBefore(childAfter) : cView.posAtEnd, typeOver: false }; + } + else if (rec.type == "characterData") { + return { from: cView.posAtStart, to: cView.posAtEnd, typeOver: rec.target.nodeValue == rec.oldValue }; + } + else { + return null; + } + } + setWindow(win) { + if (win != this.win) { + this.removeWindowListeners(this.win); + this.win = win; + this.addWindowListeners(this.win); + } + } + addWindowListeners(win) { + win.addEventListener("resize", this.onResize); + if (this.printQuery) { + if (this.printQuery.addEventListener) + this.printQuery.addEventListener("change", this.onPrint); + else + this.printQuery.addListener(this.onPrint); + } + else + win.addEventListener("beforeprint", this.onPrint); + win.addEventListener("scroll", this.onScroll); + win.document.addEventListener("selectionchange", this.onSelectionChange); + } + removeWindowListeners(win) { + win.removeEventListener("scroll", this.onScroll); + win.removeEventListener("resize", this.onResize); + if (this.printQuery) { + if (this.printQuery.removeEventListener) + this.printQuery.removeEventListener("change", this.onPrint); + else + this.printQuery.removeListener(this.onPrint); + } + else + win.removeEventListener("beforeprint", this.onPrint); + win.document.removeEventListener("selectionchange", this.onSelectionChange); + } + update(update) { + if (this.editContext) { + this.editContext.update(update); + if (update.startState.facet(editable) != update.state.facet(editable)) + update.view.contentDOM.editContext = update.state.facet(editable) ? this.editContext.editContext : null; + } + } + destroy() { + var _a, _b, _c; + this.stop(); + (_a = this.intersection) === null || _a === void 0 ? void 0 : _a.disconnect(); + (_b = this.gapIntersection) === null || _b === void 0 ? void 0 : _b.disconnect(); + (_c = this.resizeScroll) === null || _c === void 0 ? void 0 : _c.disconnect(); + for (let dom of this.scrollTargets) + dom.removeEventListener("scroll", this.onScroll); + this.removeWindowListeners(this.win); + clearTimeout(this.parentCheck); + clearTimeout(this.resizeTimeout); + this.win.cancelAnimationFrame(this.delayedFlush); + this.win.cancelAnimationFrame(this.flushingAndroidKey); + if (this.editContext) { + this.view.contentDOM.editContext = null; + this.editContext.destroy(); + } + } +} +function findChild(cView, dom, dir) { + while (dom) { + let curView = ContentView.get(dom); + if (curView && curView.parent == cView) + return curView; + let parent = dom.parentNode; + dom = parent != cView.dom ? parent : dir > 0 ? dom.nextSibling : dom.previousSibling; + } + return null; +} +function buildSelectionRangeFromRange(view, range) { + let anchorNode = range.startContainer, anchorOffset = range.startOffset; + let focusNode = range.endContainer, focusOffset = range.endOffset; + let curAnchor = view.docView.domAtPos(view.state.selection.main.anchor); + // Since such a range doesn't distinguish between anchor and head, + // use a heuristic that flips it around if its end matches the + // current anchor. + if (isEquivalentPosition(curAnchor.node, curAnchor.offset, focusNode, focusOffset)) + [anchorNode, anchorOffset, focusNode, focusOffset] = [focusNode, focusOffset, anchorNode, anchorOffset]; + return { anchorNode, anchorOffset, focusNode, focusOffset }; +} +// Used to work around a Safari Selection/shadow DOM bug (#414) +function safariSelectionRangeHack(view, selection) { + if (selection.getComposedRanges) { + let range = selection.getComposedRanges(view.root)[0]; + if (range) + return buildSelectionRangeFromRange(view, range); + } + let found = null; + // Because Safari (at least in 2018-2021) doesn't provide regular + // access to the selection inside a shadowroot, we have to perform a + // ridiculous hack to get at it—using `execCommand` to trigger a + // `beforeInput` event so that we can read the target range from the + // event. + function read(event) { + event.preventDefault(); + event.stopImmediatePropagation(); + found = event.getTargetRanges()[0]; + } + view.contentDOM.addEventListener("beforeinput", read, true); + view.dom.ownerDocument.execCommand("indent"); + view.contentDOM.removeEventListener("beforeinput", read, true); + return found ? buildSelectionRangeFromRange(view, found) : null; +} +class EditContextManager { + constructor(view) { + // The document window for which the text in the context is + // maintained. For large documents, this may be smaller than the + // editor document. This window always includes the selection head. + this.from = 0; + this.to = 0; + // When applying a transaction, this is used to compare the change + // made to the context content to the change in the transaction in + // order to make the minimal changes to the context (since touching + // that sometimes breaks series of multiple edits made for a single + // user action on some Android keyboards) + this.pendingContextChange = null; + this.handlers = Object.create(null); + // Kludge to work around the fact that EditContext does not respond + // well to having its content updated during a composition (see #1472) + this.composing = null; + this.resetRange(view.state); + let context = this.editContext = new window.EditContext({ + text: view.state.doc.sliceString(this.from, this.to), + selectionStart: this.toContextPos(Math.max(this.from, Math.min(this.to, view.state.selection.main.anchor))), + selectionEnd: this.toContextPos(view.state.selection.main.head) + }); + this.handlers.textupdate = e => { + let main = view.state.selection.main, { anchor, head } = main; + let from = this.toEditorPos(e.updateRangeStart), to = this.toEditorPos(e.updateRangeEnd); + if (view.inputState.composing >= 0 && !this.composing) + this.composing = { contextBase: e.updateRangeStart, editorBase: from, drifted: false }; + let deletes = to - from > e.text.length; + // If the window doesn't include the anchor, assume changes + // adjacent to a side go up to the anchor. + if (from == this.from && anchor < this.from) + from = anchor; + else if (to == this.to && anchor > this.to) + to = anchor; + let diff = findDiff(view.state.sliceDoc(from, to), e.text, (deletes ? main.from : main.to) - from, deletes ? "end" : null); + // Edit contexts sometimes fire empty changes + if (!diff) { + let newSel = state.EditorSelection.single(this.toEditorPos(e.selectionStart), this.toEditorPos(e.selectionEnd)); + if (!newSel.main.eq(main)) + view.dispatch({ selection: newSel, userEvent: "select" }); + return; + } + let change = { from: diff.from + from, to: diff.toA + from, + insert: state.Text.of(e.text.slice(diff.from, diff.toB).split("\n")) }; + if ((browser.mac || browser.android) && change.from == head - 1 && + /^\. ?$/.test(e.text) && view.contentDOM.getAttribute("autocorrect") == "off") + change = { from, to, insert: state.Text.of([e.text.replace(".", " ")]) }; + this.pendingContextChange = change; + if (!view.state.readOnly) { + let newLen = this.to - this.from + (change.to - change.from + change.insert.length); + applyDOMChangeInner(view, change, state.EditorSelection.single(this.toEditorPos(e.selectionStart, newLen), this.toEditorPos(e.selectionEnd, newLen))); + } + // If the transaction didn't flush our change, revert it so + // that the context is in sync with the editor state again. + if (this.pendingContextChange) { + this.revertPending(view.state); + this.setSelection(view.state); + } + // Work around missed compositionend events. See https://discuss.codemirror.net/t/a/9514 + if (change.from < change.to && !change.insert.length && view.inputState.composing >= 0 && + !/[\\p{Alphabetic}\\p{Number}_]/.test(context.text.slice(Math.max(0, e.updateRangeStart - 1), Math.min(context.text.length, e.updateRangeStart + 1)))) + this.handlers.compositionend(e); + }; + this.handlers.characterboundsupdate = e => { + let rects = [], prev = null; + for (let i = this.toEditorPos(e.rangeStart), end = this.toEditorPos(e.rangeEnd); i < end; i++) { + let rect = view.coordsForChar(i); + prev = (rect && new DOMRect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top)) + || prev || new DOMRect; + rects.push(prev); + } + context.updateCharacterBounds(e.rangeStart, rects); + }; + this.handlers.textformatupdate = e => { + let deco = []; + for (let format of e.getTextFormats()) { + let lineStyle = format.underlineStyle, thickness = format.underlineThickness; + if (!/none/i.test(lineStyle) && !/none/i.test(thickness)) { + let from = this.toEditorPos(format.rangeStart), to = this.toEditorPos(format.rangeEnd); + if (from < to) { + // These values changed from capitalized custom strings to lower-case CSS keywords in 2025 + let style = `text-decoration: underline ${/^[a-z]/.test(lineStyle) ? lineStyle + " " : lineStyle == "Dashed" ? "dashed " : lineStyle == "Squiggle" ? "wavy " : ""}${/thin/i.test(thickness) ? 1 : 2}px`; + deco.push(Decoration.mark({ attributes: { style } }).range(from, to)); + } + } + } + view.dispatch({ effects: setEditContextFormatting.of(Decoration.set(deco)) }); + }; + this.handlers.compositionstart = () => { + if (view.inputState.composing < 0) { + view.inputState.composing = 0; + view.inputState.compositionFirstChange = true; + } + }; + this.handlers.compositionend = () => { + view.inputState.composing = -1; + view.inputState.compositionFirstChange = null; + if (this.composing) { + let { drifted } = this.composing; + this.composing = null; + if (drifted) + this.reset(view.state); + } + }; + for (let event in this.handlers) + context.addEventListener(event, this.handlers[event]); + this.measureReq = { read: view => { + this.editContext.updateControlBounds(view.contentDOM.getBoundingClientRect()); + let sel = getSelection(view.root); + if (sel && sel.rangeCount) + this.editContext.updateSelectionBounds(sel.getRangeAt(0).getBoundingClientRect()); + } }; + } + applyEdits(update) { + let off = 0, abort = false, pending = this.pendingContextChange; + update.changes.iterChanges((fromA, toA, _fromB, _toB, insert) => { + if (abort) + return; + let dLen = insert.length - (toA - fromA); + if (pending && toA >= pending.to) { + if (pending.from == fromA && pending.to == toA && pending.insert.eq(insert)) { + pending = this.pendingContextChange = null; // Match + off += dLen; + this.to += dLen; + return; + } + else { // Mismatch, revert + pending = null; + this.revertPending(update.state); + } + } + fromA += off; + toA += off; + if (toA <= this.from) { // Before the window + this.from += dLen; + this.to += dLen; + } + else if (fromA < this.to) { // Overlaps with window + if (fromA < this.from || toA > this.to || (this.to - this.from) + insert.length > 30000 /* CxVp.MaxSize */) { + abort = true; + return; + } + this.editContext.updateText(this.toContextPos(fromA), this.toContextPos(toA), insert.toString()); + this.to += dLen; + } + off += dLen; + }); + if (pending && !abort) + this.revertPending(update.state); + return !abort; + } + update(update) { + let reverted = this.pendingContextChange, startSel = update.startState.selection.main; + if (this.composing && + (this.composing.drifted || + (!update.changes.touchesRange(startSel.from, startSel.to) && + update.transactions.some(tr => !tr.isUserEvent("input.type") && tr.changes.touchesRange(this.from, this.to))))) { + this.composing.drifted = true; + this.composing.editorBase = update.changes.mapPos(this.composing.editorBase); + } + else if (!this.applyEdits(update) || !this.rangeIsValid(update.state)) { + this.pendingContextChange = null; + this.reset(update.state); + } + else if (update.docChanged || update.selectionSet || reverted) { + this.setSelection(update.state); + } + if (update.geometryChanged || update.docChanged || update.selectionSet) + update.view.requestMeasure(this.measureReq); + } + resetRange(state) { + let { head } = state.selection.main; + this.from = Math.max(0, head - 10000 /* CxVp.Margin */); + this.to = Math.min(state.doc.length, head + 10000 /* CxVp.Margin */); + } + reset(state) { + this.resetRange(state); + this.editContext.updateText(0, this.editContext.text.length, state.doc.sliceString(this.from, this.to)); + this.setSelection(state); + } + revertPending(state) { + let pending = this.pendingContextChange; + this.pendingContextChange = null; + this.editContext.updateText(this.toContextPos(pending.from), this.toContextPos(pending.from + pending.insert.length), state.doc.sliceString(pending.from, pending.to)); + } + setSelection(state) { + let { main } = state.selection; + let start = this.toContextPos(Math.max(this.from, Math.min(this.to, main.anchor))); + let end = this.toContextPos(main.head); + if (this.editContext.selectionStart != start || this.editContext.selectionEnd != end) + this.editContext.updateSelection(start, end); + } + rangeIsValid(state) { + let { head } = state.selection.main; + return !(this.from > 0 && head - this.from < 500 /* CxVp.MinMargin */ || + this.to < state.doc.length && this.to - head < 500 /* CxVp.MinMargin */ || + this.to - this.from > 10000 /* CxVp.Margin */ * 3); + } + toEditorPos(contextPos, clipLen = this.to - this.from) { + contextPos = Math.min(contextPos, clipLen); + let c = this.composing; + return c && c.drifted ? c.editorBase + (contextPos - c.contextBase) : contextPos + this.from; + } + toContextPos(editorPos) { + let c = this.composing; + return c && c.drifted ? c.contextBase + (editorPos - c.editorBase) : editorPos - this.from; + } + destroy() { + for (let event in this.handlers) + this.editContext.removeEventListener(event, this.handlers[event]); + } +} + +// The editor's update state machine looks something like this: +// +// Idle → Updating ⇆ Idle (unchecked) → Measuring → Idle +// ↑ ↓ +// Updating (measure) +// +// The difference between 'Idle' and 'Idle (unchecked)' lies in +// whether a layout check has been scheduled. A regular update through +// the `update` method updates the DOM in a write-only fashion, and +// relies on a check (scheduled with `requestAnimationFrame`) to make +// sure everything is where it should be and the viewport covers the +// visible code. That check continues to measure and then optionally +// update until it reaches a coherent state. +/** +An editor view represents the editor's user interface. It holds +the editable DOM surface, and possibly other elements such as the +line number gutter. It handles events and dispatches state +transactions for editing actions. +*/ +class EditorView { + /** + The current editor state. + */ + get state() { return this.viewState.state; } + /** + To be able to display large documents without consuming too much + memory or overloading the browser, CodeMirror only draws the + code that is visible (plus a margin around it) to the DOM. This + property tells you the extent of the current drawn viewport, in + document positions. + */ + get viewport() { return this.viewState.viewport; } + /** + When there are, for example, large collapsed ranges in the + viewport, its size can be a lot bigger than the actual visible + content. Thus, if you are doing something like styling the + content in the viewport, it is preferable to only do so for + these ranges, which are the subset of the viewport that is + actually drawn. + */ + get visibleRanges() { return this.viewState.visibleRanges; } + /** + Returns false when the editor is entirely scrolled out of view + or otherwise hidden. + */ + get inView() { return this.viewState.inView; } + /** + Indicates whether the user is currently composing text via + [IME](https://en.wikipedia.org/wiki/Input_method), and at least + one change has been made in the current composition. + */ + get composing() { return !!this.inputState && this.inputState.composing > 0; } + /** + Indicates whether the user is currently in composing state. Note + that on some platforms, like Android, this will be the case a + lot, since just putting the cursor on a word starts a + composition there. + */ + get compositionStarted() { return !!this.inputState && this.inputState.composing >= 0; } + /** + The document or shadow root that the view lives in. + */ + get root() { return this._root; } + /** + @internal + */ + get win() { return this.dom.ownerDocument.defaultView || window; } + /** + Construct a new view. You'll want to either provide a `parent` + option, or put `view.dom` into your document after creating a + view, so that the user can see the editor. + */ + constructor(config = {}) { + var _a; + this.plugins = []; + this.pluginMap = new Map; + this.editorAttrs = {}; + this.contentAttrs = {}; + this.bidiCache = []; + this.destroyed = false; + /** + @internal + */ + this.updateState = 2 /* UpdateState.Updating */; + /** + @internal + */ + this.measureScheduled = -1; + /** + @internal + */ + this.measureRequests = []; + this.contentDOM = document.createElement("div"); + this.scrollDOM = document.createElement("div"); + this.scrollDOM.tabIndex = -1; + this.scrollDOM.className = "cm-scroller"; + this.scrollDOM.appendChild(this.contentDOM); + this.announceDOM = document.createElement("div"); + this.announceDOM.className = "cm-announced"; + this.announceDOM.setAttribute("aria-live", "polite"); + this.dom = document.createElement("div"); + this.dom.appendChild(this.announceDOM); + this.dom.appendChild(this.scrollDOM); + if (config.parent) + config.parent.appendChild(this.dom); + let { dispatch } = config; + this.dispatchTransactions = config.dispatchTransactions || + (dispatch && ((trs) => trs.forEach(tr => dispatch(tr, this)))) || + ((trs) => this.update(trs)); + this.dispatch = this.dispatch.bind(this); + this._root = (config.root || getRoot(config.parent) || document); + this.viewState = new ViewState(config.state || state.EditorState.create(config)); + if (config.scrollTo && config.scrollTo.is(scrollIntoView)) + this.viewState.scrollTarget = config.scrollTo.value.clip(this.viewState.state); + this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec)); + for (let plugin of this.plugins) + plugin.update(this); + this.observer = new DOMObserver(this); + this.inputState = new InputState(this); + this.inputState.ensureHandlers(this.plugins); + this.docView = new DocView(this); + this.mountStyles(); + this.updateAttrs(); + this.updateState = 0 /* UpdateState.Idle */; + this.requestMeasure(); + if ((_a = document.fonts) === null || _a === void 0 ? void 0 : _a.ready) + document.fonts.ready.then(() => this.requestMeasure()); + } + dispatch(...input) { + let trs = input.length == 1 && input[0] instanceof state.Transaction ? input + : input.length == 1 && Array.isArray(input[0]) ? input[0] + : [this.state.update(...input)]; + this.dispatchTransactions(trs, this); + } + /** + Update the view for the given array of transactions. This will + update the visible document and selection to match the state + produced by the transactions, and notify view plugins of the + change. You should usually call + [`dispatch`](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch) instead, which uses this + as a primitive. + */ + update(transactions) { + if (this.updateState != 0 /* UpdateState.Idle */) + throw new Error("Calls to EditorView.update are not allowed while an update is in progress"); + let redrawn = false, attrsChanged = false, update; + let state$1 = this.state; + for (let tr of transactions) { + if (tr.startState != state$1) + throw new RangeError("Trying to update state with a transaction that doesn't start from the previous state."); + state$1 = tr.state; + } + if (this.destroyed) { + this.viewState.state = state$1; + return; + } + let focus = this.hasFocus, focusFlag = 0, dispatchFocus = null; + if (transactions.some(tr => tr.annotation(isFocusChange))) { + this.inputState.notifiedFocused = focus; + // If a focus-change transaction is being dispatched, set this update flag. + focusFlag = 1 /* UpdateFlag.Focus */; + } + else if (focus != this.inputState.notifiedFocused) { + this.inputState.notifiedFocused = focus; + // Schedule a separate focus transaction if necessary, otherwise + // add a flag to this update + dispatchFocus = focusChangeTransaction(state$1, focus); + if (!dispatchFocus) + focusFlag = 1 /* UpdateFlag.Focus */; + } + // If there was a pending DOM change, eagerly read it and try to + // apply it after the given transactions. + let pendingKey = this.observer.delayedAndroidKey, domChange = null; + if (pendingKey) { + this.observer.clearDelayedAndroidKey(); + domChange = this.observer.readChange(); + // Only try to apply DOM changes if the transactions didn't + // change the doc or selection. + if (domChange && !this.state.doc.eq(state$1.doc) || !this.state.selection.eq(state$1.selection)) + domChange = null; + } + else { + this.observer.clear(); + } + // When the phrases change, redraw the editor + if (state$1.facet(state.EditorState.phrases) != this.state.facet(state.EditorState.phrases)) + return this.setState(state$1); + update = ViewUpdate.create(this, state$1, transactions); + update.flags |= focusFlag; + let scrollTarget = this.viewState.scrollTarget; + try { + this.updateState = 2 /* UpdateState.Updating */; + for (let tr of transactions) { + if (scrollTarget) + scrollTarget = scrollTarget.map(tr.changes); + if (tr.scrollIntoView) { + let { main } = tr.state.selection; + scrollTarget = new ScrollTarget(main.empty ? main : state.EditorSelection.cursor(main.head, main.head > main.anchor ? -1 : 1)); + } + for (let e of tr.effects) + if (e.is(scrollIntoView)) + scrollTarget = e.value.clip(this.state); + } + this.viewState.update(update, scrollTarget); + this.bidiCache = CachedOrder.update(this.bidiCache, update.changes); + if (!update.empty) { + this.updatePlugins(update); + this.inputState.update(update); + } + redrawn = this.docView.update(update); + if (this.state.facet(styleModule) != this.styleModules) + this.mountStyles(); + attrsChanged = this.updateAttrs(); + this.showAnnouncements(transactions); + this.docView.updateSelection(redrawn, transactions.some(tr => tr.isUserEvent("select.pointer"))); + } + finally { + this.updateState = 0 /* UpdateState.Idle */; + } + if (update.startState.facet(theme) != update.state.facet(theme)) + this.viewState.mustMeasureContent = true; + if (redrawn || attrsChanged || scrollTarget || this.viewState.mustEnforceCursorAssoc || this.viewState.mustMeasureContent) + this.requestMeasure(); + if (redrawn) + this.docViewUpdate(); + if (!update.empty) + for (let listener of this.state.facet(updateListener)) { + try { + listener(update); + } + catch (e) { + logException(this.state, e, "update listener"); + } + } + if (dispatchFocus || domChange) + Promise.resolve().then(() => { + if (dispatchFocus && this.state == dispatchFocus.startState) + this.dispatch(dispatchFocus); + if (domChange) { + if (!applyDOMChange(this, domChange) && pendingKey.force) + dispatchKey(this.contentDOM, pendingKey.key, pendingKey.keyCode); + } + }); + } + /** + Reset the view to the given state. (This will cause the entire + document to be redrawn and all view plugins to be reinitialized, + so you should probably only use it when the new state isn't + derived from the old state. Otherwise, use + [`dispatch`](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch) instead.) + */ + setState(newState) { + if (this.updateState != 0 /* UpdateState.Idle */) + throw new Error("Calls to EditorView.setState are not allowed while an update is in progress"); + if (this.destroyed) { + this.viewState.state = newState; + return; + } + this.updateState = 2 /* UpdateState.Updating */; + let hadFocus = this.hasFocus; + try { + for (let plugin of this.plugins) + plugin.destroy(this); + this.viewState = new ViewState(newState); + this.plugins = newState.facet(viewPlugin).map(spec => new PluginInstance(spec)); + this.pluginMap.clear(); + for (let plugin of this.plugins) + plugin.update(this); + this.docView.destroy(); + this.docView = new DocView(this); + this.inputState.ensureHandlers(this.plugins); + this.mountStyles(); + this.updateAttrs(); + this.bidiCache = []; + } + finally { + this.updateState = 0 /* UpdateState.Idle */; + } + if (hadFocus) + this.focus(); + this.requestMeasure(); + } + updatePlugins(update) { + let prevSpecs = update.startState.facet(viewPlugin), specs = update.state.facet(viewPlugin); + if (prevSpecs != specs) { + let newPlugins = []; + for (let spec of specs) { + let found = prevSpecs.indexOf(spec); + if (found < 0) { + newPlugins.push(new PluginInstance(spec)); + } + else { + let plugin = this.plugins[found]; + plugin.mustUpdate = update; + newPlugins.push(plugin); + } + } + for (let plugin of this.plugins) + if (plugin.mustUpdate != update) + plugin.destroy(this); + this.plugins = newPlugins; + this.pluginMap.clear(); + } + else { + for (let p of this.plugins) + p.mustUpdate = update; + } + for (let i = 0; i < this.plugins.length; i++) + this.plugins[i].update(this); + if (prevSpecs != specs) + this.inputState.ensureHandlers(this.plugins); + } + docViewUpdate() { + for (let plugin of this.plugins) { + let val = plugin.value; + if (val && val.docViewUpdate) { + try { + val.docViewUpdate(this); + } + catch (e) { + logException(this.state, e, "doc view update listener"); + } + } + } + } + /** + @internal + */ + measure(flush = true) { + if (this.destroyed) + return; + if (this.measureScheduled > -1) + this.win.cancelAnimationFrame(this.measureScheduled); + if (this.observer.delayedAndroidKey) { + this.measureScheduled = -1; + this.requestMeasure(); + return; + } + this.measureScheduled = 0; // Prevent requestMeasure calls from scheduling another animation frame + if (flush) + this.observer.forceFlush(); + let updated = null; + let sDOM = this.scrollDOM, scrollTop = sDOM.scrollTop * this.scaleY; + let { scrollAnchorPos, scrollAnchorHeight } = this.viewState; + if (Math.abs(scrollTop - this.viewState.scrollTop) > 1) + scrollAnchorHeight = -1; + this.viewState.scrollAnchorHeight = -1; + try { + for (let i = 0;; i++) { + if (scrollAnchorHeight < 0) { + if (isScrolledToBottom(sDOM)) { + scrollAnchorPos = -1; + scrollAnchorHeight = this.viewState.heightMap.height; + } + else { + let block = this.viewState.scrollAnchorAt(scrollTop); + scrollAnchorPos = block.from; + scrollAnchorHeight = block.top; + } + } + this.updateState = 1 /* UpdateState.Measuring */; + let changed = this.viewState.measure(this); + if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null) + break; + if (i > 5) { + console.warn(this.measureRequests.length + ? "Measure loop restarted more than 5 times" + : "Viewport failed to stabilize"); + break; + } + let measuring = []; + // Only run measure requests in this cycle when the viewport didn't change + if (!(changed & 4 /* UpdateFlag.Viewport */)) + [this.measureRequests, measuring] = [measuring, this.measureRequests]; + let measured = measuring.map(m => { + try { + return m.read(this); + } + catch (e) { + logException(this.state, e); + return BadMeasure; + } + }); + let update = ViewUpdate.create(this, this.state, []), redrawn = false; + update.flags |= changed; + if (!updated) + updated = update; + else + updated.flags |= changed; + this.updateState = 2 /* UpdateState.Updating */; + if (!update.empty) { + this.updatePlugins(update); + this.inputState.update(update); + this.updateAttrs(); + redrawn = this.docView.update(update); + if (redrawn) + this.docViewUpdate(); + } + for (let i = 0; i < measuring.length; i++) + if (measured[i] != BadMeasure) { + try { + let m = measuring[i]; + if (m.write) + m.write(measured[i], this); + } + catch (e) { + logException(this.state, e); + } + } + if (redrawn) + this.docView.updateSelection(true); + if (!update.viewportChanged && this.measureRequests.length == 0) { + if (this.viewState.editorHeight) { + if (this.viewState.scrollTarget) { + this.docView.scrollIntoView(this.viewState.scrollTarget); + this.viewState.scrollTarget = null; + scrollAnchorHeight = -1; + continue; + } + else { + let newAnchorHeight = scrollAnchorPos < 0 ? this.viewState.heightMap.height : + this.viewState.lineBlockAt(scrollAnchorPos).top; + let diff = newAnchorHeight - scrollAnchorHeight; + if (diff > 1 || diff < -1) { + scrollTop = scrollTop + diff; + sDOM.scrollTop = scrollTop / this.scaleY; + scrollAnchorHeight = -1; + continue; + } + } + } + break; + } + } + } + finally { + this.updateState = 0 /* UpdateState.Idle */; + this.measureScheduled = -1; + } + if (updated && !updated.empty) + for (let listener of this.state.facet(updateListener)) + listener(updated); + } + /** + Get the CSS classes for the currently active editor themes. + */ + get themeClasses() { + return baseThemeID + " " + + (this.state.facet(darkTheme) ? baseDarkID : baseLightID) + " " + + this.state.facet(theme); + } + updateAttrs() { + let editorAttrs = attrsFromFacet(this, editorAttributes, { + class: "cm-editor" + (this.hasFocus ? " cm-focused " : " ") + this.themeClasses + }); + let contentAttrs = { + spellcheck: "false", + autocorrect: "off", + autocapitalize: "off", + writingsuggestions: "false", + translate: "no", + contenteditable: !this.state.facet(editable) ? "false" : "true", + class: "cm-content", + style: `${browser.tabSize}: ${this.state.tabSize}`, + role: "textbox", + "aria-multiline": "true" + }; + if (this.state.readOnly) + contentAttrs["aria-readonly"] = "true"; + attrsFromFacet(this, contentAttributes, contentAttrs); + let changed = this.observer.ignore(() => { + let changedContent = updateAttrs(this.contentDOM, this.contentAttrs, contentAttrs); + let changedEditor = updateAttrs(this.dom, this.editorAttrs, editorAttrs); + return changedContent || changedEditor; + }); + this.editorAttrs = editorAttrs; + this.contentAttrs = contentAttrs; + return changed; + } + showAnnouncements(trs) { + let first = true; + for (let tr of trs) + for (let effect of tr.effects) + if (effect.is(EditorView.announce)) { + if (first) + this.announceDOM.textContent = ""; + first = false; + let div = this.announceDOM.appendChild(document.createElement("div")); + div.textContent = effect.value; + } + } + mountStyles() { + this.styleModules = this.state.facet(styleModule); + let nonce = this.state.facet(EditorView.cspNonce); + styleMod.StyleModule.mount(this.root, this.styleModules.concat(baseTheme$1).reverse(), nonce ? { nonce } : undefined); + } + readMeasured() { + if (this.updateState == 2 /* UpdateState.Updating */) + throw new Error("Reading the editor layout isn't allowed during an update"); + if (this.updateState == 0 /* UpdateState.Idle */ && this.measureScheduled > -1) + this.measure(false); + } + /** + Schedule a layout measurement, optionally providing callbacks to + do custom DOM measuring followed by a DOM write phase. Using + this is preferable reading DOM layout directly from, for + example, an event handler, because it'll make sure measuring and + drawing done by other components is synchronized, avoiding + unnecessary DOM layout computations. + */ + requestMeasure(request) { + if (this.measureScheduled < 0) + this.measureScheduled = this.win.requestAnimationFrame(() => this.measure()); + if (request) { + if (this.measureRequests.indexOf(request) > -1) + return; + if (request.key != null) + for (let i = 0; i < this.measureRequests.length; i++) { + if (this.measureRequests[i].key === request.key) { + this.measureRequests[i] = request; + return; + } + } + this.measureRequests.push(request); + } + } + /** + Get the value of a specific plugin, if present. Note that + plugins that crash can be dropped from a view, so even when you + know you registered a given plugin, it is recommended to check + the return value of this method. + */ + plugin(plugin) { + let known = this.pluginMap.get(plugin); + if (known === undefined || known && known.plugin != plugin) + this.pluginMap.set(plugin, known = this.plugins.find(p => p.plugin == plugin) || null); + return known && known.update(this).value; + } + /** + The top position of the document, in screen coordinates. This + may be negative when the editor is scrolled down. Points + directly to the top of the first line, not above the padding. + */ + get documentTop() { + return this.contentDOM.getBoundingClientRect().top + this.viewState.paddingTop; + } + /** + Reports the padding above and below the document. + */ + get documentPadding() { + return { top: this.viewState.paddingTop, bottom: this.viewState.paddingBottom }; + } + /** + If the editor is transformed with CSS, this provides the scale + along the X axis. Otherwise, it will just be 1. Note that + transforms other than translation and scaling are not supported. + */ + get scaleX() { return this.viewState.scaleX; } + /** + Provide the CSS transformed scale along the Y axis. + */ + get scaleY() { return this.viewState.scaleY; } + /** + Find the text line or block widget at the given vertical + position (which is interpreted as relative to the [top of the + document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)). + */ + elementAtHeight(height) { + this.readMeasured(); + return this.viewState.elementAtHeight(height); + } + /** + Find the line block (see + [`lineBlockAt`](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt)) at the given + height, again interpreted relative to the [top of the + document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop). + */ + lineBlockAtHeight(height) { + this.readMeasured(); + return this.viewState.lineBlockAtHeight(height); + } + /** + Get the extent and vertical position of all [line + blocks](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) in the viewport. Positions + are relative to the [top of the + document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop); + */ + get viewportLineBlocks() { + return this.viewState.viewportLines; + } + /** + Find the line block around the given document position. A line + block is a range delimited on both sides by either a + non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^replace) line break, or the + start/end of the document. It will usually just hold a line of + text, but may be broken into multiple textblocks by block + widgets. + */ + lineBlockAt(pos) { + return this.viewState.lineBlockAt(pos); + } + /** + The editor's total content height. + */ + get contentHeight() { + return this.viewState.contentHeight; + } + /** + Move a cursor position by [grapheme + cluster](https://codemirror.net/6/docs/ref/#state.findClusterBreak). `forward` determines whether + the motion is away from the line start, or towards it. In + bidirectional text, the line is traversed in visual order, using + the editor's [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection). + When the start position was the last one on the line, the + returned position will be across the line break. If there is no + further line, the original position is returned. + + By default, this method moves over a single cluster. The + optional `by` argument can be used to move across more. It will + be called with the first cluster as argument, and should return + a predicate that determines, for each subsequent cluster, + whether it should also be moved over. + */ + moveByChar(start, forward, by) { + return skipAtoms(this, start, moveByChar(this, start, forward, by)); + } + /** + Move a cursor position across the next group of either + [letters](https://codemirror.net/6/docs/ref/#state.EditorState.charCategorizer) or non-letter + non-whitespace characters. + */ + moveByGroup(start, forward) { + return skipAtoms(this, start, moveByChar(this, start, forward, initial => byGroup(this, start.head, initial))); + } + /** + Get the cursor position visually at the start or end of a line. + Note that this may differ from the _logical_ position at its + start or end (which is simply at `line.from`/`line.to`) if text + at the start or end goes against the line's base text direction. + */ + visualLineSide(line, end) { + let order = this.bidiSpans(line), dir = this.textDirectionAt(line.from); + let span = order[end ? order.length - 1 : 0]; + return state.EditorSelection.cursor(span.side(end, dir) + line.from, span.forward(!end, dir) ? 1 : -1); + } + /** + Move to the next line boundary in the given direction. If + `includeWrap` is true, line wrapping is on, and there is a + further wrap point on the current line, the wrap point will be + returned. Otherwise this function will return the start or end + of the line. + */ + moveToLineBoundary(start, forward, includeWrap = true) { + return moveToLineBoundary(this, start, forward, includeWrap); + } + /** + Move a cursor position vertically. When `distance` isn't given, + it defaults to moving to the next line (including wrapped + lines). Otherwise, `distance` should provide a positive distance + in pixels. + + When `start` has a + [`goalColumn`](https://codemirror.net/6/docs/ref/#state.SelectionRange.goalColumn), the vertical + motion will use that as a target horizontal position. Otherwise, + the cursor's own horizontal position is used. The returned + cursor will have its goal column set to whichever column was + used. + */ + moveVertically(start, forward, distance) { + return skipAtoms(this, start, moveVertically(this, start, forward, distance)); + } + /** + Find the DOM parent node and offset (child offset if `node` is + an element, character offset when it is a text node) at the + given document position. + + Note that for positions that aren't currently in + `visibleRanges`, the resulting DOM position isn't necessarily + meaningful (it may just point before or after a placeholder + element). + */ + domAtPos(pos) { + return this.docView.domAtPos(pos); + } + /** + Find the document position at the given DOM node. Can be useful + for associating positions with DOM events. Will raise an error + when `node` isn't part of the editor content. + */ + posAtDOM(node, offset = 0) { + return this.docView.posFromDOM(node, offset); + } + posAtCoords(coords, precise = true) { + this.readMeasured(); + return posAtCoords(this, coords, precise); + } + /** + Get the screen coordinates at the given document position. + `side` determines whether the coordinates are based on the + element before (-1) or after (1) the position (if no element is + available on the given side, the method will transparently use + another strategy to get reasonable coordinates). + */ + coordsAtPos(pos, side = 1) { + this.readMeasured(); + let rect = this.docView.coordsAt(pos, side); + if (!rect || rect.left == rect.right) + return rect; + let line = this.state.doc.lineAt(pos), order = this.bidiSpans(line); + let span = order[BidiSpan.find(order, pos - line.from, -1, side)]; + return flattenRect(rect, (span.dir == exports.Direction.LTR) == (side > 0)); + } + /** + Return the rectangle around a given character. If `pos` does not + point in front of a character that is in the viewport and + rendered (i.e. not replaced, not a line break), this will return + null. For space characters that are a line wrap point, this will + return the position before the line break. + */ + coordsForChar(pos) { + this.readMeasured(); + return this.docView.coordsForChar(pos); + } + /** + The default width of a character in the editor. May not + accurately reflect the width of all characters (given variable + width fonts or styling of invididual ranges). + */ + get defaultCharacterWidth() { return this.viewState.heightOracle.charWidth; } + /** + The default height of a line in the editor. May not be accurate + for all lines. + */ + get defaultLineHeight() { return this.viewState.heightOracle.lineHeight; } + /** + The text direction + ([`direction`](https://developer.mozilla.org/en-US/docs/Web/CSS/direction) + CSS property) of the editor's content element. + */ + get textDirection() { return this.viewState.defaultTextDirection; } + /** + Find the text direction of the block at the given position, as + assigned by CSS. If + [`perLineTextDirection`](https://codemirror.net/6/docs/ref/#view.EditorView^perLineTextDirection) + isn't enabled, or the given position is outside of the viewport, + this will always return the same as + [`textDirection`](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection). Note that + this may trigger a DOM layout. + */ + textDirectionAt(pos) { + let perLine = this.state.facet(perLineTextDirection); + if (!perLine || pos < this.viewport.from || pos > this.viewport.to) + return this.textDirection; + this.readMeasured(); + return this.docView.textDirectionAt(pos); + } + /** + Whether this editor [wraps lines](https://codemirror.net/6/docs/ref/#view.EditorView.lineWrapping) + (as determined by the + [`white-space`](https://developer.mozilla.org/en-US/docs/Web/CSS/white-space) + CSS property of its content element). + */ + get lineWrapping() { return this.viewState.heightOracle.lineWrapping; } + /** + Returns the bidirectional text structure of the given line + (which should be in the current document) as an array of span + objects. The order of these spans matches the [text + direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection)—if that is + left-to-right, the leftmost spans come first, otherwise the + rightmost spans come first. + */ + bidiSpans(line) { + if (line.length > MaxBidiLine) + return trivialOrder(line.length); + let dir = this.textDirectionAt(line.from), isolates; + for (let entry of this.bidiCache) { + if (entry.from == line.from && entry.dir == dir && + (entry.fresh || isolatesEq(entry.isolates, isolates = getIsolatedRanges(this, line)))) + return entry.order; + } + if (!isolates) + isolates = getIsolatedRanges(this, line); + let order = computeOrder(line.text, dir, isolates); + this.bidiCache.push(new CachedOrder(line.from, line.to, dir, isolates, true, order)); + return order; + } + /** + Check whether the editor has focus. + */ + get hasFocus() { + var _a; + // Safari return false for hasFocus when the context menu is open + // or closing, which leads us to ignore selection changes from the + // context menu because it looks like the editor isn't focused. + // This kludges around that. + return (this.dom.ownerDocument.hasFocus() || browser.safari && ((_a = this.inputState) === null || _a === void 0 ? void 0 : _a.lastContextMenu) > Date.now() - 3e4) && + this.root.activeElement == this.contentDOM; + } + /** + Put focus on the editor. + */ + focus() { + this.observer.ignore(() => { + focusPreventScroll(this.contentDOM); + this.docView.updateSelection(); + }); + } + /** + Update the [root](https://codemirror.net/6/docs/ref/##view.EditorViewConfig.root) in which the editor lives. This is only + necessary when moving the editor's existing DOM to a new window or shadow root. + */ + setRoot(root) { + if (this._root != root) { + this._root = root; + this.observer.setWindow((root.nodeType == 9 ? root : root.ownerDocument).defaultView || window); + this.mountStyles(); + } + } + /** + Clean up this editor view, removing its element from the + document, unregistering event handlers, and notifying + plugins. The view instance can no longer be used after + calling this. + */ + destroy() { + if (this.root.activeElement == this.contentDOM) + this.contentDOM.blur(); + for (let plugin of this.plugins) + plugin.destroy(this); + this.plugins = []; + this.inputState.destroy(); + this.docView.destroy(); + this.dom.remove(); + this.observer.destroy(); + if (this.measureScheduled > -1) + this.win.cancelAnimationFrame(this.measureScheduled); + this.destroyed = true; + } + /** + Returns an effect that can be + [added](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) to a transaction to + cause it to scroll the given position or range into view. + */ + static scrollIntoView(pos, options = {}) { + return scrollIntoView.of(new ScrollTarget(typeof pos == "number" ? state.EditorSelection.cursor(pos) : pos, options.y, options.x, options.yMargin, options.xMargin)); + } + /** + Return an effect that resets the editor to its current (at the + time this method was called) scroll position. Note that this + only affects the editor's own scrollable element, not parents. + See also + [`EditorViewConfig.scrollTo`](https://codemirror.net/6/docs/ref/#view.EditorViewConfig.scrollTo). + + The effect should be used with a document identical to the one + it was created for. Failing to do so is not an error, but may + not scroll to the expected position. You can + [map](https://codemirror.net/6/docs/ref/#state.StateEffect.map) the effect to account for changes. + */ + scrollSnapshot() { + let { scrollTop, scrollLeft } = this.scrollDOM; + let ref = this.viewState.scrollAnchorAt(scrollTop); + return scrollIntoView.of(new ScrollTarget(state.EditorSelection.cursor(ref.from), "start", "start", ref.top - scrollTop, scrollLeft, true)); + } + /** + Enable or disable tab-focus mode, which disables key bindings + for Tab and Shift-Tab, letting the browser's default + focus-changing behavior go through instead. This is useful to + prevent trapping keyboard users in your editor. + + Without argument, this toggles the mode. With a boolean, it + enables (true) or disables it (false). Given a number, it + temporarily enables the mode until that number of milliseconds + have passed or another non-Tab key is pressed. + */ + setTabFocusMode(to) { + if (to == null) + this.inputState.tabFocusMode = this.inputState.tabFocusMode < 0 ? 0 : -1; + else if (typeof to == "boolean") + this.inputState.tabFocusMode = to ? 0 : -1; + else if (this.inputState.tabFocusMode != 0) + this.inputState.tabFocusMode = Date.now() + to; + } + /** + Returns an extension that can be used to add DOM event handlers. + The value should be an object mapping event names to handler + functions. For any given event, such functions are ordered by + extension precedence, and the first handler to return true will + be assumed to have handled that event, and no other handlers or + built-in behavior will be activated for it. These are registered + on the [content element](https://codemirror.net/6/docs/ref/#view.EditorView.contentDOM), except + for `scroll` handlers, which will be called any time the + editor's [scroll element](https://codemirror.net/6/docs/ref/#view.EditorView.scrollDOM) or one of + its parent nodes is scrolled. + */ + static domEventHandlers(handlers) { + return ViewPlugin.define(() => ({}), { eventHandlers: handlers }); + } + /** + Create an extension that registers DOM event observers. Contrary + to event [handlers](https://codemirror.net/6/docs/ref/#view.EditorView^domEventHandlers), + observers can't be prevented from running by a higher-precedence + handler returning true. They also don't prevent other handlers + and observers from running when they return true, and should not + call `preventDefault`. + */ + static domEventObservers(observers) { + return ViewPlugin.define(() => ({}), { eventObservers: observers }); + } + /** + Create a theme extension. The first argument can be a + [`style-mod`](https://github.com/marijnh/style-mod#documentation) + style spec providing the styles for the theme. These will be + prefixed with a generated class for the style. + + Because the selectors will be prefixed with a scope class, rule + that directly match the editor's [wrapper + element](https://codemirror.net/6/docs/ref/#view.EditorView.dom)—to which the scope class will be + added—need to be explicitly differentiated by adding an `&` to + the selector for that element—for example + `&.cm-focused`. + + When `dark` is set to true, the theme will be marked as dark, + which will cause the `&dark` rules from [base + themes](https://codemirror.net/6/docs/ref/#view.EditorView^baseTheme) to be used (as opposed to + `&light` when a light theme is active). + */ + static theme(spec, options) { + let prefix = styleMod.StyleModule.newName(); + let result = [theme.of(prefix), styleModule.of(buildTheme(`.${prefix}`, spec))]; + if (options && options.dark) + result.push(darkTheme.of(true)); + return result; + } + /** + Create an extension that adds styles to the base theme. Like + with [`theme`](https://codemirror.net/6/docs/ref/#view.EditorView^theme), use `&` to indicate the + place of the editor wrapper element when directly targeting + that. You can also use `&dark` or `&light` instead to only + target editors with a dark or light theme. + */ + static baseTheme(spec) { + return state.Prec.lowest(styleModule.of(buildTheme("." + baseThemeID, spec, lightDarkIDs))); + } + /** + Retrieve an editor view instance from the view's DOM + representation. + */ + static findFromDOM(dom) { + var _a; + let content = dom.querySelector(".cm-content"); + let cView = content && ContentView.get(content) || ContentView.get(dom); + return ((_a = cView === null || cView === void 0 ? void 0 : cView.rootView) === null || _a === void 0 ? void 0 : _a.view) || null; + } +} +/** +Facet to add a [style +module](https://github.com/marijnh/style-mod#documentation) to +an editor view. The view will ensure that the module is +mounted in its [document +root](https://codemirror.net/6/docs/ref/#view.EditorView.constructor^config.root). +*/ +EditorView.styleModule = styleModule; +/** +An input handler can override the way changes to the editable +DOM content are handled. Handlers are passed the document +positions between which the change was found, and the new +content. When one returns true, no further input handlers are +called and the default behavior is prevented. + +The `insert` argument can be used to get the default transaction +that would be applied for this input. This can be useful when +dispatching the custom behavior as a separate transaction. +*/ +EditorView.inputHandler = inputHandler; +/** +Functions provided in this facet will be used to transform text +pasted or dropped into the editor. +*/ +EditorView.clipboardInputFilter = clipboardInputFilter; +/** +Transform text copied or dragged from the editor. +*/ +EditorView.clipboardOutputFilter = clipboardOutputFilter; +/** +Scroll handlers can override how things are scrolled into view. +If they return `true`, no further handling happens for the +scrolling. If they return false, the default scroll behavior is +applied. Scroll handlers should never initiate editor updates. +*/ +EditorView.scrollHandler = scrollHandler; +/** +This facet can be used to provide functions that create effects +to be dispatched when the editor's focus state changes. +*/ +EditorView.focusChangeEffect = focusChangeEffect; +/** +By default, the editor assumes all its content has the same +[text direction](https://codemirror.net/6/docs/ref/#view.Direction). Configure this with a `true` +value to make it read the text direction of every (rendered) +line separately. +*/ +EditorView.perLineTextDirection = perLineTextDirection; +/** +Allows you to provide a function that should be called when the +library catches an exception from an extension (mostly from view +plugins, but may be used by other extensions to route exceptions +from user-code-provided callbacks). This is mostly useful for +debugging and logging. See [`logException`](https://codemirror.net/6/docs/ref/#view.logException). +*/ +EditorView.exceptionSink = exceptionSink; +/** +A facet that can be used to register a function to be called +every time the view updates. +*/ +EditorView.updateListener = updateListener; +/** +Facet that controls whether the editor content DOM is editable. +When its highest-precedence value is `false`, the element will +not have its `contenteditable` attribute set. (Note that this +doesn't affect API calls that change the editor content, even +when those are bound to keys or buttons. See the +[`readOnly`](https://codemirror.net/6/docs/ref/#state.EditorState.readOnly) facet for that.) +*/ +EditorView.editable = editable; +/** +Allows you to influence the way mouse selection happens. The +functions in this facet will be called for a `mousedown` event +on the editor, and can return an object that overrides the way a +selection is computed from that mouse click or drag. +*/ +EditorView.mouseSelectionStyle = mouseSelectionStyle; +/** +Facet used to configure whether a given selection drag event +should move or copy the selection. The given predicate will be +called with the `mousedown` event, and can return `true` when +the drag should move the content. +*/ +EditorView.dragMovesSelection = dragMovesSelection$1; +/** +Facet used to configure whether a given selecting click adds a +new range to the existing selection or replaces it entirely. The +default behavior is to check `event.metaKey` on macOS, and +`event.ctrlKey` elsewhere. +*/ +EditorView.clickAddsSelectionRange = clickAddsSelectionRange; +/** +A facet that determines which [decorations](https://codemirror.net/6/docs/ref/#view.Decoration) +are shown in the view. Decorations can be provided in two +ways—directly, or via a function that takes an editor view. + +Only decoration sets provided directly are allowed to influence +the editor's vertical layout structure. The ones provided as +functions are called _after_ the new viewport has been computed, +and thus **must not** introduce block widgets or replacing +decorations that cover line breaks. + +If you want decorated ranges to behave like atomic units for +cursor motion and deletion purposes, also provide the range set +containing the decorations to +[`EditorView.atomicRanges`](https://codemirror.net/6/docs/ref/#view.EditorView^atomicRanges). +*/ +EditorView.decorations = decorations; +/** +Facet that works much like +[`decorations`](https://codemirror.net/6/docs/ref/#view.EditorView^decorations), but puts its +inputs at the very bottom of the precedence stack, meaning mark +decorations provided here will only be split by other, partially +overlapping \`outerDecorations\` ranges, and wrap around all +regular decorations. Use this for mark elements that should, as +much as possible, remain in one piece. +*/ +EditorView.outerDecorations = outerDecorations; +/** +Used to provide ranges that should be treated as atoms as far as +cursor motion is concerned. This causes methods like +[`moveByChar`](https://codemirror.net/6/docs/ref/#view.EditorView.moveByChar) and +[`moveVertically`](https://codemirror.net/6/docs/ref/#view.EditorView.moveVertically) (and the +commands built on top of them) to skip across such regions when +a selection endpoint would enter them. This does _not_ prevent +direct programmatic [selection +updates](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection) from moving into such +regions. +*/ +EditorView.atomicRanges = atomicRanges; +/** +When range decorations add a `unicode-bidi: isolate` style, they +should also include a +[`bidiIsolate`](https://codemirror.net/6/docs/ref/#view.MarkDecorationSpec.bidiIsolate) property +in their decoration spec, and be exposed through this facet, so +that the editor can compute the proper text order. (Other values +for `unicode-bidi`, except of course `normal`, are not +supported.) +*/ +EditorView.bidiIsolatedRanges = bidiIsolatedRanges; +/** +Facet that allows extensions to provide additional scroll +margins (space around the sides of the scrolling element that +should be considered invisible). This can be useful when the +plugin introduces elements that cover part of that element (for +example a horizontally fixed gutter). +*/ +EditorView.scrollMargins = scrollMargins; +/** +This facet records whether a dark theme is active. The extension +returned by [`theme`](https://codemirror.net/6/docs/ref/#view.EditorView^theme) automatically +includes an instance of this when the `dark` option is set to +true. +*/ +EditorView.darkTheme = darkTheme; +/** +Provides a Content Security Policy nonce to use when creating +the style sheets for the editor. Holds the empty string when no +nonce has been provided. +*/ +EditorView.cspNonce = state.Facet.define({ combine: values => values.length ? values[0] : "" }); +/** +Facet that provides additional DOM attributes for the editor's +editable DOM element. +*/ +EditorView.contentAttributes = contentAttributes; +/** +Facet that provides DOM attributes for the editor's outer +element. +*/ +EditorView.editorAttributes = editorAttributes; +/** +An extension that enables line wrapping in the editor (by +setting CSS `white-space` to `pre-wrap` in the content). +*/ +EditorView.lineWrapping = EditorView.contentAttributes.of({ "class": "cm-lineWrapping" }); +/** +State effect used to include screen reader announcements in a +transaction. These will be added to the DOM in a visually hidden +element with `aria-live="polite"` set, and should be used to +describe effects that are visually obvious but may not be +noticed by screen reader users (such as moving to the next +search match). +*/ +EditorView.announce = state.StateEffect.define(); +// Maximum line length for which we compute accurate bidi info +const MaxBidiLine = 4096; +const BadMeasure = {}; +class CachedOrder { + constructor(from, to, dir, isolates, fresh, order) { + this.from = from; + this.to = to; + this.dir = dir; + this.isolates = isolates; + this.fresh = fresh; + this.order = order; + } + static update(cache, changes) { + if (changes.empty && !cache.some(c => c.fresh)) + return cache; + let result = [], lastDir = cache.length ? cache[cache.length - 1].dir : exports.Direction.LTR; + for (let i = Math.max(0, cache.length - 10); i < cache.length; i++) { + let entry = cache[i]; + if (entry.dir == lastDir && !changes.touchesRange(entry.from, entry.to)) + result.push(new CachedOrder(changes.mapPos(entry.from, 1), changes.mapPos(entry.to, -1), entry.dir, entry.isolates, false, entry.order)); + } + return result; + } +} +function attrsFromFacet(view, facet, base) { + for (let sources = view.state.facet(facet), i = sources.length - 1; i >= 0; i--) { + let source = sources[i], value = typeof source == "function" ? source(view) : source; + if (value) + combineAttrs(value, base); + } + return base; +} + +const currentPlatform = browser.mac ? "mac" : browser.windows ? "win" : browser.linux ? "linux" : "key"; +function normalizeKeyName(name, platform) { + const parts = name.split(/-(?!$)/); + let result = parts[parts.length - 1]; + if (result == "Space") + result = " "; + let alt, ctrl, shift, meta; + for (let i = 0; i < parts.length - 1; ++i) { + const mod = parts[i]; + if (/^(cmd|meta|m)$/i.test(mod)) + meta = true; + else if (/^a(lt)?$/i.test(mod)) + alt = true; + else if (/^(c|ctrl|control)$/i.test(mod)) + ctrl = true; + else if (/^s(hift)?$/i.test(mod)) + shift = true; + else if (/^mod$/i.test(mod)) { + if (platform == "mac") + meta = true; + else + ctrl = true; + } + else + throw new Error("Unrecognized modifier name: " + mod); + } + if (alt) + result = "Alt-" + result; + if (ctrl) + result = "Ctrl-" + result; + if (meta) + result = "Meta-" + result; + if (shift) + result = "Shift-" + result; + return result; +} +function modifiers(name, event, shift) { + if (event.altKey) + name = "Alt-" + name; + if (event.ctrlKey) + name = "Ctrl-" + name; + if (event.metaKey) + name = "Meta-" + name; + if (shift !== false && event.shiftKey) + name = "Shift-" + name; + return name; +} +const handleKeyEvents = state.Prec.default(EditorView.domEventHandlers({ + keydown(event, view) { + return runHandlers(getKeymap(view.state), event, view, "editor"); + } +})); +/** +Facet used for registering keymaps. + +You can add multiple keymaps to an editor. Their priorities +determine their precedence (the ones specified early or with high +priority get checked first). When a handler has returned `true` +for a given key, no further handlers are called. +*/ +const keymap = state.Facet.define({ enables: handleKeyEvents }); +const Keymaps = new WeakMap(); +// This is hidden behind an indirection, rather than directly computed +// by the facet, to keep internal types out of the facet's type. +function getKeymap(state) { + let bindings = state.facet(keymap); + let map = Keymaps.get(bindings); + if (!map) + Keymaps.set(bindings, map = buildKeymap(bindings.reduce((a, b) => a.concat(b), []))); + return map; +} +/** +Run the key handlers registered for a given scope. The event +object should be a `"keydown"` event. Returns true if any of the +handlers handled it. +*/ +function runScopeHandlers(view, event, scope) { + return runHandlers(getKeymap(view.state), event, view, scope); +} +let storedPrefix = null; +const PrefixTimeout = 4000; +function buildKeymap(bindings, platform = currentPlatform) { + let bound = Object.create(null); + let isPrefix = Object.create(null); + let checkPrefix = (name, is) => { + let current = isPrefix[name]; + if (current == null) + isPrefix[name] = is; + else if (current != is) + throw new Error("Key binding " + name + " is used both as a regular binding and as a multi-stroke prefix"); + }; + let add = (scope, key, command, preventDefault, stopPropagation) => { + var _a, _b; + let scopeObj = bound[scope] || (bound[scope] = Object.create(null)); + let parts = key.split(/ (?!$)/).map(k => normalizeKeyName(k, platform)); + for (let i = 1; i < parts.length; i++) { + let prefix = parts.slice(0, i).join(" "); + checkPrefix(prefix, true); + if (!scopeObj[prefix]) + scopeObj[prefix] = { + preventDefault: true, + stopPropagation: false, + run: [(view) => { + let ourObj = storedPrefix = { view, prefix, scope }; + setTimeout(() => { if (storedPrefix == ourObj) + storedPrefix = null; }, PrefixTimeout); + return true; + }] + }; + } + let full = parts.join(" "); + checkPrefix(full, false); + let binding = scopeObj[full] || (scopeObj[full] = { + preventDefault: false, + stopPropagation: false, + run: ((_b = (_a = scopeObj._any) === null || _a === void 0 ? void 0 : _a.run) === null || _b === void 0 ? void 0 : _b.slice()) || [] + }); + if (command) + binding.run.push(command); + if (preventDefault) + binding.preventDefault = true; + if (stopPropagation) + binding.stopPropagation = true; + }; + for (let b of bindings) { + let scopes = b.scope ? b.scope.split(" ") : ["editor"]; + if (b.any) + for (let scope of scopes) { + let scopeObj = bound[scope] || (bound[scope] = Object.create(null)); + if (!scopeObj._any) + scopeObj._any = { preventDefault: false, stopPropagation: false, run: [] }; + let { any } = b; + for (let key in scopeObj) + scopeObj[key].run.push(view => any(view, currentKeyEvent)); + } + let name = b[platform] || b.key; + if (!name) + continue; + for (let scope of scopes) { + add(scope, name, b.run, b.preventDefault, b.stopPropagation); + if (b.shift) + add(scope, "Shift-" + name, b.shift, b.preventDefault, b.stopPropagation); + } + } + return bound; +} +let currentKeyEvent = null; +function runHandlers(map, event, view, scope) { + currentKeyEvent = event; + let name = w3cKeyname.keyName(event); + let charCode = state.codePointAt(name, 0), isChar = state.codePointSize(charCode) == name.length && name != " "; + let prefix = "", handled = false, prevented = false, stopPropagation = false; + if (storedPrefix && storedPrefix.view == view && storedPrefix.scope == scope) { + prefix = storedPrefix.prefix + " "; + if (modifierCodes.indexOf(event.keyCode) < 0) { + prevented = true; + storedPrefix = null; + } + } + let ran = new Set; + let runFor = (binding) => { + if (binding) { + for (let cmd of binding.run) + if (!ran.has(cmd)) { + ran.add(cmd); + if (cmd(view)) { + if (binding.stopPropagation) + stopPropagation = true; + return true; + } + } + if (binding.preventDefault) { + if (binding.stopPropagation) + stopPropagation = true; + prevented = true; + } + } + return false; + }; + let scopeObj = map[scope], baseName, shiftName; + if (scopeObj) { + if (runFor(scopeObj[prefix + modifiers(name, event, !isChar)])) { + handled = true; + } + else if (isChar && (event.altKey || event.metaKey || event.ctrlKey) && + // Ctrl-Alt may be used for AltGr on Windows + !(browser.windows && event.ctrlKey && event.altKey) && + // Alt-combinations on macOS tend to be typed characters + !(browser.mac && event.altKey && !(event.ctrlKey || event.metaKey)) && + (baseName = w3cKeyname.base[event.keyCode]) && baseName != name) { + if (runFor(scopeObj[prefix + modifiers(baseName, event, true)])) { + handled = true; + } + else if (event.shiftKey && (shiftName = w3cKeyname.shift[event.keyCode]) != name && shiftName != baseName && + runFor(scopeObj[prefix + modifiers(shiftName, event, false)])) { + handled = true; + } + } + else if (isChar && event.shiftKey && + runFor(scopeObj[prefix + modifiers(name, event, true)])) { + handled = true; + } + if (!handled && runFor(scopeObj._any)) + handled = true; + } + if (prevented) + handled = true; + if (handled && stopPropagation) + event.stopPropagation(); + currentKeyEvent = null; + return handled; +} + +/** +Implementation of [`LayerMarker`](https://codemirror.net/6/docs/ref/#view.LayerMarker) that creates +a rectangle at a given set of coordinates. +*/ +class RectangleMarker { + /** + Create a marker with the given class and dimensions. If `width` + is null, the DOM element will get no width style. + */ + constructor(className, + /** + The left position of the marker (in pixels, document-relative). + */ + left, + /** + The top position of the marker. + */ + top, + /** + The width of the marker, or null if it shouldn't get a width assigned. + */ + width, + /** + The height of the marker. + */ + height) { + this.className = className; + this.left = left; + this.top = top; + this.width = width; + this.height = height; + } + draw() { + let elt = document.createElement("div"); + elt.className = this.className; + this.adjust(elt); + return elt; + } + update(elt, prev) { + if (prev.className != this.className) + return false; + this.adjust(elt); + return true; + } + adjust(elt) { + elt.style.left = this.left + "px"; + elt.style.top = this.top + "px"; + if (this.width != null) + elt.style.width = this.width + "px"; + elt.style.height = this.height + "px"; + } + eq(p) { + return this.left == p.left && this.top == p.top && this.width == p.width && this.height == p.height && + this.className == p.className; + } + /** + Create a set of rectangles for the given selection range, + assigning them theclass`className`. Will create a single + rectangle for empty ranges, and a set of selection-style + rectangles covering the range's content (in a bidi-aware + way) for non-empty ones. + */ + static forRange(view, className, range) { + if (range.empty) { + let pos = view.coordsAtPos(range.head, range.assoc || 1); + if (!pos) + return []; + let base = getBase(view); + return [new RectangleMarker(className, pos.left - base.left, pos.top - base.top, null, pos.bottom - pos.top)]; + } + else { + return rectanglesForRange(view, className, range); + } + } +} +function getBase(view) { + let rect = view.scrollDOM.getBoundingClientRect(); + let left = view.textDirection == exports.Direction.LTR ? rect.left : rect.right - view.scrollDOM.clientWidth * view.scaleX; + return { left: left - view.scrollDOM.scrollLeft * view.scaleX, top: rect.top - view.scrollDOM.scrollTop * view.scaleY }; +} +function wrappedLine(view, pos, side, inside) { + let coords = view.coordsAtPos(pos, side * 2); + if (!coords) + return inside; + let editorRect = view.dom.getBoundingClientRect(); + let y = (coords.top + coords.bottom) / 2; + let left = view.posAtCoords({ x: editorRect.left + 1, y }); + let right = view.posAtCoords({ x: editorRect.right - 1, y }); + if (left == null || right == null) + return inside; + return { from: Math.max(inside.from, Math.min(left, right)), to: Math.min(inside.to, Math.max(left, right)) }; +} +function rectanglesForRange(view, className, range) { + if (range.to <= view.viewport.from || range.from >= view.viewport.to) + return []; + let from = Math.max(range.from, view.viewport.from), to = Math.min(range.to, view.viewport.to); + let ltr = view.textDirection == exports.Direction.LTR; + let content = view.contentDOM, contentRect = content.getBoundingClientRect(), base = getBase(view); + let lineElt = content.querySelector(".cm-line"), lineStyle = lineElt && window.getComputedStyle(lineElt); + let leftSide = contentRect.left + + (lineStyle ? parseInt(lineStyle.paddingLeft) + Math.min(0, parseInt(lineStyle.textIndent)) : 0); + let rightSide = contentRect.right - (lineStyle ? parseInt(lineStyle.paddingRight) : 0); + let startBlock = blockAt(view, from, 1), endBlock = blockAt(view, to, -1); + let visualStart = startBlock.type == exports.BlockType.Text ? startBlock : null; + let visualEnd = endBlock.type == exports.BlockType.Text ? endBlock : null; + if (visualStart && (view.lineWrapping || startBlock.widgetLineBreaks)) + visualStart = wrappedLine(view, from, 1, visualStart); + if (visualEnd && (view.lineWrapping || endBlock.widgetLineBreaks)) + visualEnd = wrappedLine(view, to, -1, visualEnd); + if (visualStart && visualEnd && visualStart.from == visualEnd.from && visualStart.to == visualEnd.to) { + return pieces(drawForLine(range.from, range.to, visualStart)); + } + else { + let top = visualStart ? drawForLine(range.from, null, visualStart) : drawForWidget(startBlock, false); + let bottom = visualEnd ? drawForLine(null, range.to, visualEnd) : drawForWidget(endBlock, true); + let between = []; + if ((visualStart || startBlock).to < (visualEnd || endBlock).from - (visualStart && visualEnd ? 1 : 0) || + startBlock.widgetLineBreaks > 1 && top.bottom + view.defaultLineHeight / 2 < bottom.top) + between.push(piece(leftSide, top.bottom, rightSide, bottom.top)); + else if (top.bottom < bottom.top && view.elementAtHeight((top.bottom + bottom.top) / 2).type == exports.BlockType.Text) + top.bottom = bottom.top = (top.bottom + bottom.top) / 2; + return pieces(top).concat(between).concat(pieces(bottom)); + } + function piece(left, top, right, bottom) { + return new RectangleMarker(className, left - base.left, top - base.top, right - left, bottom - top); + } + function pieces({ top, bottom, horizontal }) { + let pieces = []; + for (let i = 0; i < horizontal.length; i += 2) + pieces.push(piece(horizontal[i], top, horizontal[i + 1], bottom)); + return pieces; + } + // Gets passed from/to in line-local positions + function drawForLine(from, to, line) { + let top = 1e9, bottom = -1e9, horizontal = []; + function addSpan(from, fromOpen, to, toOpen, dir) { + // Passing 2/-2 is a kludge to force the view to return + // coordinates on the proper side of block widgets, since + // normalizing the side there, though appropriate for most + // coordsAtPos queries, would break selection drawing. + let fromCoords = view.coordsAtPos(from, (from == line.to ? -2 : 2)); + let toCoords = view.coordsAtPos(to, (to == line.from ? 2 : -2)); + if (!fromCoords || !toCoords) + return; + top = Math.min(fromCoords.top, toCoords.top, top); + bottom = Math.max(fromCoords.bottom, toCoords.bottom, bottom); + if (dir == exports.Direction.LTR) + horizontal.push(ltr && fromOpen ? leftSide : fromCoords.left, ltr && toOpen ? rightSide : toCoords.right); + else + horizontal.push(!ltr && toOpen ? leftSide : toCoords.left, !ltr && fromOpen ? rightSide : fromCoords.right); + } + let start = from !== null && from !== void 0 ? from : line.from, end = to !== null && to !== void 0 ? to : line.to; + // Split the range by visible range and document line + for (let r of view.visibleRanges) + if (r.to > start && r.from < end) { + for (let pos = Math.max(r.from, start), endPos = Math.min(r.to, end);;) { + let docLine = view.state.doc.lineAt(pos); + for (let span of view.bidiSpans(docLine)) { + let spanFrom = span.from + docLine.from, spanTo = span.to + docLine.from; + if (spanFrom >= endPos) + break; + if (spanTo > pos) + addSpan(Math.max(spanFrom, pos), from == null && spanFrom <= start, Math.min(spanTo, endPos), to == null && spanTo >= end, span.dir); + } + pos = docLine.to + 1; + if (pos >= endPos) + break; + } + } + if (horizontal.length == 0) + addSpan(start, from == null, end, to == null, view.textDirection); + return { top, bottom, horizontal }; + } + function drawForWidget(block, top) { + let y = contentRect.top + (top ? block.top : block.bottom); + return { top: y, bottom: y, horizontal: [] }; + } +} +function sameMarker(a, b) { + return a.constructor == b.constructor && a.eq(b); +} +class LayerView { + constructor(view, layer) { + this.view = view; + this.layer = layer; + this.drawn = []; + this.scaleX = 1; + this.scaleY = 1; + this.measureReq = { read: this.measure.bind(this), write: this.draw.bind(this) }; + this.dom = view.scrollDOM.appendChild(document.createElement("div")); + this.dom.classList.add("cm-layer"); + if (layer.above) + this.dom.classList.add("cm-layer-above"); + if (layer.class) + this.dom.classList.add(layer.class); + this.scale(); + this.dom.setAttribute("aria-hidden", "true"); + this.setOrder(view.state); + view.requestMeasure(this.measureReq); + if (layer.mount) + layer.mount(this.dom, view); + } + update(update) { + if (update.startState.facet(layerOrder) != update.state.facet(layerOrder)) + this.setOrder(update.state); + if (this.layer.update(update, this.dom) || update.geometryChanged) { + this.scale(); + update.view.requestMeasure(this.measureReq); + } + } + docViewUpdate(view) { + if (this.layer.updateOnDocViewUpdate !== false) + view.requestMeasure(this.measureReq); + } + setOrder(state) { + let pos = 0, order = state.facet(layerOrder); + while (pos < order.length && order[pos] != this.layer) + pos++; + this.dom.style.zIndex = String((this.layer.above ? 150 : -1) - pos); + } + measure() { + return this.layer.markers(this.view); + } + scale() { + let { scaleX, scaleY } = this.view; + if (scaleX != this.scaleX || scaleY != this.scaleY) { + this.scaleX = scaleX; + this.scaleY = scaleY; + this.dom.style.transform = `scale(${1 / scaleX}, ${1 / scaleY})`; + } + } + draw(markers) { + if (markers.length != this.drawn.length || markers.some((p, i) => !sameMarker(p, this.drawn[i]))) { + let old = this.dom.firstChild, oldI = 0; + for (let marker of markers) { + if (marker.update && old && marker.constructor && this.drawn[oldI].constructor && + marker.update(old, this.drawn[oldI])) { + old = old.nextSibling; + oldI++; + } + else { + this.dom.insertBefore(marker.draw(), old); + } + } + while (old) { + let next = old.nextSibling; + old.remove(); + old = next; + } + this.drawn = markers; + if (browser.safari && browser.safari_version >= 26) // Issue #1600, 1627 + this.dom.style.display = this.dom.firstChild ? "" : "none"; + } + } + destroy() { + if (this.layer.destroy) + this.layer.destroy(this.dom, this.view); + this.dom.remove(); + } +} +const layerOrder = state.Facet.define(); +/** +Define a layer. +*/ +function layer(config) { + return [ + ViewPlugin.define(v => new LayerView(v, config)), + layerOrder.of(config) + ]; +} + +const selectionConfig = state.Facet.define({ + combine(configs) { + return state.combineConfig(configs, { + cursorBlinkRate: 1200, + drawRangeCursor: true + }, { + cursorBlinkRate: (a, b) => Math.min(a, b), + drawRangeCursor: (a, b) => a || b + }); + } +}); +/** +Returns an extension that hides the browser's native selection and +cursor, replacing the selection with a background behind the text +(with the `cm-selectionBackground` class), and the +cursors with elements overlaid over the code (using +`cm-cursor-primary` and `cm-cursor-secondary`). + +This allows the editor to display secondary selection ranges, and +tends to produce a type of selection more in line with that users +expect in a text editor (the native selection styling will often +leave gaps between lines and won't fill the horizontal space after +a line when the selection continues past it). + +It does have a performance cost, in that it requires an extra DOM +layout cycle for many updates (the selection is drawn based on DOM +layout information that's only available after laying out the +content). +*/ +function drawSelection(config = {}) { + return [ + selectionConfig.of(config), + cursorLayer, + selectionLayer, + hideNativeSelection, + nativeSelectionHidden.of(true) + ]; +} +/** +Retrieve the [`drawSelection`](https://codemirror.net/6/docs/ref/#view.drawSelection) configuration +for this state. (Note that this will return a set of defaults even +if `drawSelection` isn't enabled.) +*/ +function getDrawSelectionConfig(state) { + return state.facet(selectionConfig); +} +function configChanged(update) { + return update.startState.facet(selectionConfig) != update.state.facet(selectionConfig); +} +const cursorLayer = layer({ + above: true, + markers(view) { + let { state: state$1 } = view, conf = state$1.facet(selectionConfig); + let cursors = []; + for (let r of state$1.selection.ranges) { + let prim = r == state$1.selection.main; + if (r.empty || conf.drawRangeCursor) { + let className = prim ? "cm-cursor cm-cursor-primary" : "cm-cursor cm-cursor-secondary"; + let cursor = r.empty ? r : state.EditorSelection.cursor(r.head, r.head > r.anchor ? -1 : 1); + for (let piece of RectangleMarker.forRange(view, className, cursor)) + cursors.push(piece); + } + } + return cursors; + }, + update(update, dom) { + if (update.transactions.some(tr => tr.selection)) + dom.style.animationName = dom.style.animationName == "cm-blink" ? "cm-blink2" : "cm-blink"; + let confChange = configChanged(update); + if (confChange) + setBlinkRate(update.state, dom); + return update.docChanged || update.selectionSet || confChange; + }, + mount(dom, view) { + setBlinkRate(view.state, dom); + }, + class: "cm-cursorLayer" +}); +function setBlinkRate(state, dom) { + dom.style.animationDuration = state.facet(selectionConfig).cursorBlinkRate + "ms"; +} +const selectionLayer = layer({ + above: false, + markers(view) { + return view.state.selection.ranges.map(r => r.empty ? [] : RectangleMarker.forRange(view, "cm-selectionBackground", r)) + .reduce((a, b) => a.concat(b)); + }, + update(update, dom) { + return update.docChanged || update.selectionSet || update.viewportChanged || configChanged(update); + }, + class: "cm-selectionLayer" +}); +const hideNativeSelection = state.Prec.highest(EditorView.theme({ + ".cm-line": { + "& ::selection, &::selection": { backgroundColor: "transparent !important" }, + caretColor: "transparent !important" + }, + ".cm-content": { + caretColor: "transparent !important", + "& :focus": { + caretColor: "initial !important", + "&::selection, & ::selection": { + backgroundColor: "Highlight !important" + } + } + } +})); + +const setDropCursorPos = state.StateEffect.define({ + map(pos, mapping) { return pos == null ? null : mapping.mapPos(pos); } +}); +const dropCursorPos = state.StateField.define({ + create() { return null; }, + update(pos, tr) { + if (pos != null) + pos = tr.changes.mapPos(pos); + return tr.effects.reduce((pos, e) => e.is(setDropCursorPos) ? e.value : pos, pos); + } +}); +const drawDropCursor = ViewPlugin.fromClass(class { + constructor(view) { + this.view = view; + this.cursor = null; + this.measureReq = { read: this.readPos.bind(this), write: this.drawCursor.bind(this) }; + } + update(update) { + var _a; + let cursorPos = update.state.field(dropCursorPos); + if (cursorPos == null) { + if (this.cursor != null) { + (_a = this.cursor) === null || _a === void 0 ? void 0 : _a.remove(); + this.cursor = null; + } + } + else { + if (!this.cursor) { + this.cursor = this.view.scrollDOM.appendChild(document.createElement("div")); + this.cursor.className = "cm-dropCursor"; + } + if (update.startState.field(dropCursorPos) != cursorPos || update.docChanged || update.geometryChanged) + this.view.requestMeasure(this.measureReq); + } + } + readPos() { + let { view } = this; + let pos = view.state.field(dropCursorPos); + let rect = pos != null && view.coordsAtPos(pos); + if (!rect) + return null; + let outer = view.scrollDOM.getBoundingClientRect(); + return { + left: rect.left - outer.left + view.scrollDOM.scrollLeft * view.scaleX, + top: rect.top - outer.top + view.scrollDOM.scrollTop * view.scaleY, + height: rect.bottom - rect.top + }; + } + drawCursor(pos) { + if (this.cursor) { + let { scaleX, scaleY } = this.view; + if (pos) { + this.cursor.style.left = pos.left / scaleX + "px"; + this.cursor.style.top = pos.top / scaleY + "px"; + this.cursor.style.height = pos.height / scaleY + "px"; + } + else { + this.cursor.style.left = "-100000px"; + } + } + } + destroy() { + if (this.cursor) + this.cursor.remove(); + } + setDropPos(pos) { + if (this.view.state.field(dropCursorPos) != pos) + this.view.dispatch({ effects: setDropCursorPos.of(pos) }); + } +}, { + eventObservers: { + dragover(event) { + this.setDropPos(this.view.posAtCoords({ x: event.clientX, y: event.clientY })); + }, + dragleave(event) { + if (event.target == this.view.contentDOM || !this.view.contentDOM.contains(event.relatedTarget)) + this.setDropPos(null); + }, + dragend() { + this.setDropPos(null); + }, + drop() { + this.setDropPos(null); + } + } +}); +/** +Draws a cursor at the current drop position when something is +dragged over the editor. +*/ +function dropCursor() { + return [dropCursorPos, drawDropCursor]; +} + +function iterMatches(doc, re, from, to, f) { + re.lastIndex = 0; + for (let cursor = doc.iterRange(from, to), pos = from, m; !cursor.next().done; pos += cursor.value.length) { + if (!cursor.lineBreak) + while (m = re.exec(cursor.value)) + f(pos + m.index, m); + } +} +function matchRanges(view, maxLength) { + let visible = view.visibleRanges; + if (visible.length == 1 && visible[0].from == view.viewport.from && + visible[0].to == view.viewport.to) + return visible; + let result = []; + for (let { from, to } of visible) { + from = Math.max(view.state.doc.lineAt(from).from, from - maxLength); + to = Math.min(view.state.doc.lineAt(to).to, to + maxLength); + if (result.length && result[result.length - 1].to >= from) + result[result.length - 1].to = to; + else + result.push({ from, to }); + } + return result; +} +/** +Helper class used to make it easier to maintain decorations on +visible code that matches a given regular expression. To be used +in a [view plugin](https://codemirror.net/6/docs/ref/#view.ViewPlugin). Instances of this object +represent a matching configuration. +*/ +class MatchDecorator { + /** + Create a decorator. + */ + constructor(config) { + const { regexp, decoration, decorate, boundary, maxLength = 1000 } = config; + if (!regexp.global) + throw new RangeError("The regular expression given to MatchDecorator should have its 'g' flag set"); + this.regexp = regexp; + if (decorate) { + this.addMatch = (match, view, from, add) => decorate(add, from, from + match[0].length, match, view); + } + else if (typeof decoration == "function") { + this.addMatch = (match, view, from, add) => { + let deco = decoration(match, view, from); + if (deco) + add(from, from + match[0].length, deco); + }; + } + else if (decoration) { + this.addMatch = (match, _view, from, add) => add(from, from + match[0].length, decoration); + } + else { + throw new RangeError("Either 'decorate' or 'decoration' should be provided to MatchDecorator"); + } + this.boundary = boundary; + this.maxLength = maxLength; + } + /** + Compute the full set of decorations for matches in the given + view's viewport. You'll want to call this when initializing your + plugin. + */ + createDeco(view) { + let build = new state.RangeSetBuilder(), add = build.add.bind(build); + for (let { from, to } of matchRanges(view, this.maxLength)) + iterMatches(view.state.doc, this.regexp, from, to, (from, m) => this.addMatch(m, view, from, add)); + return build.finish(); + } + /** + Update a set of decorations for a view update. `deco` _must_ be + the set of decorations produced by _this_ `MatchDecorator` for + the view state before the update. + */ + updateDeco(update, deco) { + let changeFrom = 1e9, changeTo = -1; + if (update.docChanged) + update.changes.iterChanges((_f, _t, from, to) => { + if (to >= update.view.viewport.from && from <= update.view.viewport.to) { + changeFrom = Math.min(from, changeFrom); + changeTo = Math.max(to, changeTo); + } + }); + if (update.viewportMoved || changeTo - changeFrom > 1000) + return this.createDeco(update.view); + if (changeTo > -1) + return this.updateRange(update.view, deco.map(update.changes), changeFrom, changeTo); + return deco; + } + updateRange(view, deco, updateFrom, updateTo) { + for (let r of view.visibleRanges) { + let from = Math.max(r.from, updateFrom), to = Math.min(r.to, updateTo); + if (to >= from) { + let fromLine = view.state.doc.lineAt(from), toLine = fromLine.to < to ? view.state.doc.lineAt(to) : fromLine; + let start = Math.max(r.from, fromLine.from), end = Math.min(r.to, toLine.to); + if (this.boundary) { + for (; from > fromLine.from; from--) + if (this.boundary.test(fromLine.text[from - 1 - fromLine.from])) { + start = from; + break; + } + for (; to < toLine.to; to++) + if (this.boundary.test(toLine.text[to - toLine.from])) { + end = to; + break; + } + } + let ranges = [], m; + let add = (from, to, deco) => ranges.push(deco.range(from, to)); + if (fromLine == toLine) { + this.regexp.lastIndex = start - fromLine.from; + while ((m = this.regexp.exec(fromLine.text)) && m.index < end - fromLine.from) + this.addMatch(m, view, m.index + fromLine.from, add); + } + else { + iterMatches(view.state.doc, this.regexp, start, end, (from, m) => this.addMatch(m, view, from, add)); + } + deco = deco.update({ filterFrom: start, filterTo: end, filter: (from, to) => from < start || to > end, add: ranges }); + } + } + return deco; + } +} + +const UnicodeRegexpSupport = /x/.unicode != null ? "gu" : "g"; +const Specials = new RegExp("[\u0000-\u0008\u000a-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\u202d\u202e\u2066\u2067\u2069\ufeff\ufff9-\ufffc]", UnicodeRegexpSupport); +const Names = { + 0: "null", + 7: "bell", + 8: "backspace", + 10: "newline", + 11: "vertical tab", + 13: "carriage return", + 27: "escape", + 8203: "zero width space", + 8204: "zero width non-joiner", + 8205: "zero width joiner", + 8206: "left-to-right mark", + 8207: "right-to-left mark", + 8232: "line separator", + 8237: "left-to-right override", + 8238: "right-to-left override", + 8294: "left-to-right isolate", + 8295: "right-to-left isolate", + 8297: "pop directional isolate", + 8233: "paragraph separator", + 65279: "zero width no-break space", + 65532: "object replacement" +}; +let _supportsTabSize = null; +function supportsTabSize() { + var _a; + if (_supportsTabSize == null && typeof document != "undefined" && document.body) { + let styles = document.body.style; + _supportsTabSize = ((_a = styles.tabSize) !== null && _a !== void 0 ? _a : styles.MozTabSize) != null; + } + return _supportsTabSize || false; +} +const specialCharConfig = state.Facet.define({ + combine(configs) { + let config = state.combineConfig(configs, { + render: null, + specialChars: Specials, + addSpecialChars: null + }); + if (config.replaceTabs = !supportsTabSize()) + config.specialChars = new RegExp("\t|" + config.specialChars.source, UnicodeRegexpSupport); + if (config.addSpecialChars) + config.specialChars = new RegExp(config.specialChars.source + "|" + config.addSpecialChars.source, UnicodeRegexpSupport); + return config; + } +}); +/** +Returns an extension that installs highlighting of special +characters. +*/ +function highlightSpecialChars( +/** +Configuration options. +*/ +config = {}) { + return [specialCharConfig.of(config), specialCharPlugin()]; +} +let _plugin = null; +function specialCharPlugin() { + return _plugin || (_plugin = ViewPlugin.fromClass(class { + constructor(view) { + this.view = view; + this.decorations = Decoration.none; + this.decorationCache = Object.create(null); + this.decorator = this.makeDecorator(view.state.facet(specialCharConfig)); + this.decorations = this.decorator.createDeco(view); + } + makeDecorator(conf) { + return new MatchDecorator({ + regexp: conf.specialChars, + decoration: (m, view, pos) => { + let { doc } = view.state; + let code = state.codePointAt(m[0], 0); + if (code == 9) { + let line = doc.lineAt(pos); + let size = view.state.tabSize, col = state.countColumn(line.text, size, pos - line.from); + return Decoration.replace({ + widget: new TabWidget((size - (col % size)) * this.view.defaultCharacterWidth / this.view.scaleX) + }); + } + return this.decorationCache[code] || + (this.decorationCache[code] = Decoration.replace({ widget: new SpecialCharWidget(conf, code) })); + }, + boundary: conf.replaceTabs ? undefined : /[^]/ + }); + } + update(update) { + let conf = update.state.facet(specialCharConfig); + if (update.startState.facet(specialCharConfig) != conf) { + this.decorator = this.makeDecorator(conf); + this.decorations = this.decorator.createDeco(update.view); + } + else { + this.decorations = this.decorator.updateDeco(update, this.decorations); + } + } + }, { + decorations: v => v.decorations + })); +} +const DefaultPlaceholder = "\u2022"; +// Assigns placeholder characters from the Control Pictures block to +// ASCII control characters +function placeholder$1(code) { + if (code >= 32) + return DefaultPlaceholder; + if (code == 10) + return "\u2424"; + return String.fromCharCode(9216 + code); +} +class SpecialCharWidget extends WidgetType { + constructor(options, code) { + super(); + this.options = options; + this.code = code; + } + eq(other) { return other.code == this.code; } + toDOM(view) { + let ph = placeholder$1(this.code); + let desc = view.state.phrase("Control character") + " " + (Names[this.code] || "0x" + this.code.toString(16)); + let custom = this.options.render && this.options.render(this.code, desc, ph); + if (custom) + return custom; + let span = document.createElement("span"); + span.textContent = ph; + span.title = desc; + span.setAttribute("aria-label", desc); + span.className = "cm-specialChar"; + return span; + } + ignoreEvent() { return false; } +} +class TabWidget extends WidgetType { + constructor(width) { + super(); + this.width = width; + } + eq(other) { return other.width == this.width; } + toDOM() { + let span = document.createElement("span"); + span.textContent = "\t"; + span.className = "cm-tab"; + span.style.width = this.width + "px"; + return span; + } + ignoreEvent() { return false; } +} + +const plugin = ViewPlugin.fromClass(class { + constructor() { + this.height = 1000; + this.attrs = { style: "padding-bottom: 1000px" }; + } + update(update) { + let { view } = update; + let height = view.viewState.editorHeight - + view.defaultLineHeight - view.documentPadding.top - 0.5; + if (height >= 0 && height != this.height) { + this.height = height; + this.attrs = { style: `padding-bottom: ${height}px` }; + } + } +}); +/** +Returns an extension that makes sure the content has a bottom +margin equivalent to the height of the editor, minus one line +height, so that every line in the document can be scrolled to the +top of the editor. + +This is only meaningful when the editor is scrollable, and should +not be enabled in editors that take the size of their content. +*/ +function scrollPastEnd() { + return [plugin, contentAttributes.of(view => { var _a; return ((_a = view.plugin(plugin)) === null || _a === void 0 ? void 0 : _a.attrs) || null; })]; +} + +/** +Mark lines that have a cursor on them with the `"cm-activeLine"` +DOM class. +*/ +function highlightActiveLine() { + return activeLineHighlighter; +} +const lineDeco = Decoration.line({ class: "cm-activeLine" }); +const activeLineHighlighter = ViewPlugin.fromClass(class { + constructor(view) { + this.decorations = this.getDeco(view); + } + update(update) { + if (update.docChanged || update.selectionSet) + this.decorations = this.getDeco(update.view); + } + getDeco(view) { + let lastLineStart = -1, deco = []; + for (let r of view.state.selection.ranges) { + let line = view.lineBlockAt(r.head); + if (line.from > lastLineStart) { + deco.push(lineDeco.range(line.from)); + lastLineStart = line.from; + } + } + return Decoration.set(deco); + } +}, { + decorations: v => v.decorations +}); + +class Placeholder extends WidgetType { + constructor(content) { + super(); + this.content = content; + } + toDOM(view) { + let wrap = document.createElement("span"); + wrap.className = "cm-placeholder"; + wrap.style.pointerEvents = "none"; + wrap.appendChild(typeof this.content == "string" ? document.createTextNode(this.content) : + typeof this.content == "function" ? this.content(view) : + this.content.cloneNode(true)); + wrap.setAttribute("aria-hidden", "true"); + return wrap; + } + coordsAt(dom) { + let rects = dom.firstChild ? clientRectsFor(dom.firstChild) : []; + if (!rects.length) + return null; + let style = window.getComputedStyle(dom.parentNode); + let rect = flattenRect(rects[0], style.direction != "rtl"); + let lineHeight = parseInt(style.lineHeight); + if (rect.bottom - rect.top > lineHeight * 1.5) + return { left: rect.left, right: rect.right, top: rect.top, bottom: rect.top + lineHeight }; + return rect; + } + ignoreEvent() { return false; } +} +/** +Extension that enables a placeholder—a piece of example content +to show when the editor is empty. +*/ +function placeholder(content) { + let plugin = ViewPlugin.fromClass(class { + constructor(view) { + this.view = view; + this.placeholder = content + ? Decoration.set([Decoration.widget({ widget: new Placeholder(content), side: 1 }).range(0)]) + : Decoration.none; + } + get decorations() { return this.view.state.doc.length ? Decoration.none : this.placeholder; } + }, { decorations: v => v.decorations }); + return typeof content == "string" ? [ + plugin, EditorView.contentAttributes.of({ "aria-placeholder": content }) + ] : plugin; +} + +// Don't compute precise column positions for line offsets above this +// (since it could get expensive). Assume offset==column for them. +const MaxOff = 2000; +function rectangleFor(state$1, a, b) { + let startLine = Math.min(a.line, b.line), endLine = Math.max(a.line, b.line); + let ranges = []; + if (a.off > MaxOff || b.off > MaxOff || a.col < 0 || b.col < 0) { + let startOff = Math.min(a.off, b.off), endOff = Math.max(a.off, b.off); + for (let i = startLine; i <= endLine; i++) { + let line = state$1.doc.line(i); + if (line.length <= endOff) + ranges.push(state.EditorSelection.range(line.from + startOff, line.to + endOff)); + } + } + else { + let startCol = Math.min(a.col, b.col), endCol = Math.max(a.col, b.col); + for (let i = startLine; i <= endLine; i++) { + let line = state$1.doc.line(i); + let start = state.findColumn(line.text, startCol, state$1.tabSize, true); + if (start < 0) { + ranges.push(state.EditorSelection.cursor(line.to)); + } + else { + let end = state.findColumn(line.text, endCol, state$1.tabSize); + ranges.push(state.EditorSelection.range(line.from + start, line.from + end)); + } + } + } + return ranges; +} +function absoluteColumn(view, x) { + let ref = view.coordsAtPos(view.viewport.from); + return ref ? Math.round(Math.abs((ref.left - x) / view.defaultCharacterWidth)) : -1; +} +function getPos(view, event) { + let offset = view.posAtCoords({ x: event.clientX, y: event.clientY }, false); + let line = view.state.doc.lineAt(offset), off = offset - line.from; + let col = off > MaxOff ? -1 + : off == line.length ? absoluteColumn(view, event.clientX) + : state.countColumn(line.text, view.state.tabSize, offset - line.from); + return { line: line.number, col, off }; +} +function rectangleSelectionStyle(view, event) { + let start = getPos(view, event), startSel = view.state.selection; + if (!start) + return null; + return { + update(update) { + if (update.docChanged) { + let newStart = update.changes.mapPos(update.startState.doc.line(start.line).from); + let newLine = update.state.doc.lineAt(newStart); + start = { line: newLine.number, col: start.col, off: Math.min(start.off, newLine.length) }; + startSel = startSel.map(update.changes); + } + }, + get(event, _extend, multiple) { + let cur = getPos(view, event); + if (!cur) + return startSel; + let ranges = rectangleFor(view.state, start, cur); + if (!ranges.length) + return startSel; + if (multiple) + return state.EditorSelection.create(ranges.concat(startSel.ranges)); + else + return state.EditorSelection.create(ranges); + } + }; +} +/** +Create an extension that enables rectangular selections. By +default, it will react to left mouse drag with the Alt key held +down. When such a selection occurs, the text within the rectangle +that was dragged over will be selected, as one selection +[range](https://codemirror.net/6/docs/ref/#state.SelectionRange) per line. +*/ +function rectangularSelection(options) { + let filter = (options === null || options === void 0 ? void 0 : options.eventFilter) || (e => e.altKey && e.button == 0); + return EditorView.mouseSelectionStyle.of((view, event) => filter(event) ? rectangleSelectionStyle(view, event) : null); +} +const keys = { + Alt: [18, e => !!e.altKey], + Control: [17, e => !!e.ctrlKey], + Shift: [16, e => !!e.shiftKey], + Meta: [91, e => !!e.metaKey] +}; +const showCrosshair = { style: "cursor: crosshair" }; +/** +Returns an extension that turns the pointer cursor into a +crosshair when a given modifier key, defaulting to Alt, is held +down. Can serve as a visual hint that rectangular selection is +going to happen when paired with +[`rectangularSelection`](https://codemirror.net/6/docs/ref/#view.rectangularSelection). +*/ +function crosshairCursor(options = {}) { + let [code, getter] = keys[options.key || "Alt"]; + let plugin = ViewPlugin.fromClass(class { + constructor(view) { + this.view = view; + this.isDown = false; + } + set(isDown) { + if (this.isDown != isDown) { + this.isDown = isDown; + this.view.update([]); + } + } + }, { + eventObservers: { + keydown(e) { + this.set(e.keyCode == code || getter(e)); + }, + keyup(e) { + if (e.keyCode == code || !getter(e)) + this.set(false); + }, + mousemove(e) { + this.set(getter(e)); + } + } + }); + return [ + plugin, + EditorView.contentAttributes.of(view => { var _a; return ((_a = view.plugin(plugin)) === null || _a === void 0 ? void 0 : _a.isDown) ? showCrosshair : null; }) + ]; +} + +const Outside = "-10000px"; +class TooltipViewManager { + constructor(view, facet, createTooltipView, removeTooltipView) { + this.facet = facet; + this.createTooltipView = createTooltipView; + this.removeTooltipView = removeTooltipView; + this.input = view.state.facet(facet); + this.tooltips = this.input.filter(t => t); + let prev = null; + this.tooltipViews = this.tooltips.map(t => prev = createTooltipView(t, prev)); + } + update(update, above) { + var _a; + let input = update.state.facet(this.facet); + let tooltips = input.filter(x => x); + if (input === this.input) { + for (let t of this.tooltipViews) + if (t.update) + t.update(update); + return false; + } + let tooltipViews = [], newAbove = above ? [] : null; + for (let i = 0; i < tooltips.length; i++) { + let tip = tooltips[i], known = -1; + if (!tip) + continue; + for (let i = 0; i < this.tooltips.length; i++) { + let other = this.tooltips[i]; + if (other && other.create == tip.create) + known = i; + } + if (known < 0) { + tooltipViews[i] = this.createTooltipView(tip, i ? tooltipViews[i - 1] : null); + if (newAbove) + newAbove[i] = !!tip.above; + } + else { + let tooltipView = tooltipViews[i] = this.tooltipViews[known]; + if (newAbove) + newAbove[i] = above[known]; + if (tooltipView.update) + tooltipView.update(update); + } + } + for (let t of this.tooltipViews) + if (tooltipViews.indexOf(t) < 0) { + this.removeTooltipView(t); + (_a = t.destroy) === null || _a === void 0 ? void 0 : _a.call(t); + } + if (above) { + newAbove.forEach((val, i) => above[i] = val); + above.length = newAbove.length; + } + this.input = input; + this.tooltips = tooltips; + this.tooltipViews = tooltipViews; + return true; + } +} +/** +Creates an extension that configures tooltip behavior. +*/ +function tooltips(config = {}) { + return tooltipConfig.of(config); +} +function windowSpace(view) { + let docElt = view.dom.ownerDocument.documentElement; + return { top: 0, left: 0, bottom: docElt.clientHeight, right: docElt.clientWidth }; +} +const tooltipConfig = state.Facet.define({ + combine: values => { + var _a, _b, _c; + return ({ + position: browser.ios ? "absolute" : ((_a = values.find(conf => conf.position)) === null || _a === void 0 ? void 0 : _a.position) || "fixed", + parent: ((_b = values.find(conf => conf.parent)) === null || _b === void 0 ? void 0 : _b.parent) || null, + tooltipSpace: ((_c = values.find(conf => conf.tooltipSpace)) === null || _c === void 0 ? void 0 : _c.tooltipSpace) || windowSpace, + }); + } +}); +const knownHeight = new WeakMap(); +const tooltipPlugin = ViewPlugin.fromClass(class { + constructor(view) { + this.view = view; + this.above = []; + this.inView = true; + this.madeAbsolute = false; + this.lastTransaction = 0; + this.measureTimeout = -1; + let config = view.state.facet(tooltipConfig); + this.position = config.position; + this.parent = config.parent; + this.classes = view.themeClasses; + this.createContainer(); + this.measureReq = { read: this.readMeasure.bind(this), write: this.writeMeasure.bind(this), key: this }; + this.resizeObserver = typeof ResizeObserver == "function" ? new ResizeObserver(() => this.measureSoon()) : null; + this.manager = new TooltipViewManager(view, showTooltip, (t, p) => this.createTooltip(t, p), t => { + if (this.resizeObserver) + this.resizeObserver.unobserve(t.dom); + t.dom.remove(); + }); + this.above = this.manager.tooltips.map(t => !!t.above); + this.intersectionObserver = typeof IntersectionObserver == "function" ? new IntersectionObserver(entries => { + if (Date.now() > this.lastTransaction - 50 && + entries.length > 0 && entries[entries.length - 1].intersectionRatio < 1) + this.measureSoon(); + }, { threshold: [1] }) : null; + this.observeIntersection(); + view.win.addEventListener("resize", this.measureSoon = this.measureSoon.bind(this)); + this.maybeMeasure(); + } + createContainer() { + if (this.parent) { + this.container = document.createElement("div"); + this.container.style.position = "relative"; + this.container.className = this.view.themeClasses; + this.parent.appendChild(this.container); + } + else { + this.container = this.view.dom; + } + } + observeIntersection() { + if (this.intersectionObserver) { + this.intersectionObserver.disconnect(); + for (let tooltip of this.manager.tooltipViews) + this.intersectionObserver.observe(tooltip.dom); + } + } + measureSoon() { + if (this.measureTimeout < 0) + this.measureTimeout = setTimeout(() => { + this.measureTimeout = -1; + this.maybeMeasure(); + }, 50); + } + update(update) { + if (update.transactions.length) + this.lastTransaction = Date.now(); + let updated = this.manager.update(update, this.above); + if (updated) + this.observeIntersection(); + let shouldMeasure = updated || update.geometryChanged; + let newConfig = update.state.facet(tooltipConfig); + if (newConfig.position != this.position && !this.madeAbsolute) { + this.position = newConfig.position; + for (let t of this.manager.tooltipViews) + t.dom.style.position = this.position; + shouldMeasure = true; + } + if (newConfig.parent != this.parent) { + if (this.parent) + this.container.remove(); + this.parent = newConfig.parent; + this.createContainer(); + for (let t of this.manager.tooltipViews) + this.container.appendChild(t.dom); + shouldMeasure = true; + } + else if (this.parent && this.view.themeClasses != this.classes) { + this.classes = this.container.className = this.view.themeClasses; + } + if (shouldMeasure) + this.maybeMeasure(); + } + createTooltip(tooltip, prev) { + let tooltipView = tooltip.create(this.view); + let before = prev ? prev.dom : null; + tooltipView.dom.classList.add("cm-tooltip"); + if (tooltip.arrow && !tooltipView.dom.querySelector(".cm-tooltip > .cm-tooltip-arrow")) { + let arrow = document.createElement("div"); + arrow.className = "cm-tooltip-arrow"; + tooltipView.dom.appendChild(arrow); + } + tooltipView.dom.style.position = this.position; + tooltipView.dom.style.top = Outside; + tooltipView.dom.style.left = "0px"; + this.container.insertBefore(tooltipView.dom, before); + if (tooltipView.mount) + tooltipView.mount(this.view); + if (this.resizeObserver) + this.resizeObserver.observe(tooltipView.dom); + return tooltipView; + } + destroy() { + var _a, _b, _c; + this.view.win.removeEventListener("resize", this.measureSoon); + for (let tooltipView of this.manager.tooltipViews) { + tooltipView.dom.remove(); + (_a = tooltipView.destroy) === null || _a === void 0 ? void 0 : _a.call(tooltipView); + } + if (this.parent) + this.container.remove(); + (_b = this.resizeObserver) === null || _b === void 0 ? void 0 : _b.disconnect(); + (_c = this.intersectionObserver) === null || _c === void 0 ? void 0 : _c.disconnect(); + clearTimeout(this.measureTimeout); + } + readMeasure() { + let scaleX = 1, scaleY = 1, makeAbsolute = false; + if (this.position == "fixed" && this.manager.tooltipViews.length) { + let { dom } = this.manager.tooltipViews[0]; + if (browser.gecko) { + // Firefox sets the element's `offsetParent` to the + // transformed element when a transform interferes with fixed + // positioning. + makeAbsolute = dom.offsetParent != this.container.ownerDocument.body; + } + else if (dom.style.top == Outside && dom.style.left == "0px") { + // On other browsers, we have to awkwardly try and use other + // information to detect a transform. + let rect = dom.getBoundingClientRect(); + makeAbsolute = Math.abs(rect.top + 10000) > 1 || Math.abs(rect.left) > 1; + } + } + if (makeAbsolute || this.position == "absolute") { + if (this.parent) { + let rect = this.parent.getBoundingClientRect(); + if (rect.width && rect.height) { + scaleX = rect.width / this.parent.offsetWidth; + scaleY = rect.height / this.parent.offsetHeight; + } + } + else { + ({ scaleX, scaleY } = this.view.viewState); + } + } + let visible = this.view.scrollDOM.getBoundingClientRect(), margins = getScrollMargins(this.view); + return { + visible: { + left: visible.left + margins.left, top: visible.top + margins.top, + right: visible.right - margins.right, bottom: visible.bottom - margins.bottom + }, + parent: this.parent ? this.container.getBoundingClientRect() : this.view.dom.getBoundingClientRect(), + pos: this.manager.tooltips.map((t, i) => { + let tv = this.manager.tooltipViews[i]; + return tv.getCoords ? tv.getCoords(t.pos) : this.view.coordsAtPos(t.pos); + }), + size: this.manager.tooltipViews.map(({ dom }) => dom.getBoundingClientRect()), + space: this.view.state.facet(tooltipConfig).tooltipSpace(this.view), + scaleX, scaleY, makeAbsolute + }; + } + writeMeasure(measured) { + var _a; + if (measured.makeAbsolute) { + this.madeAbsolute = true; + this.position = "absolute"; + for (let t of this.manager.tooltipViews) + t.dom.style.position = "absolute"; + } + let { visible, space, scaleX, scaleY } = measured; + let others = []; + for (let i = 0; i < this.manager.tooltips.length; i++) { + let tooltip = this.manager.tooltips[i], tView = this.manager.tooltipViews[i], { dom } = tView; + let pos = measured.pos[i], size = measured.size[i]; + // Hide tooltips that are outside of the editor. + if (!pos || tooltip.clip !== false && (pos.bottom <= Math.max(visible.top, space.top) || + pos.top >= Math.min(visible.bottom, space.bottom) || + pos.right < Math.max(visible.left, space.left) - .1 || + pos.left > Math.min(visible.right, space.right) + .1)) { + dom.style.top = Outside; + continue; + } + let arrow = tooltip.arrow ? tView.dom.querySelector(".cm-tooltip-arrow") : null; + let arrowHeight = arrow ? 7 /* Arrow.Size */ : 0; + let width = size.right - size.left, height = (_a = knownHeight.get(tView)) !== null && _a !== void 0 ? _a : size.bottom - size.top; + let offset = tView.offset || noOffset, ltr = this.view.textDirection == exports.Direction.LTR; + let left = size.width > space.right - space.left + ? (ltr ? space.left : space.right - size.width) + : ltr ? Math.max(space.left, Math.min(pos.left - (arrow ? 14 /* Arrow.Offset */ : 0) + offset.x, space.right - width)) + : Math.min(Math.max(space.left, pos.left - width + (arrow ? 14 /* Arrow.Offset */ : 0) - offset.x), space.right - width); + let above = this.above[i]; + if (!tooltip.strictSide && (above + ? pos.top - height - arrowHeight - offset.y < space.top + : pos.bottom + height + arrowHeight + offset.y > space.bottom) && + above == (space.bottom - pos.bottom > pos.top - space.top)) + above = this.above[i] = !above; + let spaceVert = (above ? pos.top - space.top : space.bottom - pos.bottom) - arrowHeight; + if (spaceVert < height && tView.resize !== false) { + if (spaceVert < this.view.defaultLineHeight) { + dom.style.top = Outside; + continue; + } + knownHeight.set(tView, height); + dom.style.height = (height = spaceVert) / scaleY + "px"; + } + else if (dom.style.height) { + dom.style.height = ""; + } + let top = above ? pos.top - height - arrowHeight - offset.y : pos.bottom + arrowHeight + offset.y; + let right = left + width; + if (tView.overlap !== true) + for (let r of others) + if (r.left < right && r.right > left && r.top < top + height && r.bottom > top) + top = above ? r.top - height - 2 - arrowHeight : r.bottom + arrowHeight + 2; + if (this.position == "absolute") { + dom.style.top = (top - measured.parent.top) / scaleY + "px"; + setLeftStyle(dom, (left - measured.parent.left) / scaleX); + } + else { + dom.style.top = top / scaleY + "px"; + setLeftStyle(dom, left / scaleX); + } + if (arrow) { + let arrowLeft = pos.left + (ltr ? offset.x : -offset.x) - (left + 14 /* Arrow.Offset */ - 7 /* Arrow.Size */); + arrow.style.left = arrowLeft / scaleX + "px"; + } + if (tView.overlap !== true) + others.push({ left, top, right, bottom: top + height }); + dom.classList.toggle("cm-tooltip-above", above); + dom.classList.toggle("cm-tooltip-below", !above); + if (tView.positioned) + tView.positioned(measured.space); + } + } + maybeMeasure() { + if (this.manager.tooltips.length) { + if (this.view.inView) + this.view.requestMeasure(this.measureReq); + if (this.inView != this.view.inView) { + this.inView = this.view.inView; + if (!this.inView) + for (let tv of this.manager.tooltipViews) + tv.dom.style.top = Outside; + } + } + } +}, { + eventObservers: { + scroll() { this.maybeMeasure(); } + } +}); +function setLeftStyle(elt, value) { + let current = parseInt(elt.style.left, 10); + if (isNaN(current) || Math.abs(value - current) > 1) + elt.style.left = value + "px"; +} +const baseTheme = EditorView.baseTheme({ + ".cm-tooltip": { + zIndex: 500, + boxSizing: "border-box" + }, + "&light .cm-tooltip": { + border: "1px solid #bbb", + backgroundColor: "#f5f5f5" + }, + "&light .cm-tooltip-section:not(:first-child)": { + borderTop: "1px solid #bbb", + }, + "&dark .cm-tooltip": { + backgroundColor: "#333338", + color: "white" + }, + ".cm-tooltip-arrow": { + height: `${7 /* Arrow.Size */}px`, + width: `${7 /* Arrow.Size */ * 2}px`, + position: "absolute", + zIndex: -1, + overflow: "hidden", + "&:before, &:after": { + content: "''", + position: "absolute", + width: 0, + height: 0, + borderLeft: `${7 /* Arrow.Size */}px solid transparent`, + borderRight: `${7 /* Arrow.Size */}px solid transparent`, + }, + ".cm-tooltip-above &": { + bottom: `-${7 /* Arrow.Size */}px`, + "&:before": { + borderTop: `${7 /* Arrow.Size */}px solid #bbb`, + }, + "&:after": { + borderTop: `${7 /* Arrow.Size */}px solid #f5f5f5`, + bottom: "1px" + } + }, + ".cm-tooltip-below &": { + top: `-${7 /* Arrow.Size */}px`, + "&:before": { + borderBottom: `${7 /* Arrow.Size */}px solid #bbb`, + }, + "&:after": { + borderBottom: `${7 /* Arrow.Size */}px solid #f5f5f5`, + top: "1px" + } + }, + }, + "&dark .cm-tooltip .cm-tooltip-arrow": { + "&:before": { + borderTopColor: "#333338", + borderBottomColor: "#333338" + }, + "&:after": { + borderTopColor: "transparent", + borderBottomColor: "transparent" + } + } +}); +const noOffset = { x: 0, y: 0 }; +/** +Facet to which an extension can add a value to show a tooltip. +*/ +const showTooltip = state.Facet.define({ + enables: [tooltipPlugin, baseTheme] +}); +const showHoverTooltip = state.Facet.define({ + combine: inputs => inputs.reduce((a, i) => a.concat(i), []) +}); +class HoverTooltipHost { + // Needs to be static so that host tooltip instances always match + static create(view) { + return new HoverTooltipHost(view); + } + constructor(view) { + this.view = view; + this.mounted = false; + this.dom = document.createElement("div"); + this.dom.classList.add("cm-tooltip-hover"); + this.manager = new TooltipViewManager(view, showHoverTooltip, (t, p) => this.createHostedView(t, p), t => t.dom.remove()); + } + createHostedView(tooltip, prev) { + let hostedView = tooltip.create(this.view); + hostedView.dom.classList.add("cm-tooltip-section"); + this.dom.insertBefore(hostedView.dom, prev ? prev.dom.nextSibling : this.dom.firstChild); + if (this.mounted && hostedView.mount) + hostedView.mount(this.view); + return hostedView; + } + mount(view) { + for (let hostedView of this.manager.tooltipViews) { + if (hostedView.mount) + hostedView.mount(view); + } + this.mounted = true; + } + positioned(space) { + for (let hostedView of this.manager.tooltipViews) { + if (hostedView.positioned) + hostedView.positioned(space); + } + } + update(update) { + this.manager.update(update); + } + destroy() { + var _a; + for (let t of this.manager.tooltipViews) + (_a = t.destroy) === null || _a === void 0 ? void 0 : _a.call(t); + } + passProp(name) { + let value = undefined; + for (let view of this.manager.tooltipViews) { + let given = view[name]; + if (given !== undefined) { + if (value === undefined) + value = given; + else if (value !== given) + return undefined; + } + } + return value; + } + get offset() { return this.passProp("offset"); } + get getCoords() { return this.passProp("getCoords"); } + get overlap() { return this.passProp("overlap"); } + get resize() { return this.passProp("resize"); } +} +const showHoverTooltipHost = showTooltip.compute([showHoverTooltip], state => { + let tooltips = state.facet(showHoverTooltip); + if (tooltips.length === 0) + return null; + return { + pos: Math.min(...tooltips.map(t => t.pos)), + end: Math.max(...tooltips.map(t => { var _a; return (_a = t.end) !== null && _a !== void 0 ? _a : t.pos; })), + create: HoverTooltipHost.create, + above: tooltips[0].above, + arrow: tooltips.some(t => t.arrow), + }; +}); +class HoverPlugin { + constructor(view, source, field, setHover, hoverTime) { + this.view = view; + this.source = source; + this.field = field; + this.setHover = setHover; + this.hoverTime = hoverTime; + this.hoverTimeout = -1; + this.restartTimeout = -1; + this.pending = null; + this.lastMove = { x: 0, y: 0, target: view.dom, time: 0 }; + this.checkHover = this.checkHover.bind(this); + view.dom.addEventListener("mouseleave", this.mouseleave = this.mouseleave.bind(this)); + view.dom.addEventListener("mousemove", this.mousemove = this.mousemove.bind(this)); + } + update() { + if (this.pending) { + this.pending = null; + clearTimeout(this.restartTimeout); + this.restartTimeout = setTimeout(() => this.startHover(), 20); + } + } + get active() { + return this.view.state.field(this.field); + } + checkHover() { + this.hoverTimeout = -1; + if (this.active.length) + return; + let hovered = Date.now() - this.lastMove.time; + if (hovered < this.hoverTime) + this.hoverTimeout = setTimeout(this.checkHover, this.hoverTime - hovered); + else + this.startHover(); + } + startHover() { + clearTimeout(this.restartTimeout); + let { view, lastMove } = this; + let desc = view.docView.nearest(lastMove.target); + if (!desc) + return; + let pos, side = 1; + if (desc instanceof WidgetView) { + pos = desc.posAtStart; + } + else { + pos = view.posAtCoords(lastMove); + if (pos == null) + return; + let posCoords = view.coordsAtPos(pos); + if (!posCoords || + lastMove.y < posCoords.top || lastMove.y > posCoords.bottom || + lastMove.x < posCoords.left - view.defaultCharacterWidth || + lastMove.x > posCoords.right + view.defaultCharacterWidth) + return; + let bidi = view.bidiSpans(view.state.doc.lineAt(pos)).find(s => s.from <= pos && s.to >= pos); + let rtl = bidi && bidi.dir == exports.Direction.RTL ? -1 : 1; + side = (lastMove.x < posCoords.left ? -rtl : rtl); + } + let open = this.source(view, pos, side); + if (open === null || open === void 0 ? void 0 : open.then) { + let pending = this.pending = { pos }; + open.then(result => { + if (this.pending == pending) { + this.pending = null; + if (result && !(Array.isArray(result) && !result.length)) + view.dispatch({ effects: this.setHover.of(Array.isArray(result) ? result : [result]) }); + } + }, e => logException(view.state, e, "hover tooltip")); + } + else if (open && !(Array.isArray(open) && !open.length)) { + view.dispatch({ effects: this.setHover.of(Array.isArray(open) ? open : [open]) }); + } + } + get tooltip() { + let plugin = this.view.plugin(tooltipPlugin); + let index = plugin ? plugin.manager.tooltips.findIndex(t => t.create == HoverTooltipHost.create) : -1; + return index > -1 ? plugin.manager.tooltipViews[index] : null; + } + mousemove(event) { + var _a, _b; + this.lastMove = { x: event.clientX, y: event.clientY, target: event.target, time: Date.now() }; + if (this.hoverTimeout < 0) + this.hoverTimeout = setTimeout(this.checkHover, this.hoverTime); + let { active, tooltip } = this; + if (active.length && tooltip && !isInTooltip(tooltip.dom, event) || this.pending) { + let { pos } = active[0] || this.pending, end = (_b = (_a = active[0]) === null || _a === void 0 ? void 0 : _a.end) !== null && _b !== void 0 ? _b : pos; + if ((pos == end ? this.view.posAtCoords(this.lastMove) != pos + : !isOverRange(this.view, pos, end, event.clientX, event.clientY))) { + this.view.dispatch({ effects: this.setHover.of([]) }); + this.pending = null; + } + } + } + mouseleave(event) { + clearTimeout(this.hoverTimeout); + this.hoverTimeout = -1; + let { active } = this; + if (active.length) { + let { tooltip } = this; + let inTooltip = tooltip && tooltip.dom.contains(event.relatedTarget); + if (!inTooltip) + this.view.dispatch({ effects: this.setHover.of([]) }); + else + this.watchTooltipLeave(tooltip.dom); + } + } + watchTooltipLeave(tooltip) { + let watch = (event) => { + tooltip.removeEventListener("mouseleave", watch); + if (this.active.length && !this.view.dom.contains(event.relatedTarget)) + this.view.dispatch({ effects: this.setHover.of([]) }); + }; + tooltip.addEventListener("mouseleave", watch); + } + destroy() { + clearTimeout(this.hoverTimeout); + this.view.dom.removeEventListener("mouseleave", this.mouseleave); + this.view.dom.removeEventListener("mousemove", this.mousemove); + } +} +const tooltipMargin = 4; +function isInTooltip(tooltip, event) { + let { left, right, top, bottom } = tooltip.getBoundingClientRect(), arrow; + if (arrow = tooltip.querySelector(".cm-tooltip-arrow")) { + let arrowRect = arrow.getBoundingClientRect(); + top = Math.min(arrowRect.top, top); + bottom = Math.max(arrowRect.bottom, bottom); + } + return event.clientX >= left - tooltipMargin && event.clientX <= right + tooltipMargin && + event.clientY >= top - tooltipMargin && event.clientY <= bottom + tooltipMargin; +} +function isOverRange(view, from, to, x, y, margin) { + let rect = view.scrollDOM.getBoundingClientRect(); + let docBottom = view.documentTop + view.documentPadding.top + view.contentHeight; + if (rect.left > x || rect.right < x || rect.top > y || Math.min(rect.bottom, docBottom) < y) + return false; + let pos = view.posAtCoords({ x, y }, false); + return pos >= from && pos <= to; +} +/** +Set up a hover tooltip, which shows up when the pointer hovers +over ranges of text. The callback is called when the mouse hovers +over the document text. It should, if there is a tooltip +associated with position `pos`, return the tooltip description +(either directly or in a promise). The `side` argument indicates +on which side of the position the pointer is—it will be -1 if the +pointer is before the position, 1 if after the position. + +Note that all hover tooltips are hosted within a single tooltip +container element. This allows multiple tooltips over the same +range to be "merged" together without overlapping. + +The return value is a valid [editor extension](https://codemirror.net/6/docs/ref/#state.Extension) +but also provides an `active` property holding a state field that +can be used to read the currently active tooltips produced by this +extension. +*/ +function hoverTooltip(source, options = {}) { + let setHover = state.StateEffect.define(); + let hoverState = state.StateField.define({ + create() { return []; }, + update(value, tr) { + if (value.length) { + if (options.hideOnChange && (tr.docChanged || tr.selection)) + value = []; + else if (options.hideOn) + value = value.filter(v => !options.hideOn(tr, v)); + if (tr.docChanged) { + let mapped = []; + for (let tooltip of value) { + let newPos = tr.changes.mapPos(tooltip.pos, -1, state.MapMode.TrackDel); + if (newPos != null) { + let copy = Object.assign(Object.create(null), tooltip); + copy.pos = newPos; + if (copy.end != null) + copy.end = tr.changes.mapPos(copy.end); + mapped.push(copy); + } + } + value = mapped; + } + } + for (let effect of tr.effects) { + if (effect.is(setHover)) + value = effect.value; + if (effect.is(closeHoverTooltipEffect)) + value = []; + } + return value; + }, + provide: f => showHoverTooltip.from(f) + }); + return { + active: hoverState, + extension: [ + hoverState, + ViewPlugin.define(view => new HoverPlugin(view, source, hoverState, setHover, options.hoverTime || 300 /* Hover.Time */)), + showHoverTooltipHost + ] + }; +} +/** +Get the active tooltip view for a given tooltip, if available. +*/ +function getTooltip(view, tooltip) { + let plugin = view.plugin(tooltipPlugin); + if (!plugin) + return null; + let found = plugin.manager.tooltips.indexOf(tooltip); + return found < 0 ? null : plugin.manager.tooltipViews[found]; +} +/** +Returns true if any hover tooltips are currently active. +*/ +function hasHoverTooltips(state) { + return state.facet(showHoverTooltip).some(x => x); +} +const closeHoverTooltipEffect = state.StateEffect.define(); +/** +Transaction effect that closes all hover tooltips. +*/ +const closeHoverTooltips = closeHoverTooltipEffect.of(null); +/** +Tell the tooltip extension to recompute the position of the active +tooltips. This can be useful when something happens (such as a +re-positioning or CSS change affecting the editor) that could +invalidate the existing tooltip positions. +*/ +function repositionTooltips(view) { + let plugin = view.plugin(tooltipPlugin); + if (plugin) + plugin.maybeMeasure(); +} + +const panelConfig = state.Facet.define({ + combine(configs) { + let topContainer, bottomContainer; + for (let c of configs) { + topContainer = topContainer || c.topContainer; + bottomContainer = bottomContainer || c.bottomContainer; + } + return { topContainer, bottomContainer }; + } +}); +/** +Configures the panel-managing extension. +*/ +function panels(config) { + return config ? [panelConfig.of(config)] : []; +} +/** +Get the active panel created by the given constructor, if any. +This can be useful when you need access to your panels' DOM +structure. +*/ +function getPanel(view, panel) { + let plugin = view.plugin(panelPlugin); + let index = plugin ? plugin.specs.indexOf(panel) : -1; + return index > -1 ? plugin.panels[index] : null; +} +const panelPlugin = ViewPlugin.fromClass(class { + constructor(view) { + this.input = view.state.facet(showPanel); + this.specs = this.input.filter(s => s); + this.panels = this.specs.map(spec => spec(view)); + let conf = view.state.facet(panelConfig); + this.top = new PanelGroup(view, true, conf.topContainer); + this.bottom = new PanelGroup(view, false, conf.bottomContainer); + this.top.sync(this.panels.filter(p => p.top)); + this.bottom.sync(this.panels.filter(p => !p.top)); + for (let p of this.panels) { + p.dom.classList.add("cm-panel"); + if (p.mount) + p.mount(); + } + } + update(update) { + let conf = update.state.facet(panelConfig); + if (this.top.container != conf.topContainer) { + this.top.sync([]); + this.top = new PanelGroup(update.view, true, conf.topContainer); + } + if (this.bottom.container != conf.bottomContainer) { + this.bottom.sync([]); + this.bottom = new PanelGroup(update.view, false, conf.bottomContainer); + } + this.top.syncClasses(); + this.bottom.syncClasses(); + let input = update.state.facet(showPanel); + if (input != this.input) { + let specs = input.filter(x => x); + let panels = [], top = [], bottom = [], mount = []; + for (let spec of specs) { + let known = this.specs.indexOf(spec), panel; + if (known < 0) { + panel = spec(update.view); + mount.push(panel); + } + else { + panel = this.panels[known]; + if (panel.update) + panel.update(update); + } + panels.push(panel); + (panel.top ? top : bottom).push(panel); + } + this.specs = specs; + this.panels = panels; + this.top.sync(top); + this.bottom.sync(bottom); + for (let p of mount) { + p.dom.classList.add("cm-panel"); + if (p.mount) + p.mount(); + } + } + else { + for (let p of this.panels) + if (p.update) + p.update(update); + } + } + destroy() { + this.top.sync([]); + this.bottom.sync([]); + } +}, { + provide: plugin => EditorView.scrollMargins.of(view => { + let value = view.plugin(plugin); + return value && { top: value.top.scrollMargin(), bottom: value.bottom.scrollMargin() }; + }) +}); +class PanelGroup { + constructor(view, top, container) { + this.view = view; + this.top = top; + this.container = container; + this.dom = undefined; + this.classes = ""; + this.panels = []; + this.syncClasses(); + } + sync(panels) { + for (let p of this.panels) + if (p.destroy && panels.indexOf(p) < 0) + p.destroy(); + this.panels = panels; + this.syncDOM(); + } + syncDOM() { + if (this.panels.length == 0) { + if (this.dom) { + this.dom.remove(); + this.dom = undefined; + } + return; + } + if (!this.dom) { + this.dom = document.createElement("div"); + this.dom.className = this.top ? "cm-panels cm-panels-top" : "cm-panels cm-panels-bottom"; + this.dom.style[this.top ? "top" : "bottom"] = "0"; + let parent = this.container || this.view.dom; + parent.insertBefore(this.dom, this.top ? parent.firstChild : null); + } + let curDOM = this.dom.firstChild; + for (let panel of this.panels) { + if (panel.dom.parentNode == this.dom) { + while (curDOM != panel.dom) + curDOM = rm(curDOM); + curDOM = curDOM.nextSibling; + } + else { + this.dom.insertBefore(panel.dom, curDOM); + } + } + while (curDOM) + curDOM = rm(curDOM); + } + scrollMargin() { + return !this.dom || this.container ? 0 + : Math.max(0, this.top ? + this.dom.getBoundingClientRect().bottom - Math.max(0, this.view.scrollDOM.getBoundingClientRect().top) : + Math.min(innerHeight, this.view.scrollDOM.getBoundingClientRect().bottom) - this.dom.getBoundingClientRect().top); + } + syncClasses() { + if (!this.container || this.classes == this.view.themeClasses) + return; + for (let cls of this.classes.split(" ")) + if (cls) + this.container.classList.remove(cls); + for (let cls of (this.classes = this.view.themeClasses).split(" ")) + if (cls) + this.container.classList.add(cls); + } +} +function rm(node) { + let next = node.nextSibling; + node.remove(); + return next; +} +/** +Opening a panel is done by providing a constructor function for +the panel through this facet. (The panel is closed again when its +constructor is no longer provided.) Values of `null` are ignored. +*/ +const showPanel = state.Facet.define({ + enables: panelPlugin +}); + +/** +Show a panel above or below the editor to show the user a message +or prompt them for input. Returns an effect that can be dispatched +to close the dialog, and a promise that resolves when the dialog +is closed or a form inside of it is submitted. + +You are encouraged, if your handling of the result of the promise +dispatches a transaction, to include the `close` effect in it. If +you don't, this function will automatically dispatch a separate +transaction right after. +*/ +function showDialog(view, config) { + let resolve; + let promise = new Promise(r => resolve = r); + let panelCtor = (view) => createDialog(view, config, resolve); + if (view.state.field(dialogField, false)) { + view.dispatch({ effects: openDialogEffect.of(panelCtor) }); + } + else { + view.dispatch({ effects: state.StateEffect.appendConfig.of(dialogField.init(() => [panelCtor])) }); + } + let close = closeDialogEffect.of(panelCtor); + return { close, result: promise.then(form => { + let queue = view.win.queueMicrotask || ((f) => view.win.setTimeout(f, 10)); + queue(() => { + if (view.state.field(dialogField).indexOf(panelCtor) > -1) + view.dispatch({ effects: close }); + }); + return form; + }) }; +} +/** +Find the [`Panel`](https://codemirror.net/6/docs/ref/#view.Panel) for an open dialog, using a class +name as identifier. +*/ +function getDialog(view, className) { + let dialogs = view.state.field(dialogField, false) || []; + for (let open of dialogs) { + let panel = getPanel(view, open); + if (panel && panel.dom.classList.contains(className)) + return panel; + } + return null; +} +const dialogField = state.StateField.define({ + create() { return []; }, + update(dialogs, tr) { + for (let e of tr.effects) { + if (e.is(openDialogEffect)) + dialogs = [e.value].concat(dialogs); + else if (e.is(closeDialogEffect)) + dialogs = dialogs.filter(d => d != e.value); + } + return dialogs; + }, + provide: f => showPanel.computeN([f], state => state.field(f)) +}); +const openDialogEffect = state.StateEffect.define(); +const closeDialogEffect = state.StateEffect.define(); +function createDialog(view, config, result) { + let content = config.content ? config.content(view, () => done(null)) : null; + if (!content) { + content = elt("form"); + if (config.input) { + let input = elt("input", config.input); + if (/^(text|password|number|email|tel|url)$/.test(input.type)) + input.classList.add("cm-textfield"); + if (!input.name) + input.name = "input"; + content.appendChild(elt("label", (config.label || "") + ": ", input)); + } + else { + content.appendChild(document.createTextNode(config.label || "")); + } + content.appendChild(document.createTextNode(" ")); + content.appendChild(elt("button", { class: "cm-button", type: "submit" }, config.submitLabel || "OK")); + } + let forms = content.nodeName == "FORM" ? [content] : content.querySelectorAll("form"); + for (let i = 0; i < forms.length; i++) { + let form = forms[i]; + form.addEventListener("keydown", (event) => { + if (event.keyCode == 27) { // Escape + event.preventDefault(); + done(null); + } + else if (event.keyCode == 13) { // Enter + event.preventDefault(); + done(form); + } + }); + form.addEventListener("submit", (event) => { + event.preventDefault(); + done(form); + }); + } + let panel = elt("div", content, elt("button", { + onclick: () => done(null), + "aria-label": view.state.phrase("close"), + class: "cm-dialog-close", + type: "button" + }, ["×"])); + if (config.class) + panel.className = config.class; + panel.classList.add("cm-dialog"); + function done(form) { + if (panel.contains(panel.ownerDocument.activeElement)) + view.focus(); + result(form); + } + return { + dom: panel, + top: config.top, + mount: () => { + if (config.focus) { + let focus; + if (typeof config.focus == "string") + focus = content.querySelector(config.focus); + else + focus = content.querySelector("input") || content.querySelector("button"); + if (focus && "select" in focus) + focus.select(); + else if (focus && "focus" in focus) + focus.focus(); + } + } + }; +} + +/** +A gutter marker represents a bit of information attached to a line +in a specific gutter. Your own custom markers have to extend this +class. +*/ +class GutterMarker extends state.RangeValue { + /** + @internal + */ + compare(other) { + return this == other || this.constructor == other.constructor && this.eq(other); + } + /** + Compare this marker to another marker of the same type. + */ + eq(other) { return false; } + /** + Called if the marker has a `toDOM` method and its representation + was removed from a gutter. + */ + destroy(dom) { } +} +GutterMarker.prototype.elementClass = ""; +GutterMarker.prototype.toDOM = undefined; +GutterMarker.prototype.mapMode = state.MapMode.TrackBefore; +GutterMarker.prototype.startSide = GutterMarker.prototype.endSide = -1; +GutterMarker.prototype.point = true; +/** +Facet used to add a class to all gutter elements for a given line. +Markers given to this facet should _only_ define an +[`elementclass`](https://codemirror.net/6/docs/ref/#view.GutterMarker.elementClass), not a +[`toDOM`](https://codemirror.net/6/docs/ref/#view.GutterMarker.toDOM) (or the marker will appear +in all gutters for the line). +*/ +const gutterLineClass = state.Facet.define(); +/** +Facet used to add a class to all gutter elements next to a widget. +Should not provide widgets with a `toDOM` method. +*/ +const gutterWidgetClass = state.Facet.define(); +const defaults = { + class: "", + renderEmptyElements: false, + elementStyle: "", + markers: () => state.RangeSet.empty, + lineMarker: () => null, + widgetMarker: () => null, + lineMarkerChange: null, + initialSpacer: null, + updateSpacer: null, + domEventHandlers: {}, + side: "before" +}; +const activeGutters = state.Facet.define(); +/** +Define an editor gutter. The order in which the gutters appear is +determined by their extension priority. +*/ +function gutter(config) { + return [gutters(), activeGutters.of({ ...defaults, ...config })]; +} +const unfixGutters = state.Facet.define({ + combine: values => values.some(x => x) +}); +/** +The gutter-drawing plugin is automatically enabled when you add a +gutter, but you can use this function to explicitly configure it. + +Unless `fixed` is explicitly set to `false`, the gutters are +fixed, meaning they don't scroll along with the content +horizontally (except on Internet Explorer, which doesn't support +CSS [`position: +sticky`](https://developer.mozilla.org/en-US/docs/Web/CSS/position#sticky)). +*/ +function gutters(config) { + let result = [ + gutterView, + ]; + if (config && config.fixed === false) + result.push(unfixGutters.of(true)); + return result; +} +const gutterView = ViewPlugin.fromClass(class { + constructor(view) { + this.view = view; + this.domAfter = null; + this.prevViewport = view.viewport; + this.dom = document.createElement("div"); + this.dom.className = "cm-gutters cm-gutters-before"; + this.dom.setAttribute("aria-hidden", "true"); + this.dom.style.minHeight = (this.view.contentHeight / this.view.scaleY) + "px"; + this.gutters = view.state.facet(activeGutters).map(conf => new SingleGutterView(view, conf)); + this.fixed = !view.state.facet(unfixGutters); + for (let gutter of this.gutters) { + if (gutter.config.side == "after") + this.getDOMAfter().appendChild(gutter.dom); + else + this.dom.appendChild(gutter.dom); + } + if (this.fixed) { + // FIXME IE11 fallback, which doesn't support position: sticky, + // by using position: relative + event handlers that realign the + // gutter (or just force fixed=false on IE11?) + this.dom.style.position = "sticky"; + } + this.syncGutters(false); + view.scrollDOM.insertBefore(this.dom, view.contentDOM); + } + getDOMAfter() { + if (!this.domAfter) { + this.domAfter = document.createElement("div"); + this.domAfter.className = "cm-gutters cm-gutters-after"; + this.domAfter.setAttribute("aria-hidden", "true"); + this.domAfter.style.minHeight = (this.view.contentHeight / this.view.scaleY) + "px"; + this.domAfter.style.position = this.fixed ? "sticky" : ""; + this.view.scrollDOM.appendChild(this.domAfter); + } + return this.domAfter; + } + update(update) { + if (this.updateGutters(update)) { + // Detach during sync when the viewport changed significantly + // (such as during scrolling), since for large updates that is + // faster. + let vpA = this.prevViewport, vpB = update.view.viewport; + let vpOverlap = Math.min(vpA.to, vpB.to) - Math.max(vpA.from, vpB.from); + this.syncGutters(vpOverlap < (vpB.to - vpB.from) * 0.8); + } + if (update.geometryChanged) { + let min = (this.view.contentHeight / this.view.scaleY) + "px"; + this.dom.style.minHeight = min; + if (this.domAfter) + this.domAfter.style.minHeight = min; + } + if (this.view.state.facet(unfixGutters) != !this.fixed) { + this.fixed = !this.fixed; + this.dom.style.position = this.fixed ? "sticky" : ""; + if (this.domAfter) + this.domAfter.style.position = this.fixed ? "sticky" : ""; + } + this.prevViewport = update.view.viewport; + } + syncGutters(detach) { + let after = this.dom.nextSibling; + if (detach) { + this.dom.remove(); + if (this.domAfter) + this.domAfter.remove(); + } + let lineClasses = state.RangeSet.iter(this.view.state.facet(gutterLineClass), this.view.viewport.from); + let classSet = []; + let contexts = this.gutters.map(gutter => new UpdateContext(gutter, this.view.viewport, -this.view.documentPadding.top)); + for (let line of this.view.viewportLineBlocks) { + if (classSet.length) + classSet = []; + if (Array.isArray(line.type)) { + let first = true; + for (let b of line.type) { + if (b.type == exports.BlockType.Text && first) { + advanceCursor(lineClasses, classSet, b.from); + for (let cx of contexts) + cx.line(this.view, b, classSet); + first = false; + } + else if (b.widget) { + for (let cx of contexts) + cx.widget(this.view, b); + } + } + } + else if (line.type == exports.BlockType.Text) { + advanceCursor(lineClasses, classSet, line.from); + for (let cx of contexts) + cx.line(this.view, line, classSet); + } + else if (line.widget) { + for (let cx of contexts) + cx.widget(this.view, line); + } + } + for (let cx of contexts) + cx.finish(); + if (detach) { + this.view.scrollDOM.insertBefore(this.dom, after); + if (this.domAfter) + this.view.scrollDOM.appendChild(this.domAfter); + } + } + updateGutters(update) { + let prev = update.startState.facet(activeGutters), cur = update.state.facet(activeGutters); + let change = update.docChanged || update.heightChanged || update.viewportChanged || + !state.RangeSet.eq(update.startState.facet(gutterLineClass), update.state.facet(gutterLineClass), update.view.viewport.from, update.view.viewport.to); + if (prev == cur) { + for (let gutter of this.gutters) + if (gutter.update(update)) + change = true; + } + else { + change = true; + let gutters = []; + for (let conf of cur) { + let known = prev.indexOf(conf); + if (known < 0) { + gutters.push(new SingleGutterView(this.view, conf)); + } + else { + this.gutters[known].update(update); + gutters.push(this.gutters[known]); + } + } + for (let g of this.gutters) { + g.dom.remove(); + if (gutters.indexOf(g) < 0) + g.destroy(); + } + for (let g of gutters) { + if (g.config.side == "after") + this.getDOMAfter().appendChild(g.dom); + else + this.dom.appendChild(g.dom); + } + this.gutters = gutters; + } + return change; + } + destroy() { + for (let view of this.gutters) + view.destroy(); + this.dom.remove(); + if (this.domAfter) + this.domAfter.remove(); + } +}, { + provide: plugin => EditorView.scrollMargins.of(view => { + let value = view.plugin(plugin); + if (!value || value.gutters.length == 0 || !value.fixed) + return null; + let before = value.dom.offsetWidth * view.scaleX, after = value.domAfter ? value.domAfter.offsetWidth * view.scaleX : 0; + return view.textDirection == exports.Direction.LTR + ? { left: before, right: after } + : { right: before, left: after }; + }) +}); +function asArray(val) { return (Array.isArray(val) ? val : [val]); } +function advanceCursor(cursor, collect, pos) { + while (cursor.value && cursor.from <= pos) { + if (cursor.from == pos) + collect.push(cursor.value); + cursor.next(); + } +} +class UpdateContext { + constructor(gutter, viewport, height) { + this.gutter = gutter; + this.height = height; + this.i = 0; + this.cursor = state.RangeSet.iter(gutter.markers, viewport.from); + } + addElement(view, block, markers) { + let { gutter } = this, above = (block.top - this.height) / view.scaleY, height = block.height / view.scaleY; + if (this.i == gutter.elements.length) { + let newElt = new GutterElement(view, height, above, markers); + gutter.elements.push(newElt); + gutter.dom.appendChild(newElt.dom); + } + else { + gutter.elements[this.i].update(view, height, above, markers); + } + this.height = block.bottom; + this.i++; + } + line(view, line, extraMarkers) { + let localMarkers = []; + advanceCursor(this.cursor, localMarkers, line.from); + if (extraMarkers.length) + localMarkers = localMarkers.concat(extraMarkers); + let forLine = this.gutter.config.lineMarker(view, line, localMarkers); + if (forLine) + localMarkers.unshift(forLine); + let gutter = this.gutter; + if (localMarkers.length == 0 && !gutter.config.renderEmptyElements) + return; + this.addElement(view, line, localMarkers); + } + widget(view, block) { + let marker = this.gutter.config.widgetMarker(view, block.widget, block), markers = marker ? [marker] : null; + for (let cls of view.state.facet(gutterWidgetClass)) { + let marker = cls(view, block.widget, block); + if (marker) + (markers || (markers = [])).push(marker); + } + if (markers) + this.addElement(view, block, markers); + } + finish() { + let gutter = this.gutter; + while (gutter.elements.length > this.i) { + let last = gutter.elements.pop(); + gutter.dom.removeChild(last.dom); + last.destroy(); + } + } +} +class SingleGutterView { + constructor(view, config) { + this.view = view; + this.config = config; + this.elements = []; + this.spacer = null; + this.dom = document.createElement("div"); + this.dom.className = "cm-gutter" + (this.config.class ? " " + this.config.class : ""); + for (let prop in config.domEventHandlers) { + this.dom.addEventListener(prop, (event) => { + let target = event.target, y; + if (target != this.dom && this.dom.contains(target)) { + while (target.parentNode != this.dom) + target = target.parentNode; + let rect = target.getBoundingClientRect(); + y = (rect.top + rect.bottom) / 2; + } + else { + y = event.clientY; + } + let line = view.lineBlockAtHeight(y - view.documentTop); + if (config.domEventHandlers[prop](view, line, event)) + event.preventDefault(); + }); + } + this.markers = asArray(config.markers(view)); + if (config.initialSpacer) { + this.spacer = new GutterElement(view, 0, 0, [config.initialSpacer(view)]); + this.dom.appendChild(this.spacer.dom); + this.spacer.dom.style.cssText += "visibility: hidden; pointer-events: none"; + } + } + update(update) { + let prevMarkers = this.markers; + this.markers = asArray(this.config.markers(update.view)); + if (this.spacer && this.config.updateSpacer) { + let updated = this.config.updateSpacer(this.spacer.markers[0], update); + if (updated != this.spacer.markers[0]) + this.spacer.update(update.view, 0, 0, [updated]); + } + let vp = update.view.viewport; + return !state.RangeSet.eq(this.markers, prevMarkers, vp.from, vp.to) || + (this.config.lineMarkerChange ? this.config.lineMarkerChange(update) : false); + } + destroy() { + for (let elt of this.elements) + elt.destroy(); + } +} +class GutterElement { + constructor(view, height, above, markers) { + this.height = -1; + this.above = 0; + this.markers = []; + this.dom = document.createElement("div"); + this.dom.className = "cm-gutterElement"; + this.update(view, height, above, markers); + } + update(view, height, above, markers) { + if (this.height != height) { + this.height = height; + this.dom.style.height = height + "px"; + } + if (this.above != above) + this.dom.style.marginTop = (this.above = above) ? above + "px" : ""; + if (!sameMarkers(this.markers, markers)) + this.setMarkers(view, markers); + } + setMarkers(view, markers) { + let cls = "cm-gutterElement", domPos = this.dom.firstChild; + for (let iNew = 0, iOld = 0;;) { + let skipTo = iOld, marker = iNew < markers.length ? markers[iNew++] : null, matched = false; + if (marker) { + let c = marker.elementClass; + if (c) + cls += " " + c; + for (let i = iOld; i < this.markers.length; i++) + if (this.markers[i].compare(marker)) { + skipTo = i; + matched = true; + break; + } + } + else { + skipTo = this.markers.length; + } + while (iOld < skipTo) { + let next = this.markers[iOld++]; + if (next.toDOM) { + next.destroy(domPos); + let after = domPos.nextSibling; + domPos.remove(); + domPos = after; + } + } + if (!marker) + break; + if (marker.toDOM) { + if (matched) + domPos = domPos.nextSibling; + else + this.dom.insertBefore(marker.toDOM(view), domPos); + } + if (matched) + iOld++; + } + this.dom.className = cls; + this.markers = markers; + } + destroy() { + this.setMarkers(null, []); // First argument not used unless creating markers + } +} +function sameMarkers(a, b) { + if (a.length != b.length) + return false; + for (let i = 0; i < a.length; i++) + if (!a[i].compare(b[i])) + return false; + return true; +} +/** +Facet used to provide markers to the line number gutter. +*/ +const lineNumberMarkers = state.Facet.define(); +/** +Facet used to create markers in the line number gutter next to widgets. +*/ +const lineNumberWidgetMarker = state.Facet.define(); +const lineNumberConfig = state.Facet.define({ + combine(values) { + return state.combineConfig(values, { formatNumber: String, domEventHandlers: {} }, { + domEventHandlers(a, b) { + let result = Object.assign({}, a); + for (let event in b) { + let exists = result[event], add = b[event]; + result[event] = exists ? (view, line, event) => exists(view, line, event) || add(view, line, event) : add; + } + return result; + } + }); + } +}); +class NumberMarker extends GutterMarker { + constructor(number) { + super(); + this.number = number; + } + eq(other) { return this.number == other.number; } + toDOM() { return document.createTextNode(this.number); } +} +function formatNumber(view, number) { + return view.state.facet(lineNumberConfig).formatNumber(number, view.state); +} +const lineNumberGutter = activeGutters.compute([lineNumberConfig], state => ({ + class: "cm-lineNumbers", + renderEmptyElements: false, + markers(view) { return view.state.facet(lineNumberMarkers); }, + lineMarker(view, line, others) { + if (others.some(m => m.toDOM)) + return null; + return new NumberMarker(formatNumber(view, view.state.doc.lineAt(line.from).number)); + }, + widgetMarker: (view, widget, block) => { + for (let m of view.state.facet(lineNumberWidgetMarker)) { + let result = m(view, widget, block); + if (result) + return result; + } + return null; + }, + lineMarkerChange: update => update.startState.facet(lineNumberConfig) != update.state.facet(lineNumberConfig), + initialSpacer(view) { + return new NumberMarker(formatNumber(view, maxLineNumber(view.state.doc.lines))); + }, + updateSpacer(spacer, update) { + let max = formatNumber(update.view, maxLineNumber(update.view.state.doc.lines)); + return max == spacer.number ? spacer : new NumberMarker(max); + }, + domEventHandlers: state.facet(lineNumberConfig).domEventHandlers, + side: "before" +})); +/** +Create a line number gutter extension. +*/ +function lineNumbers(config = {}) { + return [ + lineNumberConfig.of(config), + gutters(), + lineNumberGutter + ]; +} +function maxLineNumber(lines) { + let last = 9; + while (last < lines) + last = last * 10 + 9; + return last; +} +const activeLineGutterMarker = new class extends GutterMarker { + constructor() { + super(...arguments); + this.elementClass = "cm-activeLineGutter"; + } +}; +const activeLineGutterHighlighter = gutterLineClass.compute(["selection"], state$1 => { + let marks = [], last = -1; + for (let range of state$1.selection.ranges) { + let linePos = state$1.doc.lineAt(range.head).from; + if (linePos > last) { + last = linePos; + marks.push(activeLineGutterMarker.range(linePos)); + } + } + return state.RangeSet.of(marks); +}); +/** +Returns an extension that adds a `cm-activeLineGutter` class to +all gutter elements on the [active +line](https://codemirror.net/6/docs/ref/#view.highlightActiveLine). +*/ +function highlightActiveLineGutter() { + return activeLineGutterHighlighter; +} + +function matcher(decorator) { + return ViewPlugin.define(view => ({ + decorations: decorator.createDeco(view), + update(u) { + this.decorations = decorator.updateDeco(u, this.decorations); + }, + }), { + decorations: v => v.decorations + }); +} +const tabDeco = Decoration.mark({ class: "cm-highlightTab" }); +const spaceDeco = Decoration.mark({ class: "cm-highlightSpace" }); +const whitespaceHighlighter = matcher(new MatchDecorator({ + regexp: /\t| /g, + decoration: match => match[0] == "\t" ? tabDeco : spaceDeco, + boundary: /\S/, +})); +/** +Returns an extension that highlights whitespace, adding a +`cm-highlightSpace` class to stretches of spaces, and a +`cm-highlightTab` class to individual tab characters. By default, +the former are shown as faint dots, and the latter as arrows. +*/ +function highlightWhitespace() { + return whitespaceHighlighter; +} +const trailingHighlighter = matcher(new MatchDecorator({ + regexp: /\s+$/g, + decoration: Decoration.mark({ class: "cm-trailingSpace" }) +})); +/** +Returns an extension that adds a `cm-trailingSpace` class to all +trailing whitespace. +*/ +function highlightTrailingWhitespace() { + return trailingHighlighter; +} + +/** +@internal +*/ +const __test = { HeightMap, HeightOracle, MeasuredHeights, QueryType, ChangedRange, computeOrder, + moveVisually, clearHeightChangeFlag, getHeightChangeFlag: () => heightChangeFlag }; + +exports.BidiSpan = BidiSpan; +exports.BlockInfo = BlockInfo; +exports.Decoration = Decoration; +exports.EditorView = EditorView; +exports.GutterMarker = GutterMarker; +exports.MatchDecorator = MatchDecorator; +exports.RectangleMarker = RectangleMarker; +exports.ViewPlugin = ViewPlugin; +exports.ViewUpdate = ViewUpdate; +exports.WidgetType = WidgetType; +exports.__test = __test; +exports.closeHoverTooltips = closeHoverTooltips; +exports.crosshairCursor = crosshairCursor; +exports.drawSelection = drawSelection; +exports.dropCursor = dropCursor; +exports.getDialog = getDialog; +exports.getDrawSelectionConfig = getDrawSelectionConfig; +exports.getPanel = getPanel; +exports.getTooltip = getTooltip; +exports.gutter = gutter; +exports.gutterLineClass = gutterLineClass; +exports.gutterWidgetClass = gutterWidgetClass; +exports.gutters = gutters; +exports.hasHoverTooltips = hasHoverTooltips; +exports.highlightActiveLine = highlightActiveLine; +exports.highlightActiveLineGutter = highlightActiveLineGutter; +exports.highlightSpecialChars = highlightSpecialChars; +exports.highlightTrailingWhitespace = highlightTrailingWhitespace; +exports.highlightWhitespace = highlightWhitespace; +exports.hoverTooltip = hoverTooltip; +exports.keymap = keymap; +exports.layer = layer; +exports.lineNumberMarkers = lineNumberMarkers; +exports.lineNumberWidgetMarker = lineNumberWidgetMarker; +exports.lineNumbers = lineNumbers; +exports.logException = logException; +exports.panels = panels; +exports.placeholder = placeholder; +exports.rectangularSelection = rectangularSelection; +exports.repositionTooltips = repositionTooltips; +exports.runScopeHandlers = runScopeHandlers; +exports.scrollPastEnd = scrollPastEnd; +exports.showDialog = showDialog; +exports.showPanel = showPanel; +exports.showTooltip = showTooltip; +exports.tooltips = tooltips; diff --git a/node_modules/@codemirror/view/dist/index.d.cts b/node_modules/@codemirror/view/dist/index.d.cts new file mode 100644 index 0000000..dbd5213 --- /dev/null +++ b/node_modules/@codemirror/view/dist/index.d.cts @@ -0,0 +1,2306 @@ +import * as _codemirror_state from '@codemirror/state'; +import { RangeSet, RangeValue, Range, EditorState, Extension, Transaction, ChangeSet, SelectionRange, ChangeDesc, EditorSelection, EditorStateConfig, StateEffect, TransactionSpec, Line, Facet, StateField } from '@codemirror/state'; +import { StyleModule, StyleSpec } from 'style-mod'; + +/** +Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection). +*/ +declare enum Direction { + /** + Left-to-right. + */ + LTR = 0, + /** + Right-to-left. + */ + RTL = 1 +} +/** +Represents a contiguous range of text that has a single direction +(as in left-to-right or right-to-left). +*/ +declare class BidiSpan { + /** + The start of the span (relative to the start of the line). + */ + readonly from: number; + /** + The end of the span. + */ + readonly to: number; + /** + The ["bidi + level"](https://unicode.org/reports/tr9/#Basic_Display_Algorithm) + of the span (in this context, 0 means + left-to-right, 1 means right-to-left, 2 means left-to-right + number inside right-to-left text). + */ + readonly level: number; + /** + The direction of this span. + */ + get dir(): Direction; +} + +type Attrs = { + [name: string]: string; +}; + +/** +Basic rectangle type. +*/ +interface Rect { + readonly left: number; + readonly right: number; + readonly top: number; + readonly bottom: number; +} +type ScrollStrategy = "nearest" | "start" | "end" | "center"; + +interface MarkDecorationSpec { + /** + Whether the mark covers its start and end position or not. This + influences whether content inserted at those positions becomes + part of the mark. Defaults to false. + */ + inclusive?: boolean; + /** + Specify whether the start position of the marked range should be + inclusive. Overrides `inclusive`, when both are present. + */ + inclusiveStart?: boolean; + /** + Whether the end should be inclusive. + */ + inclusiveEnd?: boolean; + /** + Add attributes to the DOM elements that hold the text in the + marked range. + */ + attributes?: { + [key: string]: string; + }; + /** + Shorthand for `{attributes: {class: value}}`. + */ + class?: string; + /** + Add a wrapping element around the text in the marked range. Note + that there will not necessarily be a single element covering the + entire range—other decorations with lower precedence might split + this one if they partially overlap it, and line breaks always + end decoration elements. + */ + tagName?: string; + /** + When using sets of decorations in + [`bidiIsolatedRanges`](https://codemirror.net/6/docs/ref/##view.EditorView^bidiIsolatedRanges), + this property provides the direction of the isolates. When null + or not given, it indicates the range has `dir=auto`, and its + direction should be derived from the first strong directional + character in it. + */ + bidiIsolate?: Direction | null; + /** + Decoration specs allow extra properties, which can be retrieved + through the decoration's [`spec`](https://codemirror.net/6/docs/ref/#view.Decoration.spec) + property. + */ + [other: string]: any; +} +interface WidgetDecorationSpec { + /** + The type of widget to draw here. + */ + widget: WidgetType; + /** + Which side of the given position the widget is on. When this is + positive, the widget will be drawn after the cursor if the + cursor is on the same position. Otherwise, it'll be drawn before + it. When multiple widgets sit at the same position, their `side` + values will determine their ordering—those with a lower value + come first. Defaults to 0. May not be more than 10000 or less + than -10000. + */ + side?: number; + /** + By default, to avoid unintended mixing of block and inline + widgets, block widgets with a positive `side` are always drawn + after all inline widgets at that position, and those with a + non-positive side before inline widgets. Setting this option to + `true` for a block widget will turn this off and cause it to be + rendered between the inline widgets, ordered by `side`. + */ + inlineOrder?: boolean; + /** + Determines whether this is a block widgets, which will be drawn + between lines, or an inline widget (the default) which is drawn + between the surrounding text. + + Note that block-level decorations should not have vertical + margins, and if you dynamically change their height, you should + make sure to call + [`requestMeasure`](https://codemirror.net/6/docs/ref/#view.EditorView.requestMeasure), so that the + editor can update its information about its vertical layout. + */ + block?: boolean; + /** + Other properties are allowed. + */ + [other: string]: any; +} +interface ReplaceDecorationSpec { + /** + An optional widget to drawn in the place of the replaced + content. + */ + widget?: WidgetType; + /** + Whether this range covers the positions on its sides. This + influences whether new content becomes part of the range and + whether the cursor can be drawn on its sides. Defaults to false + for inline replacements, and true for block replacements. + */ + inclusive?: boolean; + /** + Set inclusivity at the start. + */ + inclusiveStart?: boolean; + /** + Set inclusivity at the end. + */ + inclusiveEnd?: boolean; + /** + Whether this is a block-level decoration. Defaults to false. + */ + block?: boolean; + /** + Other properties are allowed. + */ + [other: string]: any; +} +interface LineDecorationSpec { + /** + DOM attributes to add to the element wrapping the line. + */ + attributes?: { + [key: string]: string; + }; + /** + Shorthand for `{attributes: {class: value}}`. + */ + class?: string; + /** + Other properties are allowed. + */ + [other: string]: any; +} +/** +Widgets added to the content are described by subclasses of this +class. Using a description object like that makes it possible to +delay creating of the DOM structure for a widget until it is +needed, and to avoid redrawing widgets even if the decorations +that define them are recreated. +*/ +declare abstract class WidgetType { + /** + Build the DOM structure for this widget instance. + */ + abstract toDOM(view: EditorView): HTMLElement; + /** + Compare this instance to another instance of the same type. + (TypeScript can't express this, but only instances of the same + specific class will be passed to this method.) This is used to + avoid redrawing widgets when they are replaced by a new + decoration of the same type. The default implementation just + returns `false`, which will cause new instances of the widget to + always be redrawn. + */ + eq(widget: WidgetType): boolean; + /** + Update a DOM element created by a widget of the same type (but + different, non-`eq` content) to reflect this widget. May return + true to indicate that it could update, false to indicate it + couldn't (in which case the widget will be redrawn). The default + implementation just returns false. + */ + updateDOM(dom: HTMLElement, view: EditorView): boolean; + /** + The estimated height this widget will have, to be used when + estimating the height of content that hasn't been drawn. May + return -1 to indicate you don't know. The default implementation + returns -1. + */ + get estimatedHeight(): number; + /** + For inline widgets that are displayed inline (as opposed to + `inline-block`) and introduce line breaks (through `
` tags + or textual newlines), this must indicate the amount of line + breaks they introduce. Defaults to 0. + */ + get lineBreaks(): number; + /** + Can be used to configure which kinds of events inside the widget + should be ignored by the editor. The default is to ignore all + events. + */ + ignoreEvent(event: Event): boolean; + /** + Override the way screen coordinates for positions at/in the + widget are found. `pos` will be the offset into the widget, and + `side` the side of the position that is being queried—less than + zero for before, greater than zero for after, and zero for + directly at that position. + */ + coordsAt(dom: HTMLElement, pos: number, side: number): Rect | null; + /** + This is called when the an instance of the widget is removed + from the editor view. + */ + destroy(dom: HTMLElement): void; +} +/** +A decoration set represents a collection of decorated ranges, +organized for efficient access and mapping. See +[`RangeSet`](https://codemirror.net/6/docs/ref/#state.RangeSet) for its methods. +*/ +type DecorationSet = RangeSet; +/** +The different types of blocks that can occur in an editor view. +*/ +declare enum BlockType { + /** + A line of text. + */ + Text = 0, + /** + A block widget associated with the position after it. + */ + WidgetBefore = 1, + /** + A block widget associated with the position before it. + */ + WidgetAfter = 2, + /** + A block widget [replacing](https://codemirror.net/6/docs/ref/#view.Decoration^replace) a range of content. + */ + WidgetRange = 3 +} +/** +A decoration provides information on how to draw or style a piece +of content. You'll usually use it wrapped in a +[`Range`](https://codemirror.net/6/docs/ref/#state.Range), which adds a start and end position. +@nonabstract +*/ +declare abstract class Decoration extends RangeValue { + /** + The config object used to create this decoration. You can + include additional properties in there to store metadata about + your decoration. + */ + readonly spec: any; + protected constructor( + /** + @internal + */ + startSide: number, + /** + @internal + */ + endSide: number, + /** + @internal + */ + widget: WidgetType | null, + /** + The config object used to create this decoration. You can + include additional properties in there to store metadata about + your decoration. + */ + spec: any); + abstract eq(other: Decoration): boolean; + /** + Create a mark decoration, which influences the styling of the + content in its range. Nested mark decorations will cause nested + DOM elements to be created. Nesting order is determined by + precedence of the [facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations), with + the higher-precedence decorations creating the inner DOM nodes. + Such elements are split on line boundaries and on the boundaries + of lower-precedence decorations. + */ + static mark(spec: MarkDecorationSpec): Decoration; + /** + Create a widget decoration, which displays a DOM element at the + given position. + */ + static widget(spec: WidgetDecorationSpec): Decoration; + /** + Create a replace decoration which replaces the given range with + a widget, or simply hides it. + */ + static replace(spec: ReplaceDecorationSpec): Decoration; + /** + Create a line decoration, which can add DOM attributes to the + line starting at the given position. + */ + static line(spec: LineDecorationSpec): Decoration; + /** + Build a [`DecorationSet`](https://codemirror.net/6/docs/ref/#view.DecorationSet) from the given + decorated range or ranges. If the ranges aren't already sorted, + pass `true` for `sort` to make the library sort them for you. + */ + static set(of: Range | readonly Range[], sort?: boolean): DecorationSet; + /** + The empty set of decorations. + */ + static none: DecorationSet; +} + +/** +Command functions are used in key bindings and other types of user +actions. Given an editor view, they check whether their effect can +apply to the editor, and if it can, perform it as a side effect +(which usually means [dispatching](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch) a +transaction) and return `true`. +*/ +type Command = (target: EditorView) => boolean; +declare class ScrollTarget { + readonly range: SelectionRange; + readonly y: ScrollStrategy; + readonly x: ScrollStrategy; + readonly yMargin: number; + readonly xMargin: number; + readonly isSnapshot: boolean; + constructor(range: SelectionRange, y?: ScrollStrategy, x?: ScrollStrategy, yMargin?: number, xMargin?: number, isSnapshot?: boolean); + map(changes: ChangeDesc): ScrollTarget; + clip(state: EditorState): ScrollTarget; +} +/** +Log or report an unhandled exception in client code. Should +probably only be used by extension code that allows client code to +provide functions, and calls those functions in a context where an +exception can't be propagated to calling code in a reasonable way +(for example when in an event handler). + +Either calls a handler registered with +[`EditorView.exceptionSink`](https://codemirror.net/6/docs/ref/#view.EditorView^exceptionSink), +`window.onerror`, if defined, or `console.error` (in which case +it'll pass `context`, when given, as first argument). +*/ +declare function logException(state: EditorState, exception: any, context?: string): void; +/** +This is the interface plugin objects conform to. +*/ +interface PluginValue extends Object { + /** + Notifies the plugin of an update that happened in the view. This + is called _before_ the view updates its own DOM. It is + responsible for updating the plugin's internal state (including + any state that may be read by plugin fields) and _writing_ to + the DOM for the changes in the update. To avoid unnecessary + layout recomputations, it should _not_ read the DOM layout—use + [`requestMeasure`](https://codemirror.net/6/docs/ref/#view.EditorView.requestMeasure) to schedule + your code in a DOM reading phase if you need to. + */ + update?(update: ViewUpdate): void; + /** + Called when the document view is updated (due to content, + decoration, or viewport changes). Should not try to immediately + start another view update. Often useful for calling + [`requestMeasure`](https://codemirror.net/6/docs/ref/#view.EditorView.requestMeasure). + */ + docViewUpdate?(view: EditorView): void; + /** + Called when the plugin is no longer going to be used. Should + revert any changes the plugin made to the DOM. + */ + destroy?(): void; +} +/** +Provides additional information when defining a [view +plugin](https://codemirror.net/6/docs/ref/#view.ViewPlugin). +*/ +interface PluginSpec { + /** + Register the given [event + handlers](https://codemirror.net/6/docs/ref/#view.EditorView^domEventHandlers) for the plugin. + When called, these will have their `this` bound to the plugin + value. + */ + eventHandlers?: DOMEventHandlers; + /** + Registers [event observers](https://codemirror.net/6/docs/ref/#view.EditorView^domEventObservers) + for the plugin. Will, when called, have their `this` bound to + the plugin value. + */ + eventObservers?: DOMEventHandlers; + /** + Specify that the plugin provides additional extensions when + added to an editor configuration. + */ + provide?: (plugin: ViewPlugin) => Extension; + /** + Allow the plugin to provide decorations. When given, this should + be a function that take the plugin value and return a + [decoration set](https://codemirror.net/6/docs/ref/#view.DecorationSet). See also the caveat about + [layout-changing decorations](https://codemirror.net/6/docs/ref/#view.EditorView^decorations) that + depend on the view. + */ + decorations?: (value: V) => DecorationSet; +} +/** +View plugins associate stateful values with a view. They can +influence the way the content is drawn, and are notified of things +that happen in the view. They optionally take an argument, in +which case you need to call [`of`](https://codemirror.net/6/docs/ref/#view.ViewPlugin.of) to create +an extension for the plugin. When the argument type is undefined, +you can use the plugin instance as an extension directly. +*/ +declare class ViewPlugin { + /** + When `Arg` is undefined, instances of this class act as + extensions. Otherwise, you have to call `of` to create an + extension value. + */ + extension: Arg extends undefined ? Extension : null; + private baseExtensions; + private constructor(); + /** + Create an extension for this plugin with the given argument. + */ + of(arg: Arg): Extension; + /** + Define a plugin from a constructor function that creates the + plugin's value, given an editor view. + */ + static define(create: (view: EditorView, arg: Arg) => V, spec?: PluginSpec): ViewPlugin; + /** + Create a plugin for a class whose constructor takes a single + editor view as argument. + */ + static fromClass(cls: { + new (view: EditorView, arg: Arg): V; + }, spec?: PluginSpec): ViewPlugin; +} +interface MeasureRequest { + /** + Called in a DOM read phase to gather information that requires + DOM layout. Should _not_ mutate the document. + */ + read(view: EditorView): T; + /** + Called in a DOM write phase to update the document. Should _not_ + do anything that triggers DOM layout. + */ + write?(measure: T, view: EditorView): void; + /** + When multiple requests with the same key are scheduled, only the + last one will actually be run. + */ + key?: any; +} +type AttrSource = Attrs | ((view: EditorView) => Attrs | null); +/** +View [plugins](https://codemirror.net/6/docs/ref/#view.ViewPlugin) are given instances of this +class, which describe what happened, whenever the view is updated. +*/ +declare class ViewUpdate { + /** + The editor view that the update is associated with. + */ + readonly view: EditorView; + /** + The new editor state. + */ + readonly state: EditorState; + /** + The transactions involved in the update. May be empty. + */ + readonly transactions: readonly Transaction[]; + /** + The changes made to the document by this update. + */ + readonly changes: ChangeSet; + /** + The previous editor state. + */ + readonly startState: EditorState; + private constructor(); + /** + Tells you whether the [viewport](https://codemirror.net/6/docs/ref/#view.EditorView.viewport) or + [visible ranges](https://codemirror.net/6/docs/ref/#view.EditorView.visibleRanges) changed in this + update. + */ + get viewportChanged(): boolean; + /** + Returns true when + [`viewportChanged`](https://codemirror.net/6/docs/ref/#view.ViewUpdate.viewportChanged) is true + and the viewport change is not just the result of mapping it in + response to document changes. + */ + get viewportMoved(): boolean; + /** + Indicates whether the height of a block element in the editor + changed in this update. + */ + get heightChanged(): boolean; + /** + Returns true when the document was modified or the size of the + editor, or elements within the editor, changed. + */ + get geometryChanged(): boolean; + /** + True when this update indicates a focus change. + */ + get focusChanged(): boolean; + /** + Whether the document changed in this update. + */ + get docChanged(): boolean; + /** + Whether the selection was explicitly set in this update. + */ + get selectionSet(): boolean; +} + +/** +Interface that objects registered with +[`EditorView.mouseSelectionStyle`](https://codemirror.net/6/docs/ref/#view.EditorView^mouseSelectionStyle) +must conform to. +*/ +interface MouseSelectionStyle { + /** + Return a new selection for the mouse gesture that starts with + the event that was originally given to the constructor, and ends + with the event passed here. In case of a plain click, those may + both be the `mousedown` event, in case of a drag gesture, the + latest `mousemove` event will be passed. + + When `extend` is true, that means the new selection should, if + possible, extend the start selection. If `multiple` is true, the + new selection should be added to the original selection. + */ + get: (curEvent: MouseEvent, extend: boolean, multiple: boolean) => EditorSelection; + /** + Called when the view is updated while the gesture is in + progress. When the document changes, it may be necessary to map + some data (like the original selection or start position) + through the changes. + + This may return `true` to indicate that the `get` method should + get queried again after the update, because something in the + update could change its result. Be wary of infinite loops when + using this (where `get` returns a new selection, which will + trigger `update`, which schedules another `get` in response). + */ + update: (update: ViewUpdate) => boolean | void; +} +type MakeSelectionStyle = (view: EditorView, event: MouseEvent) => MouseSelectionStyle | null; + +/** +Record used to represent information about a block-level element +in the editor view. +*/ +declare class BlockInfo { + /** + The start of the element in the document. + */ + readonly from: number; + /** + The length of the element. + */ + readonly length: number; + /** + The top position of the element (relative to the top of the + document). + */ + readonly top: number; + /** + Its height. + */ + readonly height: number; + /** + The type of element this is. When querying lines, this may be + an array of all the blocks that make up the line. + */ + get type(): BlockType | readonly BlockInfo[]; + /** + The end of the element as a document position. + */ + get to(): number; + /** + The bottom position of the element. + */ + get bottom(): number; + /** + If this is a widget block, this will return the widget + associated with it. + */ + get widget(): WidgetType | null; + /** + If this is a textblock, this holds the number of line breaks + that appear in widgets inside the block. + */ + get widgetLineBreaks(): number; +} + +/** +The type of object given to the [`EditorView`](https://codemirror.net/6/docs/ref/#view.EditorView) +constructor. +*/ +interface EditorViewConfig extends EditorStateConfig { + /** + The view's initial state. If not given, a new state is created + by passing this configuration object to + [`EditorState.create`](https://codemirror.net/6/docs/ref/#state.EditorState^create), using its + `doc`, `selection`, and `extensions` field (if provided). + */ + state?: EditorState; + /** + When given, the editor is immediately appended to the given + element on creation. (Otherwise, you'll have to place the view's + [`dom`](https://codemirror.net/6/docs/ref/#view.EditorView.dom) element in the document yourself.) + */ + parent?: Element | DocumentFragment; + /** + If the view is going to be mounted in a shadow root or document + other than the one held by the global variable `document` (the + default), you should pass it here. If you provide `parent`, but + not this option, the editor will automatically look up a root + from the parent. + */ + root?: Document | ShadowRoot; + /** + Pass an effect created with + [`EditorView.scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) or + [`EditorView.scrollSnapshot`](https://codemirror.net/6/docs/ref/#view.EditorView.scrollSnapshot) + here to set an initial scroll position. + */ + scrollTo?: StateEffect; + /** + Override the way transactions are + [dispatched](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch) for this editor view. + Your implementation, if provided, should probably call the + view's [`update` method](https://codemirror.net/6/docs/ref/#view.EditorView.update). + */ + dispatchTransactions?: (trs: readonly Transaction[], view: EditorView) => void; + /** + **Deprecated** single-transaction version of + `dispatchTransactions`. Will force transactions to be dispatched + one at a time when used. + */ + dispatch?: (tr: Transaction, view: EditorView) => void; +} +/** +An editor view represents the editor's user interface. It holds +the editable DOM surface, and possibly other elements such as the +line number gutter. It handles events and dispatches state +transactions for editing actions. +*/ +declare class EditorView { + /** + The current editor state. + */ + get state(): EditorState; + /** + To be able to display large documents without consuming too much + memory or overloading the browser, CodeMirror only draws the + code that is visible (plus a margin around it) to the DOM. This + property tells you the extent of the current drawn viewport, in + document positions. + */ + get viewport(): { + from: number; + to: number; + }; + /** + When there are, for example, large collapsed ranges in the + viewport, its size can be a lot bigger than the actual visible + content. Thus, if you are doing something like styling the + content in the viewport, it is preferable to only do so for + these ranges, which are the subset of the viewport that is + actually drawn. + */ + get visibleRanges(): readonly { + from: number; + to: number; + }[]; + /** + Returns false when the editor is entirely scrolled out of view + or otherwise hidden. + */ + get inView(): boolean; + /** + Indicates whether the user is currently composing text via + [IME](https://en.wikipedia.org/wiki/Input_method), and at least + one change has been made in the current composition. + */ + get composing(): boolean; + /** + Indicates whether the user is currently in composing state. Note + that on some platforms, like Android, this will be the case a + lot, since just putting the cursor on a word starts a + composition there. + */ + get compositionStarted(): boolean; + private dispatchTransactions; + private _root; + /** + The document or shadow root that the view lives in. + */ + get root(): DocumentOrShadowRoot; + /** + The DOM element that wraps the entire editor view. + */ + readonly dom: HTMLElement; + /** + The DOM element that can be styled to scroll. (Note that it may + not have been, so you can't assume this is scrollable.) + */ + readonly scrollDOM: HTMLElement; + /** + The editable DOM element holding the editor content. You should + not, usually, interact with this content directly though the + DOM, since the editor will immediately undo most of the changes + you make. Instead, [dispatch](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch) + [transactions](https://codemirror.net/6/docs/ref/#state.Transaction) to modify content, and + [decorations](https://codemirror.net/6/docs/ref/#view.Decoration) to style it. + */ + readonly contentDOM: HTMLElement; + private announceDOM; + private plugins; + private pluginMap; + private editorAttrs; + private contentAttrs; + private styleModules; + private bidiCache; + private destroyed; + /** + Construct a new view. You'll want to either provide a `parent` + option, or put `view.dom` into your document after creating a + view, so that the user can see the editor. + */ + constructor(config?: EditorViewConfig); + /** + All regular editor state updates should go through this. It + takes a transaction, array of transactions, or transaction spec + and updates the view to show the new state produced by that + transaction. Its implementation can be overridden with an + [option](https://codemirror.net/6/docs/ref/#view.EditorView.constructor^config.dispatchTransactions). + This function is bound to the view instance, so it does not have + to be called as a method. + + Note that when multiple `TransactionSpec` arguments are + provided, these define a single transaction (the specs will be + merged), not a sequence of transactions. + */ + dispatch(tr: Transaction): void; + dispatch(trs: readonly Transaction[]): void; + dispatch(...specs: TransactionSpec[]): void; + /** + Update the view for the given array of transactions. This will + update the visible document and selection to match the state + produced by the transactions, and notify view plugins of the + change. You should usually call + [`dispatch`](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch) instead, which uses this + as a primitive. + */ + update(transactions: readonly Transaction[]): void; + /** + Reset the view to the given state. (This will cause the entire + document to be redrawn and all view plugins to be reinitialized, + so you should probably only use it when the new state isn't + derived from the old state. Otherwise, use + [`dispatch`](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch) instead.) + */ + setState(newState: EditorState): void; + private updatePlugins; + private docViewUpdate; + /** + Get the CSS classes for the currently active editor themes. + */ + get themeClasses(): string; + private updateAttrs; + private showAnnouncements; + private mountStyles; + private readMeasured; + /** + Schedule a layout measurement, optionally providing callbacks to + do custom DOM measuring followed by a DOM write phase. Using + this is preferable reading DOM layout directly from, for + example, an event handler, because it'll make sure measuring and + drawing done by other components is synchronized, avoiding + unnecessary DOM layout computations. + */ + requestMeasure(request?: MeasureRequest): void; + /** + Get the value of a specific plugin, if present. Note that + plugins that crash can be dropped from a view, so even when you + know you registered a given plugin, it is recommended to check + the return value of this method. + */ + plugin(plugin: ViewPlugin): T | null; + /** + The top position of the document, in screen coordinates. This + may be negative when the editor is scrolled down. Points + directly to the top of the first line, not above the padding. + */ + get documentTop(): number; + /** + Reports the padding above and below the document. + */ + get documentPadding(): { + top: number; + bottom: number; + }; + /** + If the editor is transformed with CSS, this provides the scale + along the X axis. Otherwise, it will just be 1. Note that + transforms other than translation and scaling are not supported. + */ + get scaleX(): number; + /** + Provide the CSS transformed scale along the Y axis. + */ + get scaleY(): number; + /** + Find the text line or block widget at the given vertical + position (which is interpreted as relative to the [top of the + document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)). + */ + elementAtHeight(height: number): BlockInfo; + /** + Find the line block (see + [`lineBlockAt`](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt)) at the given + height, again interpreted relative to the [top of the + document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop). + */ + lineBlockAtHeight(height: number): BlockInfo; + /** + Get the extent and vertical position of all [line + blocks](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) in the viewport. Positions + are relative to the [top of the + document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop); + */ + get viewportLineBlocks(): BlockInfo[]; + /** + Find the line block around the given document position. A line + block is a range delimited on both sides by either a + non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^replace) line break, or the + start/end of the document. It will usually just hold a line of + text, but may be broken into multiple textblocks by block + widgets. + */ + lineBlockAt(pos: number): BlockInfo; + /** + The editor's total content height. + */ + get contentHeight(): number; + /** + Move a cursor position by [grapheme + cluster](https://codemirror.net/6/docs/ref/#state.findClusterBreak). `forward` determines whether + the motion is away from the line start, or towards it. In + bidirectional text, the line is traversed in visual order, using + the editor's [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection). + When the start position was the last one on the line, the + returned position will be across the line break. If there is no + further line, the original position is returned. + + By default, this method moves over a single cluster. The + optional `by` argument can be used to move across more. It will + be called with the first cluster as argument, and should return + a predicate that determines, for each subsequent cluster, + whether it should also be moved over. + */ + moveByChar(start: SelectionRange, forward: boolean, by?: (initial: string) => (next: string) => boolean): SelectionRange; + /** + Move a cursor position across the next group of either + [letters](https://codemirror.net/6/docs/ref/#state.EditorState.charCategorizer) or non-letter + non-whitespace characters. + */ + moveByGroup(start: SelectionRange, forward: boolean): SelectionRange; + /** + Get the cursor position visually at the start or end of a line. + Note that this may differ from the _logical_ position at its + start or end (which is simply at `line.from`/`line.to`) if text + at the start or end goes against the line's base text direction. + */ + visualLineSide(line: Line, end: boolean): SelectionRange; + /** + Move to the next line boundary in the given direction. If + `includeWrap` is true, line wrapping is on, and there is a + further wrap point on the current line, the wrap point will be + returned. Otherwise this function will return the start or end + of the line. + */ + moveToLineBoundary(start: SelectionRange, forward: boolean, includeWrap?: boolean): SelectionRange; + /** + Move a cursor position vertically. When `distance` isn't given, + it defaults to moving to the next line (including wrapped + lines). Otherwise, `distance` should provide a positive distance + in pixels. + + When `start` has a + [`goalColumn`](https://codemirror.net/6/docs/ref/#state.SelectionRange.goalColumn), the vertical + motion will use that as a target horizontal position. Otherwise, + the cursor's own horizontal position is used. The returned + cursor will have its goal column set to whichever column was + used. + */ + moveVertically(start: SelectionRange, forward: boolean, distance?: number): SelectionRange; + /** + Find the DOM parent node and offset (child offset if `node` is + an element, character offset when it is a text node) at the + given document position. + + Note that for positions that aren't currently in + `visibleRanges`, the resulting DOM position isn't necessarily + meaningful (it may just point before or after a placeholder + element). + */ + domAtPos(pos: number): { + node: Node; + offset: number; + }; + /** + Find the document position at the given DOM node. Can be useful + for associating positions with DOM events. Will raise an error + when `node` isn't part of the editor content. + */ + posAtDOM(node: Node, offset?: number): number; + /** + Get the document position at the given screen coordinates. For + positions not covered by the visible viewport's DOM structure, + this will return null, unless `false` is passed as second + argument, in which case it'll return an estimated position that + would be near the coordinates if it were rendered. + */ + posAtCoords(coords: { + x: number; + y: number; + }, precise: false): number; + posAtCoords(coords: { + x: number; + y: number; + }): number | null; + /** + Get the screen coordinates at the given document position. + `side` determines whether the coordinates are based on the + element before (-1) or after (1) the position (if no element is + available on the given side, the method will transparently use + another strategy to get reasonable coordinates). + */ + coordsAtPos(pos: number, side?: -1 | 1): Rect | null; + /** + Return the rectangle around a given character. If `pos` does not + point in front of a character that is in the viewport and + rendered (i.e. not replaced, not a line break), this will return + null. For space characters that are a line wrap point, this will + return the position before the line break. + */ + coordsForChar(pos: number): Rect | null; + /** + The default width of a character in the editor. May not + accurately reflect the width of all characters (given variable + width fonts or styling of invididual ranges). + */ + get defaultCharacterWidth(): number; + /** + The default height of a line in the editor. May not be accurate + for all lines. + */ + get defaultLineHeight(): number; + /** + The text direction + ([`direction`](https://developer.mozilla.org/en-US/docs/Web/CSS/direction) + CSS property) of the editor's content element. + */ + get textDirection(): Direction; + /** + Find the text direction of the block at the given position, as + assigned by CSS. If + [`perLineTextDirection`](https://codemirror.net/6/docs/ref/#view.EditorView^perLineTextDirection) + isn't enabled, or the given position is outside of the viewport, + this will always return the same as + [`textDirection`](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection). Note that + this may trigger a DOM layout. + */ + textDirectionAt(pos: number): Direction; + /** + Whether this editor [wraps lines](https://codemirror.net/6/docs/ref/#view.EditorView.lineWrapping) + (as determined by the + [`white-space`](https://developer.mozilla.org/en-US/docs/Web/CSS/white-space) + CSS property of its content element). + */ + get lineWrapping(): boolean; + /** + Returns the bidirectional text structure of the given line + (which should be in the current document) as an array of span + objects. The order of these spans matches the [text + direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection)—if that is + left-to-right, the leftmost spans come first, otherwise the + rightmost spans come first. + */ + bidiSpans(line: Line): readonly BidiSpan[]; + /** + Check whether the editor has focus. + */ + get hasFocus(): boolean; + /** + Put focus on the editor. + */ + focus(): void; + /** + Update the [root](https://codemirror.net/6/docs/ref/##view.EditorViewConfig.root) in which the editor lives. This is only + necessary when moving the editor's existing DOM to a new window or shadow root. + */ + setRoot(root: Document | ShadowRoot): void; + /** + Clean up this editor view, removing its element from the + document, unregistering event handlers, and notifying + plugins. The view instance can no longer be used after + calling this. + */ + destroy(): void; + /** + Returns an effect that can be + [added](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) to a transaction to + cause it to scroll the given position or range into view. + */ + static scrollIntoView(pos: number | SelectionRange, options?: { + /** + By default (`"nearest"`) the position will be vertically + scrolled only the minimal amount required to move the given + position into view. You can set this to `"start"` to move it + to the top of the view, `"end"` to move it to the bottom, or + `"center"` to move it to the center. + */ + y?: ScrollStrategy; + /** + Effect similar to + [`y`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView^options.y), but for the + horizontal scroll position. + */ + x?: ScrollStrategy; + /** + Extra vertical distance to add when moving something into + view. Not used with the `"center"` strategy. Defaults to 5. + Must be less than the height of the editor. + */ + yMargin?: number; + /** + Extra horizontal distance to add. Not used with the `"center"` + strategy. Defaults to 5. Must be less than the width of the + editor. + */ + xMargin?: number; + }): StateEffect; + /** + Return an effect that resets the editor to its current (at the + time this method was called) scroll position. Note that this + only affects the editor's own scrollable element, not parents. + See also + [`EditorViewConfig.scrollTo`](https://codemirror.net/6/docs/ref/#view.EditorViewConfig.scrollTo). + + The effect should be used with a document identical to the one + it was created for. Failing to do so is not an error, but may + not scroll to the expected position. You can + [map](https://codemirror.net/6/docs/ref/#state.StateEffect.map) the effect to account for changes. + */ + scrollSnapshot(): StateEffect; + /** + Enable or disable tab-focus mode, which disables key bindings + for Tab and Shift-Tab, letting the browser's default + focus-changing behavior go through instead. This is useful to + prevent trapping keyboard users in your editor. + + Without argument, this toggles the mode. With a boolean, it + enables (true) or disables it (false). Given a number, it + temporarily enables the mode until that number of milliseconds + have passed or another non-Tab key is pressed. + */ + setTabFocusMode(to?: boolean | number): void; + /** + Facet to add a [style + module](https://github.com/marijnh/style-mod#documentation) to + an editor view. The view will ensure that the module is + mounted in its [document + root](https://codemirror.net/6/docs/ref/#view.EditorView.constructor^config.root). + */ + static styleModule: Facet; + /** + Returns an extension that can be used to add DOM event handlers. + The value should be an object mapping event names to handler + functions. For any given event, such functions are ordered by + extension precedence, and the first handler to return true will + be assumed to have handled that event, and no other handlers or + built-in behavior will be activated for it. These are registered + on the [content element](https://codemirror.net/6/docs/ref/#view.EditorView.contentDOM), except + for `scroll` handlers, which will be called any time the + editor's [scroll element](https://codemirror.net/6/docs/ref/#view.EditorView.scrollDOM) or one of + its parent nodes is scrolled. + */ + static domEventHandlers(handlers: DOMEventHandlers): Extension; + /** + Create an extension that registers DOM event observers. Contrary + to event [handlers](https://codemirror.net/6/docs/ref/#view.EditorView^domEventHandlers), + observers can't be prevented from running by a higher-precedence + handler returning true. They also don't prevent other handlers + and observers from running when they return true, and should not + call `preventDefault`. + */ + static domEventObservers(observers: DOMEventHandlers): Extension; + /** + An input handler can override the way changes to the editable + DOM content are handled. Handlers are passed the document + positions between which the change was found, and the new + content. When one returns true, no further input handlers are + called and the default behavior is prevented. + + The `insert` argument can be used to get the default transaction + that would be applied for this input. This can be useful when + dispatching the custom behavior as a separate transaction. + */ + static inputHandler: Facet<(view: EditorView, from: number, to: number, text: string, insert: () => Transaction) => boolean, readonly ((view: EditorView, from: number, to: number, text: string, insert: () => Transaction) => boolean)[]>; + /** + Functions provided in this facet will be used to transform text + pasted or dropped into the editor. + */ + static clipboardInputFilter: Facet<(text: string, state: EditorState) => string, readonly ((text: string, state: EditorState) => string)[]>; + /** + Transform text copied or dragged from the editor. + */ + static clipboardOutputFilter: Facet<(text: string, state: EditorState) => string, readonly ((text: string, state: EditorState) => string)[]>; + /** + Scroll handlers can override how things are scrolled into view. + If they return `true`, no further handling happens for the + scrolling. If they return false, the default scroll behavior is + applied. Scroll handlers should never initiate editor updates. + */ + static scrollHandler: Facet<(view: EditorView, range: SelectionRange, options: { + x: ScrollStrategy; + y: ScrollStrategy; + xMargin: number; + yMargin: number; + }) => boolean, readonly ((view: EditorView, range: SelectionRange, options: { + x: ScrollStrategy; + y: ScrollStrategy; + xMargin: number; + yMargin: number; + }) => boolean)[]>; + /** + This facet can be used to provide functions that create effects + to be dispatched when the editor's focus state changes. + */ + static focusChangeEffect: Facet<(state: EditorState, focusing: boolean) => StateEffect | null, readonly ((state: EditorState, focusing: boolean) => StateEffect | null)[]>; + /** + By default, the editor assumes all its content has the same + [text direction](https://codemirror.net/6/docs/ref/#view.Direction). Configure this with a `true` + value to make it read the text direction of every (rendered) + line separately. + */ + static perLineTextDirection: Facet; + /** + Allows you to provide a function that should be called when the + library catches an exception from an extension (mostly from view + plugins, but may be used by other extensions to route exceptions + from user-code-provided callbacks). This is mostly useful for + debugging and logging. See [`logException`](https://codemirror.net/6/docs/ref/#view.logException). + */ + static exceptionSink: Facet<(exception: any) => void, readonly ((exception: any) => void)[]>; + /** + A facet that can be used to register a function to be called + every time the view updates. + */ + static updateListener: Facet<(update: ViewUpdate) => void, readonly ((update: ViewUpdate) => void)[]>; + /** + Facet that controls whether the editor content DOM is editable. + When its highest-precedence value is `false`, the element will + not have its `contenteditable` attribute set. (Note that this + doesn't affect API calls that change the editor content, even + when those are bound to keys or buttons. See the + [`readOnly`](https://codemirror.net/6/docs/ref/#state.EditorState.readOnly) facet for that.) + */ + static editable: Facet; + /** + Allows you to influence the way mouse selection happens. The + functions in this facet will be called for a `mousedown` event + on the editor, and can return an object that overrides the way a + selection is computed from that mouse click or drag. + */ + static mouseSelectionStyle: Facet; + /** + Facet used to configure whether a given selection drag event + should move or copy the selection. The given predicate will be + called with the `mousedown` event, and can return `true` when + the drag should move the content. + */ + static dragMovesSelection: Facet<(event: MouseEvent) => boolean, readonly ((event: MouseEvent) => boolean)[]>; + /** + Facet used to configure whether a given selecting click adds a + new range to the existing selection or replaces it entirely. The + default behavior is to check `event.metaKey` on macOS, and + `event.ctrlKey` elsewhere. + */ + static clickAddsSelectionRange: Facet<(event: MouseEvent) => boolean, readonly ((event: MouseEvent) => boolean)[]>; + /** + A facet that determines which [decorations](https://codemirror.net/6/docs/ref/#view.Decoration) + are shown in the view. Decorations can be provided in two + ways—directly, or via a function that takes an editor view. + + Only decoration sets provided directly are allowed to influence + the editor's vertical layout structure. The ones provided as + functions are called _after_ the new viewport has been computed, + and thus **must not** introduce block widgets or replacing + decorations that cover line breaks. + + If you want decorated ranges to behave like atomic units for + cursor motion and deletion purposes, also provide the range set + containing the decorations to + [`EditorView.atomicRanges`](https://codemirror.net/6/docs/ref/#view.EditorView^atomicRanges). + */ + static decorations: Facet DecorationSet), readonly (DecorationSet | ((view: EditorView) => DecorationSet))[]>; + /** + Facet that works much like + [`decorations`](https://codemirror.net/6/docs/ref/#view.EditorView^decorations), but puts its + inputs at the very bottom of the precedence stack, meaning mark + decorations provided here will only be split by other, partially + overlapping \`outerDecorations\` ranges, and wrap around all + regular decorations. Use this for mark elements that should, as + much as possible, remain in one piece. + */ + static outerDecorations: Facet DecorationSet), readonly (DecorationSet | ((view: EditorView) => DecorationSet))[]>; + /** + Used to provide ranges that should be treated as atoms as far as + cursor motion is concerned. This causes methods like + [`moveByChar`](https://codemirror.net/6/docs/ref/#view.EditorView.moveByChar) and + [`moveVertically`](https://codemirror.net/6/docs/ref/#view.EditorView.moveVertically) (and the + commands built on top of them) to skip across such regions when + a selection endpoint would enter them. This does _not_ prevent + direct programmatic [selection + updates](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection) from moving into such + regions. + */ + static atomicRanges: Facet<(view: EditorView) => _codemirror_state.RangeSet, readonly ((view: EditorView) => _codemirror_state.RangeSet)[]>; + /** + When range decorations add a `unicode-bidi: isolate` style, they + should also include a + [`bidiIsolate`](https://codemirror.net/6/docs/ref/#view.MarkDecorationSpec.bidiIsolate) property + in their decoration spec, and be exposed through this facet, so + that the editor can compute the proper text order. (Other values + for `unicode-bidi`, except of course `normal`, are not + supported.) + */ + static bidiIsolatedRanges: Facet DecorationSet), readonly (DecorationSet | ((view: EditorView) => DecorationSet))[]>; + /** + Facet that allows extensions to provide additional scroll + margins (space around the sides of the scrolling element that + should be considered invisible). This can be useful when the + plugin introduces elements that cover part of that element (for + example a horizontally fixed gutter). + */ + static scrollMargins: Facet<(view: EditorView) => Partial | null, readonly ((view: EditorView) => Partial | null)[]>; + /** + Create a theme extension. The first argument can be a + [`style-mod`](https://github.com/marijnh/style-mod#documentation) + style spec providing the styles for the theme. These will be + prefixed with a generated class for the style. + + Because the selectors will be prefixed with a scope class, rule + that directly match the editor's [wrapper + element](https://codemirror.net/6/docs/ref/#view.EditorView.dom)—to which the scope class will be + added—need to be explicitly differentiated by adding an `&` to + the selector for that element—for example + `&.cm-focused`. + + When `dark` is set to true, the theme will be marked as dark, + which will cause the `&dark` rules from [base + themes](https://codemirror.net/6/docs/ref/#view.EditorView^baseTheme) to be used (as opposed to + `&light` when a light theme is active). + */ + static theme(spec: { + [selector: string]: StyleSpec; + }, options?: { + dark?: boolean; + }): Extension; + /** + This facet records whether a dark theme is active. The extension + returned by [`theme`](https://codemirror.net/6/docs/ref/#view.EditorView^theme) automatically + includes an instance of this when the `dark` option is set to + true. + */ + static darkTheme: Facet; + /** + Create an extension that adds styles to the base theme. Like + with [`theme`](https://codemirror.net/6/docs/ref/#view.EditorView^theme), use `&` to indicate the + place of the editor wrapper element when directly targeting + that. You can also use `&dark` or `&light` instead to only + target editors with a dark or light theme. + */ + static baseTheme(spec: { + [selector: string]: StyleSpec; + }): Extension; + /** + Provides a Content Security Policy nonce to use when creating + the style sheets for the editor. Holds the empty string when no + nonce has been provided. + */ + static cspNonce: Facet; + /** + Facet that provides additional DOM attributes for the editor's + editable DOM element. + */ + static contentAttributes: Facet; + /** + Facet that provides DOM attributes for the editor's outer + element. + */ + static editorAttributes: Facet; + /** + An extension that enables line wrapping in the editor (by + setting CSS `white-space` to `pre-wrap` in the content). + */ + static lineWrapping: Extension; + /** + State effect used to include screen reader announcements in a + transaction. These will be added to the DOM in a visually hidden + element with `aria-live="polite"` set, and should be used to + describe effects that are visually obvious but may not be + noticed by screen reader users (such as moving to the next + search match). + */ + static announce: _codemirror_state.StateEffectType; + /** + Retrieve an editor view instance from the view's DOM + representation. + */ + static findFromDOM(dom: HTMLElement): EditorView | null; +} +/** +Helper type that maps event names to event object types, or the +`any` type for unknown events. +*/ +interface DOMEventMap extends HTMLElementEventMap { + [other: string]: any; +} +/** +Event handlers are specified with objects like this. For event +types known by TypeScript, this will infer the event argument type +to hold the appropriate event object type. For unknown events, it +is inferred to `any`, and should be explicitly set if you want type +checking. +*/ +type DOMEventHandlers = { + [event in keyof DOMEventMap]?: (this: This, event: DOMEventMap[event], view: EditorView) => boolean | void; +}; + +/** +Key bindings associate key names with +[command](https://codemirror.net/6/docs/ref/#view.Command)-style functions. + +Key names may be strings like `"Shift-Ctrl-Enter"`—a key identifier +prefixed with zero or more modifiers. Key identifiers are based on +the strings that can appear in +[`KeyEvent.key`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key). +Use lowercase letters to refer to letter keys (or uppercase letters +if you want shift to be held). You may use `"Space"` as an alias +for the `" "` name. + +Modifiers can be given in any order. `Shift-` (or `s-`), `Alt-` (or +`a-`), `Ctrl-` (or `c-` or `Control-`) and `Cmd-` (or `m-` or +`Meta-`) are recognized. + +When a key binding contains multiple key names separated by +spaces, it represents a multi-stroke binding, which will fire when +the user presses the given keys after each other. + +You can use `Mod-` as a shorthand for `Cmd-` on Mac and `Ctrl-` on +other platforms. So `Mod-b` is `Ctrl-b` on Linux but `Cmd-b` on +macOS. +*/ +interface KeyBinding { + /** + The key name to use for this binding. If the platform-specific + property (`mac`, `win`, or `linux`) for the current platform is + used as well in the binding, that one takes precedence. If `key` + isn't defined and the platform-specific binding isn't either, + a binding is ignored. + */ + key?: string; + /** + Key to use specifically on macOS. + */ + mac?: string; + /** + Key to use specifically on Windows. + */ + win?: string; + /** + Key to use specifically on Linux. + */ + linux?: string; + /** + The command to execute when this binding is triggered. When the + command function returns `false`, further bindings will be tried + for the key. + */ + run?: Command; + /** + When given, this defines a second binding, using the (possibly + platform-specific) key name prefixed with `Shift-` to activate + this command. + */ + shift?: Command; + /** + When this property is present, the function is called for every + key that is not a multi-stroke prefix. + */ + any?: (view: EditorView, event: KeyboardEvent) => boolean; + /** + By default, key bindings apply when focus is on the editor + content (the `"editor"` scope). Some extensions, mostly those + that define their own panels, might want to allow you to + register bindings local to that panel. Such bindings should use + a custom scope name. You may also assign multiple scope names to + a binding, separating them by spaces. + */ + scope?: string; + /** + When set to true (the default is false), this will always + prevent the further handling for the bound key, even if the + command(s) return false. This can be useful for cases where the + native behavior of the key is annoying or irrelevant but the + command doesn't always apply (such as, Mod-u for undo selection, + which would cause the browser to view source instead when no + selection can be undone). + */ + preventDefault?: boolean; + /** + When set to true, `stopPropagation` will be called on keyboard + events that have their `preventDefault` called in response to + this key binding (see also + [`preventDefault`](https://codemirror.net/6/docs/ref/#view.KeyBinding.preventDefault)). + */ + stopPropagation?: boolean; +} +/** +Facet used for registering keymaps. + +You can add multiple keymaps to an editor. Their priorities +determine their precedence (the ones specified early or with high +priority get checked first). When a handler has returned `true` +for a given key, no further handlers are called. +*/ +declare const keymap: Facet; +/** +Run the key handlers registered for a given scope. The event +object should be a `"keydown"` event. Returns true if any of the +handlers handled it. +*/ +declare function runScopeHandlers(view: EditorView, event: KeyboardEvent, scope: string): boolean; + +type SelectionConfig = { + /** + The length of a full cursor blink cycle, in milliseconds. + Defaults to 1200. Can be set to 0 to disable blinking. + */ + cursorBlinkRate?: number; + /** + Whether to show a cursor for non-empty ranges. Defaults to + true. + */ + drawRangeCursor?: boolean; +}; +/** +Returns an extension that hides the browser's native selection and +cursor, replacing the selection with a background behind the text +(with the `cm-selectionBackground` class), and the +cursors with elements overlaid over the code (using +`cm-cursor-primary` and `cm-cursor-secondary`). + +This allows the editor to display secondary selection ranges, and +tends to produce a type of selection more in line with that users +expect in a text editor (the native selection styling will often +leave gaps between lines and won't fill the horizontal space after +a line when the selection continues past it). + +It does have a performance cost, in that it requires an extra DOM +layout cycle for many updates (the selection is drawn based on DOM +layout information that's only available after laying out the +content). +*/ +declare function drawSelection(config?: SelectionConfig): Extension; +/** +Retrieve the [`drawSelection`](https://codemirror.net/6/docs/ref/#view.drawSelection) configuration +for this state. (Note that this will return a set of defaults even +if `drawSelection` isn't enabled.) +*/ +declare function getDrawSelectionConfig(state: EditorState): SelectionConfig; + +/** +Draws a cursor at the current drop position when something is +dragged over the editor. +*/ +declare function dropCursor(): Extension; + +interface SpecialCharConfig { + /** + An optional function that renders the placeholder elements. + + The `description` argument will be text that clarifies what the + character is, which should be provided to screen readers (for + example with the + [`aria-label`](https://www.w3.org/TR/wai-aria/#aria-label) + attribute) and optionally shown to the user in other ways (such + as the + [`title`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title) + attribute). + + The given placeholder string is a suggestion for how to display + the character visually. + */ + render?: ((code: number, description: string | null, placeholder: string) => HTMLElement) | null; + /** + Regular expression that matches the special characters to + highlight. Must have its 'g'/global flag set. + */ + specialChars?: RegExp; + /** + Regular expression that can be used to add characters to the + default set of characters to highlight. + */ + addSpecialChars?: RegExp | null; +} +/** +Returns an extension that installs highlighting of special +characters. +*/ +declare function highlightSpecialChars( +/** +Configuration options. +*/ +config?: SpecialCharConfig): Extension; + +/** +Returns an extension that makes sure the content has a bottom +margin equivalent to the height of the editor, minus one line +height, so that every line in the document can be scrolled to the +top of the editor. + +This is only meaningful when the editor is scrollable, and should +not be enabled in editors that take the size of their content. +*/ +declare function scrollPastEnd(): Extension; + +/** +Mark lines that have a cursor on them with the `"cm-activeLine"` +DOM class. +*/ +declare function highlightActiveLine(): Extension; + +/** +Extension that enables a placeholder—a piece of example content +to show when the editor is empty. +*/ +declare function placeholder(content: string | HTMLElement | ((view: EditorView) => HTMLElement)): Extension; + +/** +Markers shown in a [layer](https://codemirror.net/6/docs/ref/#view.layer) must conform to this +interface. They are created in a measuring phase, and have to +contain all their positioning information, so that they can be +drawn without further DOM layout reading. + +Markers are automatically absolutely positioned. Their parent +element has the same top-left corner as the document, so they +should be positioned relative to the document. +*/ +interface LayerMarker { + /** + Compare this marker to a marker of the same type. Used to avoid + unnecessary redraws. + */ + eq(other: LayerMarker): boolean; + /** + Draw the marker to the DOM. + */ + draw(): HTMLElement; + /** + Update an existing marker of this type to this marker. + */ + update?(dom: HTMLElement, oldMarker: LayerMarker): boolean; +} +/** +Implementation of [`LayerMarker`](https://codemirror.net/6/docs/ref/#view.LayerMarker) that creates +a rectangle at a given set of coordinates. +*/ +declare class RectangleMarker implements LayerMarker { + private className; + /** + The left position of the marker (in pixels, document-relative). + */ + readonly left: number; + /** + The top position of the marker. + */ + readonly top: number; + /** + The width of the marker, or null if it shouldn't get a width assigned. + */ + readonly width: number | null; + /** + The height of the marker. + */ + readonly height: number; + /** + Create a marker with the given class and dimensions. If `width` + is null, the DOM element will get no width style. + */ + constructor(className: string, + /** + The left position of the marker (in pixels, document-relative). + */ + left: number, + /** + The top position of the marker. + */ + top: number, + /** + The width of the marker, or null if it shouldn't get a width assigned. + */ + width: number | null, + /** + The height of the marker. + */ + height: number); + draw(): HTMLDivElement; + update(elt: HTMLElement, prev: RectangleMarker): boolean; + private adjust; + eq(p: RectangleMarker): boolean; + /** + Create a set of rectangles for the given selection range, + assigning them theclass`className`. Will create a single + rectangle for empty ranges, and a set of selection-style + rectangles covering the range's content (in a bidi-aware + way) for non-empty ones. + */ + static forRange(view: EditorView, className: string, range: SelectionRange): readonly RectangleMarker[]; +} +interface LayerConfig { + /** + Determines whether this layer is shown above or below the text. + */ + above: boolean; + /** + When given, this class is added to the DOM element that will + wrap the markers. + */ + class?: string; + /** + Called on every view update. Returning true triggers a marker + update (a call to `markers` and drawing of those markers). + */ + update(update: ViewUpdate, layer: HTMLElement): boolean; + /** + Whether to update this layer every time the document view + changes. Defaults to true. + */ + updateOnDocViewUpdate?: boolean; + /** + Build a set of markers for this layer, and measure their + dimensions. + */ + markers(view: EditorView): readonly LayerMarker[]; + /** + If given, this is called when the layer is created. + */ + mount?(layer: HTMLElement, view: EditorView): void; + /** + If given, called when the layer is removed from the editor or + the entire editor is destroyed. + */ + destroy?(layer: HTMLElement, view: EditorView): void; +} +/** +Define a layer. +*/ +declare function layer(config: LayerConfig): Extension; + +/** +Helper class used to make it easier to maintain decorations on +visible code that matches a given regular expression. To be used +in a [view plugin](https://codemirror.net/6/docs/ref/#view.ViewPlugin). Instances of this object +represent a matching configuration. +*/ +declare class MatchDecorator { + private regexp; + private addMatch; + private boundary; + private maxLength; + /** + Create a decorator. + */ + constructor(config: { + /** + The regular expression to match against the content. Will only + be matched inside lines (not across them). Should have its 'g' + flag set. + */ + regexp: RegExp; + /** + The decoration to apply to matches, either directly or as a + function of the match. + */ + decoration?: Decoration | ((match: RegExpExecArray, view: EditorView, pos: number) => Decoration | null); + /** + Customize the way decorations are added for matches. This + function, when given, will be called for matches and should + call `add` to create decorations for them. Note that the + decorations should appear *in* the given range, and the + function should have no side effects beyond calling `add`. + + The `decoration` option is ignored when `decorate` is + provided. + */ + decorate?: (add: (from: number, to: number, decoration: Decoration) => void, from: number, to: number, match: RegExpExecArray, view: EditorView) => void; + /** + By default, changed lines are re-matched entirely. You can + provide a boundary expression, which should match single + character strings that can never occur in `regexp`, to reduce + the amount of re-matching. + */ + boundary?: RegExp; + /** + Matching happens by line, by default, but when lines are + folded or very long lines are only partially drawn, the + decorator may avoid matching part of them for speed. This + controls how much additional invisible content it should + include in its matches. Defaults to 1000. + */ + maxLength?: number; + }); + /** + Compute the full set of decorations for matches in the given + view's viewport. You'll want to call this when initializing your + plugin. + */ + createDeco(view: EditorView): _codemirror_state.RangeSet; + /** + Update a set of decorations for a view update. `deco` _must_ be + the set of decorations produced by _this_ `MatchDecorator` for + the view state before the update. + */ + updateDeco(update: ViewUpdate, deco: DecorationSet): DecorationSet; + private updateRange; +} + +/** +Create an extension that enables rectangular selections. By +default, it will react to left mouse drag with the Alt key held +down. When such a selection occurs, the text within the rectangle +that was dragged over will be selected, as one selection +[range](https://codemirror.net/6/docs/ref/#state.SelectionRange) per line. +*/ +declare function rectangularSelection(options?: { + /** + A custom predicate function, which takes a `mousedown` event and + returns true if it should be used for rectangular selection. + */ + eventFilter?: (event: MouseEvent) => boolean; +}): Extension; +/** +Returns an extension that turns the pointer cursor into a +crosshair when a given modifier key, defaulting to Alt, is held +down. Can serve as a visual hint that rectangular selection is +going to happen when paired with +[`rectangularSelection`](https://codemirror.net/6/docs/ref/#view.rectangularSelection). +*/ +declare function crosshairCursor(options?: { + key?: "Alt" | "Control" | "Shift" | "Meta"; +}): Extension; + +/** +Creates an extension that configures tooltip behavior. +*/ +declare function tooltips(config?: { + /** + By default, tooltips use `"fixed"` + [positioning](https://developer.mozilla.org/en-US/docs/Web/CSS/position), + which has the advantage that tooltips don't get cut off by + scrollable parent elements. However, CSS rules like `contain: + layout` can break fixed positioning in child nodes, which can be + worked about by using `"absolute"` here. + + On iOS, which at the time of writing still doesn't properly + support fixed positioning, the library always uses absolute + positioning. + + If the tooltip parent element sits in a transformed element, the + library also falls back to absolute positioning. + */ + position?: "fixed" | "absolute"; + /** + The element to put the tooltips into. By default, they are put + in the editor (`cm-editor`) element, and that is usually what + you want. But in some layouts that can lead to positioning + issues, and you need to use a different parent to work around + those. + */ + parent?: HTMLElement; + /** + By default, when figuring out whether there is room for a + tooltip at a given position, the extension considers the entire + space between 0,0 and + `documentElement.clientWidth`/`clientHeight` to be available for + showing tooltips. You can provide a function here that returns + an alternative rectangle. + */ + tooltipSpace?: (view: EditorView) => Rect; +}): Extension; +/** +Describes a tooltip. Values of this type, when provided through +the [`showTooltip`](https://codemirror.net/6/docs/ref/#view.showTooltip) facet, control the +individual tooltips on the editor. +*/ +interface Tooltip { + /** + The document position at which to show the tooltip. + */ + pos: number; + /** + The end of the range annotated by this tooltip, if different + from `pos`. + */ + end?: number; + /** + A constructor function that creates the tooltip's [DOM + representation](https://codemirror.net/6/docs/ref/#view.TooltipView). + */ + create(view: EditorView): TooltipView; + /** + Whether the tooltip should be shown above or below the target + position. Not guaranteed to be respected for hover tooltips + since all hover tooltips for the same range are always + positioned together. Defaults to false. + */ + above?: boolean; + /** + Whether the `above` option should be honored when there isn't + enough space on that side to show the tooltip inside the + viewport. Defaults to false. + */ + strictSide?: boolean; + /** + When set to true, show a triangle connecting the tooltip element + to position `pos`. + */ + arrow?: boolean; + /** + By default, tooltips are hidden when their position is outside + of the visible editor content. Set this to false to turn that + off. + */ + clip?: boolean; +} +/** +Describes the way a tooltip is displayed. +*/ +interface TooltipView { + /** + The DOM element to position over the editor. + */ + dom: HTMLElement; + /** + Adjust the position of the tooltip relative to its anchor + position. A positive `x` value will move the tooltip + horizontally along with the text direction (so right in + left-to-right context, left in right-to-left). A positive `y` + will move the tooltip up when it is above its anchor, and down + otherwise. + */ + offset?: { + x: number; + y: number; + }; + /** + By default, a tooltip's screen position will be based on the + text position of its `pos` property. This method can be provided + to make the tooltip view itself responsible for finding its + screen position. + */ + getCoords?: (pos: number) => Rect; + /** + By default, tooltips are moved when they overlap with other + tooltips. Set this to `true` to disable that behavior for this + tooltip. + */ + overlap?: boolean; + /** + Called after the tooltip is added to the DOM for the first time. + */ + mount?(view: EditorView): void; + /** + Update the DOM element for a change in the view's state. + */ + update?(update: ViewUpdate): void; + /** + Called when the tooltip is removed from the editor or the editor + is destroyed. + */ + destroy?(): void; + /** + Called when the tooltip has been (re)positioned. The argument is + the [space](https://codemirror.net/6/docs/ref/#view.tooltips^config.tooltipSpace) available to the + tooltip. + */ + positioned?(space: Rect): void; + /** + By default, the library will restrict the size of tooltips so + that they don't stick out of the available space. Set this to + false to disable that. + */ + resize?: boolean; +} +/** +Facet to which an extension can add a value to show a tooltip. +*/ +declare const showTooltip: Facet; +/** +The type of function that can be used as a [hover tooltip +source](https://codemirror.net/6/docs/ref/#view.hoverTooltip^source). +*/ +type HoverTooltipSource = (view: EditorView, pos: number, side: -1 | 1) => Tooltip | readonly Tooltip[] | null | Promise; +/** +Set up a hover tooltip, which shows up when the pointer hovers +over ranges of text. The callback is called when the mouse hovers +over the document text. It should, if there is a tooltip +associated with position `pos`, return the tooltip description +(either directly or in a promise). The `side` argument indicates +on which side of the position the pointer is—it will be -1 if the +pointer is before the position, 1 if after the position. + +Note that all hover tooltips are hosted within a single tooltip +container element. This allows multiple tooltips over the same +range to be "merged" together without overlapping. + +The return value is a valid [editor extension](https://codemirror.net/6/docs/ref/#state.Extension) +but also provides an `active` property holding a state field that +can be used to read the currently active tooltips produced by this +extension. +*/ +declare function hoverTooltip(source: HoverTooltipSource, options?: { + /** + Controls whether a transaction hides the tooltip. The default + is to not hide. + */ + hideOn?: (tr: Transaction, tooltip: Tooltip) => boolean; + /** + When enabled (this defaults to false), close the tooltip + whenever the document changes or the selection is set. + */ + hideOnChange?: boolean | "touch"; + /** + Hover time after which the tooltip should appear, in + milliseconds. Defaults to 300ms. + */ + hoverTime?: number; +}): Extension & { + active: StateField; +}; +/** +Get the active tooltip view for a given tooltip, if available. +*/ +declare function getTooltip(view: EditorView, tooltip: Tooltip): TooltipView | null; +/** +Returns true if any hover tooltips are currently active. +*/ +declare function hasHoverTooltips(state: EditorState): boolean; +/** +Transaction effect that closes all hover tooltips. +*/ +declare const closeHoverTooltips: StateEffect; +/** +Tell the tooltip extension to recompute the position of the active +tooltips. This can be useful when something happens (such as a +re-positioning or CSS change affecting the editor) that could +invalidate the existing tooltip positions. +*/ +declare function repositionTooltips(view: EditorView): void; + +type PanelConfig = { + /** + By default, panels will be placed inside the editor's DOM + structure. You can use this option to override where panels with + `top: true` are placed. + */ + topContainer?: HTMLElement; + /** + Override where panels with `top: false` are placed. + */ + bottomContainer?: HTMLElement; +}; +/** +Configures the panel-managing extension. +*/ +declare function panels(config?: PanelConfig): Extension; +/** +Object that describes an active panel. +*/ +interface Panel { + /** + The element representing this panel. The library will add the + `"cm-panel"` DOM class to this. + */ + dom: HTMLElement; + /** + Optionally called after the panel has been added to the editor. + */ + mount?(): void; + /** + Update the DOM for a given view update. + */ + update?(update: ViewUpdate): void; + /** + Called when the panel is removed from the editor or the editor + is destroyed. + */ + destroy?(): void; + /** + Whether the panel should be at the top or bottom of the editor. + Defaults to false. + */ + top?: boolean; +} +/** +Get the active panel created by the given constructor, if any. +This can be useful when you need access to your panels' DOM +structure. +*/ +declare function getPanel(view: EditorView, panel: PanelConstructor): Panel | null; +/** +A function that initializes a panel. Used in +[`showPanel`](https://codemirror.net/6/docs/ref/#view.showPanel). +*/ +type PanelConstructor = (view: EditorView) => Panel; +/** +Opening a panel is done by providing a constructor function for +the panel through this facet. (The panel is closed again when its +constructor is no longer provided.) Values of `null` are ignored. +*/ +declare const showPanel: Facet; + +type DialogConfig = { + /** + A function to render the content of the dialog. The result + should contain at least one `
` element. Submit handlers a + handler for the Escape key will be added to the form. + + If this is not given, the `label`, `input`, and `submitLabel` + fields will be used to create a simple form for you. + */ + content?: (view: EditorView, close: () => void) => HTMLElement; + /** + When `content` isn't given, this provides the text shown in the + dialog. + */ + label?: string; + /** + The attributes for an input element shown next to the label. If + not given, no input element is added. + */ + input?: { + [attr: string]: string; + }; + /** + The label for the button that submits the form. Defaults to + `"OK"`. + */ + submitLabel?: string; + /** + Extra classes to add to the panel. + */ + class?: string; + /** + A query selector to find the field that should be focused when + the dialog is opened. When set to true, this picks the first + `` or `