basant307/AI_Governance_Project
048
1import { NodeProp, IterMode, Tree, TreeFragment, Parser, NodeType, NodeSet } from '@lezer/common';2import { StateEffect, StateField, Facet, EditorState, countColumn, combineConfig, RangeSet, RangeSetBuilder, Prec } from '@codemirror/state';3import { ViewPlugin, logException, EditorView, Decoration, WidgetType, gutter, GutterMarker, Direction } from '@codemirror/view';4import { tags, tagHighlighter, highlightTree, styleTags } from '@lezer/highlight';5import { StyleModule } from 'style-mod';6 7var _a;8/**9Node prop stored in a parser's top syntax node to provide the10facet that stores language-specific data for that language.11*/12const languageDataProp = /*@__PURE__*/new NodeProp();13/**14Helper function to define a facet (to be added to the top syntax15node(s) for a language via16[`languageDataProp`](https://codemirror.net/6/docs/ref/#language.languageDataProp)), that will be17used to associate language data with the language. You18probably only need this when subclassing19[`Language`](https://codemirror.net/6/docs/ref/#language.Language).20*/21function defineLanguageFacet(baseData) {22 return Facet.define({23 combine: baseData ? values => values.concat(baseData) : undefined24 });25}26/**27Syntax node prop used to register sublanguages. Should be added to28the top level node type for the language.29*/30const sublanguageProp = /*@__PURE__*/new NodeProp();31/**32A language object manages parsing and per-language33[metadata](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt). Parse data is34managed as a [Lezer](https://lezer.codemirror.net) tree. The class35can be used directly, via the [`LRLanguage`](https://codemirror.net/6/docs/ref/#language.LRLanguage)36subclass for [Lezer](https://lezer.codemirror.net/) LR parsers, or37via the [`StreamLanguage`](https://codemirror.net/6/docs/ref/#language.StreamLanguage) subclass38for stream parsers.39*/40class Language {41 /**42 Construct a language object. If you need to invoke this43 directly, first define a data facet with44 [`defineLanguageFacet`](https://codemirror.net/6/docs/ref/#language.defineLanguageFacet), and then45 configure your parser to [attach](https://codemirror.net/6/docs/ref/#language.languageDataProp) it46 to the language's outer syntax node.47 */48 constructor(49 /**50 The [language data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) facet51 used for this language.52 */53 data, parser, extraExtensions = [], 54 /**55 A language name.56 */57 name = "") {58 this.data = data;59 this.name = name;60 // Kludge to define EditorState.tree as a debugging helper,61 // without the EditorState package actually knowing about62 // languages and lezer trees.63 if (!EditorState.prototype.hasOwnProperty("tree"))64 Object.defineProperty(EditorState.prototype, "tree", { get() { return syntaxTree(this); } });65 this.parser = parser;66 this.extension = [67 language.of(this),68 EditorState.languageData.of((state, pos, side) => {69 let top = topNodeAt(state, pos, side), data = top.type.prop(languageDataProp);70 if (!data)71 return [];72 let base = state.facet(data), sub = top.type.prop(sublanguageProp);73 if (sub) {74 let innerNode = top.resolve(pos - top.from, side);75 for (let sublang of sub)76 if (sublang.test(innerNode, state)) {77 let data = state.facet(sublang.facet);78 return sublang.type == "replace" ? data : data.concat(base);79 }80 }81 return base;82 })83 ].concat(extraExtensions);84 }85 /**86 Query whether this language is active at the given position.87 */88 isActiveAt(state, pos, side = -1) {89 return topNodeAt(state, pos, side).type.prop(languageDataProp) == this.data;90 }91 /**92 Find the document regions that were parsed using this language.93 The returned regions will _include_ any nested languages rooted94 in this language, when those exist.95 */96 findRegions(state) {97 let lang = state.facet(language);98 if ((lang === null || lang === void 0 ? void 0 : lang.data) == this.data)99 return [{ from: 0, to: state.doc.length }];100 if (!lang || !lang.allowsNesting)101 return [];102 let result = [];103 let explore = (tree, from) => {104 if (tree.prop(languageDataProp) == this.data) {105 result.push({ from, to: from + tree.length });106 return;107 }108 let mount = tree.prop(NodeProp.mounted);109 if (mount) {110 if (mount.tree.prop(languageDataProp) == this.data) {111 if (mount.overlay)112 for (let r of mount.overlay)113 result.push({ from: r.from + from, to: r.to + from });114 else115 result.push({ from: from, to: from + tree.length });116 return;117 }118 else if (mount.overlay) {119 let size = result.length;120 explore(mount.tree, mount.overlay[0].from + from);121 if (result.length > size)122 return;123 }124 }125 for (let i = 0; i < tree.children.length; i++) {126 let ch = tree.children[i];127 if (ch instanceof Tree)128 explore(ch, tree.positions[i] + from);129 }130 };131 explore(syntaxTree(state), 0);132 return result;133 }134 /**135 Indicates whether this language allows nested languages. The136 default implementation returns true.137 */138 get allowsNesting() { return true; }139}140/**141@internal142*/143Language.setState = /*@__PURE__*/StateEffect.define();144function topNodeAt(state, pos, side) {145 let topLang = state.facet(language), tree = syntaxTree(state).topNode;146 if (!topLang || topLang.allowsNesting) {147 for (let node = tree; node; node = node.enter(pos, side, IterMode.ExcludeBuffers | IterMode.EnterBracketed))148 if (node.type.isTop)149 tree = node;150 }151 return tree;152}153/**154A subclass of [`Language`](https://codemirror.net/6/docs/ref/#language.Language) for use with Lezer155[LR parsers](https://lezer.codemirror.net/docs/ref#lr.LRParser)156parsers.157*/158class LRLanguage extends Language {159 constructor(data, parser, name) {160 super(data, parser, [], name);161 this.parser = parser;162 }163 /**164 Define a language from a parser.165 */166 static define(spec) {167 let data = defineLanguageFacet(spec.languageData);168 return new LRLanguage(data, spec.parser.configure({169 props: [languageDataProp.add(type => type.isTop ? data : undefined)]170 }), spec.name);171 }172 /**173 Create a new instance of this language with a reconfigured174 version of its parser and optionally a new name.175 */176 configure(options, name) {177 return new LRLanguage(this.data, this.parser.configure(options), name || this.name);178 }179 get allowsNesting() { return this.parser.hasWrappers(); }180}181/**182Get the syntax tree for a state, which is the current (possibly183incomplete) parse tree of the active184[language](https://codemirror.net/6/docs/ref/#language.Language), or the empty tree if there is no185language available.186*/187function syntaxTree(state) {188 let field = state.field(Language.state, false);189 return field ? field.tree : Tree.empty;190}191/**192Try to get a parse tree that spans at least up to `upto`. The193method will do at most `timeout` milliseconds of work to parse194up to that point if the tree isn't already available.195*/196function ensureSyntaxTree(state, upto, timeout = 50) {197 var _a;198 let parse = (_a = state.field(Language.state, false)) === null || _a === void 0 ? void 0 : _a.context;199 if (!parse)200 return null;201 let oldVieport = parse.viewport;202 parse.updateViewport({ from: 0, to: upto });203 let result = parse.isDone(upto) || parse.work(timeout, upto) ? parse.tree : null;204 parse.updateViewport(oldVieport);205 return result;206}207/**208Queries whether there is a full syntax tree available up to the209given document position. If there isn't, the background parse210process _might_ still be working and update the tree further, but211there is no guarantee of that—the parser will [stop212working](https://codemirror.net/6/docs/ref/#language.syntaxParserRunning) when it has spent a213certain amount of time or has moved beyond the visible viewport.214Always returns false if no language has been enabled.215*/216function syntaxTreeAvailable(state, upto = state.doc.length) {217 var _a;218 return ((_a = state.field(Language.state, false)) === null || _a === void 0 ? void 0 : _a.context.isDone(upto)) || false;219}220/**221Move parsing forward, and update the editor state afterwards to222reflect the new tree. Will work for at most `timeout`223milliseconds. Returns true if the parser managed get to the given224position in that time.225*/226function forceParsing(view, upto = view.viewport.to, timeout = 100) {227 let success = ensureSyntaxTree(view.state, upto, timeout);228 if (success != syntaxTree(view.state))229 view.dispatch({});230 return !!success;231}232/**233Tells you whether the language parser is planning to do more234parsing work (in a `requestIdleCallback` pseudo-thread) or has235stopped running, either because it parsed the entire document,236because it spent too much time and was cut off, or because there237is no language parser enabled.238*/239function syntaxParserRunning(view) {240 var _a;241 return ((_a = view.plugin(parseWorker)) === null || _a === void 0 ? void 0 : _a.isWorking()) || false;242}243/**244Lezer-style245[`Input`](https://lezer.codemirror.net/docs/ref#common.Input)246object for a [`Text`](https://codemirror.net/6/docs/ref/#state.Text) object.247*/248class DocInput {249 /**250 Create an input object for the given document.251 */252 constructor(doc) {253 this.doc = doc;254 this.cursorPos = 0;255 this.string = "";256 this.cursor = doc.iter();257 }258 get length() { return this.doc.length; }259 syncTo(pos) {260 this.string = this.cursor.next(pos - this.cursorPos).value;261 this.cursorPos = pos + this.string.length;262 return this.cursorPos - this.string.length;263 }264 chunk(pos) {265 this.syncTo(pos);266 return this.string;267 }268 get lineChunks() { return true; }269 read(from, to) {270 let stringStart = this.cursorPos - this.string.length;271 if (from < stringStart || to >= this.cursorPos)272 return this.doc.sliceString(from, to);273 else274 return this.string.slice(from - stringStart, to - stringStart);275 }276}277let currentContext = null;278/**279A parse context provided to parsers working on the editor content.280*/281class ParseContext {282 constructor(parser, 283 /**284 The current editor state.285 */286 state, 287 /**288 Tree fragments that can be reused by incremental re-parses.289 */290 fragments = [], 291 /**292 @internal293 */294 tree, 295 /**296 @internal297 */298 treeLen, 299 /**300 The current editor viewport (or some overapproximation301 thereof). Intended to be used for opportunistically avoiding302 work (in which case303 [`skipUntilInView`](https://codemirror.net/6/docs/ref/#language.ParseContext.skipUntilInView)304 should be called to make sure the parser is restarted when the305 skipped region becomes visible).306 */307 viewport, 308 /**309 @internal310 */311 skipped, 312 /**313 This is where skipping parsers can register a promise that,314 when resolved, will schedule a new parse. It is cleared when315 the parse worker picks up the promise. @internal316 */317 scheduleOn) {318 this.parser = parser;319 this.state = state;320 this.fragments = fragments;321 this.tree = tree;322 this.treeLen = treeLen;323 this.viewport = viewport;324 this.skipped = skipped;325 this.scheduleOn = scheduleOn;326 this.parse = null;327 /**328 @internal329 */330 this.tempSkipped = [];331 }332 /**333 @internal334 */335 static create(parser, state, viewport) {336 return new ParseContext(parser, state, [], Tree.empty, 0, viewport, [], null);337 }338 startParse() {339 return this.parser.startParse(new DocInput(this.state.doc), this.fragments);340 }341 /**342 @internal343 */344 work(until, upto) {345 if (upto != null && upto >= this.state.doc.length)346 upto = undefined;347 if (this.tree != Tree.empty && this.isDone(upto !== null && upto !== void 0 ? upto : this.state.doc.length)) {348 this.takeTree();349 return true;350 }351 return this.withContext(() => {352 var _a;353 if (typeof until == "number") {354 let endTime = Date.now() + until;355 until = () => Date.now() > endTime;356 }357 if (!this.parse)358 this.parse = this.startParse();359 if (upto != null && (this.parse.stoppedAt == null || this.parse.stoppedAt > upto) &&360 upto < this.state.doc.length)361 this.parse.stopAt(upto);362 for (;;) {363 let done = this.parse.advance();364 if (done) {365 this.fragments = this.withoutTempSkipped(TreeFragment.addTree(done, this.fragments, this.parse.stoppedAt != null));366 this.treeLen = (_a = this.parse.stoppedAt) !== null && _a !== void 0 ? _a : this.state.doc.length;367 this.tree = done;368 this.parse = null;369 if (this.treeLen < (upto !== null && upto !== void 0 ? upto : this.state.doc.length))370 this.parse = this.startParse();371 else372 return true;373 }374 if (until())375 return false;376 }377 });378 }379 /**380 @internal381 */382 takeTree() {383 let pos, tree;384 if (this.parse && (pos = this.parse.parsedPos) >= this.treeLen) {385 if (this.parse.stoppedAt == null || this.parse.stoppedAt > pos)386 this.parse.stopAt(pos);387 this.withContext(() => { while (!(tree = this.parse.advance())) { } });388 this.treeLen = pos;389 this.tree = tree;390 this.fragments = this.withoutTempSkipped(TreeFragment.addTree(this.tree, this.fragments, true));391 this.parse = null;392 }393 }394 withContext(f) {395 let prev = currentContext;396 currentContext = this;397 try {398 return f();399 }400 finally {401 currentContext = prev;402 }403 }404 withoutTempSkipped(fragments) {405 for (let r; r = this.tempSkipped.pop();)406 fragments = cutFragments(fragments, r.from, r.to);407 return fragments;408 }409 /**410 @internal411 */412 changes(changes, newState) {413 let { fragments, tree, treeLen, viewport, skipped } = this;414 this.takeTree();415 if (!changes.empty) {416 let ranges = [];417 changes.iterChangedRanges((fromA, toA, fromB, toB) => ranges.push({ fromA, toA, fromB, toB }));418 fragments = TreeFragment.applyChanges(fragments, ranges);419 tree = Tree.empty;420 treeLen = 0;421 viewport = { from: changes.mapPos(viewport.from, -1), to: changes.mapPos(viewport.to, 1) };422 if (this.skipped.length) {423 skipped = [];424 for (let r of this.skipped) {425 let from = changes.mapPos(r.from, 1), to = changes.mapPos(r.to, -1);426 if (from < to)427 skipped.push({ from, to });428 }429 }430 }431 return new ParseContext(this.parser, newState, fragments, tree, treeLen, viewport, skipped, this.scheduleOn);432 }433 /**434 @internal435 */436 updateViewport(viewport) {437 if (this.viewport.from == viewport.from && this.viewport.to == viewport.to)438 return false;439 this.viewport = viewport;440 let startLen = this.skipped.length;441 for (let i = 0; i < this.skipped.length; i++) {442 let { from, to } = this.skipped[i];443 if (from < viewport.to && to > viewport.from) {444 this.fragments = cutFragments(this.fragments, from, to);445 this.skipped.splice(i--, 1);446 }447 }448 if (this.skipped.length >= startLen)449 return false;450 this.reset();451 return true;452 }453 /**454 @internal455 */456 reset() {457 if (this.parse) {458 this.takeTree();459 this.parse = null;460 }461 }462 /**463 Notify the parse scheduler that the given region was skipped464 because it wasn't in view, and the parse should be restarted465 when it comes into view.466 */467 skipUntilInView(from, to) {468 this.skipped.push({ from, to });469 }470 /**471 Returns a parser intended to be used as placeholder when472 asynchronously loading a nested parser. It'll skip its input and473 mark it as not-really-parsed, so that the next update will parse474 it again.475 476 When `until` is given, a reparse will be scheduled when that477 promise resolves.478 */479 static getSkippingParser(until) {480 return new class extends Parser {481 createParse(input, fragments, ranges) {482 let from = ranges[0].from, to = ranges[ranges.length - 1].to;483 let parser = {484 parsedPos: from,485 advance() {486 let cx = currentContext;487 if (cx) {488 for (let r of ranges)489 cx.tempSkipped.push(r);490 if (until)491 cx.scheduleOn = cx.scheduleOn ? Promise.all([cx.scheduleOn, until]) : until;492 }493 this.parsedPos = to;494 return new Tree(NodeType.none, [], [], to - from);495 },496 stoppedAt: null,497 stopAt() { }498 };499 return parser;500 }501 };502 }503 /**504 @internal505 */506 isDone(upto) {507 upto = Math.min(upto, this.state.doc.length);508 let frags = this.fragments;509 return this.treeLen >= upto && frags.length && frags[0].from == 0 && frags[0].to >= upto;510 }511 /**512 Get the context for the current parse, or `null` if no editor513 parse is in progress.514 */515 static get() { return currentContext; }516}517function cutFragments(fragments, from, to) {518 return TreeFragment.applyChanges(fragments, [{ fromA: from, toA: to, fromB: from, toB: to }]);519}520class LanguageState {521 constructor(522 // A mutable parse state that is used to preserve work done during523 // the lifetime of a state when moving to the next state.524 context) {525 this.context = context;526 this.tree = context.tree;527 }528 apply(tr) {529 if (!tr.docChanged && this.tree == this.context.tree)530 return this;531 let newCx = this.context.changes(tr.changes, tr.state);532 // If the previous parse wasn't done, go forward only up to its533 // end position or the end of the viewport, to avoid slowing down534 // state updates with parse work beyond the viewport.535 let upto = this.context.treeLen == tr.startState.doc.length ? undefined536 : Math.max(tr.changes.mapPos(this.context.treeLen), newCx.viewport.to);537 if (!newCx.work(20 /* Work.Apply */, upto))538 newCx.takeTree();539 return new LanguageState(newCx);540 }541 static init(state) {542 let vpTo = Math.min(3000 /* Work.InitViewport */, state.doc.length);543 let parseState = ParseContext.create(state.facet(language).parser, state, { from: 0, to: vpTo });544 if (!parseState.work(20 /* Work.Apply */, vpTo))545 parseState.takeTree();546 return new LanguageState(parseState);547 }548}549Language.state = /*@__PURE__*/StateField.define({550 create: LanguageState.init,551 update(value, tr) {552 for (let e of tr.effects)553 if (e.is(Language.setState))554 return e.value;555 if (tr.startState.facet(language) != tr.state.facet(language))556 return LanguageState.init(tr.state);557 return value.apply(tr);558 }559});560let requestIdle = (callback) => {561 let timeout = setTimeout(() => callback(), 500 /* Work.MaxPause */);562 return () => clearTimeout(timeout);563};564if (typeof requestIdleCallback != "undefined")565 requestIdle = (callback) => {566 let idle = -1, timeout = setTimeout(() => {567 idle = requestIdleCallback(callback, { timeout: 500 /* Work.MaxPause */ - 100 /* Work.MinPause */ });568 }, 100 /* Work.MinPause */);569 return () => idle < 0 ? clearTimeout(timeout) : cancelIdleCallback(idle);570 };571const isInputPending = typeof navigator != "undefined" && ((_a = navigator.scheduling) === null || _a === void 0 ? void 0 : _a.isInputPending)572 ? () => navigator.scheduling.isInputPending() : null;573const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {574 constructor(view) {575 this.view = view;576 this.working = null;577 this.workScheduled = 0;578 // End of the current time chunk579 this.chunkEnd = -1;580 // Milliseconds of budget left for this chunk581 this.chunkBudget = -1;582 this.work = this.work.bind(this);583 this.scheduleWork();584 }585 update(update) {586 let cx = this.view.state.field(Language.state).context;587 if (cx.updateViewport(update.view.viewport) || this.view.viewport.to > cx.treeLen)588 this.scheduleWork();589 if (update.docChanged || update.selectionSet) {590 if (this.view.hasFocus)591 this.chunkBudget += 50 /* Work.ChangeBonus */;592 this.scheduleWork();593 }594 this.checkAsyncSchedule(cx);595 }596 scheduleWork() {597 if (this.working)598 return;599 let { state } = this.view, field = state.field(Language.state);600 if (field.tree != field.context.tree || !field.context.isDone(state.doc.length))601 this.working = requestIdle(this.work);602 }603 work(deadline) {604 this.working = null;605 let now = Date.now();606 if (this.chunkEnd < now && (this.chunkEnd < 0 || this.view.hasFocus)) { // Start a new chunk607 this.chunkEnd = now + 30000 /* Work.ChunkTime */;608 this.chunkBudget = 3000 /* Work.ChunkBudget */;609 }610 if (this.chunkBudget <= 0)611 return; // No more budget612 let { state, viewport: { to: vpTo } } = this.view, field = state.field(Language.state);613 if (field.tree == field.context.tree && field.context.isDone(vpTo + 100000 /* Work.MaxParseAhead */))614 return;615 let endTime = Date.now() + Math.min(this.chunkBudget, 100 /* Work.Slice */, deadline && !isInputPending ? Math.max(25 /* Work.MinSlice */, deadline.timeRemaining() - 5) : 1e9);616 let viewportFirst = field.context.treeLen < vpTo && state.doc.length > vpTo + 1000;617 let done = field.context.work(() => {618 return isInputPending && isInputPending() || Date.now() > endTime;619 }, vpTo + (viewportFirst ? 0 : 100000 /* Work.MaxParseAhead */));620 this.chunkBudget -= Date.now() - now;621 if (done || this.chunkBudget <= 0) {622 field.context.takeTree();623 this.view.dispatch({ effects: Language.setState.of(new LanguageState(field.context)) });624 }625 if (this.chunkBudget > 0 && !(done && !viewportFirst))626 this.scheduleWork();627 this.checkAsyncSchedule(field.context);628 }629 checkAsyncSchedule(cx) {630 if (cx.scheduleOn) {631 this.workScheduled++;632 cx.scheduleOn633 .then(() => this.scheduleWork())634 .catch(err => logException(this.view.state, err))635 .then(() => this.workScheduled--);636 cx.scheduleOn = null;637 }638 }639 destroy() {640 if (this.working)641 this.working();642 }643 isWorking() {644 return !!(this.working || this.workScheduled > 0);645 }646}, {647 eventHandlers: { focus() { this.scheduleWork(); } }648});649/**650The facet used to associate a language with an editor state. Used651by `Language` object's `extension` property (so you don't need to652manually wrap your languages in this). Can be used to access the653current language on a state.654*/655const language = /*@__PURE__*/Facet.define({656 combine(languages) { return languages.length ? languages[0] : null; },657 enables: language => [658 Language.state,659 parseWorker,660 EditorView.contentAttributes.compute([language], state => {661 let lang = state.facet(language);662 return lang && lang.name ? { "data-language": lang.name } : {};663 })664 ]665});666/**667This class bundles a [language](https://codemirror.net/6/docs/ref/#language.Language) with an668optional set of supporting extensions. Language packages are669encouraged to export a function that optionally takes a670configuration object and returns a `LanguageSupport` instance, as671the main way for client code to use the package.672*/673class LanguageSupport {674 /**675 Create a language support object.676 */677 constructor(678 /**679 The language object.680 */681 language, 682 /**683 An optional set of supporting extensions. When nesting a684 language in another language, the outer language is encouraged685 to include the supporting extensions for its inner languages686 in its own set of support extensions.687 */688 support = []) {689 this.language = language;690 this.support = support;691 this.extension = [language, support];692 }693}694/**695Language descriptions are used to store metadata about languages696and to dynamically load them. Their main role is finding the697appropriate language for a filename or dynamically loading nested698parsers.699*/700class LanguageDescription {701 constructor(702 /**703 The name of this language.704 */705 name, 706 /**707 Alternative names for the mode (lowercased, includes `this.name`).708 */709 alias, 710 /**711 File extensions associated with this language.712 */713 extensions, 714 /**715 Optional filename pattern that should be associated with this716 language.717 */718 filename, loadFunc, 719 /**720 If the language has been loaded, this will hold its value.721 */722 support = undefined) {723 this.name = name;724 this.alias = alias;725 this.extensions = extensions;726 this.filename = filename;727 this.loadFunc = loadFunc;728 this.support = support;729 this.loading = null;730 }731 /**732 Start loading the the language. Will return a promise that733 resolves to a [`LanguageSupport`](https://codemirror.net/6/docs/ref/#language.LanguageSupport)734 object when the language successfully loads.735 */736 load() {737 return this.loading || (this.loading = this.loadFunc().then(support => this.support = support, err => { this.loading = null; throw err; }));738 }739 /**740 Create a language description.741 */742 static of(spec) {743 let { load, support } = spec;744 if (!load) {745 if (!support)746 throw new RangeError("Must pass either 'load' or 'support' to LanguageDescription.of");747 load = () => Promise.resolve(support);748 }749 return new LanguageDescription(spec.name, (spec.alias || []).concat(spec.name).map(s => s.toLowerCase()), spec.extensions || [], spec.filename, load, support);750 }751 /**752 Look for a language in the given array of descriptions that753 matches the filename. Will first match754 [`filename`](https://codemirror.net/6/docs/ref/#language.LanguageDescription.filename) patterns,755 and then [extensions](https://codemirror.net/6/docs/ref/#language.LanguageDescription.extensions),756 and return the first language that matches.757 */758 static matchFilename(descs, filename) {759 for (let d of descs)760 if (d.filename && d.filename.test(filename))761 return d;762 let ext = /\.([^.]+)$/.exec(filename);763 if (ext)764 for (let d of descs)765 if (d.extensions.indexOf(ext[1]) > -1)766 return d;767 return null;768 }769 /**770 Look for a language whose name or alias matches the the given771 name (case-insensitively). If `fuzzy` is true, and no direct772 matchs is found, this'll also search for a language whose name773 or alias occurs in the string (for names shorter than three774 characters, only when surrounded by non-word characters).775 */776 static matchLanguageName(descs, name, fuzzy = true) {777 name = name.toLowerCase();778 for (let d of descs)779 if (d.alias.some(a => a == name))780 return d;781 if (fuzzy)782 for (let d of descs)783 for (let a of d.alias) {784 let found = name.indexOf(a);785 if (found > -1 && (a.length > 2 || !/\w/.test(name[found - 1]) && !/\w/.test(name[found + a.length])))786 return d;787 }788 return null;789 }790}791 792/**793Facet that defines a way to provide a function that computes the794appropriate indentation depth, as a column number (see795[`indentString`](https://codemirror.net/6/docs/ref/#language.indentString)), at the start of a given796line. A return value of `null` indicates no indentation can be797determined, and the line should inherit the indentation of the one798above it. A return value of `undefined` defers to the next indent799service.800*/801const indentService = /*@__PURE__*/Facet.define();802/**803Facet for overriding the unit by which indentation happens. Should804be a string consisting entirely of the same whitespace character.805When not set, this defaults to 2 spaces.806*/807const indentUnit = /*@__PURE__*/Facet.define({808 combine: values => {809 if (!values.length)810 return " ";811 let unit = values[0];812 if (!unit || /\S/.test(unit) || Array.from(unit).some(e => e != unit[0]))813 throw new Error("Invalid indent unit: " + JSON.stringify(values[0]));814 return unit;815 }816});817/**818Return the _column width_ of an indent unit in the state.819Determined by the [`indentUnit`](https://codemirror.net/6/docs/ref/#language.indentUnit)820facet, and [`tabSize`](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize) when that821contains tabs.822*/823function getIndentUnit(state) {824 let unit = state.facet(indentUnit);825 return unit.charCodeAt(0) == 9 ? state.tabSize * unit.length : unit.length;826}827/**828Create an indentation string that covers columns 0 to `cols`.829Will use tabs for as much of the columns as possible when the830[`indentUnit`](https://codemirror.net/6/docs/ref/#language.indentUnit) facet contains831tabs.832*/833function indentString(state, cols) {834 let result = "", ts = state.tabSize, ch = state.facet(indentUnit)[0];835 if (ch == "\t") {836 while (cols >= ts) {837 result += "\t";838 cols -= ts;839 }840 ch = " ";841 }842 for (let i = 0; i < cols; i++)843 result += ch;844 return result;845}846/**847Get the indentation, as a column number, at the given position.848Will first consult any [indent services](https://codemirror.net/6/docs/ref/#language.indentService)849that are registered, and if none of those return an indentation,850this will check the syntax tree for the [indent node851prop](https://codemirror.net/6/docs/ref/#language.indentNodeProp) and use that if found. Returns a852number when an indentation could be determined, and null853otherwise.854*/855function getIndentation(context, pos) {856 if (context instanceof EditorState)857 context = new IndentContext(context);858 for (let service of context.state.facet(indentService)) {859 let result = service(context, pos);860 if (result !== undefined)861 return result;862 }863 let tree = syntaxTree(context.state);864 return tree.length >= pos ? syntaxIndentation(context, tree, pos) : null;865}866/**867Create a change set that auto-indents all lines touched by the868given document range.869*/870function indentRange(state, from, to) {871 let updated = Object.create(null);872 let context = new IndentContext(state, { overrideIndentation: start => { var _a; return (_a = updated[start]) !== null && _a !== void 0 ? _a : -1; } });873 let changes = [];874 for (let pos = from; pos <= to;) {875 let line = state.doc.lineAt(pos);876 pos = line.to + 1;877 let indent = getIndentation(context, line.from);878 if (indent == null)879 continue;880 if (!/\S/.test(line.text))881 indent = 0;882 let cur = /^\s*/.exec(line.text)[0];883 let norm = indentString(state, indent);884 if (cur != norm) {885 updated[line.from] = indent;886 changes.push({ from: line.from, to: line.from + cur.length, insert: norm });887 }888 }889 return state.changes(changes);890}891/**892Indentation contexts are used when calling [indentation893services](https://codemirror.net/6/docs/ref/#language.indentService). They provide helper utilities894useful in indentation logic, and can selectively override the895indentation reported for some lines.896*/897class IndentContext {898 /**899 Create an indent context.900 */901 constructor(902 /**903 The editor state.904 */905 state, 906 /**907 @internal908 */909 options = {}) {910 this.state = state;911 this.options = options;912 this.unit = getIndentUnit(state);913 }914 /**915 Get a description of the line at the given position, taking916 [simulated line917 breaks](https://codemirror.net/6/docs/ref/#language.IndentContext.constructor^options.simulateBreak)918 into account. If there is such a break at `pos`, the `bias`919 argument determines whether the part of the line line before or920 after the break is used.921 */922 lineAt(pos, bias = 1) {923 let line = this.state.doc.lineAt(pos);924 let { simulateBreak, simulateDoubleBreak } = this.options;925 if (simulateBreak != null && simulateBreak >= line.from && simulateBreak <= line.to) {926 if (simulateDoubleBreak && simulateBreak == pos)927 return { text: "", from: pos };928 else if (bias < 0 ? simulateBreak < pos : simulateBreak <= pos)929 return { text: line.text.slice(simulateBreak - line.from), from: simulateBreak };930 else931 return { text: line.text.slice(0, simulateBreak - line.from), from: line.from };932 }933 return line;934 }935 /**936 Get the text directly after `pos`, either the entire line937 or the next 100 characters, whichever is shorter.938 */939 textAfterPos(pos, bias = 1) {940 if (this.options.simulateDoubleBreak && pos == this.options.simulateBreak)941 return "";942 let { text, from } = this.lineAt(pos, bias);943 return text.slice(pos - from, Math.min(text.length, pos + 100 - from));944 }945 /**946 Find the column for the given position.947 */948 column(pos, bias = 1) {949 let { text, from } = this.lineAt(pos, bias);950 let result = this.countColumn(text, pos - from);951 let override = this.options.overrideIndentation ? this.options.overrideIndentation(from) : -1;952 if (override > -1)953 result += override - this.countColumn(text, text.search(/\S|$/));954 return result;955 }956 /**957 Find the column position (taking tabs into account) of the given958 position in the given string.959 */960 countColumn(line, pos = line.length) {961 return countColumn(line, this.state.tabSize, pos);962 }963 /**964 Find the indentation column of the line at the given point.965 */966 lineIndent(pos, bias = 1) {967 let { text, from } = this.lineAt(pos, bias);968 let override = this.options.overrideIndentation;969 if (override) {970 let overriden = override(from);971 if (overriden > -1)972 return overriden;973 }974 return this.countColumn(text, text.search(/\S|$/));975 }976 /**977 Returns the [simulated line978 break](https://codemirror.net/6/docs/ref/#language.IndentContext.constructor^options.simulateBreak)979 for this context, if any.980 */981 get simulatedBreak() {982 return this.options.simulateBreak || null;983 }984}985/**986A syntax tree node prop used to associate indentation strategies987with node types. Such a strategy is a function from an indentation988context to a column number (see also989[`indentString`](https://codemirror.net/6/docs/ref/#language.indentString)) or null, where null990indicates that no definitive indentation can be determined.991*/992const indentNodeProp = /*@__PURE__*/new NodeProp();993// Compute the indentation for a given position from the syntax tree.994function syntaxIndentation(cx, ast, pos) {995 let stack = ast.resolveStack(pos);996 let inner = ast.resolveInner(pos, -1).resolve(pos, 0).enterUnfinishedNodesBefore(pos);997 if (inner != stack.node) {998 let add = [];999 for (let cur = inner; cur && !(cur.from < stack.node.from || cur.to > stack.node.to ||1000 cur.from == stack.node.from && cur.type == stack.node.type); cur = cur.parent)1001 add.push(cur);1002 for (let i = add.length - 1; i >= 0; i--)1003 stack = { node: add[i], next: stack };1004 }1005 return indentFor(stack, cx, pos);1006}1007function indentFor(stack, cx, pos) {1008 for (let cur = stack; cur; cur = cur.next) {1009 let strategy = indentStrategy(cur.node);1010 if (strategy)1011 return strategy(TreeIndentContext.create(cx, pos, cur));1012 }1013 return 0;1014}1015function ignoreClosed(cx) {1016 return cx.pos == cx.options.simulateBreak && cx.options.simulateDoubleBreak;1017}1018function indentStrategy(tree) {1019 let strategy = tree.type.prop(indentNodeProp);1020 if (strategy)1021 return strategy;1022 let first = tree.firstChild, close;1023 if (first && (close = first.type.prop(NodeProp.closedBy))) {1024 let last = tree.lastChild, closed = last && close.indexOf(last.name) > -1;1025 return cx => delimitedStrategy(cx, true, 1, undefined, closed && !ignoreClosed(cx) ? last.from : undefined);1026 }1027 return tree.parent == null ? topIndent : null;1028}1029function topIndent() { return 0; }1030/**1031Objects of this type provide context information and helper1032methods to indentation functions registered on syntax nodes.1033*/1034class TreeIndentContext extends IndentContext {1035 constructor(base, 1036 /**1037 The position at which indentation is being computed.1038 */1039 pos, 1040 /**1041 @internal1042 */1043 context) {1044 super(base.state, base.options);1045 this.base = base;1046 this.pos = pos;1047 this.context = context;1048 }1049 /**1050 The syntax tree node to which the indentation strategy1051 applies.1052 */1053 get node() { return this.context.node; }1054 /**1055 @internal1056 */1057 static create(base, pos, context) {1058 return new TreeIndentContext(base, pos, context);1059 }1060 /**1061 Get the text directly after `this.pos`, either the entire line1062 or the next 100 characters, whichever is shorter.1063 */1064 get textAfter() {1065 return this.textAfterPos(this.pos);1066 }1067 /**1068 Get the indentation at the reference line for `this.node`, which1069 is the line on which it starts, unless there is a node that is1070 _not_ a parent of this node covering the start of that line. If1071 so, the line at the start of that node is tried, again skipping1072 on if it is covered by another such node.1073 */1074 get baseIndent() {1075 return this.baseIndentFor(this.node);1076 }1077 /**1078 Get the indentation for the reference line of the given node1079 (see [`baseIndent`](https://codemirror.net/6/docs/ref/#language.TreeIndentContext.baseIndent)).1080 */1081 baseIndentFor(node) {1082 let line = this.state.doc.lineAt(node.from);1083 // Skip line starts that are covered by a sibling (or cousin, etc)1084 for (;;) {1085 let atBreak = node.resolve(line.from);1086 while (atBreak.parent && atBreak.parent.from == atBreak.from)1087 atBreak = atBreak.parent;1088 if (isParent(atBreak, node))1089 break;1090 line = this.state.doc.lineAt(atBreak.from);1091 }1092 return this.lineIndent(line.from);1093 }1094 /**1095 Continue looking for indentations in the node's parent nodes,1096 and return the result of that.1097 */1098 continue() {1099 return indentFor(this.context.next, this.base, this.pos);1100 }1101}1102function isParent(parent, of) {1103 for (let cur = of; cur; cur = cur.parent)1104 if (parent == cur)1105 return true;1106 return false;1107}1108// Check whether a delimited node is aligned (meaning there are1109// non-skipped nodes on the same line as the opening delimiter). And1110// if so, return the opening token.1111function bracketedAligned(context) {1112 let tree = context.node;1113 let openToken = tree.childAfter(tree.from), last = tree.lastChild;1114 if (!openToken)1115 return null;1116 let sim = context.options.simulateBreak;1117 let openLine = context.state.doc.lineAt(openToken.from);1118 let lineEnd = sim == null || sim <= openLine.from ? openLine.to : Math.min(openLine.to, sim);1119 for (let pos = openToken.to;;) {1120 let next = tree.childAfter(pos);1121 if (!next || next == last)1122 return null;1123 if (!next.type.isSkipped) {1124 if (next.from >= lineEnd)1125 return null;1126 let space = /^ */.exec(openLine.text.slice(openToken.to - openLine.from))[0].length;1127 return { from: openToken.from, to: openToken.to + space };1128 }1129 pos = next.to;1130 }1131}1132/**1133An indentation strategy for delimited (usually bracketed) nodes.1134Will, by default, indent one unit more than the parent's base1135indent unless the line starts with a closing token. When `align`1136is true and there are non-skipped nodes on the node's opening1137line, the content of the node will be aligned with the end of the1138opening node, like this:1139 1140 foo(bar,1141 baz)1142*/1143function delimitedIndent({ closing, align = true, units = 1 }) {1144 return (context) => delimitedStrategy(context, align, units, closing);1145}1146function delimitedStrategy(context, align, units, closing, closedAt) {1147 let after = context.textAfter, space = after.match(/^\s*/)[0].length;1148 let closed = closing && after.slice(space, space + closing.length) == closing || closedAt == context.pos + space;1149 let aligned = align ? bracketedAligned(context) : null;1150 if (aligned)1151 return closed ? context.column(aligned.from) : context.column(aligned.to);1152 return context.baseIndent + (closed ? 0 : context.unit * units);1153}1154/**1155An indentation strategy that aligns a node's content to its base1156indentation.1157*/1158const flatIndent = (context) => context.baseIndent;1159/**1160Creates an indentation strategy that, by default, indents1161continued lines one unit more than the node's base indentation.1162You can provide `except` to prevent indentation of lines that1163match a pattern (for example `/^else\b/` in `if`/`else`1164constructs), and you can change the amount of units used with the1165`units` option.1166*/1167function continuedIndent({ except, units = 1 } = {}) {1168 return (context) => {1169 let matchExcept = except && except.test(context.textAfter);1170 return context.baseIndent + (matchExcept ? 0 : units * context.unit);1171 };1172}1173const DontIndentBeyond = 200;1174/**1175Enables reindentation on input. When a language defines an1176`indentOnInput` field in its [language1177data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt), which must hold a regular1178expression, the line at the cursor will be reindented whenever new1179text is typed and the input from the start of the line up to the1180cursor matches that regexp.1181 1182To avoid unneccesary reindents, it is recommended to start the1183regexp with `^` (usually followed by `\s*`), and end it with `$`.1184For example, `/^\s*\}$/` will reindent when a closing brace is1185added at the start of a line.1186*/1187function indentOnInput() {1188 return EditorState.transactionFilter.of(tr => {1189 if (!tr.docChanged || !tr.isUserEvent("input.type") && !tr.isUserEvent("input.complete"))1190 return tr;1191 let rules = tr.startState.languageDataAt("indentOnInput", tr.startState.selection.main.head);1192 if (!rules.length)1193 return tr;1194 let doc = tr.newDoc, { head } = tr.newSelection.main, line = doc.lineAt(head);1195 if (head > line.from + DontIndentBeyond)1196 return tr;1197 let lineStart = doc.sliceString(line.from, head);1198 if (!rules.some(r => r.test(lineStart)))1199 return tr;1200 let { state } = tr, last = -1, changes = [];