diff --git a/dist/index.d.ts b/dist/index.d.ts index 382f4d6..bbf0a00 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -1,6 +1,6 @@ /** A text iterator iterates over a sequence of strings. When -iterating over a [`Text`](https://codemirror.net/6/docs/ref/#text.Text) document, result values will +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 { @@ -26,7 +26,7 @@ interface TextIterator extends Iterator, Iterable { lineBreak: boolean; } /** -The data structure for documents. +The data structure for documents. @nonabstract */ declare abstract class Text implements Iterable { /** @@ -68,8 +68,7 @@ declare abstract class Text implements Iterable { /** 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, and for long lines, might split lines - themselves into multiple chunks as well. + them as separate strings. */ iter(dir?: 1 | -1): TextIterator; /** @@ -87,7 +86,7 @@ declare abstract class Text implements Iterable { iterLines(from?: number, to?: number): TextIterator; /** Convert the document to an array of lines (which can be - deserialized again via [`Text.of`](https://codemirror.net/6/docs/ref/#text.Text^of)). + deserialized again via [`Text.of`](https://codemirror.net/6/docs/ref/#state.Text^of)). */ toJSON(): string[]; /** @@ -107,7 +106,7 @@ declare abstract class Text implements Iterable { } /** This type describes a line in the document. It is created -on-demand when lines are [queried](https://codemirror.net/6/docs/ref/#text.Text.lineAt). +on-demand when lines are [queried](https://codemirror.net/6/docs/ref/#state.Text.lineAt). */ declare class Line$1 { /** @@ -174,13 +173,18 @@ declare class ChangeDesc { */ get empty(): boolean; /** - Iterate over the unchanged parts left by these changes. + 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 @@ -476,9 +480,9 @@ declare 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 - will immediately be called on creating the facet, with an empty - array, to compute the facet's default value when no inputs are - present. + 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; /** @@ -494,7 +498,7 @@ declare type FacetConfig = { */ compareInput?: (a: Input, b: Input) => boolean; /** - Static facets can not contain dynamic inputs. + Forbids dynamic inputs to this facet. */ static?: boolean; /** @@ -511,10 +515,10 @@ 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 facets are the [theme](https://codemirror.net/6/docs/ref/#view.EditorView^theme) styles -associated with an editor or the [tab -size](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize) (which is reduced to a single -value, using the input with the hightest precedence). +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). */ declare class Facet { private isStatic; @@ -524,7 +528,7 @@ declare class Facet { */ static define(config?: FacetConfig): Facet; /** - Returns an extension that adds the given value for this facet. + Returns an extension that adds the given value to this facet. */ of(value: Input): Extension; /** @@ -533,9 +537,8 @@ declare class Facet { this value depends on, since your function is only called again for a new state when one of those parts changed. - In most cases, you'll want to use the - [`provide`](https://codemirror.net/6/docs/ref/#state.StateField^define^config.provide) option when - defining a field instead. + 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; /** @@ -549,7 +552,7 @@ declare class Facet { 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): Extension; from(field: StateField, get: (value: T) => Input): Extension; } declare type Slot = Facet | StateField | "doc" | "selection"; @@ -571,12 +574,12 @@ declare type StateFieldSpec = { */ compare?: (a: Value, b: Value) => boolean; /** - Provide values for facets based on the value of 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 by this field. + 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; /** @@ -787,7 +790,7 @@ interface TransactionSpec { */ annotations?: Annotation | readonly Annotation[]; /** - Shorthand for `annotations: `[`Transaction.userEvent`](https://codemirror.net/6/docs/ref/#state.Transaction^userEvent)[`.of(...)`. + Shorthand for `annotations:` [`Transaction.userEvent`](https://codemirror.net/6/docs/ref/#state.Transaction^userEvent)`.of(...)`. */ userEvent?: string; /** @@ -799,7 +802,9 @@ interface TransactionSpec { 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. + 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; /** @@ -817,7 +822,9 @@ 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). +[`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 { /** @@ -860,7 +867,7 @@ declare class Transaction { get newSelection(): EditorSelection; /** The new state created by the transaction. Computed on demand - (but retained for subsequent access), so itis recommended not to + (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. */ @@ -889,7 +896,8 @@ declare class Transaction { */ isUserEvent(event: string): boolean; /** - Annotation used to store transaction timestamps. + Annotation used to store transaction timestamps. Automatically + added to every transaction, holding `Date.now()`. */ static time: AnnotationType; /** @@ -963,7 +971,7 @@ interface EditorStateConfig { 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/#text.Text) class (which is what the state will use + 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; @@ -1025,11 +1033,7 @@ declare class EditorState { 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): { - changes: ChangeSet; - selection: EditorSelection; - effects: readonly StateEffect[]; - }; + 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 @@ -1059,7 +1063,7 @@ declare class EditorState { /** Using the state's [line separator](https://codemirror.net/6/docs/ref/#state.EditorState^lineSeparator), create a - [`Text`](https://codemirror.net/6/docs/ref/#text.Text) instance from the given string. + [`Text`](https://codemirror.net/6/docs/ref/#state.Text) instance from the given string. */ toText(string: string): Text; /** @@ -1181,7 +1185,7 @@ declare class EditorState { 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/#text.findClusterBreak)) + represent a single [grapheme cluster](https://codemirror.net/6/docs/ref/#state.findClusterBreak)) into one of: - Word (contains an alphanumeric character or a character @@ -1208,7 +1212,7 @@ declare class EditorState { 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 number indicating the start and end of a + 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. */ @@ -1239,12 +1243,12 @@ declare class EditorState { 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 the transaction has disabled regular + 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 applied. + Extenders run _after_ filters, when both are present. */ static transactionExtender: Facet<(tr: Transaction) => Pick | null, readonly ((tr: Transaction) => Pick | null)[]>; } @@ -1261,15 +1265,259 @@ declare type StateCommand = (target: { /** Utility function for combining behaviors to fill in a config -object from an array of provided configs. Will, by default, error +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 +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; +} +/** +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; +} +/** +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; +} +declare 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 { + /** + 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. `from` and + `to` are coordinates in the _new_ space, after these changes. + */ + 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; + /** + The empty set of ranges. + */ + static empty: RangeSet; +} + interface ChangedRange { fromA: number; toA: number; @@ -1375,6 +1623,12 @@ declare class NodeSet { constructor(types: readonly NodeType[]); extend(...props: NodePropSource[]): NodeSet; } +declare enum IterMode { + ExcludeBuffers = 1, + IncludeAnonymous = 2, + IgnoreMounts = 4, + IgnoreOverlays = 8 +} declare class Tree { readonly type: NodeType; readonly children: readonly (Tree | TreeBuffer)[]; @@ -1382,16 +1636,17 @@ declare class Tree { readonly length: number; constructor(type: NodeType, children: readonly (Tree | TreeBuffer)[], positions: readonly number[], length: number, props?: readonly [NodeProp | number, any][]); static empty: Tree; - cursor(pos?: number, side?: -1 | 0 | 1): TreeCursor; - fullCursor(): TreeCursor; + cursor(mode?: IterMode): TreeCursor; + cursorAt(pos: number, side?: -1 | 0 | 1, mode?: IterMode): TreeCursor; get topNode(): SyntaxNode; resolve(pos: number, side?: -1 | 0 | 1): SyntaxNode; resolveInner(pos: number, side?: -1 | 0 | 1): SyntaxNode; iterate(spec: { - enter(type: NodeType, from: number, to: number, get: () => SyntaxNode): false | void; - leave?(type: NodeType, from: number, to: number, get: () => SyntaxNode): void; + enter(node: SyntaxNodeRef): boolean | void; + leave?(node: SyntaxNodeRef): void; from?: number; to?: number; + mode?: IterMode; }): void; prop(prop: NodeProp): T | undefined; get propValues(): readonly [NodeProp | number, any][]; @@ -1426,36 +1681,38 @@ declare class TreeBuffer { readonly set: NodeSet; constructor(buffer: Uint16Array, length: number, set: NodeSet); } -interface SyntaxNode { - type: NodeType; - name: string; - from: number; - to: number; +interface SyntaxNodeRef { + readonly from: number; + readonly to: number; + readonly type: NodeType; + readonly name: string; + readonly tree: Tree | null; + readonly node: SyntaxNode; +} +interface SyntaxNode extends SyntaxNodeRef { parent: SyntaxNode | null; firstChild: SyntaxNode | null; lastChild: SyntaxNode | null; childAfter(pos: number): SyntaxNode | null; childBefore(pos: number): SyntaxNode | null; - enter(pos: number, side: -1 | 0 | 1, overlays?: boolean, buffers?: boolean): SyntaxNode | null; + enter(pos: number, side: -1 | 0 | 1, mode?: IterMode): SyntaxNode | null; nextSibling: SyntaxNode | null; prevSibling: SyntaxNode | null; - cursor: TreeCursor; + cursor(mode?: IterMode): TreeCursor; resolve(pos: number, side?: -1 | 0 | 1): SyntaxNode; resolveInner(pos: number, side?: -1 | 0 | 1): SyntaxNode; enterUnfinishedNodesBefore(pos: number): SyntaxNode; - tree: Tree | null; toTree(): Tree; getChild(type: string | number, before?: string | number | null, after?: string | number | null): SyntaxNode | null; getChildren(type: string | number, before?: string | number | null, after?: string | number | null): SyntaxNode[]; + matchContext(context: readonly string[]): boolean; } -declare class TreeCursor { +declare class TreeCursor implements SyntaxNodeRef { type: NodeType; get name(): string; from: number; to: number; - private buffer; private stack; - private index; private bufferNode; private yieldNode; private yieldBuf; @@ -1464,7 +1721,7 @@ declare class TreeCursor { lastChild(): boolean; childAfter(pos: number): boolean; childBefore(pos: number): boolean; - enter(pos: number, side: -1 | 0 | 1, overlays?: boolean, buffers?: boolean): boolean; + enter(pos: number, side: -1 | 0 | 1, mode?: IterMode): boolean; parent(): boolean; nextSibling(): boolean; prevSibling(): boolean; @@ -1475,6 +1732,8 @@ declare class TreeCursor { moveTo(pos: number, side?: -1 | 0 | 1): this; get node(): SyntaxNode; get tree(): Tree | null; + iterate(enter: (node: SyntaxNodeRef) => boolean | void, leave?: (node: SyntaxNodeRef) => void): void; + matchContext(context: readonly string[]): boolean; } interface NestedParse { @@ -1482,575 +1741,22 @@ interface NestedParse { overlay?: readonly { from: number; to: number; - }[] | ((node: TreeCursor) => { + }[] | ((node: SyntaxNodeRef) => { from: number; to: number; } | boolean); } -declare function parseMixed(nest: (node: TreeCursor, input: Input) => NestedParse | null): ParseWrapper; - -declare class StyleModule { - constructor(spec: {[selector: string]: StyleSpec}, options?: { - finish?(sel: string): string - }) - getRules(): string - static mount(root: Document | ShadowRoot | DocumentOrShadowRoot, module: StyleModule | ReadonlyArray): void - static newName(): string -} +declare function parseMixed(nest: (node: SyntaxNodeRef, input: Input) => NestedParse | null): ParseWrapper; -type StyleSpec = { - [propOrSelector: string]: string | number | StyleSpec | null -} - -/** -Highlighting tags are markers that denote a highlighting category. -They are [associated](https://codemirror.net/6/docs/ref/#highlight.styleTags) with parts of a syntax -tree by a language mode, and then mapped to an actual CSS style by -a [highlight style](https://codemirror.net/6/docs/ref/#highlight.HighlightStyle). - -Because syntax tree node types and highlight styles have to be -able to talk the same language, CodeMirror uses a mostly _closed_ -[vocabulary](https://codemirror.net/6/docs/ref/#highlight.tags) of syntax tags (as opposed to -traditional open string-based systems, which make it hard for -highlighting themes to cover all the tokens produced by the -various languages). - -It _is_ possible to [define](https://codemirror.net/6/docs/ref/#highlight.Tag^define) your own -highlighting tags for system-internal use (where you control both -the language package and the highlighter), but such tags will not -be picked up by regular highlighters (though you can derive them -from standard tags to allow highlighters to fall back to those). -*/ -declare class Tag { - /** - Define a new tag. If `parent` is given, the tag is treated as a - sub-tag of that parent, and [highlight - styles](https://codemirror.net/6/docs/ref/#highlight.HighlightStyle) that don't mention this tag - will try to fall back to the parent tag (or grandparent tag, - etc). - */ - static define(parent?: Tag): Tag; - /** - Define a tag _modifier_, which is a function that, given a tag, - will return a tag that is a subtag of the original. Applying the - same modifier to a twice tag will return the same value (`m1(t1) - == m1(t1)`) and applying multiple modifiers will, regardless or - order, produce the same tag (`m1(m2(t1)) == m2(m1(t1))`). - - When multiple modifiers are applied to a given base tag, each - smaller set of modifiers is registered as a parent, so that for - example `m1(m2(m3(t1)))` is a subtype of `m1(m2(t1))`, - `m1(m3(t1)`, and so on. - */ - static defineModifier(): (tag: Tag) => Tag; -} -/** -A highlight style associates CSS styles with higlighting -[tags](https://codemirror.net/6/docs/ref/#highlight.Tag). -*/ -declare class HighlightStyle { - /** - Extension that registers this style with an editor. When - multiple highlight styles are given, they _all_ apply, assigning - the combination of their matching styles to tokens. - */ - readonly extension: Extension; - /** - An extension that installs this highlighter as a fallback - highlight style, which will only be used if no other highlight - styles are configured. - */ - readonly fallback: Extension; - /** - A style module holding the CSS rules for this highlight style. - When using [`highlightTree`](https://codemirror.net/6/docs/ref/#highlight.highlightTree), you may - want to manually mount this module to show the highlighting. - */ - readonly module: StyleModule | null; - private map; - private scope; - private all; - private constructor(); - /** - Returns the CSS class associated with the given tag, if any. - This method is bound to the instance by the constructor. - */ - match(tag: Tag, scope: NodeType): string | null; - /** - Combines an array of highlight styles into a single match - function that returns all of the classes assigned by the styles - for a given tag. - */ - static combinedMatch(styles: readonly HighlightStyle[]): (tag: Tag, scope: NodeType) => any; - /** - Create a highlighter style that associates the given styles to - the given tags. The spec must be objects that hold a style tag - or array of tags in their `tag` property, and either a single - `class` property providing a static CSS class (for highlighters - like [`classHighlightStyle`](https://codemirror.net/6/docs/ref/#highlight.classHighlightStyle) - that rely on external styling), or a - [`style-mod`](https://github.com/marijnh/style-mod#documentation)-style - set of CSS properties (which define the styling for those tags). - - The CSS rules created for a highlighter will be emitted in the - order of the spec's properties. That means that for elements that - have multiple tags associated with them, styles defined further - down in the list will have a higher CSS precedence than styles - defined earlier. - */ - static define(specs: readonly TagStyle[], options?: { - /** - By default, highlighters apply to the entire document. You can - scope them to a single language by providing the language's - [top node](https://codemirror.net/6/docs/ref/#language.Language.topNode) here. - */ - scope?: NodeType; - /** - Add a style to _all_ content. Probably only useful in - combination with `scope`. - */ - all?: string | StyleSpec; - /** - Specify that this highlight style should only be active then - the theme is dark or light. By default, it is active - regardless of theme. - */ - themeType?: "dark" | "light"; - }): HighlightStyle; - /** - Returns the CSS classes (if any) that the highlight styles - active in the given state would assign to the given a style - [tag](https://codemirror.net/6/docs/ref/#highlight.Tag) and (optional) language - [scope](https://codemirror.net/6/docs/ref/#highlight.HighlightStyle^define^options.scope). - */ - static get(state: EditorState, tag: Tag, scope?: NodeType): string | null; -} -/** -The type of object used in -[`HighlightStyle.define`](https://codemirror.net/6/docs/ref/#highlight.HighlightStyle^define). -Assigns a style to one or more highlighting -[tags](https://codemirror.net/6/docs/ref/#highlight.Tag), which can either be a fixed class name -(which must be defined elsewhere), or a set of CSS properties, for -which the library will define an anonymous class. -*/ -interface TagStyle { - /** - The tag or tags to target. - */ - tag: Tag | readonly Tag[]; - /** - If given, this maps the tags to a fixed class name. - */ - class?: string; - /** - Any further properties (if `class` isn't given) will be - interpreted as in style objects given to - [style-mod](https://github.com/marijnh/style-mod#documentation). - The type here is `any` because of TypeScript limitations. - */ - [styleProperty: string]: any; -} -/** -The default set of highlighting [tags](https://codemirror.net/6/docs/ref/#highlight.Tag^define) used -by regular language packages and themes. - -This collection is heavily biased towards programming languages, -and necessarily incomplete. A full ontology of syntactic -constructs would fill a stack of books, and be impractical to -write themes for. So try to make do with this set. If all else -fails, [open an -issue](https://github.com/codemirror/codemirror.next) to propose a -new tag, or [define](https://codemirror.net/6/docs/ref/#highlight.Tag^define) a local custom tag for -your use case. - -Note that it is not obligatory to always attach the most specific -tag possible to an element—if your grammar can't easily -distinguish a certain type of element (such as a local variable), -it is okay to style it as its more general variant (a variable). - -For tags that extend some parent tag, the documentation links to -the parent. -*/ -declare const tags: { - /** - A comment. - */ - comment: Tag; - /** - A line [comment](https://codemirror.net/6/docs/ref/#highlight.tags.comment). - */ - lineComment: Tag; - /** - A block [comment](https://codemirror.net/6/docs/ref/#highlight.tags.comment). - */ - blockComment: Tag; - /** - A documentation [comment](https://codemirror.net/6/docs/ref/#highlight.tags.comment). - */ - docComment: Tag; - /** - Any kind of identifier. - */ - name: Tag; - /** - The [name](https://codemirror.net/6/docs/ref/#highlight.tags.name) of a variable. - */ - variableName: Tag; - /** - A type [name](https://codemirror.net/6/docs/ref/#highlight.tags.name). - */ - typeName: Tag; - /** - A tag name (subtag of [`typeName`](https://codemirror.net/6/docs/ref/#highlight.tags.typeName)). - */ - tagName: Tag; - /** - A property or field [name](https://codemirror.net/6/docs/ref/#highlight.tags.name). - */ - propertyName: Tag; - /** - An attribute name (subtag of [`propertyName`](https://codemirror.net/6/docs/ref/#highlight.tags.propertyName)). - */ - attributeName: Tag; - /** - The [name](https://codemirror.net/6/docs/ref/#highlight.tags.name) of a class. - */ - className: Tag; - /** - A label [name](https://codemirror.net/6/docs/ref/#highlight.tags.name). - */ - labelName: Tag; - /** - A namespace [name](https://codemirror.net/6/docs/ref/#highlight.tags.name). - */ - namespace: Tag; - /** - The [name](https://codemirror.net/6/docs/ref/#highlight.tags.name) of a macro. - */ - macroName: Tag; - /** - A literal value. - */ - literal: Tag; - /** - A string [literal](https://codemirror.net/6/docs/ref/#highlight.tags.literal). - */ - string: Tag; - /** - A documentation [string](https://codemirror.net/6/docs/ref/#highlight.tags.string). - */ - docString: Tag; - /** - A character literal (subtag of [string](https://codemirror.net/6/docs/ref/#highlight.tags.string)). - */ - character: Tag; - /** - An attribute value (subtag of [string](https://codemirror.net/6/docs/ref/#highlight.tags.string)). - */ - attributeValue: Tag; - /** - A number [literal](https://codemirror.net/6/docs/ref/#highlight.tags.literal). - */ - number: Tag; - /** - An integer [number](https://codemirror.net/6/docs/ref/#highlight.tags.number) literal. - */ - integer: Tag; - /** - A floating-point [number](https://codemirror.net/6/docs/ref/#highlight.tags.number) literal. - */ - float: Tag; - /** - A boolean [literal](https://codemirror.net/6/docs/ref/#highlight.tags.literal). - */ - bool: Tag; - /** - Regular expression [literal](https://codemirror.net/6/docs/ref/#highlight.tags.literal). - */ - regexp: Tag; - /** - An escape [literal](https://codemirror.net/6/docs/ref/#highlight.tags.literal), for example a - backslash escape in a string. - */ - escape: Tag; - /** - A color [literal](https://codemirror.net/6/docs/ref/#highlight.tags.literal). - */ - color: Tag; - /** - A URL [literal](https://codemirror.net/6/docs/ref/#highlight.tags.literal). - */ - url: Tag; - /** - A language keyword. - */ - keyword: Tag; - /** - The [keyword](https://codemirror.net/6/docs/ref/#highlight.tags.keyword) for the self or this - object. - */ - self: Tag; - /** - The [keyword](https://codemirror.net/6/docs/ref/#highlight.tags.keyword) for null. - */ - null: Tag; - /** - A [keyword](https://codemirror.net/6/docs/ref/#highlight.tags.keyword) denoting some atomic value. - */ - atom: Tag; - /** - A [keyword](https://codemirror.net/6/docs/ref/#highlight.tags.keyword) that represents a unit. - */ - unit: Tag; - /** - A modifier [keyword](https://codemirror.net/6/docs/ref/#highlight.tags.keyword). - */ - modifier: Tag; - /** - A [keyword](https://codemirror.net/6/docs/ref/#highlight.tags.keyword) that acts as an operator. - */ - operatorKeyword: Tag; - /** - A control-flow related [keyword](https://codemirror.net/6/docs/ref/#highlight.tags.keyword). - */ - controlKeyword: Tag; - /** - A [keyword](https://codemirror.net/6/docs/ref/#highlight.tags.keyword) that defines something. - */ - definitionKeyword: Tag; - /** - A [keyword](https://codemirror.net/6/docs/ref/#highlight.tags.keyword) related to defining or - interfacing with modules. - */ - moduleKeyword: Tag; - /** - An operator. - */ - operator: Tag; - /** - An [operator](https://codemirror.net/6/docs/ref/#highlight.tags.operator) that defines something. - */ - derefOperator: Tag; - /** - Arithmetic-related [operator](https://codemirror.net/6/docs/ref/#highlight.tags.operator). - */ - arithmeticOperator: Tag; - /** - Logical [operator](https://codemirror.net/6/docs/ref/#highlight.tags.operator). - */ - logicOperator: Tag; - /** - Bit [operator](https://codemirror.net/6/docs/ref/#highlight.tags.operator). - */ - bitwiseOperator: Tag; - /** - Comparison [operator](https://codemirror.net/6/docs/ref/#highlight.tags.operator). - */ - compareOperator: Tag; - /** - [Operator](https://codemirror.net/6/docs/ref/#highlight.tags.operator) that updates its operand. - */ - updateOperator: Tag; - /** - [Operator](https://codemirror.net/6/docs/ref/#highlight.tags.operator) that defines something. - */ - definitionOperator: Tag; - /** - Type-related [operator](https://codemirror.net/6/docs/ref/#highlight.tags.operator). - */ - typeOperator: Tag; - /** - Control-flow [operator](https://codemirror.net/6/docs/ref/#highlight.tags.operator). - */ - controlOperator: Tag; - /** - Program or markup punctuation. - */ - punctuation: Tag; - /** - [Punctuation](https://codemirror.net/6/docs/ref/#highlight.tags.punctuation) that separates - things. - */ - separator: Tag; - /** - Bracket-style [punctuation](https://codemirror.net/6/docs/ref/#highlight.tags.punctuation). - */ - bracket: Tag; - /** - Angle [brackets](https://codemirror.net/6/docs/ref/#highlight.tags.bracket) (usually `<` and `>` - tokens). - */ - angleBracket: Tag; - /** - Square [brackets](https://codemirror.net/6/docs/ref/#highlight.tags.bracket) (usually `[` and `]` - tokens). - */ - squareBracket: Tag; - /** - Parentheses (usually `(` and `)` tokens). Subtag of - [bracket](https://codemirror.net/6/docs/ref/#highlight.tags.bracket). - */ - paren: Tag; - /** - Braces (usually `{` and `}` tokens). Subtag of - [bracket](https://codemirror.net/6/docs/ref/#highlight.tags.bracket). - */ - brace: Tag; - /** - Content, for example plain text in XML or markup documents. - */ - content: Tag; - /** - [Content](https://codemirror.net/6/docs/ref/#highlight.tags.content) that represents a heading. - */ - heading: Tag; - /** - A level 1 [heading](https://codemirror.net/6/docs/ref/#highlight.tags.heading). - */ - heading1: Tag; - /** - A level 2 [heading](https://codemirror.net/6/docs/ref/#highlight.tags.heading). - */ - heading2: Tag; - /** - A level 3 [heading](https://codemirror.net/6/docs/ref/#highlight.tags.heading). - */ - heading3: Tag; - /** - A level 4 [heading](https://codemirror.net/6/docs/ref/#highlight.tags.heading). - */ - heading4: Tag; - /** - A level 5 [heading](https://codemirror.net/6/docs/ref/#highlight.tags.heading). - */ - heading5: Tag; - /** - A level 6 [heading](https://codemirror.net/6/docs/ref/#highlight.tags.heading). - */ - heading6: Tag; - /** - A prose separator (such as a horizontal rule). - */ - contentSeparator: Tag; - /** - [Content](https://codemirror.net/6/docs/ref/#highlight.tags.content) that represents a list. - */ - list: Tag; - /** - [Content](https://codemirror.net/6/docs/ref/#highlight.tags.content) that represents a quote. - */ - quote: Tag; - /** - [Content](https://codemirror.net/6/docs/ref/#highlight.tags.content) that is emphasized. - */ - emphasis: Tag; - /** - [Content](https://codemirror.net/6/docs/ref/#highlight.tags.content) that is styled strong. - */ - strong: Tag; - /** - [Content](https://codemirror.net/6/docs/ref/#highlight.tags.content) that is part of a link. - */ - link: Tag; - /** - [Content](https://codemirror.net/6/docs/ref/#highlight.tags.content) that is styled as code or - monospace. - */ - monospace: Tag; - /** - [Content](https://codemirror.net/6/docs/ref/#highlight.tags.content) that has a strike-through - style. - */ - strikethrough: Tag; - /** - Inserted text in a change-tracking format. - */ - inserted: Tag; - /** - Deleted text. - */ - deleted: Tag; - /** - Changed text. - */ - changed: Tag; - /** - An invalid or unsyntactic element. - */ - invalid: Tag; - /** - Metadata or meta-instruction. - */ - meta: Tag; - /** - [Metadata](https://codemirror.net/6/docs/ref/#highlight.tags.meta) that applies to the entire - document. - */ - documentMeta: Tag; - /** - [Metadata](https://codemirror.net/6/docs/ref/#highlight.tags.meta) that annotates or adds - attributes to a given syntactic element. - */ - annotation: Tag; - /** - Processing instruction or preprocessor directive. Subtag of - [meta](https://codemirror.net/6/docs/ref/#highlight.tags.meta). - */ - processingInstruction: Tag; - /** - [Modifier](https://codemirror.net/6/docs/ref/#highlight.Tag^defineModifier) that indicates that a - given element is being defined. Expected to be used with the - various [name](https://codemirror.net/6/docs/ref/#highlight.tags.name) tags. - */ - definition: (tag: Tag) => Tag; - /** - [Modifier](https://codemirror.net/6/docs/ref/#highlight.Tag^defineModifier) that indicates that - something is constant. Mostly expected to be used with - [variable names](https://codemirror.net/6/docs/ref/#highlight.tags.variableName). - */ - constant: (tag: Tag) => Tag; - /** - [Modifier](https://codemirror.net/6/docs/ref/#highlight.Tag^defineModifier) used to indicate that - a [variable](https://codemirror.net/6/docs/ref/#highlight.tags.variableName) or [property - name](https://codemirror.net/6/docs/ref/#highlight.tags.propertyName) is being called or defined - as a function. - */ - function: (tag: Tag) => Tag; - /** - [Modifier](https://codemirror.net/6/docs/ref/#highlight.Tag^defineModifier) that can be applied to - [names](https://codemirror.net/6/docs/ref/#highlight.tags.name) to indicate that they belong to - the language's standard environment. - */ - standard: (tag: Tag) => Tag; - /** - [Modifier](https://codemirror.net/6/docs/ref/#highlight.Tag^defineModifier) that indicates a given - [names](https://codemirror.net/6/docs/ref/#highlight.tags.name) is local to some scope. - */ - local: (tag: Tag) => Tag; - /** - A generic variant [modifier](https://codemirror.net/6/docs/ref/#highlight.Tag^defineModifier) that - can be used to tag language-specific alternative variants of - some common tag. It is recommended for themes to define special - forms of at least the [string](https://codemirror.net/6/docs/ref/#highlight.tags.string) and - [variable name](https://codemirror.net/6/docs/ref/#highlight.tags.variableName) tags, since those - come up a lot. - */ - special: (tag: Tag) => Tag; -}; -/** -A default highlight style (works well with light themes). -*/ -declare const defaultHighlightStyle: HighlightStyle; - -declare class Stack { - pos: number; - get context(): any; - canShift(term: number): boolean; - get parser(): LRParser; - dialectEnabled(dialectID: number): boolean; - private shiftContext; - private reduceContext; - private updateContext; +declare class Stack { + pos: number; + get context(): any; + canShift(term: number): boolean; + get parser(): LRParser; + dialectEnabled(dialectID: number): boolean; + private shiftContext; + private reduceContext; + private updateContext; } declare class InputStream { @@ -2109,247 +1815,22 @@ declare class LRParser extends Parser { to: number; }[]): PartialParse; configure(config: ParserConfig): LRParser; + hasWrappers(): boolean; getName(term: number): string; get topNode(): NodeType; } -/** -Each range is associated with a value, which must inherit from -this class. -*/ -declare abstract class RangeValue { - /** - Compare this value with another value. The default - implementation compares by identity. - */ - 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; - /** - Whether this value marks a point range, which is treated as - atomic and shadows the ranges contained in it. - */ - point: boolean; - /** - Create a [range](https://codemirror.net/6/docs/ref/#rangeset.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; -} -/** -Collection of methods used when comparing range sets. -*/ -interface RangeComparator { - /** - Notifies the comparator that the given range has the given set - of values associated with it. - */ - compareRange(from: number, to: number, activeA: T[], activeB: T[]): void; - /** - Notification for a point range. - */ - comparePoint(from: number, to: number, byA: T | null, byB: T | null): 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): void; - /** - When provided, this will be called for each point processed, - causing the ones for which it returns false to be ignored. - */ - filterPoint?(from: number, to: number, value: T, index: number): boolean; -} -/** -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; +declare class StyleModule { + constructor(spec: {[selector: string]: StyleSpec}, options?: { + finish?(sel: string): string + }) + getRules(): string + static mount(root: Document | ShadowRoot | DocumentOrShadowRoot, module: StyleModule | ReadonlyArray): void + static newName(): string } -declare 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/#rangeset.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/#rangeset.Range) in a -way that makes them efficient to [map](https://codemirror.net/6/docs/ref/#rangeset.RangeSet.map) and -[update](https://codemirror.net/6/docs/ref/#rangeset.RangeSet.update). This is an immutable data -structure. -*/ -declare class RangeSet { - /** - The number of ranges in the set. - */ - get size(): number; - /** - Update the range set, optionally adding new ranges or filtering - out existing ones. - - (The extra 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. `from` and - `to` are coordinates in the _new_ space, after these changes. - */ - 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/#rangeset.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; - /** - The empty set of ranges. - */ - static empty: RangeSet; + +type StyleSpec = { + [propOrSelector: string]: string | number | StyleSpec | null } declare type Attrs = { @@ -2385,9 +1866,10 @@ interface MarkDecorationSpec { class?: string; /** Add a wrapping element around the text in the marked range. Note - that there will not be a single element covering the entire - range—content is split on mark starts and ends, and each piece - gets its own element. + 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; /** @@ -2478,7 +1960,7 @@ interface LineDecorationSpec { 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 when the decorations +needed, and to avoid redrawing widgets even if the decorations that define them are recreated. */ declare abstract class WidgetType { @@ -2495,7 +1977,7 @@ declare abstract class WidgetType { returns `false`, which will cause new instances of the widget to always be redrawn. */ - eq(_widget: WidgetType): boolean; + 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 @@ -2503,7 +1985,7 @@ declare abstract class WidgetType { couldn't (in which case the widget will be redrawn). The default implementation just returns false. */ - updateDOM(_dom: HTMLElement): boolean; + updateDOM(dom: HTMLElement): boolean; /** The estimated height this widget will have, to be used when estimating the height of content that hasn't been drawn. May @@ -2516,17 +1998,17 @@ declare abstract class WidgetType { should be ignored by the editor. The default is to ignore all events. */ - ignoreEvent(_event: Event): boolean; + ignoreEvent(event: Event): boolean; /** This is called when the an instance of the widget is removed from the editor view. */ - destroy(_dom: HTMLElement): void; + 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/#rangeset.RangeSet) for its methods. +[`RangeSet`](https://codemirror.net/6/docs/ref/#state.RangeSet) for its methods. */ declare type DecorationSet = RangeSet; /** @@ -2553,7 +2035,8 @@ declare enum 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/#rangeset.Range), which adds a start and end position. +[`Range`](https://codemirror.net/6/docs/ref/#state.Range), which adds a start and end position. +@nonabstract */ declare abstract class Decoration extends RangeValue { /** @@ -2562,21 +2045,39 @@ declare abstract class Decoration extends RangeValue { 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) or - (below the facet-provided decorations) [view - plugin](https://codemirror.net/6/docs/ref/#view.PluginSpec.decorations). Such elements are split - on line boundaries and on the boundaries of higher-precedence - decorations. + 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 adds an element at the given - position. + Create a widget decoration, which displays a DOM element at the + given position. */ static widget(spec: WidgetDecorationSpec): Decoration; /** @@ -2623,7 +2124,7 @@ declare type Command = (target: EditorView) => boolean; /** This is the interface plugin objects conform to. */ -interface PluginValue { +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 @@ -2634,75 +2135,13 @@ interface PluginValue { [`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; + update?(update: ViewUpdate): void; /** Called when the plugin is no longer going to be used. Should revert any changes the plugin made to the DOM. */ destroy?(): void; } -declare const isFieldProvider: unique symbol; -/** -Used to [declare](https://codemirror.net/6/docs/ref/#view.PluginSpec.provide) which -[fields](https://codemirror.net/6/docs/ref/#view.PluginValue) a [view plugin](https://codemirror.net/6/docs/ref/#view.ViewPlugin) -provides. -*/ -declare class PluginFieldProvider { - private [isFieldProvider]; -} -/** -Plugin fields are a mechanism for allowing plugins to provide -values that can be retrieved through the -[`pluginField`](https://codemirror.net/6/docs/ref/#view.EditorView.pluginField) view method. -*/ -declare class PluginField { - /** - Create a [provider](https://codemirror.net/6/docs/ref/#view.PluginFieldProvider) for this field, - to use with a plugin's [provide](https://codemirror.net/6/docs/ref/#view.PluginSpec.provide) - option. - */ - from(get: (value: V) => T): PluginFieldProvider; - /** - Define a new plugin field. - */ - static define(): PluginField; - /** - This field can be used by plugins to provide - [decorations](https://codemirror.net/6/docs/ref/#view.Decoration). - - **Note**: For reasons of data flow (plugins are only updated - after the viewport is computed), decorations produced by plugins - are _not_ taken into account when predicting the vertical layout - structure of the editor. They **must not** introduce block - widgets (that will raise an error) or replacing decorations that - cover line breaks (these will be ignored if they occur). Such - decorations, or others that cause a large amount of vertical - size shift compared to the undecorated content, should be - provided through the state-level [`decorations` - facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations) instead. - */ - static decorations: PluginField; - /** - 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: PluginField>; - /** - Plugins can provide additional scroll margins (space around the - sides of the scrolling element that should be considered - invisible) through this field. This can be useful when the - plugin introduces elements that cover part of that element (for - example a horizontally fixed gutter). - */ - static scrollMargins: PluginField | null>; -} /** Provides additional information when defining a [view plugin](https://codemirror.net/6/docs/ref/#view.ViewPlugin). @@ -2716,20 +2155,18 @@ interface PluginSpec { */ eventHandlers?: DOMEventHandlers; /** - Allow the plugin to provide decorations. When given, this should - 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.PluginField^decorations) - from plugins. + Specify that the plugin provides additional extensions when + added to an editor configuration. */ - decorations?: (value: V) => DecorationSet; + provide?: (plugin: ViewPlugin) => Extension; /** - Specify that the plugin provides [plugin - field](https://codemirror.net/6/docs/ref/#view.PluginField) values. Use a field's - [`from`](https://codemirror.net/6/docs/ref/#view.PluginField.from) method to create these - providers. + 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. */ - provide?: PluginFieldProvider | readonly PluginFieldProvider[]; + decorations?: (value: V) => DecorationSet; } /** View plugins associate stateful values with a view. They can @@ -2746,12 +2183,12 @@ declare class ViewPlugin { Define a plugin from a constructor function that creates the plugin's value, given an editor view. */ - static define(create: (view: EditorView) => V, spec?: PluginSpec): ViewPlugin; + static define(create: (view: EditorView) => V, spec?: PluginSpec): ViewPlugin; /** Create a plugin for a class whose constructor takes a single editor view as argument. */ - static fromClass(cls: { + static fromClass(cls: { new (view: EditorView): V; }, spec?: PluginSpec): ViewPlugin; } @@ -2798,6 +2235,7 @@ declare class ViewUpdate { 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 @@ -2805,8 +2243,8 @@ declare class ViewUpdate { */ get viewportChanged(): boolean; /** - Indicates whether the height of an element in the editor changed - in this update. + Indicates whether the height of a block element in the editor + changed in this update. */ get heightChanged(): boolean; /** @@ -2862,6 +2300,43 @@ interface MouseSelectionStyle { } declare 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. + */ + readonly 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; +} + /** Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection). */ @@ -2902,43 +2377,6 @@ declare class BidiSpan { get dir(): Direction; } -/** -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. - */ - readonly 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; -} - interface EditorConfig { /** The view's initial state. Defaults to an extension-less state @@ -2946,6 +2384,12 @@ interface EditorConfig { */ 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 @@ -2961,12 +2405,6 @@ interface EditorConfig { method](https://codemirror.net/6/docs/ref/#view.EditorView.update). */ dispatch?: (tr: Transaction) => void; - /** - 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; } /** An editor view represents the editor's user interface. It holds @@ -3052,9 +2490,9 @@ declare class EditorView { private bidiCache; private destroyed; /** - Construct a new view. You'll usually want to put `view.dom` into - your document after creating a view, so that the user can see - it. + 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( /** @@ -3106,12 +2544,7 @@ declare class EditorView { drawing done by other components is synchronized, avoiding unnecessary DOM layout computations. */ - requestMeasure(request?: MeasureRequest): void; - /** - Collect all values provided by the active plugins for a given - field. - */ - pluginField(field: PluginField): readonly T[]; + 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 @@ -3133,55 +2566,18 @@ declare class EditorView { bottom: number; }; /** - Find the line or block widget at the given vertical position. - - By default, this position is interpreted as a screen position, - meaning `docTop` is set to the DOM top position of the editor - content (forcing a layout). You can pass a different `docTop` - value—for example 0 to interpret `height` as a document-relative - position, or a precomputed document top - (`view.contentDOM.getBoundingClientRect().top`) to limit layout - queries. - - *Deprecated: use `elementAtHeight` instead.* - */ - blockAtHeight(height: number, docTop?: number): BlockInfo; - /** 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 information for the visual line (see - [`visualLineAt`](https://codemirror.net/6/docs/ref/#view.EditorView.visualLineAt)) at the given - vertical position. The resulting block info might hold another - array of block info structs in its `type` field if this line - consists of more than one block. - - Defaults to treating `height` as a screen position. See - [`blockAtHeight`](https://codemirror.net/6/docs/ref/#view.EditorView.blockAtHeight) for the - interpretation of the `docTop` parameter. - - *Deprecated: use `lineBlockAtHeight` instead.* - */ - visualLineAtHeight(height: number, docTop?: number): BlockInfo; - /** Find the line block (see [`lineBlockAt`](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) at the given height. */ lineBlockAtHeight(height: number): BlockInfo; /** - Iterate over the height information of the visual lines in the - viewport. The heights of lines are reported relative to the - given document top, which defaults to the screen position of the - document (forcing a layout). - - *Deprecated: use `viewportLineBlocks` instead.* - */ - viewportLines(f: (line: BlockInfo) => void, docTop?: number): void; - /** 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 @@ -3189,22 +2585,9 @@ declare class EditorView { */ get viewportLineBlocks(): BlockInfo[]; /** - Find the extent and height of the visual line (a range delimited - on both sides by either non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^range) - line breaks, or the start/end of the document) at the given position. - - Vertical positions are computed relative to the `docTop` - argument, which defaults to 0 for this method. You can pass - `view.contentDOM.getBoundingClientRect().top` here to get screen - coordinates. - - *Deprecated: use `lineBlockAt` instead.* - */ - visualLineAt(pos: number, docTop?: number): 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^range) line breaks, or the + non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^replace) line breaks, 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. @@ -3216,13 +2599,13 @@ declare class EditorView { get contentHeight(): number; /** Move a cursor position by [grapheme - cluster](https://codemirror.net/6/docs/ref/#text.findClusterBreak). `forward` determines whether - the motion is away from the line start, or towards it. Motion in - bidirectional text is in visual order, in 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. + 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 @@ -3259,7 +2642,6 @@ declare class EditorView { used. */ moveVertically(start: SelectionRange, forward: boolean, distance?: number): SelectionRange; - scrollPosIntoView(pos: number): void; /** Find the DOM parent node and offset (child offset if `node` is an element, character offset when it is a text node) at the @@ -3317,10 +2699,20 @@ declare class EditorView { /** The text direction ([`direction`](https://developer.mozilla.org/en-US/docs/Web/CSS/direction) - CSS property) of the editor. + 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) @@ -3352,20 +2744,6 @@ declare class EditorView { */ destroy(): void; /** - Effect that can be [added](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) to a - transaction to make it scroll the given range into view. - - *Deprecated*. Use [`scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) instead. - */ - static scrollTo: StateEffectType; - /** - Effect that makes the editor scroll the given range to the - center of the visible view. - - *Deprecated*. Use [`scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) instead. - */ - static centerOn: StateEffectType; - /** 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. @@ -3405,16 +2783,16 @@ declare class EditorView { */ static styleModule: Facet; /** - Facet that can be used to add DOM event handlers. The value - should be an object mapping event names to handler functions. The - first such function 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. + 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; /** @@ -3426,6 +2804,13 @@ declare class EditorView { */ static inputHandler: Facet<(view: EditorView, from: number, to: number, text: string) => boolean, readonly ((view: EditorView, from: number, to: number, text: string) => boolean)[]>; /** + 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 @@ -3441,9 +2826,9 @@ declare class EditorView { /** Facet that controls whether the editor content DOM is editable. When its highest-precedence value is `false`, the element will - not longer 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 + 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; @@ -3462,17 +2847,44 @@ declare class EditorView { */ 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. + 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. See also [view - plugins](https://codemirror.net/6/docs/ref/#view.EditorView^decorations), which have a separate - mechanism for providing decorations. + 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. + */ + static decorations: 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) => RangeSet, readonly ((view: EditorView) => RangeSet)[]>; + /** + 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 decorations: Facet; + 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) @@ -3574,7 +2986,7 @@ Modifiers can be given in any order. `Shift-` (or `s-`), `Alt-` (or 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 order. +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 @@ -3618,8 +3030,8 @@ interface KeyBinding { 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 set multiple scope names, - separated by spaces. + a custom scope name. You may also assign multiple scope names to + a binding, separating them by spaces. */ scope?: string; /** @@ -3719,29 +3131,154 @@ to show when the editor is empty. */ declare function placeholder(content: string | HTMLElement): Extension; +/** +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; +declare type Handlers$1 = { + [event: string]: (view: EditorView, line: BlockInfo, event: Event) => boolean; +}; +interface LineNumberConfig { + /** + How to display line numbers. Defaults to simply converting them + to string. + */ + formatNumber?: (lineNo: number, state: EditorState) => string; + /** + Supply event handlers for DOM events on this gutter. + */ + domEventHandlers?: Handlers$1; +} +/** +Create a line number gutter extension. +*/ +declare function lineNumbers(config?: LineNumberConfig): Extension; + +declare class Tag { + readonly set: Tag[]; + static define(parent?: Tag): Tag; + static defineModifier(): (tag: Tag) => Tag; +} +interface Highlighter { + style(tags: readonly Tag[]): string | null; + scope?(node: NodeType): boolean; +} +declare const tags: { + comment: Tag; + lineComment: Tag; + blockComment: Tag; + docComment: Tag; + name: Tag; + variableName: Tag; + typeName: Tag; + tagName: Tag; + propertyName: Tag; + attributeName: Tag; + className: Tag; + labelName: Tag; + namespace: Tag; + macroName: Tag; + literal: Tag; + string: Tag; + docString: Tag; + character: Tag; + attributeValue: Tag; + number: Tag; + integer: Tag; + float: Tag; + bool: Tag; + regexp: Tag; + escape: Tag; + color: Tag; + url: Tag; + keyword: Tag; + self: Tag; + null: Tag; + atom: Tag; + unit: Tag; + modifier: Tag; + operatorKeyword: Tag; + controlKeyword: Tag; + definitionKeyword: Tag; + moduleKeyword: Tag; + operator: Tag; + derefOperator: Tag; + arithmeticOperator: Tag; + logicOperator: Tag; + bitwiseOperator: Tag; + compareOperator: Tag; + updateOperator: Tag; + definitionOperator: Tag; + typeOperator: Tag; + controlOperator: Tag; + punctuation: Tag; + separator: Tag; + bracket: Tag; + angleBracket: Tag; + squareBracket: Tag; + paren: Tag; + brace: Tag; + content: Tag; + heading: Tag; + heading1: Tag; + heading2: Tag; + heading3: Tag; + heading4: Tag; + heading5: Tag; + heading6: Tag; + contentSeparator: Tag; + list: Tag; + quote: Tag; + emphasis: Tag; + strong: Tag; + link: Tag; + monospace: Tag; + strikethrough: Tag; + inserted: Tag; + deleted: Tag; + changed: Tag; + invalid: Tag; + meta: Tag; + documentMeta: Tag; + annotation: Tag; + processingInstruction: Tag; + definition: (tag: Tag) => Tag; + constant: (tag: Tag) => Tag; + function: (tag: Tag) => Tag; + standard: (tag: Tag) => Tag; + local: (tag: Tag) => Tag; + special: (tag: Tag) => Tag; +}; + /** A language object manages parsing and per-language [metadata](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt). Parse data is -managed as a [Lezer](https://lezer.codemirror.net) tree. You'll -want to subclass this class for custom parsers, or use the -[`LRLanguage`](https://codemirror.net/6/docs/ref/#language.LRLanguage) or -[`StreamLanguage`](https://codemirror.net/6/docs/ref/#stream-parser.StreamLanguage) abstractions for -[Lezer](https://lezer.codemirror.net/) or stream parsers. +managed as a [Lezer](https://lezer.codemirror.net) tree. The class +can be used directly, via the [`LRLanguage`](https://codemirror.net/6/docs/ref/#language.LRLanguage) +subclass for [Lezer](https://lezer.codemirror.net/) LR parsers, or +via the [`StreamLanguage`](https://codemirror.net/6/docs/ref/#language.StreamLanguage) subclass +for stream parsers. */ declare class Language { /** - The [language data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) data - facet used for this language. + The [language data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) facet + used for this language. */ readonly data: Facet<{ [name: string]: any; }>; /** - The node type of the top node of trees produced by this parser. - */ - readonly topNode: NodeType; - /** - The extension value to install this provider. + The extension value to install this as the document language. */ readonly extension: Extension; /** @@ -3750,23 +3287,20 @@ declare class Language { */ parser: Parser; /** - Construct a language object. You usually don't need to invoke - this directly. But when you do, make sure you use - [`defineLanguageFacet`](https://codemirror.net/6/docs/ref/#language.defineLanguageFacet) to create - the first argument. + Construct a language object. If you need to invoke this + directly, first define a data facet with + [`defineLanguageFacet`](https://codemirror.net/6/docs/ref/#language.defineLanguageFacet), and then + configure your parser to [attach](https://codemirror.net/6/docs/ref/#language.languageDataProp) it + to the language's outer syntax node. */ constructor( /** - The [language data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) data - facet used for this language. + The [language data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) facet + used for this language. */ data: Facet<{ [name: string]: any; - }>, parser: Parser, - /** - The node type of the top node of trees produced by this parser. - */ - topNode: NodeType, extraExtensions?: Extension[]); + }>, parser: Parser, extraExtensions?: Extension[]); /** Query whether this language is active at the given position. */ @@ -3821,8 +3355,9 @@ declare class LRLanguage extends Language { } /** Get the syntax tree for a state, which is the current (possibly -incomplete) parse tree of active [language](https://codemirror.net/6/docs/ref/#language.Language), -or the empty tree if there is no language available. +incomplete) parse tree of the active +[language](https://codemirror.net/6/docs/ref/#language.Language), or the empty tree if there is no +language available. */ declare function syntaxTree(state: EditorState): Tree; /** @@ -3830,13 +3365,13 @@ Queries whether there is a full syntax tree available up to the given document position. If there isn't, the background parse process _might_ still be working and update the tree further, but there is no guarantee of that—the parser will [stop -working](https://codemirror.net/6/docs/ref/#language.syntaxParserStopped) when it has spent a +working](https://codemirror.net/6/docs/ref/#language.syntaxParserRunning) when it has spent a certain amount of time or has moved beyond the visible viewport. Always returns false if no language has been enabled. */ declare function syntaxTreeAvailable(state: EditorState, upto?: number): boolean; /** -This class bundles a [language object](https://codemirror.net/6/docs/ref/#language.Language) with an +This class bundles a [language](https://codemirror.net/6/docs/ref/#language.Language) with an optional set of supporting extensions. Language packages are encouraged to export a function that optionally takes a configuration object and returns a `LanguageSupport` instance, as @@ -3861,7 +3396,7 @@ declare class LanguageSupport { */ extension: Extension; /** - Create a support object. + Create a language support object. */ constructor( /** @@ -3968,93 +3503,6 @@ entirely of tabs. When not set, this defaults to 2 spaces. */ declare const indentUnit: Facet; /** -Indentation contexts are used when calling [indentation -services](https://codemirror.net/6/docs/ref/#language.indentService). They provide helper utilities -useful in indentation logic, and can selectively override the -indentation reported for some lines. -*/ -declare class IndentContext { - /** - The editor state. - */ - readonly state: EditorState; - /** - The indent unit (number of columns per indentation level). - */ - unit: number; - /** - Create an indent context. - */ - constructor( - /** - The editor state. - */ - state: EditorState, - /** - @internal - */ - options?: { - /** - Override line indentations provided to the indentation - helper function, which is useful when implementing region - indentation, where indentation for later lines needs to refer - to previous lines, which may have been reindented compared to - the original start state. If given, this function should - return -1 for lines (given by start position) that didn't - change, and an updated indentation otherwise. - */ - overrideIndentation?: (pos: number) => number; - /** - Make it look, to the indent logic, like a line break was - added at the given position (which is mostly just useful for - implementing something like - [`insertNewlineAndIndent`](https://codemirror.net/6/docs/ref/#commands.insertNewlineAndIndent)). - */ - simulateBreak?: number; - /** - When `simulateBreak` is given, this can be used to make the - simulate break behave like a double line break. - */ - simulateDoubleBreak?: boolean; - }); - /** - Get a description of the line at the given position, taking - [simulated line - breaks](https://codemirror.net/6/docs/ref/#language.IndentContext.constructor^options.simulateBreak) - into account. If there is such a break at `pos`, the `bias` - argument determines whether the part of the line line before or - after the break is used. - */ - lineAt(pos: number, bias?: -1 | 1): { - text: string; - from: number; - }; - /** - Get the text directly after `pos`, either the entire line - or the next 100 characters, whichever is shorter. - */ - textAfterPos(pos: number, bias?: -1 | 1): string; - /** - Find the column for the given position. - */ - column(pos: number, bias?: -1 | 1): number; - /** - Find the column position (taking tabs into account) of the given - position in the given string. - */ - countColumn(line: string, pos?: number): number; - /** - Find the indentation column of the line at the given point. - */ - lineIndent(pos: number, bias?: -1 | 1): number; - /** - Returns the [simulated line - break](https://codemirror.net/6/docs/ref/#language.IndentContext.constructor^options.simulateBreak) - for this context, if any. - */ - get simulatedBreak(): number | null; -} -/** Enables reindentation on input. When a language defines an `indentOnInput` field in its [language data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt), which must hold a regular @@ -4068,196 +3516,209 @@ For example, `/^\s*\}$/` will reindent when a closing brace is added at the start of a line. */ declare function indentOnInput(): Extension; - /** -Encapsulates a single line of input. Given to stream syntax code, -which uses it to tokenize the content. +Default fold-related key bindings. + + - Ctrl-Shift-[ (Cmd-Alt-[ on macOS): [`foldCode`](https://codemirror.net/6/docs/ref/#language.foldCode). + - Ctrl-Shift-] (Cmd-Alt-] on macOS): [`unfoldCode`](https://codemirror.net/6/docs/ref/#language.unfoldCode). + - Ctrl-Alt-[: [`foldAll`](https://codemirror.net/6/docs/ref/#language.foldAll). + - Ctrl-Alt-]: [`unfoldAll`](https://codemirror.net/6/docs/ref/#language.unfoldAll). */ -declare class StringStream { - /** - The line. - */ - string: string; - private tabSize; - /** - The current indent unit size. - */ - indentUnit: number; - /** - The current position on the line. - */ - pos: number; - /** - The start position of the current token. - */ - start: number; - private lastColumnPos; - private lastColumnValue; - /** - True if we are at the end of the line. - */ - eol(): boolean; - /** - True if we are at the start of the line. - */ - sol(): boolean; - /** - Get the next code unit after the current position, or undefined - if we're at the end of the line. - */ - peek(): string | undefined; - /** - Read the next code unit and advance `this.pos`. - */ - next(): string | void; +declare const foldKeymap: readonly KeyBinding[]; +declare type Handlers = { + [event: string]: (view: EditorView, line: BlockInfo, event: Event) => boolean; +}; +interface FoldGutterConfig { /** - Match the next character against the given string, regular - expression, or predicate. Consume and return it if it matches. + A function that creates the DOM element used to indicate a + given line is folded or can be folded. + When not given, the `openText`/`closeText` option will be used instead. */ - eat(match: string | RegExp | ((ch: string) => boolean)): string | void; + markerDOM?: ((open: boolean) => HTMLElement) | null; /** - Continue matching characters that match the given string, - regular expression, or predicate function. Return true if any - characters were consumed. + Text used to indicate that a given line can be folded. + Defaults to `"⌄"`. */ - eatWhile(match: string | RegExp | ((ch: string) => boolean)): boolean; + openText?: string; /** - Consume whitespace ahead of `this.pos`. Return true if any was - found. + Text used to indicate that a given line is folded. + Defaults to `"›"`. */ - eatSpace(): boolean; + closedText?: string; /** - Move to the end of the line. + Supply event handlers for DOM events on this gutter. */ - skipToEnd(): void; + domEventHandlers?: Handlers; +} +/** +Create an extension that registers a fold gutter, which shows a +fold status indicator before foldable lines (which can be clicked +to fold or unfold the line). +*/ +declare function foldGutter(config?: FoldGutterConfig): Extension; + +/** +A highlight style associates CSS styles with higlighting +[tags](https://lezer.codemirror.net/docs/ref#highlight.Tag). +*/ +declare class HighlightStyle implements Highlighter { /** - Move to directly before the given character, if found on the - current line. + A style module holding the CSS rules for this highlight style. + When using + [`highlightTree`](https://lezer.codemirror.net/docs/ref#highlight.highlightTree) + outside of the editor, you may want to manually mount this + module to show the highlighting. */ - skipTo(ch: string): boolean | void; + readonly module: StyleModule | null; + readonly style: (tags: readonly Tag[]) => string | null; + readonly scope: ((type: NodeType) => boolean) | undefined; + private constructor(); /** - Move back `n` characters. + Create a highlighter style that associates the given styles to + the given tags. The specs must be objects that hold a style tag + or array of tags in their `tag` property, and either a single + `class` property providing a static CSS class (for highlighter + that rely on external styling), or a + [`style-mod`](https://github.com/marijnh/style-mod#documentation)-style + set of CSS properties (which define the styling for those tags). + + The CSS rules created for a highlighter will be emitted in the + order of the spec's properties. That means that for elements that + have multiple tags associated with them, styles defined further + down in the list will have a higher CSS precedence than styles + defined earlier. */ - backUp(n: number): void; + static define(specs: readonly TagStyle[], options?: { + /** + By default, highlighters apply to the entire document. You can + scope them to a single language by providing the language + object or a language's top node type here. + */ + scope?: Language | NodeType; + /** + Add a style to _all_ content. Probably only useful in + combination with `scope`. + */ + all?: string | StyleSpec; + /** + Specify that this highlight style should only be active then + the theme is dark or light. By default, it is active + regardless of theme. + */ + themeType?: "dark" | "light"; + }): HighlightStyle; +} +/** +Wrap a highlighter in an editor extension that uses it to apply +syntax highlighting to the editor content. + +When multiple (non-fallback) styles are provided, the styling +applied is the union of the classes they emit. +*/ +declare function syntaxHighlighting(highlighter: Highlighter, options?: { /** - Get the column position at `this.pos`. + When enabled, this marks the highlighter as a fallback, which + only takes effect if no other highlighters are registered. */ - column(): number; + fallback: boolean; +}): Extension; +/** +The type of object used in +[`HighlightStyle.define`](https://codemirror.net/6/docs/ref/#language.HighlightStyle^define). +Assigns a style to one or more highlighting +[tags](https://lezer.codemirror.net/docs/ref#highlight.Tag), which can either be a fixed class name +(which must be defined elsewhere), or a set of CSS properties, for +which the library will define an anonymous class. +*/ +interface TagStyle { /** - Get the indentation column of the current line. + The tag or tags to target. */ - indentation(): number; + tag: Tag | readonly Tag[]; /** - Match the input against the given string or regular expression - (which should start with a `^`). Return true or the regexp match - if it matches. - - Unless `consume` is set to `false`, this will move `this.pos` - past the matched text. - - When matching a string `caseInsensitive` can be set to true to - make the match case-insensitive. + If given, this maps the tags to a fixed class name. */ - match(pattern: string | RegExp, consume?: boolean, caseInsensitive?: boolean): boolean | RegExpMatchArray | null; + class?: string; /** - Get the current token. + Any further properties (if `class` isn't given) will be + interpreted as in style objects given to + [style-mod](https://github.com/marijnh/style-mod#documentation). + (The type here is `any` because of TypeScript limitations.) */ - current(): string; + [styleProperty: string]: any; } - /** -A stream parser parses or tokenizes content from start to end, -emitting tokens as it goes over it. It keeps a mutable (but -copyable) object with state, in which it can store information -about the current context. +A default highlight style (works well with light themes). */ -interface StreamParser { - /** - Read one token, advancing the stream past it, and returning a - string indicating the token's style tag—either the name of one - of the tags in [`tags`](https://codemirror.net/6/docs/ref/#highlight.tags), or such a name - suffixed by one or more tag - [modifier](https://codemirror.net/6/docs/ref/#highlight.Tag^defineModifier) names, separated by - spaces. For example `"keyword"` or "`variableName.constant"`. - - It is okay to return a zero-length token, but only if that - updates the state so that the next call will return a non-empty - token again. - */ - token(stream: StringStream, state: State): string | null; +declare const defaultHighlightStyle: HighlightStyle; + +interface Config { /** - This notifies the parser of a blank line in the input. It can - update its state here if it needs to. + Whether the bracket matching should look at the character after + the cursor when matching (if the one before isn't a bracket). + Defaults to true. */ - blankLine?(state: State, indentUnit: number): void; + afterCursor?: boolean; /** - Produce a start state for the parser. + The bracket characters to match, as a string of pairs. Defaults + to `"()[]{}"`. Note that these are only used as fallback when + there is no [matching + information](https://lezer.codemirror.net/docs/ref/#common.NodeProp^closedBy) + in the syntax tree. */ - startState?(indentUnit: number): State; + brackets?: string; /** - Copy a given state. By default, a shallow object copy is done - which also copies arrays held at the top level of the object. + The maximum distance to scan for matching brackets. This is only + relevant for brackets not encoded in the syntax tree. Defaults + to 10 000. */ - copyState?(state: State): State; + maxScanDistance?: number; /** - Compute automatic indentation for the line that starts with the - given state and text. + Can be used to configure the way in which brackets are + decorated. The default behavior is to add the + `cm-matchingBracket` class for matching pairs, and + `cm-nonmatchingBracket` for mismatched pairs or single brackets. */ - indent?(state: State, textAfter: string, context: IndentContext): number | null; + renderMatch?: (match: MatchResult, state: EditorState) => readonly Range[]; +} +/** +Create an extension that enables bracket matching. Whenever the +cursor is next to a bracket, that bracket and the one it matches +are highlighted. Or, when no matching bracket is found, another +highlighting style is used to indicate this. +*/ +declare function bracketMatching(config?: Config): Extension; +/** +The result returned from `matchBrackets`. +*/ +interface MatchResult { /** - Default [language data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) to - attach to this language. + The extent of the bracket token found. */ - languageData?: { - [name: string]: any; + start: { + from: number; + to: number; }; /** - Extra tokens to use in this parser. When the tokenizer returns a - token name that exists as a property in this object, the - corresponding tag will be assigned to the token. + The extent of the matched token, if any was found. */ - tokenTable?: { - [name: string]: Tag; + end?: { + from: number; + to: number; }; -} -/** -A [language](https://codemirror.net/6/docs/ref/#language.Language) class based on a streaming -parser. -*/ -declare class StreamLanguage extends Language { - private constructor(); - static define(spec: StreamParser): StreamLanguage; - private getIndent; - get allowsNesting(): boolean; + /** + Whether the tokens match. This can be false even when `end` has + a value, if that token doesn't match the opening token. + */ + matched: boolean; } -declare const julia$1: StreamParser - declare type JuliaLanguageConfig = { /** Enable keyword completion */ enableKeywordCompletion?: boolean; }; declare function julia(config?: JuliaLanguageConfig): LanguageSupport; -declare type Handlers$1 = { - [event: string]: (view: EditorView, line: BlockInfo, event: Event) => boolean; -}; -interface LineNumberConfig { - /** - How to display line numbers. Defaults to simply converting them - to string. - */ - formatNumber?: (lineNo: number, state: EditorState) => string; - /** - Supply event handlers for DOM events on this gutter. - */ - domEventHandlers?: Handlers$1; -} -/** -Create a line number gutter extension. -*/ -declare function lineNumbers(config?: LineNumberConfig): Extension; - interface HistoryConfig { /** The minimum depth (amount of events) to store. Defaults to 100. @@ -4276,13 +3737,12 @@ declare function history(config?: HistoryConfig): Extension; /** Default key bindings for the undo history. -- Mod-z: [`undo`](https://codemirror.net/6/docs/ref/#history.undo). -- Mod-y (Mod-Shift-z on macOS): [`redo`](https://codemirror.net/6/docs/ref/#history.redo). -- Mod-u: [`undoSelection`](https://codemirror.net/6/docs/ref/#history.undoSelection). -- Alt-u (Mod-Shift-u on macOS): [`redoSelection`](https://codemirror.net/6/docs/ref/#history.redoSelection). +- Mod-z: [`undo`](https://codemirror.net/6/docs/ref/#commands.undo). +- Mod-y (Mod-Shift-z on macOS): [`redo`](https://codemirror.net/6/docs/ref/#commands.redo). +- Mod-u: [`undoSelection`](https://codemirror.net/6/docs/ref/#commands.undoSelection). +- Alt-u (Mod-Shift-u on macOS): [`redoSelection`](https://codemirror.net/6/docs/ref/#commands.redoSelection). */ declare const historyKeymap: readonly KeyBinding[]; - /** Add a [unit](https://codemirror.net/6/docs/ref/#language.indentUnit) of indentation to all selected lines. @@ -4312,109 +3772,11 @@ The default keymap. Includes all bindings from - Ctrl-Alt-\\ (Cmd-Alt-\\ on macOS): [`indentSelection`](https://codemirror.net/6/docs/ref/#commands.indentSelection) - Shift-Ctrl-k (Shift-Cmd-k on macOS): [`deleteLine`](https://codemirror.net/6/docs/ref/#commands.deleteLine) - Shift-Ctrl-\\ (Shift-Cmd-\\ on macOS): [`cursorMatchingBracket`](https://codemirror.net/6/docs/ref/#commands.cursorMatchingBracket) +- Ctrl-/ (Cmd-/ on macOS): [`toggleComment`](https://codemirror.net/6/docs/ref/#commands.toggleComment). +- Shift-Alt-a: [`toggleBlockComment`](https://codemirror.net/6/docs/ref/#commands.toggleBlockComment). */ declare const defaultKeymap: readonly KeyBinding[]; -/** -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; - -/** -Default fold-related key bindings. - - - Ctrl-Shift-[ (Cmd-Alt-[ on macOS): [`foldCode`](https://codemirror.net/6/docs/ref/#fold.foldCode). - - Ctrl-Shift-] (Cmd-Alt-] on macOS): [`unfoldCode`](https://codemirror.net/6/docs/ref/#fold.unfoldCode). - - Ctrl-Alt-[: [`foldAll`](https://codemirror.net/6/docs/ref/#fold.foldAll). - - Ctrl-Alt-]: [`unfoldAll`](https://codemirror.net/6/docs/ref/#fold.unfoldAll). -*/ -declare const foldKeymap: readonly KeyBinding[]; -declare type Handlers = { - [event: string]: (view: EditorView, line: BlockInfo, event: Event) => boolean; -}; -interface FoldGutterConfig { - /** - A function that creates the DOM element used to indicate a - given line is folded or can be folded. - When not given, the `openText`/`closeText` option will be used instead. - */ - markerDOM?: ((open: boolean) => HTMLElement) | null; - /** - Text used to indicate that a given line can be folded. - Defaults to `"⌄"`. - */ - openText?: string; - /** - Text used to indicate that a given line is folded. - Defaults to `"›"`. - */ - closedText?: string; - /** - Supply event handlers for DOM events on this gutter. - */ - domEventHandlers?: Handlers; -} -/** -Create an extension that registers a fold gutter, which shows a -fold status indicator before foldable lines (which can be clicked -to fold or unfold the line). -*/ -declare function foldGutter(config?: FoldGutterConfig): Extension; - -interface Config { - /** - Whether the bracket matching should look at the character after - the cursor when matching (if the one before isn't a bracket). - Defaults to true. - */ - afterCursor?: boolean; - /** - The bracket characters to match, as a string of pairs. Defaults - to `"()[]{}"`. Note that these are only used as fallback when - there is no [matching - information](https://lezer.codemirror.net/docs/ref/#common.NodeProp^closedBy) - in the syntax tree. - */ - brackets?: string; - /** - The maximum distance to scan for matching brackets. This is only - relevant for brackets not encoded in the syntax tree. Defaults - to 10 000. - */ - maxScanDistance?: number; -} -/** -Create an extension that enables bracket matching. Whenever the -cursor is next to a bracket, that bracket and the one it matches -are highlighted. Or, when no matching bracket is found, another -highlighting style is used to indicate this. -*/ -declare function bracketMatching(config?: Config): Extension; - -/** -Extension to enable bracket-closing behavior. When a closeable -bracket is typed, its closing bracket is immediately inserted -after the cursor. When closing a bracket directly in front of a -closing bracket inserted by the extension, the cursor moves over -that bracket. -*/ -declare function closeBrackets(): Extension; -/** -Close-brackets related key bindings. Binds Backspace to -[`deleteBracketPair`](https://codemirror.net/6/docs/ref/#closebrackets.deleteBracketPair). -*/ -declare const closeBracketsKeymap: readonly KeyBinding[]; - interface CompletionConfig { /** When enabled (defaults to true), autocompletion will start @@ -4430,6 +3792,11 @@ interface CompletionConfig { */ override?: readonly CompletionSource[] | null; /** + Determines whether the completion tooltip is closed when the + editor loses focus. Defaults to true. + */ + closeOnBlur?: boolean; + /** The maximum number of options to render to the DOM. */ maxRenderedOptions?: number; @@ -4464,8 +3831,7 @@ interface CompletionConfig { completion, and should produce a DOM node to show. `position` determines where in the DOM the result appears, relative to other added widgets and the standard content. The default icons - have position 20, the label position 50, and the detail position - 70. + have position 20, the label position 50, and the detail position 70. */ addToOptions?: { render: (completion: Completion, state: EditorState) => Node | null; @@ -4638,23 +4004,40 @@ interface CompletionResult { */ options: readonly Completion[]; /** - When given, further input that causes the part of the document - between ([mapped](https://codemirror.net/6/docs/ref/#state.ChangeDesc.mapPos)) `from` and `to` to - match this regular expression will not query the completion - source again, but continue with this list of options. This can - help a lot with responsiveness, since it allows the completion - list to be updated synchronously. + When given, further typing or deletion that causes the part of + the document between ([mapped](https://codemirror.net/6/docs/ref/#state.ChangeDesc.mapPos)) `from` + and `to` to match this regular expression or predicate function + will not query the completion source again, but continue with + this list of options. This can help a lot with responsiveness, + since it allows the completion list to be updated synchronously. */ - span?: RegExp; + validFor?: RegExp | ((text: string, from: number, to: number, state: EditorState) => boolean); /** By default, the library filters and scores completions. Set `filter` to `false` to disable this, and cause your completions to all be included, in the order they were given. When there are other sources, unfiltered completions appear at the top of the - list of completions. `span` must not be given when `filter` is - `false`, because it only works when filtering. + list of completions. `validFor` must not be given when `filter` + is `false`, because it only works when filtering. */ filter?: boolean; + /** + When [`filter`](https://codemirror.net/6/docs/ref/#autocomplete.CompletionResult.filter) is set to + `false`, this may be provided to compute the ranges on the label + that match the input. Should return an array of numbers where + each pair of adjacent numbers provide the start and end of a + range. + */ + getMatch?: (completion: Completion) => readonly number[]; + /** + Synchronously update the completion result after typing or + deletion. If given, this should not do any expensive work, since + it will be called during editor state updates. The function + should make sure (similar to + [`validFor`](https://codemirror.net/6/docs/ref/#autocomplete.CompletionResult.validFor)) that the + completion still applies in the new state. + */ + update?: (current: CompletionResult, from: number, to: number, context: CompletionContext) => CompletionResult | null; } /** This annotation is added to transactions that are produced by @@ -4663,8 +4046,9 @@ picking a completion. declare const pickedCompletion: AnnotationType; /** -Convert a snippet template to a function that can apply it. -Snippets are written using syntax like this: +Convert a snippet template to a function that can +[apply](https://codemirror.net/6/docs/ref/#autocomplete.Completion.apply) it. Snippets are written +using syntax like this: "for (let ${index} = 0; ${index} < ${end}; ${index}++) {\n\t${}\n}" @@ -4743,6 +4127,56 @@ return those as completions. */ declare const completeAnyWord: CompletionSource; +/** +Configures bracket closing behavior for a syntax (via +[language data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt)) using the `"closeBrackets"` +identifier. +*/ +interface CloseBracketConfig { + /** + The opening brackets to close. Defaults to `["(", "[", "{", "'", + '"']`. Brackets may be single characters or a triple of quotes + (as in `"''''"`). + */ + brackets?: string[]; + /** + Characters in front of which newly opened brackets are + automatically closed. Closing always happens in front of + whitespace. Defaults to `")]}:;>"`. + */ + before?: string; +} +/** +Extension to enable bracket-closing behavior. When a closeable +bracket is typed, its closing bracket is immediately inserted +after the cursor. When closing a bracket directly in front of a +closing bracket inserted by the extension, the cursor moves over +that bracket. +*/ +declare function closeBrackets(): Extension; +/** +Command that implements deleting a pair of matching brackets when +the cursor is between them. +*/ +declare const deleteBracketPair: StateCommand; +/** +Close-brackets related key bindings. Binds Backspace to +[`deleteBracketPair`](https://codemirror.net/6/docs/ref/#autocomplete.deleteBracketPair). +*/ +declare const closeBracketsKeymap: readonly KeyBinding[]; +/** +Implements the extension's behavior on text insertion. If the +given string counts as a bracket in the language around the +selection, and replacing the selection with it requires custom +behavior (inserting a closing version or skipping past a +previously-closed bracket), this function returns a transaction +representing that custom behavior. (You only need this if you want +to programmatically insert brackets—the +[`closeBrackets`](https://codemirror.net/6/docs/ref/#autocomplete.closeBrackets) extension will +take care of running this for user input.) +*/ +declare function insertBracket(state: EditorState, bracket: string): Transaction | null; + /** Returns an extension that enables autocompletion. */ @@ -4785,6 +4219,7 @@ the currently selected completion. */ declare function setSelectedCompletion(index: number): StateEffect; +type index_CloseBracketConfig = CloseBracketConfig; type index_Completion = Completion; type index_CompletionContext = CompletionContext; declare const index_CompletionContext: typeof CompletionContext; @@ -4793,14 +4228,18 @@ type index_CompletionSource = CompletionSource; declare const index_acceptCompletion: typeof acceptCompletion; declare const index_autocompletion: typeof autocompletion; declare const index_clearSnippet: typeof clearSnippet; +declare const index_closeBrackets: typeof closeBrackets; +declare const index_closeBracketsKeymap: typeof closeBracketsKeymap; declare const index_closeCompletion: typeof closeCompletion; declare const index_completeAnyWord: typeof completeAnyWord; declare const index_completeFromList: typeof completeFromList; declare const index_completionKeymap: typeof completionKeymap; declare const index_completionStatus: typeof completionStatus; declare const index_currentCompletions: typeof currentCompletions; +declare const index_deleteBracketPair: typeof deleteBracketPair; declare const index_ifIn: typeof ifIn; declare const index_ifNotIn: typeof ifNotIn; +declare const index_insertBracket: typeof insertBracket; declare const index_moveCompletionSelection: typeof moveCompletionSelection; declare const index_nextSnippetField: typeof nextSnippetField; declare const index_pickedCompletion: typeof pickedCompletion; @@ -4814,6 +4253,7 @@ declare const index_snippetKeymap: typeof snippetKeymap; declare const index_startCompletion: typeof startCompletion; declare namespace index { export { + index_CloseBracketConfig as CloseBracketConfig, index_Completion as Completion, index_CompletionContext as CompletionContext, index_CompletionResult as CompletionResult, @@ -4821,14 +4261,18 @@ declare namespace index { index_acceptCompletion as acceptCompletion, index_autocompletion as autocompletion, index_clearSnippet as clearSnippet, + index_closeBrackets as closeBrackets, + index_closeBracketsKeymap as closeBracketsKeymap, index_closeCompletion as closeCompletion, index_completeAnyWord as completeAnyWord, index_completeFromList as completeFromList, index_completionKeymap as completionKeymap, index_completionStatus as completionStatus, index_currentCompletions as currentCompletions, + index_deleteBracketPair as deleteBracketPair, index_ifIn as ifIn, index_ifNotIn as ifNotIn, + index_insertBracket as insertBracket, index_moveCompletionSelection as moveCompletionSelection, index_nextSnippetField as nextSnippetField, index_pickedCompletion as pickedCompletion, @@ -4882,14 +4326,6 @@ Default search-related key bindings. */ declare const searchKeymap: readonly KeyBinding[]; -/** -Default key bindings for this package. - - - Ctrl-/ (Cmd-/ on macOS): [`toggleComment`](https://codemirror.net/6/docs/ref/#comment.toggleComment). - - Shift-Alt-a: [`toggleBlockComment`](https://codemirror.net/6/docs/ref/#comment.toggleBlockComment). -*/ -declare const commentKeymap: readonly KeyBinding[]; - declare class LeafBlock { readonly start: number; content: string; @@ -4940,6 +4376,9 @@ interface NodeSpec { name: string; block?: boolean; composite?(cx: BlockContext, line: Line, value: number): boolean; + style?: Tag | readonly Tag[] | { + [selector: string]: Tag | readonly Tag[]; + }; } interface InlineParser { name: string; @@ -5088,6 +4527,17 @@ declare function javascript(config?: { typescript?: boolean; }): LanguageSupport; +/** +A language provider based on the [Lezer CSS +parser](https://github.com/lezer-parser/css), extended with +highlighting and indentation information. +*/ +declare const cssLanguage: LRLanguage; +/** +Language support for CSS. +*/ +declare function css(): LanguageSupport; + declare type SQLDialectSpec = { /** A space-separated list of keywords for the dialect. @@ -5240,4 +4690,4 @@ Create an instance of the collaborative editing plugin. */ declare function collab(config?: CollabConfig): Extension; -export { Annotation, Compartment, Decoration, EditorSelection, EditorState, EditorView, Facet, HighlightStyle, NodeProp, PostgreSQL, SelectionRange, StateEffect, StateField, StreamLanguage, Text, Transaction, TreeCursor, ViewPlugin, ViewUpdate, WidgetType, index as autocomplete, bracketMatching, closeBrackets, closeBracketsKeymap, collab, combineConfig, commentKeymap, completionKeymap, defaultHighlightStyle, defaultKeymap, drawSelection, foldGutter, foldKeymap, highlightSelectionMatches, highlightSpecialChars, history, historyKeymap, html, htmlLanguage, indentLess, indentMore, indentOnInput, indentUnit, javascript, javascriptLanguage, julia as julia_andrey, julia$1 as julia_legacy, keymap, lineNumbers, markdown, markdownLanguage, parseCode, parseMixed, placeholder, python, pythonLanguage, rectangularSelection, searchKeymap, sql, syntaxTree, syntaxTreeAvailable, tags }; +export { Annotation, Compartment, Decoration, EditorSelection, EditorState, EditorView, Facet, HighlightStyle, NodeProp, PostgreSQL, SelectionRange, StateEffect, StateField, Text, Transaction, TreeCursor, ViewPlugin, ViewUpdate, WidgetType, index as autocomplete, bracketMatching, closeBrackets, closeBracketsKeymap, collab, combineConfig, completionKeymap, css, cssLanguage, defaultHighlightStyle, defaultKeymap, drawSelection, foldGutter, foldKeymap, highlightSelectionMatches, highlightSpecialChars, history, historyKeymap, html, htmlLanguage, indentLess, indentMore, indentOnInput, indentUnit, javascript, javascriptLanguage, julia as julia_andrey, keymap, lineNumbers, markdown, markdownLanguage, parseCode, parseMixed, placeholder, python, pythonLanguage, rectangularSelection, searchKeymap, sql, syntaxHighlighting, syntaxTree, syntaxTreeAvailable, tags }; diff --git a/dist/index.es.js b/dist/index.es.js index a6fb0bd..9354593 100644 --- a/dist/index.es.js +++ b/dist/index.es.js @@ -1,148 +1,5 @@ -// Compressed representation of the Grapheme_Cluster_Break=Extend -// information from -// http://www.unicode.org/Public/13.0.0/ucd/auxiliary/GraphemeBreakProperty.txt. -// Each pair of elements represents a range, as an offet from the -// previous range and a length. Numbers are in base-36, with the empty -// string being a shorthand for 1. -let extend = /*@__PURE__*/"lc,34,7n,7,7b,19,,,,2,,2,,,20,b,1c,l,g,,2t,7,2,6,2,2,,4,z,,u,r,2j,b,1m,9,9,,o,4,,9,,3,,5,17,3,3b,f,,w,1j,,,,4,8,4,,3,7,a,2,t,,1m,,,,2,4,8,,9,,a,2,q,,2,2,1l,,4,2,4,2,2,3,3,,u,2,3,,b,2,1l,,4,5,,2,4,,k,2,m,6,,,1m,,,2,,4,8,,7,3,a,2,u,,1n,,,,c,,9,,14,,3,,1l,3,5,3,,4,7,2,b,2,t,,1m,,2,,2,,3,,5,2,7,2,b,2,s,2,1l,2,,,2,4,8,,9,,a,2,t,,20,,4,,2,3,,,8,,29,,2,7,c,8,2q,,2,9,b,6,22,2,r,,,,,,1j,e,,5,,2,5,b,,10,9,,2u,4,,6,,2,2,2,p,2,4,3,g,4,d,,2,2,6,,f,,jj,3,qa,3,t,3,t,2,u,2,1s,2,,7,8,,2,b,9,,19,3,3b,2,y,,3a,3,4,2,9,,6,3,63,2,2,,1m,,,7,,,,,2,8,6,a,2,,1c,h,1r,4,1c,7,,,5,,14,9,c,2,w,4,2,2,,3,1k,,,2,3,,,3,1m,8,2,2,48,3,,d,,7,4,,6,,3,2,5i,1m,,5,ek,,5f,x,2da,3,3x,,2o,w,fe,6,2x,2,n9w,4,,a,w,2,28,2,7k,,3,,4,,p,2,5,,47,2,q,i,d,,12,8,p,b,1a,3,1c,,2,4,2,2,13,,1v,6,2,2,2,2,c,,8,,1b,,1f,,,3,2,2,5,2,,,16,2,8,,6m,,2,,4,,fn4,,kh,g,g,g,a6,2,gt,,6a,,45,5,1ae,3,,2,5,4,14,3,4,,4l,2,fx,4,ar,2,49,b,4w,,1i,f,1k,3,1d,4,2,2,1x,3,10,5,,8,1q,,c,2,1g,9,a,4,2,,2n,3,2,,,2,6,,4g,,3,8,l,2,1l,2,,,,,m,,e,7,3,5,5f,8,2,3,,,n,,29,,2,6,,,2,,,2,,2,6j,,2,4,6,2,,2,r,2,2d,8,2,,,2,2y,,,,2,6,,,2t,3,2,4,,5,77,9,,2,6t,,a,2,,,4,,40,4,2,2,4,,w,a,14,6,2,4,8,,9,6,2,3,1a,d,,2,ba,7,,6,,,2a,m,2,7,,2,,2,3e,6,3,,,2,,7,,,20,2,3,,,,9n,2,f0b,5,1n,7,t4,,1r,4,29,,f5k,2,43q,,,3,4,5,8,8,2,7,u,4,44,3,1iz,1j,4,1e,8,,e,,m,5,,f,11s,7,,h,2,7,,2,,5,79,7,c5,4,15s,7,31,7,240,5,gx7k,2o,3k,6o".split(",").map(s => s ? parseInt(s, 36) : 1); -// Convert offsets into absolute values -for (let i = 1; i < extend.length; i++) - extend[i] += extend[i - 1]; -function isExtendingChar(code) { - for (let i = 1; i < extend.length; i += 2) - if (extend[i] > code) - return extend[i - 1] <= code; - return false; -} -function isRegionalIndicator(code) { - return code >= 0x1F1E6 && code <= 0x1F1FF; -} -const ZWJ = 0x200d; -/** -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 (forward ? nextClusterBreak : prevClusterBreak)(str, pos, includeExtending); -} -function nextClusterBreak(str, pos, includeExtending) { - if (pos == str.length) - return pos; - // If pos is in the middle of a surrogate pair, move to its start - if (pos && surrogateLow(str.charCodeAt(pos)) && surrogateHigh(str.charCodeAt(pos - 1))) - pos--; - let prev = codePointAt(str, pos); - pos += codePointSize(prev); - while (pos < str.length) { - let next = codePointAt(str, pos); - if (prev == ZWJ || next == ZWJ || includeExtending && isExtendingChar(next)) { - pos += codePointSize(next); - prev = next; - } - else if (isRegionalIndicator(next)) { - let countBefore = 0, i = pos - 2; - while (i >= 0 && isRegionalIndicator(codePointAt(str, i))) { - countBefore++; - i -= 2; - } - if (countBefore % 2 == 0) - break; - else - pos += 2; - } - else { - break; - } - } - return pos; -} -function prevClusterBreak(str, pos, includeExtending) { - while (pos > 0) { - let found = nextClusterBreak(str, pos - 2, includeExtending); - if (found < pos) - return found; - pos--; - } - return 0; -} -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 first character that takes up two positions in a JavaScript -string. It is often useful to compare with this after calling -`codePointAt`, to figure out whether your character takes up 1 or -2 index positions. -*/ -function codePointSize(code) { return code < 0x10000 ? 1 : 2; } - -/** -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; -} - /** -The data structure for documents. +The data structure for documents. @nonabstract */ class Text { /** @@ -214,8 +71,7 @@ class Text { /** 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, and for long lines, might split lines - themselves into multiple chunks as well. + them as separate strings. */ iter(dir = 1) { return new RawTextCursor(this, dir); } /** @@ -249,7 +105,7 @@ class Text { 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/#text.Text^of)). + deserialized again via [`Text.of`](https://codemirror.net/6/docs/ref/#state.Text^of)). */ toJSON() { let lines = []; @@ -654,7 +510,7 @@ if (typeof Symbol != "undefined") { } /** This type describes a line in the document. It is created -on-demand when lines are [queried](https://codemirror.net/6/docs/ref/#text.Text.lineAt). +on-demand when lines are [queried](https://codemirror.net/6/docs/ref/#state.Text.lineAt). */ class Line$1 { /** @@ -689,6 +545,112 @@ class Line$1 { get length() { return this.to - this.from; } } +// Compressed representation of the Grapheme_Cluster_Break=Extend +// information from +// http://www.unicode.org/Public/13.0.0/ucd/auxiliary/GraphemeBreakProperty.txt. +// Each pair of elements represents a range, as an offet from the +// previous range and a length. Numbers are in base-36, with the empty +// string being a shorthand for 1. +let extend = /*@__PURE__*/"lc,34,7n,7,7b,19,,,,2,,2,,,20,b,1c,l,g,,2t,7,2,6,2,2,,4,z,,u,r,2j,b,1m,9,9,,o,4,,9,,3,,5,17,3,3b,f,,w,1j,,,,4,8,4,,3,7,a,2,t,,1m,,,,2,4,8,,9,,a,2,q,,2,2,1l,,4,2,4,2,2,3,3,,u,2,3,,b,2,1l,,4,5,,2,4,,k,2,m,6,,,1m,,,2,,4,8,,7,3,a,2,u,,1n,,,,c,,9,,14,,3,,1l,3,5,3,,4,7,2,b,2,t,,1m,,2,,2,,3,,5,2,7,2,b,2,s,2,1l,2,,,2,4,8,,9,,a,2,t,,20,,4,,2,3,,,8,,29,,2,7,c,8,2q,,2,9,b,6,22,2,r,,,,,,1j,e,,5,,2,5,b,,10,9,,2u,4,,6,,2,2,2,p,2,4,3,g,4,d,,2,2,6,,f,,jj,3,qa,3,t,3,t,2,u,2,1s,2,,7,8,,2,b,9,,19,3,3b,2,y,,3a,3,4,2,9,,6,3,63,2,2,,1m,,,7,,,,,2,8,6,a,2,,1c,h,1r,4,1c,7,,,5,,14,9,c,2,w,4,2,2,,3,1k,,,2,3,,,3,1m,8,2,2,48,3,,d,,7,4,,6,,3,2,5i,1m,,5,ek,,5f,x,2da,3,3x,,2o,w,fe,6,2x,2,n9w,4,,a,w,2,28,2,7k,,3,,4,,p,2,5,,47,2,q,i,d,,12,8,p,b,1a,3,1c,,2,4,2,2,13,,1v,6,2,2,2,2,c,,8,,1b,,1f,,,3,2,2,5,2,,,16,2,8,,6m,,2,,4,,fn4,,kh,g,g,g,a6,2,gt,,6a,,45,5,1ae,3,,2,5,4,14,3,4,,4l,2,fx,4,ar,2,49,b,4w,,1i,f,1k,3,1d,4,2,2,1x,3,10,5,,8,1q,,c,2,1g,9,a,4,2,,2n,3,2,,,2,6,,4g,,3,8,l,2,1l,2,,,,,m,,e,7,3,5,5f,8,2,3,,,n,,29,,2,6,,,2,,,2,,2,6j,,2,4,6,2,,2,r,2,2d,8,2,,,2,2y,,,,2,6,,,2t,3,2,4,,5,77,9,,2,6t,,a,2,,,4,,40,4,2,2,4,,w,a,14,6,2,4,8,,9,6,2,3,1a,d,,2,ba,7,,6,,,2a,m,2,7,,2,,2,3e,6,3,,,2,,7,,,20,2,3,,,,9n,2,f0b,5,1n,7,t4,,1r,4,29,,f5k,2,43q,,,3,4,5,8,8,2,7,u,4,44,3,1iz,1j,4,1e,8,,e,,m,5,,f,11s,7,,h,2,7,,2,,5,79,7,c5,4,15s,7,31,7,240,5,gx7k,2o,3k,6o".split(",").map(s => s ? parseInt(s, 36) : 1); +// Convert offsets into absolute values +for (let i = 1; i < extend.length; i++) + extend[i] += extend[i - 1]; +function isExtendingChar(code) { + for (let i = 1; i < extend.length; i += 2) + if (extend[i] > code) + return extend[i - 1] <= code; + return false; +} +function isRegionalIndicator(code) { + return code >= 0x1F1E6 && code <= 0x1F1FF; +} +const ZWJ = 0x200d; +/** +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 (forward ? nextClusterBreak : prevClusterBreak)(str, pos, includeExtending); +} +function nextClusterBreak(str, pos, includeExtending) { + if (pos == str.length) + return pos; + // If pos is in the middle of a surrogate pair, move to its start + if (pos && surrogateLow(str.charCodeAt(pos)) && surrogateHigh(str.charCodeAt(pos - 1))) + pos--; + let prev = codePointAt(str, pos); + pos += codePointSize(prev); + while (pos < str.length) { + let next = codePointAt(str, pos); + if (prev == ZWJ || next == ZWJ || includeExtending && isExtendingChar(next)) { + pos += codePointSize(next); + prev = next; + } + else if (isRegionalIndicator(next)) { + let countBefore = 0, i = pos - 2; + while (i >= 0 && isRegionalIndicator(codePointAt(str, i))) { + countBefore++; + i -= 2; + } + if (countBefore % 2 == 0) + break; + else + pos += 2; + } + else { + break; + } + } + return pos; +} +function prevClusterBreak(str, pos, includeExtending) { + while (pos > 0) { + let found = nextClusterBreak(str, pos - 2, includeExtending); + if (found < pos) + return found; + pos--; + } + return 0; +} +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 first character that takes up two positions in a JavaScript +string. It is often useful to compare with this after calling +`codePointAt`, to figure out whether your character takes up 1 or +2 index positions. +*/ +function codePointSize(code) { return code < 0x10000 ? 1 : 2; } + const DefaultSplit = /\r\n?|\n/; /** Distinguishes different ways in which positions can be mapped. @@ -758,7 +720,9 @@ class ChangeDesc { */ get empty() { return this.sections.length == 0 || this.sections.length == 2 && this.sections[1] < 0; } /** - Iterate over the unchanged parts left by these changes. + 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;) { @@ -777,6 +741,9 @@ class ChangeDesc { 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 @@ -1559,10 +1526,10 @@ 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 facets are the [theme](https://codemirror.net/6/docs/ref/#view.EditorView^theme) styles -associated with an editor or the [tab -size](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize) (which is reduced to a single -value, using the input with the hightest precedence). +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). */ class Facet { constructor( @@ -1600,7 +1567,7 @@ class Facet { return new Facet(config.combine || ((a) => a), config.compareInput || ((a, b) => a === b), config.compare || (!config.combine ? sameArray$1 : (a, b) => a === b), !!config.static, config.enables); } /** - Returns an extension that adds the given value for this facet. + Returns an extension that adds the given value to this facet. */ of(value) { return new FacetProvider([], this, 0 /* Static */, value); @@ -1611,9 +1578,8 @@ class Facet { this value depends on, since your function is only called again for a new state when one of those parts changed. - In most cases, you'll want to use the - [`provide`](https://codemirror.net/6/docs/ref/#state.StateField^define^config.provide) option when - defining a field instead. + 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) @@ -1666,8 +1632,7 @@ class FacetProvider { return 1 /* Changed */; }, update(state, tr) { - if ((depDoc && tr.docChanged) || (depSel && (tr.docChanged || tr.selection)) || - depAddrs.some(addr => (ensureAddr(state, addr) & 1 /* Changed */) > 0)) { + 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; @@ -1676,12 +1641,15 @@ class FacetProvider { } return 0; }, - reconfigure(state, oldState) { + reconfigure: (state, oldState) => { let newVal = getter(state); let oldAddr = oldState.config.address[id]; if (oldAddr != null) { let oldVal = getAddr(oldState, oldAddr); - if (multi ? compareArray(newVal, oldVal, compare) : compare(newVal, oldVal)) { + 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, oldVal, compare) : compare(newVal, oldVal))) { state.values[idx] = oldVal; return 0; } @@ -1700,6 +1668,13 @@ function compareArray(a, b, compare) { return false; return true; } +function ensureAll(state, addrs) { + let changed = false; + for (let addr of addrs) + if (ensureAddr(state, addr) & 1 /* 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); @@ -1725,7 +1700,7 @@ function dynamicFacetSlot(addresses, facet, providers) { return 1 /* Changed */; }, update(state, tr) { - if (!dynamic.some(dynAddr => ensureAddr(state, dynAddr) & 1 /* Changed */)) + if (!ensureAll(state, dynamic)) return 0; let value = get(state); if (facet.compare(value, state.values[idx])) @@ -1734,7 +1709,7 @@ function dynamicFacetSlot(addresses, facet, providers) { return 1 /* Changed */; }, reconfigure(state, oldState) { - let depChanged = providerAddrs.some(addr => ensureAddr(state, addr) & 1 /* Changed */); + let depChanged = ensureAll(state, providerAddrs); let oldProviders = oldState.config.facets[facet.id], oldValue = oldState.facet(facet); if (oldProviders && !depChanged && sameArray$1(providers, oldProviders)) { state.values[idx] = oldValue; @@ -1847,42 +1822,29 @@ precedence and then by order within each precedence. */ const Prec = { /** - The lowest precedence level. Meant for things that should end up - near the end of the extension order. + The highest precedence level, for extensions that should end up + near the start of the precedence ordering. */ - lowest: /*@__PURE__*/prec(Prec_.lowest), - /** - A lower-than-default precedence, for extensions. - */ - low: /*@__PURE__*/prec(Prec_.low), - /** - The default precedence, which is also used for extensions - without an explicit precedence. - */ - default: /*@__PURE__*/prec(Prec_.default), + 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 highest precedence level, for extensions that should end up - near the start of the precedence ordering. - */ - highest: /*@__PURE__*/prec(Prec_.highest), - // FIXME Drop these in some future breaking version - /** - Backwards-compatible synonym for `Prec.lowest`. + The default precedence, which is also used for extensions + without an explicit precedence. */ - fallback: /*@__PURE__*/prec(Prec_.lowest), + default: /*@__PURE__*/prec(Prec_.default), /** - Backwards-compatible synonym for `Prec.high`. + A lower-than-default precedence. */ - extend: /*@__PURE__*/prec(Prec_.high), + low: /*@__PURE__*/prec(Prec_.low), /** - Backwards-compatible synonym for `Prec.highest`. + The lowest precedence level. Meant for things that should end up + near the end of the extension order. */ - override: /*@__PURE__*/prec(Prec_.highest) + lowest: /*@__PURE__*/prec(Prec_.lowest) }; class PrecExtension { constructor(inner, prec) { @@ -1996,7 +1958,7 @@ function flatten(extension, compartments, newCompartments) { function inner(ext, prec) { let known = seen.get(ext); if (known != null) { - if (known >= prec) + if (known <= prec) return; let found = result[known].indexOf(ext); if (found > -1) @@ -2211,7 +2173,9 @@ 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). +[`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 { /** @@ -2285,7 +2249,7 @@ class Transaction { } /** The new state created by the transaction. Computed on demand - (but retained for subsequent access), so itis recommended not to + (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. */ @@ -2327,7 +2291,8 @@ class Transaction { } } /** -Annotation used to store transaction timestamps. +Annotation used to store transaction timestamps. Automatically +added to every transaction, holding `Date.now()`. */ Transaction.time = /*@__PURE__*/Annotation.define(); /** @@ -2485,9 +2450,9 @@ function extendTransaction(tr) { } return spec == tr ? tr : new Transaction(state, tr.changes, tr.selection, spec.effects, spec.annotations, spec.scrollIntoView); } -const none$4 = []; +const none$3 = []; function asArray$1(value) { - return value == null ? none$4 : Array.isArray(value) ? value : [value]; + return value == null ? none$3 : Array.isArray(value) ? value : [value]; } /** @@ -2699,7 +2664,7 @@ class EditorState { /** Using the state's [line separator](https://codemirror.net/6/docs/ref/#state.EditorState^lineSeparator), create a - [`Text`](https://codemirror.net/6/docs/ref/#text.Text) instance from the given string. + [`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)); @@ -2819,7 +2784,7 @@ class EditorState { } /** Return a function that can categorize strings (expected to - represent a single [grapheme cluster](https://codemirror.net/6/docs/ref/#text.findClusterBreak)) + represent a single [grapheme cluster](https://codemirror.net/6/docs/ref/#state.findClusterBreak)) into one of: - Word (contains an alphanumeric character or a character @@ -2903,7 +2868,12 @@ Registers translation phrases. The all objects registered with this facet to find translations for its argument. */ -EditorState.phrases = /*@__PURE__*/Facet.define(); +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. @@ -2919,7 +2889,7 @@ 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 number indicating the start and end of a +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. */ @@ -2950,19 +2920,22 @@ This is a more limited form of 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 the transaction has disabled regular +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 applied. +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. Will, by default, error +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. */ @@ -2986,25424 +2959,32403 @@ combine = {}) { return result; } -// FIXME profile adding a per-Tree TreeNode cache, validating it by -// parent pointer -/// The default maximum length of a `TreeBuffer` node (1024). -const DefaultBufferLength = 1024; -let nextPropID = 0; +/** +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 new Range$1(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$1 { - constructor(from, to) { + /** + @internal + */ + 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; } } -/// Each [node type](#common.NodeType) or [individual tree](#common.Tree) -/// can have metadata associated with it in props. Instances of this -/// class represent prop names. -class NodeProp { - /// Create a new node prop type. - constructor(config = {}) { - this.id = nextPropID++; - this.perNode = !!config.perNode; - this.deserialize = config.deserialize || (() => { - throw new Error("This node type doesn't define a deserialize function"); - }); - } - /// This is meant to be used with - /// [`NodeSet.extend`](#common.NodeSet.extend) or - /// [`LRParser.configure`](#lr.ParserConfig.props) to compute - /// prop values for each node type in the set. Takes a [match - /// object](#common.NodeType^match) or function that returns undefined - /// if the node type doesn't get this prop, and the prop's value if - /// it does. - add(match) { - if (this.perNode) - throw new RangeError("Can't add per-node props to node types"); - if (typeof match != "function") - match = NodeType.match(match); - return (type) => { - let result = match(type); - return result === undefined ? null : [this, result]; - }; - } +function cmpRange(a, b) { + return a.from - b.from || a.value.startSide - b.value.startSide; } -/// Prop that is used to describe matching delimiters. For opening -/// delimiters, this holds an array of node names (written as a -/// space-separated string when declaring this prop in a grammar) -/// for the node types of closing delimiters that match it. -NodeProp.closedBy = new NodeProp({ deserialize: str => str.split(" ") }); -/// The inverse of [`closedBy`](#common.NodeProp^closedBy). This is -/// attached to closing delimiters, holding an array of node names -/// of types of matching opening delimiters. -NodeProp.openedBy = new NodeProp({ deserialize: str => str.split(" ") }); -/// Used to assign node types to groups (for example, all node -/// types that represent an expression could be tagged with an -/// `"Expression"` group). -NodeProp.group = new NodeProp({ deserialize: str => str.split(" ") }); -/// The hash of the [context](#lr.ContextTracker.constructor) -/// that the node was parsed in, if any. Used to limit reuse of -/// contextual nodes. -NodeProp.contextHash = new NodeProp({ perNode: true }); -/// The distance beyond the end of the node that the tokenizer -/// looked ahead for any of the tokens inside the node. (The LR -/// parser only stores this when it is larger than 25, for -/// efficiency reasons.) -NodeProp.lookAhead = new NodeProp({ perNode: true }); -/// This per-node prop is used to replace a given node, or part of a -/// node, with another tree. This is useful to include trees from -/// different languages. -NodeProp.mounted = new NodeProp({ perNode: true }); -/// A mounted tree, which can be [stored](#common.NodeProp^mounted) on -/// a tree node to indicate that parts of its content are -/// represented by another tree. -class MountedTree { - constructor( - /// The inner tree. - tree, - /// If this is null, this tree replaces the entire node (it will - /// be included in the regular iteration instead of its host - /// node). If not, only the given ranges are considered to be - /// covered by this tree. This is used for trees that are mixed in - /// a way that isn't strictly hierarchical. Such mounted trees are - /// only entered by [`resolveInner`](#common.Tree.resolveInner) - /// and [`enter`](#common.SyntaxNode.enter). - overlay, - /// The parser used to create this subtree. - parser) { - this.tree = tree; - this.overlay = overlay; - this.parser = parser; +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; } -} -const noProps = Object.create(null); -/// Each node in a syntax tree has a node type associated with it. -class NodeType { - /// @internal - constructor( - /// The name of the node type. Not necessarily unique, but if the - /// grammar was written properly, different node types with the - /// same name within a node set should play the same semantic - /// role. - name, - /// @internal - props, - /// The id of this node in its set. Corresponds to the term ids - /// used in the parser. - id, - /// @internal - flags = 0) { - this.name = name; - this.props = props; - this.id = id; - this.flags = flags; + 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; + } } - static define(spec) { - let props = spec.props && spec.props.length ? Object.create(null) : noProps; - let flags = (spec.top ? 1 /* Top */ : 0) | (spec.skipped ? 2 /* Skipped */ : 0) | - (spec.error ? 4 /* Error */ : 0) | (spec.name == null ? 8 /* Anonymous */ : 0); - let type = new NodeType(spec.name || "", props, spec.id, flags); - if (spec.props) - for (let src of spec.props) { - if (!Array.isArray(src)) - src = src(type); - if (src) { - if (src[0].perNode) - throw new RangeError("Can't store a per-node prop on a node type"); - props[src[0].id] = src[1]; + between(offset, from, to, f) { + for (let i = this.findIndex(from, -1000000000 /* Far */, true), e = this.findIndex(to, 1000000000 /* 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; } } - return type; - } - /// Retrieves a node prop for this type. Will return `undefined` if - /// the prop isn't present on this node. - prop(prop) { return this.props[prop.id]; } - /// True when this is the top node of a grammar. - get isTop() { return (this.flags & 1 /* Top */) > 0; } - /// True when this node is produced by a skip rule. - get isSkipped() { return (this.flags & 2 /* Skipped */) > 0; } - /// Indicates whether this is an error node. - get isError() { return (this.flags & 4 /* Error */) > 0; } - /// When true, this node type doesn't correspond to a user-declared - /// named node, for example because it is used to cache repetition. - get isAnonymous() { return (this.flags & 8 /* Anonymous */) > 0; } - /// Returns true when this node's name or one of its - /// [groups](#common.NodeProp^group) matches the given string. - is(name) { - if (typeof name == 'string') { - if (this.name == name) - return true; - let group = this.prop(NodeProp.group); - return group ? group.indexOf(name) > -1 : false; - } - return this.id == name; - } - /// Create a function from node types to arbitrary values by - /// specifying an object whose property names are node or - /// [group](#common.NodeProp^group) names. Often useful with - /// [`NodeProp.add`](#common.NodeProp.add). You can put multiple - /// names, separated by spaces, in a single property name to map - /// multiple node names to a single value. - static match(map) { - let direct = Object.create(null); - for (let prop in map) - for (let name of prop.split(" ")) - direct[name] = map[prop]; - return (node) => { - for (let groups = node.prop(NodeProp.group), i = -1; i < (groups ? groups.length : 0); i++) { - let found = direct[i < 0 ? node.name : groups[i]]; - if (found) - return found; - } - }; + 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 }; } } -/// An empty dummy node type to use when no actual type is available. -NodeType.none = new NodeType("", Object.create(null), 0, 8 /* Anonymous */); -/// A node set holds a collection of node types. It is used to -/// compactly represent trees by storing their type ids, rather than a -/// full pointer to the type object, in a numeric array. Each parser -/// [has](#lr.LRParser.nodeSet) a node set, and [tree -/// buffers](#common.TreeBuffer) can only store collections of nodes -/// from the same set. A set can have a maximum of 2**16 (65536) node -/// types in it, so that the ids fit into 16-bit typed array slots. -class NodeSet { - /// Create a set with the given types. The `id` property of each - /// type should correspond to its position within the array. +/** +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 { + /** + @internal + */ constructor( - /// The node types in this set, by id. - types) { - this.types = types; - for (let i = 0; i < types.length; i++) - if (types[i].id != i) - throw new RangeError("Node type ids should correspond to array positions when creating a node set"); + /** + @internal + */ + chunkPos, + /** + @internal + */ + chunk, + /** + @internal + */ + nextLayer = RangeSet.empty, + /** + @internal + */ + maxPoint) { + this.chunkPos = chunkPos; + this.chunk = chunk; + this.nextLayer = nextLayer; + this.maxPoint = maxPoint; } - /// Create a copy of this set with some node properties added. The - /// arguments to this method should be created with - /// [`NodeProp.add`](#common.NodeProp.add). - extend(...props) { - let newTypes = []; - for (let type of this.types) { - let newProps = null; - for (let source of props) { - let add = source(type); - if (add) { - if (!newProps) - newProps = Object.assign({}, type.props); - newProps[add[0].id] = add[1]; + /** + @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(new Range$1(cur.from, cur.to, cur.value)); } + cur.next(); } - newTypes.push(newProps ? new NodeType(type.name, newProps, type.id, type.flags) : type); - } - return new NodeSet(newTypes); - } -} -const CachedNode = new WeakMap(), CachedInnerNode = new WeakMap(); -/// A piece of syntax tree. There are two ways to approach these -/// trees: the way they are actually stored in memory, and the -/// convenient way. -/// -/// Syntax trees are stored as a tree of `Tree` and `TreeBuffer` -/// objects. By packing detail information into `TreeBuffer` leaf -/// nodes, the representation is made a lot more memory-efficient. -/// -/// However, when you want to actually work with tree nodes, this -/// representation is very awkward, so most client code will want to -/// use the [`TreeCursor`](#common.TreeCursor) or -/// [`SyntaxNode`](#common.SyntaxNode) interface instead, which provides -/// a view on some part of this data structure, and can be used to -/// move around to adjacent nodes. -class Tree { - /// Construct a new tree. See also [`Tree.build`](#common.Tree^build). - constructor( - /// The type of the top node. - type, - /// This node's child nodes. - children, - /// The positions (offsets relative to the start of this tree) of - /// the children. - positions, - /// The total length of this tree - length, - /// Per-node [node props](#common.NodeProp) to associate with this node. - props) { - this.type = type; - this.children = children; - this.positions = positions; - this.length = length; - /// @internal - this.props = null; - if (props && props.length) { - this.props = Object.create(null); - for (let [prop, value] of props) - this.props[typeof prop == "number" ? prop : prop.id] = value; } + return builder.finishInner(this.nextLayer.isEmpty && !spill.length ? RangeSet.empty + : this.nextLayer.update({ add: spill, filter, filterFrom, filterTo })); } - /// @internal - toString() { - let mounted = this.prop(NodeProp.mounted); - if (mounted && !mounted.overlay) - return mounted.tree.toString(); - let children = ""; - for (let ch of this.children) { - let str = ch.toString(); - if (str) { - if (children) - children += ","; - children += str; + /** + 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); + } } } - return !this.type.name ? children : - (/\W/.test(this.type.name) && !this.type.isError ? JSON.stringify(this.type.name) : this.type.name) + - (children.length ? "(" + children + ")" : ""); + let next = this.nextLayer.map(changes); + return chunks.length == 0 ? next : new RangeSet(chunkPos, chunks, next, maxPoint); } - /// Get a [tree cursor](#common.TreeCursor) rooted at this tree. When - /// `pos` is given, the cursor is [moved](#common.TreeCursor.moveTo) - /// to the given position and side. - cursor(pos, side = 0) { - let scope = (pos != null && CachedNode.get(this)) || this.topNode; - let cursor = new TreeCursor(scope); - if (pos != null) { - cursor.moveTo(pos, side); - CachedNode.set(this, cursor._tree); + /** + 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; } - return cursor; - } - /// Get a [tree cursor](#common.TreeCursor) that, unlike regular - /// cursors, doesn't skip through - /// [anonymous](#common.NodeType.isAnonymous) nodes and doesn't - /// automatically enter mounted nodes. - fullCursor() { - return new TreeCursor(this.topNode, 1 /* Full */); + this.nextLayer.between(from, to, f); } - /// Get a [syntax node](#common.SyntaxNode) object for the top of the - /// tree. - get topNode() { - return new TreeNode(this, 0, 0, null); + /** + 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); } - /// Get the [syntax node](#common.SyntaxNode) at the given position. - /// If `side` is -1, this will move into nodes that end at the - /// position. If 1, it'll move into nodes that start at the - /// position. With 0, it'll only enter nodes that cover the position - /// from both sides. - resolve(pos, side = 0) { - let node = resolveNode(CachedNode.get(this) || this.topNode, pos, side, false); - CachedNode.set(this, node); - return node; + /** + @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); } - /// Like [`resolve`](#common.Tree.resolve), but will enter - /// [overlaid](#common.MountedTree.overlay) nodes, producing a syntax node - /// pointing into the innermost overlaid tree at the given position - /// (with parent links going through all parent structure, including - /// the host trees). - resolveInner(pos, side = 0) { - let node = resolveNode(CachedInnerNode.get(this) || this.topNode, pos, side, true); - CachedInnerNode.set(this, node); - return node; + /** + 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. `from` and + `to` are coordinates in the _new_ space, after these changes. + */ + 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); } - /// Iterate over the tree and its children, calling `enter` for any - /// node that touches the `from`/`to` region (if given) before - /// running over such a node's children, and `leave` (if given) when - /// leaving the node. When `enter` returns `false`, that node will - /// not have its children iterated over (or `leave` called). - iterate(spec) { - let { enter, leave, from = 0, to = this.length } = spec; - for (let c = this.cursor(), get = () => c.node;;) { - let mustLeave = false; - if (c.from <= to && c.to >= from && (c.type.isAnonymous || enter(c.type, c.from, c.to, get) !== false)) { - if (c.firstChild()) - continue; - if (!c.type.isAnonymous) - mustLeave = true; + /** + 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 /* Far */; + 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 open = cursor.openStart; + for (;;) { + let curTo = Math.min(cursor.to, to); + if (cursor.point) { + iterator.point(pos, curTo, cursor.point, cursor.activeForPoint(cursor.to), open, cursor.pointRank); + open = cursor.openEnd(curTo) + (cursor.to > curTo ? 1 : 0); } - for (;;) { - if (mustLeave && leave) - leave(c.type, c.from, c.to, get); - mustLeave = c.type.isAnonymous; - if (c.nextSibling()) - break; - if (!c.parent()) - return; - mustLeave = true; + else if (curTo > pos) { + iterator.span(pos, curTo, cursor.active, open); + open = cursor.openEnd(curTo); } + if (cursor.to > to) + break; + pos = cursor.to; + cursor.next(); } + return open; } - /// Get the value of the given [node prop](#common.NodeProp) for this - /// node. Works with both per-node and per-type props. - prop(prop) { - return !prop.perNode ? this.type.prop(prop) : this.props ? this.props[prop.id] : undefined; - } - /// Returns the node's [per-node props](#common.NodeProp.perNode) in a - /// format that can be passed to the [`Tree`](#common.Tree) - /// constructor. - get propValues() { - let result = []; - if (this.props) - for (let id in this.props) - result.push([+id, this.props[id]]); - return result; - } - /// Balance the direct children of this tree, producing a copy of - /// which may have children grouped into subtrees with type - /// [`NodeType.none`](#common.NodeType^none). - balance(config = {}) { - return this.children.length <= 8 /* BranchFactor */ ? this : - balanceRange(NodeType.none, this.children, this.positions, 0, this.children.length, 0, this.length, (children, positions, length) => new Tree(this.type, children, positions, length, this.propValues), config.makeTree || ((children, positions, length) => new Tree(NodeType.none, children, positions, length))); - } - /// Build a tree from a postfix-ordered buffer of node information, - /// or a cursor over such a buffer. - static build(data) { return buildTree(data); } -} -/// The empty tree -Tree.empty = new Tree(NodeType.none, [], [], 0); -class FlatBufferCursor { - constructor(buffer, index) { - this.buffer = buffer; - this.index = index; + /** + 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$1 ? [ranges] : sort ? lazySort(ranges) : ranges) + build.add(range.from, range.to, range.value); + return build.finish(); } - get id() { return this.buffer[this.index - 4]; } - get start() { return this.buffer[this.index - 3]; } - get end() { return this.buffer[this.index - 2]; } - get size() { return this.buffer[this.index - 1]; } - get pos() { return this.index; } - next() { this.index -= 4; } - fork() { return new FlatBufferCursor(this.buffer, this.index); } } -/// Tree buffers contain (type, start, end, endIndex) quads for each -/// node. In such a buffer, nodes are stored in prefix order (parents -/// before children, with the endIndex of the parent indicating which -/// children belong to it) -class TreeBuffer { - /// Create a tree buffer. - constructor( - /// The buffer's content. - buffer, - /// The total length of the group of nodes in the buffer. - length, - /// The node set used in this buffer. - set) { - this.buffer = buffer; - this.length = length; - this.set = set; - } - /// @internal - get type() { return NodeType.none; } - /// @internal - toString() { - let result = []; - for (let index = 0; index < this.buffer.length;) { - result.push(this.childString(index)); - index = this.buffer[index + 3]; +/** +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 result.join(","); + 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 { + /** + Create an empty builder. + */ + constructor() { + this.chunks = []; + this.chunkPos = []; + this.chunkStart = -1; + this.last = null; + this.lastFrom = -1000000000 /* Far */; + this.lastTo = -1000000000 /* Far */; + this.from = []; + this.to = []; + this.value = []; + this.maxPoint = -1; + this.setMaxPoint = -1; + this.nextLayer = null; } - /// @internal - childString(index) { - let id = this.buffer[index], endIndex = this.buffer[index + 3]; - let type = this.set.types[id], result = type.name; - if (/\W/.test(result) && !type.isError) - result = JSON.stringify(result); - index += 4; - if (endIndex == index) - return result; - let children = []; - while (index < endIndex) { - children.push(this.childString(index)); - index = this.buffer[index + 3]; + 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 = []; } - return result + "(" + children.join(",") + ")"; } - /// @internal - findChild(startIndex, endIndex, dir, pos, side) { - let { buffer } = this, pick = -1; - for (let i = startIndex; i != endIndex; i = buffer[i + 3]) { - if (checkSide(side, pos, buffer[i + 1], buffer[i + 2])) { - pick = i; - if (dir > 0) - break; - } - } - return pick; + /** + 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 - slice(startI, endI, from, to) { - let b = this.buffer; - let copy = new Uint16Array(endI - startI); - for (let i = startI, j = 0; i < endI;) { - copy[j++] = b[i++]; - copy[j++] = b[i++] - from; - copy[j++] = b[i++] - from; - copy[j++] = b[i++] - startI; - } - return new TreeBuffer(copy, to - from, this.set); + /** + @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 /* 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; } -} -function checkSide(side, pos, from, to) { - switch (side) { - case -2 /* Before */: return from < pos; - case -1 /* AtOrBefore */: return to >= pos && from < pos; - case 0 /* Around */: return from < pos && to > pos; - case 1 /* AtOrAfter */: return from <= pos && to > pos; - case 2 /* After */: return to > pos; - case 4 /* DontCare */: 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 = new RangeSet(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 enterUnfinishedNodesBefore(node, pos) { - let scan = node.childBefore(pos); - while (scan) { - let last = scan.lastChild; - if (!last || last.to != scan.to) - break; - if (last.type.isError && last.from == last.to) { - node = scan; - scan = last.prevSibling; - } - else { - scan = last; +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 node; + return shared; } -function resolveNode(node, pos, side, overlays) { - var _a; - // Move up to a node that actually holds the position, if possible - while (node.from == node.to || - (side < 1 ? node.from >= pos : node.from > pos) || - (side > -1 ? node.to <= pos : node.to < pos)) { - let parent = !overlays && node instanceof TreeNode && node.index < 0 ? null : node.parent; - if (!parent) - return node; - node = parent; +class LayerCursor { + constructor(layer, skip, minPoint, rank = 0) { + this.layer = layer; + this.skip = skip; + this.minPoint = minPoint; + this.rank = rank; } - // Must go up out of overlays when those do not overlap with pos - if (overlays) - for (let scan = node, parent = scan.parent; parent; scan = parent, parent = scan.parent) { - if (scan instanceof TreeNode && scan.index < 0 && ((_a = parent.enter(pos, side, true)) === null || _a === void 0 ? void 0 : _a.from) != scan.from) - node = parent; + get startSide() { return this.value ? this.value.startSide : 0; } + get endSide() { return this.value ? this.value.endSide : 0; } + goto(pos, side = -1000000000 /* 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; } - for (;;) { - let inner = node.enter(pos, side, overlays); - if (!inner) - return node; - node = inner; + 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(); } -} -class TreeNode { - constructor(node, _from, - // Index in parent node, set to -1 if the node is not a direct child of _parent.node (overlay) - index, _parent) { - this.node = node; - this._from = _from; - this.index = index; - this._parent = _parent; + forward(pos, side) { + if ((this.to - pos || this.endSide - side) < 0) + this.gotoInner(pos, side, true); } - get type() { return this.node.type; } - get name() { return this.node.type.name; } - get from() { return this._from; } - get to() { return this._from + this.node.length; } - nextChild(i, dir, pos, side, mode = 0) { - for (let parent = this;;) { - for (let { children, positions } = parent.node, e = dir > 0 ? children.length : -1; i != e; i += dir) { - let next = children[i], start = positions[i] + parent._from; - if (!checkSide(side, pos, start, start + next.length)) - continue; - if (next instanceof TreeBuffer) { - if (mode & 2 /* NoEnterBuffer */) - continue; - let index = next.findChild(0, next.buffer.length, dir, pos - start, side); - if (index > -1) - return new BufferNode(new BufferContext(parent, next, i, start), null, index); - } - else if ((mode & 1 /* Full */) || (!next.type.isAnonymous || hasChild(next))) { - let mounted; - if (!(mode & 1 /* Full */) && next.props && (mounted = next.prop(NodeProp.mounted)) && !mounted.overlay) - return new TreeNode(mounted.tree, start, i, parent); - let inner = new TreeNode(next, start, i, parent); - return (mode & 1 /* Full */) || !inner.type.isAnonymous ? inner - : inner.nextChild(dir < 0 ? next.children.length - 1 : 0, dir, pos, side); - } + next() { + for (;;) { + if (this.chunkIndex == this.layer.chunk.length) { + this.from = this.to = 1000000000 /* 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; } - if ((mode & 1 /* Full */) || !parent.type.isAnonymous) - return null; - if (parent.index >= 0) - i = parent.index + dir; - else - i = dir < 0 ? -1 : parent._parent.node.children.length; - parent = parent._parent; - if (!parent) - return null; } } - get firstChild() { return this.nextChild(0, 1, 0, 4 /* DontCare */); } - get lastChild() { return this.nextChild(this.node.children.length - 1, -1, 0, 4 /* DontCare */); } - childAfter(pos) { return this.nextChild(0, 1, pos, 2 /* After */); } - childBefore(pos) { return this.nextChild(this.node.children.length - 1, -1, pos, -2 /* Before */); } - enter(pos, side, overlays = true, buffers = true) { - let mounted; - if (overlays && (mounted = this.node.prop(NodeProp.mounted)) && mounted.overlay) { - let rPos = pos - this.from; - for (let { from, to } of mounted.overlay) { - if ((side > 0 ? from <= rPos : from < rPos) && - (side < 0 ? to >= rPos : to > rPos)) - return new TreeNode(mounted.tree, mounted.overlay[0].from + this.from, -1, this); + 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; } - return this.nextChild(0, 1, pos, side, buffers ? 0 : 2 /* NoEnterBuffer */); - } - nextSignificantParent() { - let val = this; - while (val.type.isAnonymous && val._parent) - val = val._parent; - return val; } - get parent() { - return this._parent ? this._parent.nextSignificantParent() : null; + nextChunk() { + this.chunkIndex++; + this.rangeIndex = 0; + this.next(); } - get nextSibling() { - return this._parent && this.index >= 0 ? this._parent.nextChild(this.index + 1, 1, 0, 4 /* DontCare */) : null; + compare(other) { + return this.from - other.from || this.startSide - other.startSide || this.rank - other.rank || + this.to - other.to || this.endSide - other.endSide; } - get prevSibling() { - return this._parent && this.index >= 0 ? this._parent.nextChild(this.index - 1, -1, 0, 4 /* DontCare */) : null; +} +class HeapCursor { + constructor(heap) { + this.heap = heap; } - get cursor() { return new TreeCursor(this); } - get tree() { return this.node; } - toTree() { return this.node; } - resolve(pos, side = 0) { - return resolveNode(this, pos, side, false); - } - resolveInner(pos, side = 0) { - return resolveNode(this, pos, side, true); + 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); } - enterUnfinishedNodesBefore(pos) { return enterUnfinishedNodesBefore(this, pos); } - getChild(type, before = null, after = null) { - let r = getChildren(this, type, before, after); - return r.length ? r[0] : null; + get startSide() { return this.value ? this.value.startSide : 0; } + goto(pos, side = -1000000000 /* 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; } - getChildren(type, before = null, after = null) { - return getChildren(this, type, before, after); + 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(); } - /// @internal - toString() { return this.node.toString(); } -} -function getChildren(node, type, before, after) { - let cur = node.cursor, result = []; - if (!cur.firstChild()) - return result; - if (before != null) - while (!cur.type.is(before)) - if (!cur.nextSibling()) - return result; - for (;;) { - if (after != null && cur.type.is(after)) - return result; - if (cur.type.is(type)) - result.push(cur.node); - if (!cur.nextSibling()) - return after == null ? result : []; + next() { + if (this.heap.length == 0) { + this.from = this.to = 1000000000 /* 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); + } } } -class BufferContext { - constructor(parent, buffer, index, start) { - this.parent = parent; - this.buffer = buffer; - this.index = index; - this.start = start; +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 BufferNode { - constructor(context, _parent, index) { - this.context = context; - this._parent = _parent; - this.index = index; - this.type = context.buffer.set.types[context.buffer.buffer[index]]; - } - get name() { return this.type.name; } - get from() { return this.context.start + this.context.buffer.buffer[this.index + 1]; } - get to() { return this.context.start + this.context.buffer.buffer[this.index + 2]; } - child(dir, pos, side) { - let { buffer } = this.context; - let index = buffer.findChild(this.index + 4, buffer.buffer[this.index + 3], dir, pos - this.context.start, side); - return index < 0 ? null : new BufferNode(this.context, this, index); - } - get firstChild() { return this.child(1, 0, 4 /* DontCare */); } - get lastChild() { return this.child(-1, 0, 4 /* DontCare */); } - childAfter(pos) { return this.child(1, pos, 2 /* After */); } - childBefore(pos) { return this.child(-1, pos, -2 /* Before */); } - enter(pos, side, overlays, buffers = true) { - if (!buffers) - return null; - let { buffer } = this.context; - let index = buffer.findChild(this.index + 4, buffer.buffer[this.index + 3], side > 0 ? 1 : -1, pos - this.context.start, side); - return index < 0 ? null : new BufferNode(this.context, this, index); +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 /* Far */; + this.endSide = 0; + this.openStart = -1; + this.cursor = HeapCursor.from(sets, skip, minPoint); } - get parent() { - return this._parent || this.context.parent.nextSignificantParent(); + goto(pos, side = -1000000000 /* 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; } - externalSibling(dir) { - return this._parent ? null : this.context.parent.nextChild(this.context.index + dir, dir, 0, 4 /* DontCare */); + 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); } - get nextSibling() { - let { buffer } = this.context; - let after = buffer.buffer[this.index + 3]; - if (after < (this._parent ? buffer.buffer[this._parent.index + 3] : buffer.buffer.length)) - return new BufferNode(this.context, this._parent, after); - return this.externalSibling(1); + removeActive(index) { + remove(this.active, index); + remove(this.activeTo, index); + remove(this.activeRank, index); + this.minActive = findMinIndex(this.active, this.activeTo); } - get prevSibling() { - let { buffer } = this.context; - let parentStart = this._parent ? this._parent.index + 4 : 0; - if (this.index == parentStart) - return this.externalSibling(-1); - return new BufferNode(this.context, this._parent, buffer.findChild(parentStart, this.index, -1, 0, 4 /* DontCare */)); + addActive(trackOpen) { + let i = 0, { value, to, rank } = this.cursor; + while (i < this.activeRank.length && this.activeRank[i] <= rank) + 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); } - get cursor() { return new TreeCursor(this); } - get tree() { return null; } - toTree() { - let children = [], positions = []; - let { buffer } = this.context; - let startI = this.index + 4, endI = buffer.buffer[this.index + 3]; - if (endI > startI) { - let from = buffer.buffer[this.index + 1], to = buffer.buffer[this.index + 2]; - children.push(buffer.slice(startI, endI, from, to)); - positions.push(0); + // 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, trackExtra = 0; + 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 /* 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; + if (this.cursor.from < from) + trackExtra = 1; + this.cursor.next(); + this.forward(this.to, this.endSide); + break; + } + } + } + if (trackOpen) { + let openStart = 0; + while (openStart < trackOpen.length && trackOpen[openStart] < from) + openStart++; + this.openStart = openStart + trackExtra; } - return new Tree(this.type, children, positions, this.to - this.from); } - resolve(pos, side = 0) { - return resolveNode(this, pos, side, false); + 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(); } - resolveInner(pos, side = 0) { - return resolveNode(this, pos, side, true); - } - enterUnfinishedNodesBefore(pos) { return enterUnfinishedNodesBefore(this, pos); } - /// @internal - toString() { return this.context.buffer.childString(this.index); } - getChild(type, before = null, after = null) { - let r = getChildren(this, type, before, after); - return r.length ? r[0] : null; - } - getChildren(type, before = null, after = null) { - return getChildren(this, type, before, after); + openEnd(to) { + let open = 0; + for (let i = this.activeTo.length - 1; i >= 0 && this.activeTo[i] > to; i--) + open++; + return open; } } -/// A tree cursor object focuses on a given node in a syntax tree, and -/// allows you to move to adjacent nodes. -class TreeCursor { - /// @internal - constructor(node, - /// @internal - mode = 0) { - this.mode = mode; - this.buffer = null; - this.stack = []; - this.index = 0; - this.bufferNode = null; - if (node instanceof TreeNode) { - this.yieldNode(node); +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 diff = (a.to + dPos) - b.to || 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 + dPos), b.activeForPoint(b.to)))) + comparator.comparePoint(pos, clipEnd, a.point, b.point); } else { - this._tree = node.context.parent; - this.buffer = node.context; - for (let n = node._parent; n; n = n._parent) - this.stack.unshift(n.index); - this.bufferNode = node; - this.yieldBuf(node.index); + if (clipEnd > pos && !sameValues(a.active, b.active)) + comparator.compareRange(pos, clipEnd, a.active, b.active); } + if (end > endB) + break; + pos = end; + if (diff <= 0) + a.next(); + if (diff >= 0) + b.next(); } - /// Shorthand for `.type.name`. - get name() { return this.type.name; } - yieldNode(node) { - if (!node) - return false; - this._tree = node; - this.type = node.type; - this.from = node.from; - this.to = node.to; - return true; - } - yieldBuf(index, type) { - this.index = index; - let { start, buffer } = this.buffer; - this.type = type || buffer.set.types[buffer.buffer[index]]; - this.from = start + buffer.buffer[index + 1]; - this.to = start + buffer.buffer[index + 2]; - return true; - } - yield(node) { - if (!node) +} +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; - if (node instanceof TreeNode) { - this.buffer = null; - return this.yieldNode(node); + 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 /* 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]; } - this.buffer = node.context; - return this.yieldBuf(node.index, node.type); - } - /// @internal - toString() { - return this.buffer ? this.buffer.buffer.childString(this.index) : this._tree.toString(); - } - /// @internal - enterChild(dir, pos, side) { - if (!this.buffer) - return this.yield(this._tree.nextChild(dir < 0 ? this._tree.node.children.length - 1 : 0, dir, pos, side, this.mode)); - let { buffer } = this.buffer; - let index = buffer.findChild(this.index + 4, buffer.buffer[this.index + 3], dir, pos - this.buffer.start, side); - if (index < 0) - return false; - this.stack.push(this.index); - return this.yieldBuf(index); - } - /// Move the cursor to this node's first child. When this returns - /// false, the node has no child, and the cursor has not been moved. - firstChild() { return this.enterChild(1, 0, 4 /* DontCare */); } - /// Move the cursor to this node's last child. - lastChild() { return this.enterChild(-1, 0, 4 /* DontCare */); } - /// Move the cursor to the first child that ends after `pos`. - childAfter(pos) { return this.enterChild(1, pos, 2 /* After */); } - /// Move to the last child that starts before `pos`. - childBefore(pos) { return this.enterChild(-1, pos, -2 /* Before */); } - /// Move the cursor to the child around `pos`. If side is -1 the - /// child may end at that position, when 1 it may start there. This - /// will also enter [overlaid](#common.MountedTree.overlay) - /// [mounted](#common.NodeProp^mounted) trees unless `overlays` is - /// set to false. - enter(pos, side, overlays = true, buffers = true) { - if (!this.buffer) - return this.yield(this._tree.enter(pos, side, overlays && !(this.mode & 1 /* Full */), buffers)); - return buffers ? this.enterChild(1, pos, side) : false; - } - /// Move to the node's parent node, if this isn't the top node. - parent() { - if (!this.buffer) - return this.yieldNode((this.mode & 1 /* Full */) ? this._tree._parent : this._tree.parent); - if (this.stack.length) - return this.yieldBuf(this.stack.pop()); - let parent = (this.mode & 1 /* Full */) ? this.buffer.parent : this.buffer.parent.nextSignificantParent(); - this.buffer = null; - return this.yieldNode(parent); - } - /// @internal - sibling(dir) { - if (!this.buffer) - return !this._tree._parent ? false - : this.yield(this._tree.index < 0 ? null - : this._tree._parent.nextChild(this._tree.index + dir, dir, 0, 4 /* DontCare */, this.mode)); - let { buffer } = this.buffer, d = this.stack.length - 1; - if (dir < 0) { - let parentStart = d < 0 ? 0 : this.stack[d] + 4; - if (this.index != parentStart) - return this.yieldBuf(buffer.findChild(parentStart, this.index, -1, 0, 4 /* DontCare */)); + 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 { - let after = buffer.buffer[this.index + 3]; - if (after < (d < 0 ? buffer.buffer.length : buffer.buffer[this.stack[d] + 3])) - return this.yieldBuf(after); + n++; + i = findClusterBreak(string, i); } - return d < 0 ? this.yield(this.buffer.parent.nextChild(this.buffer.index + dir, dir, 0, 4 /* DontCare */, this.mode)) : false; } - /// Move to this node's next sibling, if any. - nextSibling() { return this.sibling(1); } - /// Move to this node's previous sibling, if any. - prevSibling() { return this.sibling(-1); } - atLastNode(dir) { - let index, parent, { buffer } = this; - if (buffer) { - if (dir > 0) { - if (this.index < buffer.buffer.buffer.length) - return false; - } - else { - for (let i = 0; i < this.index; i++) - if (buffer.buffer.buffer[i + 3] < this.index) - return false; - } - ({ index, parent } = buffer); - } - else { - ({ index, _parent: parent } = this._tree); - } - for (; parent; { index, _parent: parent } = parent) { - if (index > -1) - for (let i = index + dir, e = dir < 0 ? -1 : parent.node.children.length; i != e; i += dir) { - let child = parent.node.children[i]; - if ((this.mode & 1 /* Full */) || child instanceof TreeBuffer || !child.type.isAnonymous || hasChild(child)) - return false; - } - } - return true; + 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); } - move(dir, enter) { - if (enter && this.enterChild(dir, 0, 4 /* DontCare */)) - return true; - for (;;) { - if (this.sibling(dir)) - return true; - if (this.atLastNode(dir) || !this.parent()) - return false; - } + return strict === true ? -1 : string.length; +} + +// FIXME profile adding a per-Tree TreeNode cache, validating it by +// parent pointer +/// The default maximum length of a `TreeBuffer` node (1024). +const DefaultBufferLength = 1024; +let nextPropID = 0; +class Range { + constructor(from, to) { + this.from = from; + this.to = to; } - /// Move to the next node in a - /// [pre-order](https://en.wikipedia.org/wiki/Tree_traversal#Pre-order_(NLR)) - /// traversal, going from a node to its first child or, if the - /// current node is empty or `enter` is false, its next sibling or - /// the next sibling of the first parent node that has one. - next(enter = true) { return this.move(1, enter); } - /// Move to the next node in a last-to-first pre-order traveral. A - /// node is followed by its last child or, if it has none, its - /// previous sibling or the previous sibling of the first parent - /// node that has one. - prev(enter = true) { return this.move(-1, enter); } - /// Move the cursor to the innermost node that covers `pos`. If - /// `side` is -1, it will enter nodes that end at `pos`. If it is 1, - /// it will enter nodes that start at `pos`. - moveTo(pos, side = 0) { - // Move up to a node that actually holds the position, if possible - while (this.from == this.to || - (side < 1 ? this.from >= pos : this.from > pos) || - (side > -1 ? this.to <= pos : this.to < pos)) - if (!this.parent()) - break; - // Then scan down into child nodes as far as possible - while (this.enterChild(1, pos, side)) { } - return this; - } - /// Get a [syntax node](#common.SyntaxNode) at the cursor's current - /// position. - get node() { - if (!this.buffer) - return this._tree; - let cache = this.bufferNode, result = null, depth = 0; - if (cache && cache.context == this.buffer) { - scan: for (let index = this.index, d = this.stack.length; d >= 0;) { - for (let c = cache; c; c = c._parent) - if (c.index == index) { - if (index == this.index) - return c; - result = c; - depth = d + 1; - break scan; - } - index = this.stack[--d]; - } - } - for (let i = depth; i < this.stack.length; i++) - result = new BufferNode(this.buffer, result, this.stack[i]); - return this.bufferNode = new BufferNode(this.buffer, result, this.index); +} +/// Each [node type](#common.NodeType) or [individual tree](#common.Tree) +/// can have metadata associated with it in props. Instances of this +/// class represent prop names. +class NodeProp { + /// Create a new node prop type. + constructor(config = {}) { + this.id = nextPropID++; + this.perNode = !!config.perNode; + this.deserialize = config.deserialize || (() => { + throw new Error("This node type doesn't define a deserialize function"); + }); } - /// Get the [tree](#common.Tree) that represents the current node, if - /// any. Will return null when the node is in a [tree - /// buffer](#common.TreeBuffer). - get tree() { - return this.buffer ? null : this._tree.node; + /// This is meant to be used with + /// [`NodeSet.extend`](#common.NodeSet.extend) or + /// [`LRParser.configure`](#lr.ParserConfig.props) to compute + /// prop values for each node type in the set. Takes a [match + /// object](#common.NodeType^match) or function that returns undefined + /// if the node type doesn't get this prop, and the prop's value if + /// it does. + add(match) { + if (this.perNode) + throw new RangeError("Can't add per-node props to node types"); + if (typeof match != "function") + match = NodeType.match(match); + return (type) => { + let result = match(type); + return result === undefined ? null : [this, result]; + }; } } -function hasChild(tree) { - return tree.children.some(ch => ch instanceof TreeBuffer || !ch.type.isAnonymous || hasChild(ch)); +/// Prop that is used to describe matching delimiters. For opening +/// delimiters, this holds an array of node names (written as a +/// space-separated string when declaring this prop in a grammar) +/// for the node types of closing delimiters that match it. +NodeProp.closedBy = new NodeProp({ deserialize: str => str.split(" ") }); +/// The inverse of [`closedBy`](#common.NodeProp^closedBy). This is +/// attached to closing delimiters, holding an array of node names +/// of types of matching opening delimiters. +NodeProp.openedBy = new NodeProp({ deserialize: str => str.split(" ") }); +/// Used to assign node types to groups (for example, all node +/// types that represent an expression could be tagged with an +/// `"Expression"` group). +NodeProp.group = new NodeProp({ deserialize: str => str.split(" ") }); +/// The hash of the [context](#lr.ContextTracker.constructor) +/// that the node was parsed in, if any. Used to limit reuse of +/// contextual nodes. +NodeProp.contextHash = new NodeProp({ perNode: true }); +/// The distance beyond the end of the node that the tokenizer +/// looked ahead for any of the tokens inside the node. (The LR +/// parser only stores this when it is larger than 25, for +/// efficiency reasons.) +NodeProp.lookAhead = new NodeProp({ perNode: true }); +/// This per-node prop is used to replace a given node, or part of a +/// node, with another tree. This is useful to include trees from +/// different languages. +NodeProp.mounted = new NodeProp({ perNode: true }); +/// A mounted tree, which can be [stored](#common.NodeProp^mounted) on +/// a tree node to indicate that parts of its content are +/// represented by another tree. +class MountedTree { + constructor( + /// The inner tree. + tree, + /// If this is null, this tree replaces the entire node (it will + /// be included in the regular iteration instead of its host + /// node). If not, only the given ranges are considered to be + /// covered by this tree. This is used for trees that are mixed in + /// a way that isn't strictly hierarchical. Such mounted trees are + /// only entered by [`resolveInner`](#common.Tree.resolveInner) + /// and [`enter`](#common.SyntaxNode.enter). + overlay, + /// The parser used to create this subtree. + parser) { + this.tree = tree; + this.overlay = overlay; + this.parser = parser; + } } -function buildTree(data) { - var _a; - let { buffer, nodeSet, maxBufferLength = DefaultBufferLength, reused = [], minRepeatType = nodeSet.types.length } = data; - let cursor = Array.isArray(buffer) ? new FlatBufferCursor(buffer, buffer.length) : buffer; - let types = nodeSet.types; - let contextHash = 0, lookAhead = 0; - function takeNode(parentStart, minPos, children, positions, inRepeat) { - let { id, start, end, size } = cursor; - let lookAheadAtStart = lookAhead; - while (size < 0) { - cursor.next(); - if (size == -1 /* Reuse */) { - let node = reused[id]; - children.push(node); - positions.push(start - parentStart); - return; - } - else if (size == -3 /* ContextChange */) { // Context change - contextHash = id; - return; - } - else if (size == -4 /* LookAhead */) { - lookAhead = id; - return; - } - else { - throw new RangeError(`Unrecognized record size: ${size}`); - } - } - let type = types[id], node, buffer; - let startPos = start - parentStart; - if (end - start <= maxBufferLength && (buffer = findBufferSize(cursor.pos - minPos, inRepeat))) { - // Small enough for a buffer, and no reused nodes inside - let data = new Uint16Array(buffer.size - buffer.skip); - let endPos = cursor.pos - buffer.size, index = data.length; - while (cursor.pos > endPos) - index = copyToBuffer(buffer.start, data, index); - node = new TreeBuffer(data, end - buffer.start, nodeSet); - startPos = buffer.start - parentStart; - } - else { // Make it a node - let endPos = cursor.pos - size; - cursor.next(); - let localChildren = [], localPositions = []; - let localInRepeat = id >= minRepeatType ? id : -1; - let lastGroup = 0, lastEnd = end; - while (cursor.pos > endPos) { - if (localInRepeat >= 0 && cursor.id == localInRepeat && cursor.size >= 0) { - if (cursor.end <= lastEnd - maxBufferLength) { - makeRepeatLeaf(localChildren, localPositions, start, lastGroup, cursor.end, lastEnd, localInRepeat, lookAheadAtStart); - lastGroup = localChildren.length; - lastEnd = cursor.end; - } - cursor.next(); - } - else { - takeNode(start, endPos, localChildren, localPositions, localInRepeat); - } - } - if (localInRepeat >= 0 && lastGroup > 0 && lastGroup < localChildren.length) - makeRepeatLeaf(localChildren, localPositions, start, lastGroup, start, lastEnd, localInRepeat, lookAheadAtStart); - localChildren.reverse(); - localPositions.reverse(); - if (localInRepeat > -1 && lastGroup > 0) { - let make = makeBalanced(type); - node = balanceRange(type, localChildren, localPositions, 0, localChildren.length, 0, end - start, make, make); - } - else { - node = makeTree(type, localChildren, localPositions, end - start, lookAheadAtStart - end); - } - } - children.push(node); - positions.push(startPos); +const noProps = Object.create(null); +/// Each node in a syntax tree has a node type associated with it. +class NodeType { + /// @internal + constructor( + /// The name of the node type. Not necessarily unique, but if the + /// grammar was written properly, different node types with the + /// same name within a node set should play the same semantic + /// role. + name, + /// @internal + props, + /// The id of this node in its set. Corresponds to the term ids + /// used in the parser. + id, + /// @internal + flags = 0) { + this.name = name; + this.props = props; + this.id = id; + this.flags = flags; } - function makeBalanced(type) { - return (children, positions, length) => { - let lookAhead = 0, lastI = children.length - 1, last, lookAheadProp; - if (lastI >= 0 && (last = children[lastI]) instanceof Tree) { - if (!lastI && last.type == type && last.length == length) - return last; - if (lookAheadProp = last.prop(NodeProp.lookAhead)) - lookAhead = positions[lastI] + last.length + lookAheadProp; + static define(spec) { + let props = spec.props && spec.props.length ? Object.create(null) : noProps; + let flags = (spec.top ? 1 /* Top */ : 0) | (spec.skipped ? 2 /* Skipped */ : 0) | + (spec.error ? 4 /* Error */ : 0) | (spec.name == null ? 8 /* Anonymous */ : 0); + let type = new NodeType(spec.name || "", props, spec.id, flags); + if (spec.props) + for (let src of spec.props) { + if (!Array.isArray(src)) + src = src(type); + if (src) { + if (src[0].perNode) + throw new RangeError("Can't store a per-node prop on a node type"); + props[src[0].id] = src[1]; + } } - return makeTree(type, children, positions, length, lookAhead); - }; + return type; } - function makeRepeatLeaf(children, positions, base, i, from, to, type, lookAhead) { - let localChildren = [], localPositions = []; - while (children.length > i) { - localChildren.push(children.pop()); - localPositions.push(positions.pop() + base - from); + /// Retrieves a node prop for this type. Will return `undefined` if + /// the prop isn't present on this node. + prop(prop) { return this.props[prop.id]; } + /// True when this is the top node of a grammar. + get isTop() { return (this.flags & 1 /* Top */) > 0; } + /// True when this node is produced by a skip rule. + get isSkipped() { return (this.flags & 2 /* Skipped */) > 0; } + /// Indicates whether this is an error node. + get isError() { return (this.flags & 4 /* Error */) > 0; } + /// When true, this node type doesn't correspond to a user-declared + /// named node, for example because it is used to cache repetition. + get isAnonymous() { return (this.flags & 8 /* Anonymous */) > 0; } + /// Returns true when this node's name or one of its + /// [groups](#common.NodeProp^group) matches the given string. + is(name) { + if (typeof name == 'string') { + if (this.name == name) + return true; + let group = this.prop(NodeProp.group); + return group ? group.indexOf(name) > -1 : false; } - children.push(makeTree(nodeSet.types[type], localChildren, localPositions, to - from, lookAhead - to)); - positions.push(from - base); + return this.id == name; } - function makeTree(type, children, positions, length, lookAhead = 0, props) { - if (contextHash) { - let pair = [NodeProp.contextHash, contextHash]; - props = props ? [pair].concat(props) : [pair]; - } - if (lookAhead > 25) { - let pair = [NodeProp.lookAhead, lookAhead]; - props = props ? [pair].concat(props) : [pair]; - } - return new Tree(type, children, positions, length, props); - } - function findBufferSize(maxSize, inRepeat) { - // Scan through the buffer to find previous siblings that fit - // together in a TreeBuffer, and don't contain any reused nodes - // (which can't be stored in a buffer). - // If `inRepeat` is > -1, ignore node boundaries of that type for - // nesting, but make sure the end falls either at the start - // (`maxSize`) or before such a node. - let fork = cursor.fork(); - let size = 0, start = 0, skip = 0, minStart = fork.end - maxBufferLength; - let result = { size: 0, start: 0, skip: 0 }; - scan: for (let minPos = fork.pos - maxSize; fork.pos > minPos;) { - let nodeSize = fork.size; - // Pretend nested repeat nodes of the same type don't exist - if (fork.id == inRepeat && nodeSize >= 0) { - // Except that we store the current state as a valid return - // value. - result.size = size; - result.start = start; - result.skip = skip; - skip += 4; - size += 4; - fork.next(); - continue; + /// Create a function from node types to arbitrary values by + /// specifying an object whose property names are node or + /// [group](#common.NodeProp^group) names. Often useful with + /// [`NodeProp.add`](#common.NodeProp.add). You can put multiple + /// names, separated by spaces, in a single property name to map + /// multiple node names to a single value. + static match(map) { + let direct = Object.create(null); + for (let prop in map) + for (let name of prop.split(" ")) + direct[name] = map[prop]; + return (node) => { + for (let groups = node.prop(NodeProp.group), i = -1; i < (groups ? groups.length : 0); i++) { + let found = direct[i < 0 ? node.name : groups[i]]; + if (found) + return found; } - let startPos = fork.pos - nodeSize; - if (nodeSize < 0 || startPos < minPos || fork.start < minStart) - break; - let localSkipped = fork.id >= minRepeatType ? 4 : 0; - let nodeStart = fork.start; - fork.next(); - while (fork.pos > startPos) { - if (fork.size < 0) { - if (fork.size == -3 /* ContextChange */) - localSkipped += 4; - else - break scan; - } - else if (fork.id >= minRepeatType) { - localSkipped += 4; + }; + } +} +/// An empty dummy node type to use when no actual type is available. +NodeType.none = new NodeType("", Object.create(null), 0, 8 /* Anonymous */); +/// A node set holds a collection of node types. It is used to +/// compactly represent trees by storing their type ids, rather than a +/// full pointer to the type object, in a numeric array. Each parser +/// [has](#lr.LRParser.nodeSet) a node set, and [tree +/// buffers](#common.TreeBuffer) can only store collections of nodes +/// from the same set. A set can have a maximum of 2**16 (65536) node +/// types in it, so that the ids fit into 16-bit typed array slots. +class NodeSet { + /// Create a set with the given types. The `id` property of each + /// type should correspond to its position within the array. + constructor( + /// The node types in this set, by id. + types) { + this.types = types; + for (let i = 0; i < types.length; i++) + if (types[i].id != i) + throw new RangeError("Node type ids should correspond to array positions when creating a node set"); + } + /// Create a copy of this set with some node properties added. The + /// arguments to this method should be created with + /// [`NodeProp.add`](#common.NodeProp.add). + extend(...props) { + let newTypes = []; + for (let type of this.types) { + let newProps = null; + for (let source of props) { + let add = source(type); + if (add) { + if (!newProps) + newProps = Object.assign({}, type.props); + newProps[add[0].id] = add[1]; } - fork.next(); } - start = nodeStart; - size += nodeSize; - skip += localSkipped; + newTypes.push(newProps ? new NodeType(type.name, newProps, type.id, type.flags) : type); } - if (inRepeat < 0 || size == maxSize) { - result.size = size; - result.start = start; - result.skip = skip; + return new NodeSet(newTypes); + } +} +const CachedNode = new WeakMap(), CachedInnerNode = new WeakMap(); +/// Options that control iteration. Can be combined with the `|` +/// operator to enable multiple ones. +var IterMode; +(function (IterMode) { + /// When enabled, iteration will only visit [`Tree`](#common.Tree) + /// objects, not nodes packed into + /// [`TreeBuffer`](#common.TreeBuffer)s. + IterMode[IterMode["ExcludeBuffers"] = 1] = "ExcludeBuffers"; + /// Enable this to make iteration include anonymous nodes (such as + /// the nodes that wrap repeated grammar constructs into a balanced + /// tree). + IterMode[IterMode["IncludeAnonymous"] = 2] = "IncludeAnonymous"; + /// By default, regular [mounted](#common.NodeProp^mounted) nodes + /// replace their base node in iteration. Enable this to ignore them + /// instead. + IterMode[IterMode["IgnoreMounts"] = 4] = "IgnoreMounts"; + /// This option only applies in + /// [`enter`](#common.SyntaxNode.enter)-style methods. It tells the + /// library to not enter mounted overlays if one covers the given + /// position. + IterMode[IterMode["IgnoreOverlays"] = 8] = "IgnoreOverlays"; +})(IterMode || (IterMode = {})); +/// A piece of syntax tree. There are two ways to approach these +/// trees: the way they are actually stored in memory, and the +/// convenient way. +/// +/// Syntax trees are stored as a tree of `Tree` and `TreeBuffer` +/// objects. By packing detail information into `TreeBuffer` leaf +/// nodes, the representation is made a lot more memory-efficient. +/// +/// However, when you want to actually work with tree nodes, this +/// representation is very awkward, so most client code will want to +/// use the [`TreeCursor`](#common.TreeCursor) or +/// [`SyntaxNode`](#common.SyntaxNode) interface instead, which provides +/// a view on some part of this data structure, and can be used to +/// move around to adjacent nodes. +class Tree { + /// Construct a new tree. See also [`Tree.build`](#common.Tree^build). + constructor( + /// The type of the top node. + type, + /// This node's child nodes. + children, + /// The positions (offsets relative to the start of this tree) of + /// the children. + positions, + /// The total length of this tree + length, + /// Per-node [node props](#common.NodeProp) to associate with this node. + props) { + this.type = type; + this.children = children; + this.positions = positions; + this.length = length; + /// @internal + this.props = null; + if (props && props.length) { + this.props = Object.create(null); + for (let [prop, value] of props) + this.props[typeof prop == "number" ? prop : prop.id] = value; } - return result.size > 4 ? result : undefined; } - function copyToBuffer(bufferStart, buffer, index) { - let { id, start, end, size } = cursor; - cursor.next(); - if (size >= 0 && id < minRepeatType) { - let startIndex = index; - if (size > 4) { - let endPos = cursor.pos - (size - 4); - while (cursor.pos > endPos) - index = copyToBuffer(bufferStart, buffer, index); + /// @internal + toString() { + let mounted = this.prop(NodeProp.mounted); + if (mounted && !mounted.overlay) + return mounted.tree.toString(); + let children = ""; + for (let ch of this.children) { + let str = ch.toString(); + if (str) { + if (children) + children += ","; + children += str; } - buffer[--index] = startIndex; - buffer[--index] = end - bufferStart; - buffer[--index] = start - bufferStart; - buffer[--index] = id; - } - else if (size == -3 /* ContextChange */) { - contextHash = id; - } - else if (size == -4 /* LookAhead */) { - lookAhead = id; } - return index; + return !this.type.name ? children : + (/\W/.test(this.type.name) && !this.type.isError ? JSON.stringify(this.type.name) : this.type.name) + + (children.length ? "(" + children + ")" : ""); } - let children = [], positions = []; - while (cursor.pos > 0) - takeNode(data.start || 0, data.bufferStart || 0, children, positions, -1); - let length = (_a = data.length) !== null && _a !== void 0 ? _a : (children.length ? positions[0] + children[0].length : 0); - return new Tree(types[data.topID], children.reverse(), positions.reverse(), length); -} -const nodeSizeCache = new WeakMap; -function nodeSize(balanceType, node) { - if (!balanceType.isAnonymous || node instanceof TreeBuffer || node.type != balanceType) - return 1; - let size = nodeSizeCache.get(node); - if (size == null) { - size = 1; - for (let child of node.children) { - if (child.type != balanceType || !(child instanceof Tree)) { - size = 1; - break; + /// Get a [tree cursor](#common.TreeCursor) positioned at the top of + /// the tree. Mode can be used to [control](#common.IterMode) which + /// nodes the cursor visits. + cursor(mode = 0) { + return new TreeCursor(this.topNode, mode); + } + /// Get a [tree cursor](#common.TreeCursor) pointing into this tree + /// at the given position and side (see + /// [`moveTo`](#common.TreeCursor.moveTo). + cursorAt(pos, side = 0, mode = 0) { + let scope = CachedNode.get(this) || this.topNode; + let cursor = new TreeCursor(scope); + cursor.moveTo(pos, side); + CachedNode.set(this, cursor._tree); + return cursor; + } + /// Get a [syntax node](#common.SyntaxNode) object for the top of the + /// tree. + get topNode() { + return new TreeNode(this, 0, 0, null); + } + /// Get the [syntax node](#common.SyntaxNode) at the given position. + /// If `side` is -1, this will move into nodes that end at the + /// position. If 1, it'll move into nodes that start at the + /// position. With 0, it'll only enter nodes that cover the position + /// from both sides. + resolve(pos, side = 0) { + let node = resolveNode(CachedNode.get(this) || this.topNode, pos, side, false); + CachedNode.set(this, node); + return node; + } + /// Like [`resolve`](#common.Tree.resolve), but will enter + /// [overlaid](#common.MountedTree.overlay) nodes, producing a syntax node + /// pointing into the innermost overlaid tree at the given position + /// (with parent links going through all parent structure, including + /// the host trees). + resolveInner(pos, side = 0) { + let node = resolveNode(CachedInnerNode.get(this) || this.topNode, pos, side, true); + CachedInnerNode.set(this, node); + return node; + } + /// Iterate over the tree and its children, calling `enter` for any + /// node that touches the `from`/`to` region (if given) before + /// running over such a node's children, and `leave` (if given) when + /// leaving the node. When `enter` returns `false`, that node will + /// not have its children iterated over (or `leave` called). + iterate(spec) { + let { enter, leave, from = 0, to = this.length } = spec; + for (let c = this.cursor((spec.mode || 0) | IterMode.IncludeAnonymous);;) { + let entered = false; + if (c.from <= to && c.to >= from && (c.type.isAnonymous || enter(c) !== false)) { + if (c.firstChild()) + continue; + entered = true; + } + for (;;) { + if (entered && leave && !c.type.isAnonymous) + leave(c); + if (c.nextSibling()) + break; + if (!c.parent()) + return; + entered = true; } - size += nodeSize(balanceType, child); } - nodeSizeCache.set(node, size); } - return size; -} -function balanceRange( -// The type the balanced tree's inner nodes. -balanceType, -// The direct children and their positions -children, positions, -// The index range in children/positions to use -from, to, -// The start position of the nodes, relative to their parent. -start, -// Length of the outer node -length, -// Function to build the top node of the balanced tree -mkTop, -// Function to build internal nodes for the balanced tree -mkTree) { - let total = 0; - for (let i = from; i < to; i++) - total += nodeSize(balanceType, children[i]); - let maxChild = Math.ceil((total * 1.5) / 8 /* BranchFactor */); - let localChildren = [], localPositions = []; - function divide(children, positions, from, to, offset) { - for (let i = from; i < to;) { - let groupFrom = i, groupStart = positions[i], groupSize = nodeSize(balanceType, children[i]); - i++; - for (; i < to; i++) { - let nextSize = nodeSize(balanceType, children[i]); - if (groupSize + nextSize >= maxChild) - break; - groupSize += nextSize; - } - if (i == groupFrom + 1) { - if (groupSize > maxChild) { - let only = children[groupFrom]; // Only trees can have a size > 1 - divide(only.children, only.positions, 0, only.children.length, positions[groupFrom] + offset); - continue; - } - localChildren.push(children[groupFrom]); - } - else { - let length = positions[i - 1] + children[i - 1].length - groupStart; - localChildren.push(balanceRange(balanceType, children, positions, groupFrom, i, groupStart, length, null, mkTree)); - } - localPositions.push(groupStart + offset - start); - } + /// Get the value of the given [node prop](#common.NodeProp) for this + /// node. Works with both per-node and per-type props. + prop(prop) { + return !prop.perNode ? this.type.prop(prop) : this.props ? this.props[prop.id] : undefined; } - divide(children, positions, from, to, 0); - return (mkTop || mkTree)(localChildren, localPositions, length); + /// Returns the node's [per-node props](#common.NodeProp.perNode) in a + /// format that can be passed to the [`Tree`](#common.Tree) + /// constructor. + get propValues() { + let result = []; + if (this.props) + for (let id in this.props) + result.push([+id, this.props[id]]); + return result; + } + /// Balance the direct children of this tree, producing a copy of + /// which may have children grouped into subtrees with type + /// [`NodeType.none`](#common.NodeType^none). + balance(config = {}) { + return this.children.length <= 8 /* BranchFactor */ ? this : + balanceRange(NodeType.none, this.children, this.positions, 0, this.children.length, 0, this.length, (children, positions, length) => new Tree(this.type, children, positions, length, this.propValues), config.makeTree || ((children, positions, length) => new Tree(NodeType.none, children, positions, length))); + } + /// Build a tree from a postfix-ordered buffer of node information, + /// or a cursor over such a buffer. + static build(data) { return buildTree(data); } } - -/// Tree fragments are used during [incremental -/// parsing](#common.Parser.startParse) to track parts of old trees -/// that can be reused in a new parse. An array of fragments is used -/// to track regions of an old tree whose nodes might be reused in new -/// parses. Use the static -/// [`applyChanges`](#common.TreeFragment^applyChanges) method to -/// update fragments for document changes. -class TreeFragment { - /// Construct a tree fragment. - constructor( - /// The start of the unchanged range pointed to by this fragment. - /// This refers to an offset in the _updated_ document (as opposed - /// to the original tree). - from, - /// The end of the unchanged range. - to, - /// The tree that this fragment is based on. - tree, - /// The offset between the fragment's tree and the document that - /// this fragment can be used against. Add this when going from - /// document to tree positions, subtract it to go from tree to - /// document positions. - offset, openStart = false, openEnd = false) { - this.from = from; - this.to = to; - this.tree = tree; - this.offset = offset; - this.open = (openStart ? 1 /* Start */ : 0) | (openEnd ? 2 /* End */ : 0); +/// The empty tree +Tree.empty = new Tree(NodeType.none, [], [], 0); +class FlatBufferCursor { + constructor(buffer, index) { + this.buffer = buffer; + this.index = index; } - /// Whether the start of the fragment represents the start of a - /// parse, or the end of a change. (In the second case, it may not - /// be safe to reuse some nodes at the start, depending on the - /// parsing algorithm.) - get openStart() { return (this.open & 1 /* Start */) > 0; } - /// Whether the end of the fragment represents the end of a - /// full-document parse, or the start of a change. - get openEnd() { return (this.open & 2 /* End */) > 0; } - /// Create a set of fragments from a freshly parsed tree, or update - /// an existing set of fragments by replacing the ones that overlap - /// with a tree with content from the new tree. When `partial` is - /// true, the parse is treated as incomplete, and the resulting - /// fragment has [`openEnd`](#common.TreeFragment.openEnd) set to - /// true. - static addTree(tree, fragments = [], partial = false) { - let result = [new TreeFragment(0, tree.length, tree, 0, false, partial)]; - for (let f of fragments) - if (f.to > tree.length) - result.push(f); - return result; + get id() { return this.buffer[this.index - 4]; } + get start() { return this.buffer[this.index - 3]; } + get end() { return this.buffer[this.index - 2]; } + get size() { return this.buffer[this.index - 1]; } + get pos() { return this.index; } + next() { this.index -= 4; } + fork() { return new FlatBufferCursor(this.buffer, this.index); } +} +/// Tree buffers contain (type, start, end, endIndex) quads for each +/// node. In such a buffer, nodes are stored in prefix order (parents +/// before children, with the endIndex of the parent indicating which +/// children belong to it) +class TreeBuffer { + /// Create a tree buffer. + constructor( + /// The buffer's content. + buffer, + /// The total length of the group of nodes in the buffer. + length, + /// The node set used in this buffer. + set) { + this.buffer = buffer; + this.length = length; + this.set = set; } - /// Apply a set of edits to an array of fragments, removing or - /// splitting fragments as necessary to remove edited ranges, and - /// adjusting offsets for fragments that moved. - static applyChanges(fragments, changes, minGap = 128) { - if (!changes.length) - return fragments; + /// @internal + get type() { return NodeType.none; } + /// @internal + toString() { let result = []; - let fI = 1, nextF = fragments.length ? fragments[0] : null; - for (let cI = 0, pos = 0, off = 0;; cI++) { - let nextC = cI < changes.length ? changes[cI] : null; - let nextPos = nextC ? nextC.fromA : 1e9; - if (nextPos - pos >= minGap) - while (nextF && nextF.from < nextPos) { - let cut = nextF; - if (pos >= cut.from || nextPos <= cut.to || off) { - let fFrom = Math.max(cut.from, pos) - off, fTo = Math.min(cut.to, nextPos) - off; - cut = fFrom >= fTo ? null : new TreeFragment(fFrom, fTo, cut.tree, cut.offset + off, cI > 0, !!nextC); - } - if (cut) - result.push(cut); - if (nextF.to > nextPos) - break; - nextF = fI < fragments.length ? fragments[fI++] : null; - } - if (!nextC) - break; - pos = nextC.toA; - off = nextC.toA - nextC.toB; + for (let index = 0; index < this.buffer.length;) { + result.push(this.childString(index)); + index = this.buffer[index + 3]; } - return result; + return result.join(","); } -} -/// A superclass that parsers should extend. -class Parser { - /// Start a parse, returning a [partial parse](#common.PartialParse) - /// object. [`fragments`](#common.TreeFragment) can be passed in to - /// make the parse incremental. - /// - /// By default, the entire input is parsed. You can pass `ranges`, - /// which should be a sorted array of non-empty, non-overlapping - /// ranges, to parse only those ranges. The tree returned in that - /// case will start at `ranges[0].from`. - startParse(input, fragments, ranges) { - if (typeof input == "string") - input = new StringInput(input); - ranges = !ranges ? [new Range$1(0, input.length)] : ranges.length ? ranges.map(r => new Range$1(r.from, r.to)) : [new Range$1(0, 0)]; - return this.createParse(input, fragments || [], ranges); + /// @internal + childString(index) { + let id = this.buffer[index], endIndex = this.buffer[index + 3]; + let type = this.set.types[id], result = type.name; + if (/\W/.test(result) && !type.isError) + result = JSON.stringify(result); + index += 4; + if (endIndex == index) + return result; + let children = []; + while (index < endIndex) { + children.push(this.childString(index)); + index = this.buffer[index + 3]; + } + return result + "(" + children.join(",") + ")"; } - /// Run a full parse, returning the resulting tree. - parse(input, fragments, ranges) { - let parse = this.startParse(input, fragments, ranges); - for (;;) { - let done = parse.advance(); - if (done) - return done; + /// @internal + findChild(startIndex, endIndex, dir, pos, side) { + let { buffer } = this, pick = -1; + for (let i = startIndex; i != endIndex; i = buffer[i + 3]) { + if (checkSide(side, pos, buffer[i + 1], buffer[i + 2])) { + pick = i; + if (dir > 0) + break; + } + } + return pick; + } + /// @internal + slice(startI, endI, from, to) { + let b = this.buffer; + let copy = new Uint16Array(endI - startI); + for (let i = startI, j = 0; i < endI;) { + copy[j++] = b[i++]; + copy[j++] = b[i++] - from; + copy[j++] = b[i++] - from; + copy[j++] = b[i++] - startI; } + return new TreeBuffer(copy, to - from, this.set); } } -class StringInput { - constructor(string) { - this.string = string; +function checkSide(side, pos, from, to) { + switch (side) { + case -2 /* Before */: return from < pos; + case -1 /* AtOrBefore */: return to >= pos && from < pos; + case 0 /* Around */: return from < pos && to > pos; + case 1 /* AtOrAfter */: return from <= pos && to > pos; + case 2 /* After */: return to > pos; + case 4 /* DontCare */: return true; } - get length() { return this.string.length; } - chunk(from) { return this.string.slice(from); } - get lineChunks() { return false; } - read(from, to) { return this.string.slice(from, to); } } - -/// Create a parse wrapper that, after the inner parse completes, -/// scans its tree for mixed language regions with the `nest` -/// function, runs the resulting [inner parses](#common.NestedParse), -/// and then [mounts](#common.NodeProp^mounted) their results onto the -/// tree. -/// -/// The nesting function is passed a cursor to provide context for a -/// node, but _should not_ move that cursor, only inspect its -/// properties and optionally access its -/// [node object](#common.TreeCursor.node). -function parseMixed(nest) { - return (parse, input, fragments, ranges) => new MixedParse(parse, nest, input, fragments, ranges); -} -class InnerParse { - constructor(parser, parse, overlay, target, ranges) { - this.parser = parser; - this.parse = parse; - this.overlay = overlay; - this.target = target; - this.ranges = ranges; - } -} -class ActiveOverlay { - constructor(parser, predicate, mounts, index, start, target, prev) { - this.parser = parser; - this.predicate = predicate; - this.mounts = mounts; - this.index = index; - this.start = start; - this.target = target; - this.prev = prev; - this.depth = 0; - this.ranges = []; - } -} -const stoppedInner = new NodeProp({ perNode: true }); -class MixedParse { - constructor(base, nest, input, fragments, ranges) { - this.nest = nest; - this.input = input; - this.fragments = fragments; - this.ranges = ranges; - this.inner = []; - this.innerDone = 0; - this.baseTree = null; - this.stoppedAt = null; - this.baseParse = base; - } - advance() { - if (this.baseParse) { - let done = this.baseParse.advance(); - if (!done) - return null; - this.baseParse = null; - this.baseTree = done; - this.startInner(); - if (this.stoppedAt != null) - for (let inner of this.inner) - inner.parse.stopAt(this.stoppedAt); - } - if (this.innerDone == this.inner.length) { - let result = this.baseTree; - if (this.stoppedAt != null) - result = new Tree(result.type, result.children, result.positions, result.length, result.propValues.concat([[stoppedInner, this.stoppedAt]])); - return result; +function enterUnfinishedNodesBefore(node, pos) { + let scan = node.childBefore(pos); + while (scan) { + let last = scan.lastChild; + if (!last || last.to != scan.to) + break; + if (last.type.isError && last.from == last.to) { + node = scan; + scan = last.prevSibling; } - let inner = this.inner[this.innerDone], done = inner.parse.advance(); - if (done) { - this.innerDone++; - // This is a somewhat dodgy but super helpful hack where we - // patch up nodes created by the inner parse (and thus - // presumably not aliased anywhere else) to hold the information - // about the inner parse. - let props = Object.assign(Object.create(null), inner.target.props); - props[NodeProp.mounted.id] = new MountedTree(done, inner.overlay, inner.parser); - inner.target.props = props; + else { + scan = last; } - return null; } - get parsedPos() { - if (this.baseParse) - return 0; - let pos = this.input.length; - for (let i = this.innerDone; i < this.inner.length; i++) { - if (this.inner[i].ranges[0].from < pos) - pos = Math.min(pos, this.inner[i].parse.parsedPos); + return node; +} +function resolveNode(node, pos, side, overlays) { + var _a; + // Move up to a node that actually holds the position, if possible + while (node.from == node.to || + (side < 1 ? node.from >= pos : node.from > pos) || + (side > -1 ? node.to <= pos : node.to < pos)) { + let parent = !overlays && node instanceof TreeNode && node.index < 0 ? null : node.parent; + if (!parent) + return node; + node = parent; + } + let mode = overlays ? 0 : IterMode.IgnoreOverlays; + // Must go up out of overlays when those do not overlap with pos + if (overlays) + for (let scan = node, parent = scan.parent; parent; scan = parent, parent = scan.parent) { + if (scan instanceof TreeNode && scan.index < 0 && ((_a = parent.enter(pos, side, mode)) === null || _a === void 0 ? void 0 : _a.from) != scan.from) + node = parent; } - return pos; + for (;;) { + let inner = node.enter(pos, side, mode); + if (!inner) + return node; + node = inner; } - stopAt(pos) { - this.stoppedAt = pos; - if (this.baseParse) - this.baseParse.stopAt(pos); - else - for (let i = this.innerDone; i < this.inner.length; i++) - this.inner[i].parse.stopAt(pos); +} +class TreeNode { + constructor(_tree, from, + // Index in parent node, set to -1 if the node is not a direct child of _parent.node (overlay) + index, _parent) { + this._tree = _tree; + this.from = from; + this.index = index; + this._parent = _parent; } - startInner() { - let fragmentCursor = new FragmentCursor$2(this.fragments); - let overlay = null; - let covered = null; - let cursor = new TreeCursor(new TreeNode(this.baseTree, this.ranges[0].from, 0, null), 1 /* Full */); - scan: for (let nest, isCovered; this.stoppedAt == null || cursor.from < this.stoppedAt;) { - let enter = true, range; - if (fragmentCursor.hasNode(cursor)) { - if (overlay) { - let match = overlay.mounts.find(m => m.frag.from <= cursor.from && m.frag.to >= cursor.to && m.mount.overlay); - if (match) - for (let r of match.mount.overlay) { - let from = r.from + match.pos, to = r.to + match.pos; - if (from >= cursor.from && to <= cursor.to && !overlay.ranges.some(r => r.from < to && r.to > from)) - overlay.ranges.push({ from, to }); - } - } - enter = false; - } - else if (covered && (isCovered = checkCover(covered.ranges, cursor.from, cursor.to))) { - enter = isCovered != 2 /* Full */; - } - else if (!cursor.type.isAnonymous && cursor.from < cursor.to && (nest = this.nest(cursor, this.input))) { - if (!cursor.tree) - materialize(cursor); - let oldMounts = fragmentCursor.findMounts(cursor.from, nest.parser); - if (typeof nest.overlay == "function") { - overlay = new ActiveOverlay(nest.parser, nest.overlay, oldMounts, this.inner.length, cursor.from, cursor.tree, overlay); - } - else { - let ranges = punchRanges(this.ranges, nest.overlay || [new Range$1(cursor.from, cursor.to)]); - if (ranges.length) - this.inner.push(new InnerParse(nest.parser, nest.parser.startParse(this.input, enterFragments(oldMounts, ranges), ranges), nest.overlay ? nest.overlay.map(r => new Range$1(r.from - cursor.from, r.to - cursor.from)) : null, cursor.tree, ranges)); - if (!nest.overlay) - enter = false; - else if (ranges.length) - covered = { ranges, depth: 0, prev: covered }; + get type() { return this._tree.type; } + get name() { return this._tree.type.name; } + get to() { return this.from + this._tree.length; } + nextChild(i, dir, pos, side, mode = 0) { + for (let parent = this;;) { + for (let { children, positions } = parent._tree, e = dir > 0 ? children.length : -1; i != e; i += dir) { + let next = children[i], start = positions[i] + parent.from; + if (!checkSide(side, pos, start, start + next.length)) + continue; + if (next instanceof TreeBuffer) { + if (mode & IterMode.ExcludeBuffers) + continue; + let index = next.findChild(0, next.buffer.length, dir, pos - start, side); + if (index > -1) + return new BufferNode(new BufferContext(parent, next, i, start), null, index); } - } - else if (overlay && (range = overlay.predicate(cursor))) { - if (range === true) - range = new Range$1(cursor.from, cursor.to); - if (range.from < range.to) - overlay.ranges.push(range); - } - if (enter && cursor.firstChild()) { - if (overlay) - overlay.depth++; - if (covered) - covered.depth++; - } - else { - for (;;) { - if (cursor.nextSibling()) - break; - if (!cursor.parent()) - break scan; - if (overlay && !--overlay.depth) { - let ranges = punchRanges(this.ranges, overlay.ranges); - if (ranges.length) - this.inner.splice(overlay.index, 0, new InnerParse(overlay.parser, overlay.parser.startParse(this.input, enterFragments(overlay.mounts, ranges), ranges), overlay.ranges.map(r => new Range$1(r.from - overlay.start, r.to - overlay.start)), overlay.target, ranges)); - overlay = overlay.prev; - } - if (covered && !--covered.depth) - covered = covered.prev; + else if ((mode & IterMode.IncludeAnonymous) || (!next.type.isAnonymous || hasChild(next))) { + let mounted; + if (!(mode & IterMode.IgnoreMounts) && + next.props && (mounted = next.prop(NodeProp.mounted)) && !mounted.overlay) + return new TreeNode(mounted.tree, start, i, parent); + let inner = new TreeNode(next, start, i, parent); + return (mode & IterMode.IncludeAnonymous) || !inner.type.isAnonymous ? inner + : inner.nextChild(dir < 0 ? next.children.length - 1 : 0, dir, pos, side); } } + if ((mode & IterMode.IncludeAnonymous) || !parent.type.isAnonymous) + return null; + if (parent.index >= 0) + i = parent.index + dir; + else + i = dir < 0 ? -1 : parent._parent._tree.children.length; + parent = parent._parent; + if (!parent) + return null; } } -} -function checkCover(covered, from, to) { - for (let range of covered) { - if (range.from >= to) - break; - if (range.to > from) - return range.from <= from && range.to >= to ? 2 /* Full */ : 1 /* Partial */; + get firstChild() { return this.nextChild(0, 1, 0, 4 /* DontCare */); } + get lastChild() { return this.nextChild(this._tree.children.length - 1, -1, 0, 4 /* DontCare */); } + childAfter(pos) { return this.nextChild(0, 1, pos, 2 /* After */); } + childBefore(pos) { return this.nextChild(this._tree.children.length - 1, -1, pos, -2 /* Before */); } + enter(pos, side, mode = 0) { + let mounted; + if (!(mode & IterMode.IgnoreOverlays) && (mounted = this._tree.prop(NodeProp.mounted)) && mounted.overlay) { + let rPos = pos - this.from; + for (let { from, to } of mounted.overlay) { + if ((side > 0 ? from <= rPos : from < rPos) && + (side < 0 ? to >= rPos : to > rPos)) + return new TreeNode(mounted.tree, mounted.overlay[0].from + this.from, -1, this); + } + } + return this.nextChild(0, 1, pos, side, mode); } - return 0 /* None */; -} -// Take a piece of buffer and convert it into a stand-alone -// TreeBuffer. -function sliceBuf(buf, startI, endI, nodes, positions, off) { - if (startI < endI) { - let from = buf.buffer[startI + 1], to = buf.buffer[endI - 2]; - nodes.push(buf.slice(startI, endI, from, to)); - positions.push(from - off); + nextSignificantParent() { + let val = this; + while (val.type.isAnonymous && val._parent) + val = val._parent; + return val; } -} -// This function takes a node that's in a buffer, and converts it, and -// its parent buffer nodes, into a Tree. This is again acting on the -// assumption that the trees and buffers have been constructed by the -// parse that was ran via the mix parser, and thus aren't shared with -// any other code, making violations of the immutability safe. -function materialize(cursor) { - let { node } = cursor, depth = 0; - // Scan up to the nearest tree - do { - cursor.parent(); - depth++; - } while (!cursor.tree); - // Find the index of the buffer in that tree - let i = 0, base = cursor.tree, off = 0; - for (;; i++) { - off = base.positions[i] + cursor.from; - if (off <= node.from && off + base.children[i].length >= node.to) - break; + get parent() { + return this._parent ? this._parent.nextSignificantParent() : null; } - let buf = base.children[i], b = buf.buffer; - // Split a level in the buffer, putting the nodes before and after - // the child that contains `node` into new buffers. - function split(startI, endI, type, innerOffset, length) { - let i = startI; - while (b[i + 2] + off <= node.from) - i = b[i + 3]; - let children = [], positions = []; - sliceBuf(buf, startI, i, children, positions, innerOffset); - let from = b[i + 1], to = b[i + 2]; - let isTarget = from + off == node.from && to + off == node.to && b[i] == node.type.id; - children.push(isTarget ? node.toTree() : split(i + 4, b[i + 3], buf.set.types[b[i]], from, to - from)); - positions.push(from - innerOffset); - sliceBuf(buf, b[i + 3], endI, children, positions, innerOffset); - return new Tree(type, children, positions, length); - } - base.children[i] = split(0, b.length, NodeType.none, 0, buf.length); - // Move the cursor back to the target node - for (let d = 0; d <= depth; d++) - cursor.childAfter(node.from); -} -class StructureCursor { - constructor(root, offset) { - this.offset = offset; - this.done = false; - this.cursor = root.fullCursor(); + get nextSibling() { + return this._parent && this.index >= 0 ? this._parent.nextChild(this.index + 1, 1, 0, 4 /* DontCare */) : null; } - // Move to the first node (in pre-order) that starts at or after `pos`. - moveTo(pos) { - let { cursor } = this, p = pos - this.offset; - while (!this.done && cursor.from < p) { - if (cursor.to >= pos && cursor.enter(p, 1, false, false)) ; - else if (!cursor.next(false)) - this.done = true; - } + get prevSibling() { + return this._parent && this.index >= 0 ? this._parent.nextChild(this.index - 1, -1, 0, 4 /* DontCare */) : null; } - hasNode(cursor) { - this.moveTo(cursor.from); - if (!this.done && this.cursor.from + this.offset == cursor.from && this.cursor.tree) { - for (let tree = this.cursor.tree;;) { - if (tree == cursor.tree) - return true; - if (tree.children.length && tree.positions[0] == 0 && tree.children[0] instanceof Tree) - tree = tree.children[0]; - else - break; - } - } - return false; + cursor(mode = 0) { return new TreeCursor(this, mode); } + get tree() { return this._tree; } + toTree() { return this._tree; } + resolve(pos, side = 0) { + return resolveNode(this, pos, side, false); } -} -class FragmentCursor$2 { - constructor(fragments) { - var _a; - this.fragments = fragments; - this.curTo = 0; - this.fragI = 0; - if (fragments.length) { - let first = this.curFrag = fragments[0]; - this.curTo = (_a = first.tree.prop(stoppedInner)) !== null && _a !== void 0 ? _a : first.to; - this.inner = new StructureCursor(first.tree, -first.offset); - } - else { - this.curFrag = this.inner = null; - } + resolveInner(pos, side = 0) { + return resolveNode(this, pos, side, true); } - hasNode(node) { - while (this.curFrag && node.from >= this.curTo) - this.nextFrag(); - return this.curFrag && this.curFrag.from <= node.from && this.curTo >= node.to && this.inner.hasNode(node); + enterUnfinishedNodesBefore(pos) { return enterUnfinishedNodesBefore(this, pos); } + getChild(type, before = null, after = null) { + let r = getChildren(this, type, before, after); + return r.length ? r[0] : null; } - nextFrag() { - var _a; - this.fragI++; - if (this.fragI == this.fragments.length) { - this.curFrag = this.inner = null; - } - else { - let frag = this.curFrag = this.fragments[this.fragI]; - this.curTo = (_a = frag.tree.prop(stoppedInner)) !== null && _a !== void 0 ? _a : frag.to; - this.inner = new StructureCursor(frag.tree, -frag.offset); - } + getChildren(type, before = null, after = null) { + return getChildren(this, type, before, after); } - findMounts(pos, parser) { - var _a; - let result = []; - if (this.inner) { - this.inner.cursor.moveTo(pos, 1); - for (let pos = this.inner.cursor.node; pos; pos = pos.parent) { - let mount = (_a = pos.tree) === null || _a === void 0 ? void 0 : _a.prop(NodeProp.mounted); - if (mount && mount.parser == parser) { - for (let i = this.fragI; i < this.fragments.length; i++) { - let frag = this.fragments[i]; - if (frag.from >= pos.to) - break; - if (frag.tree == this.curFrag.tree) - result.push({ - frag, - pos: pos.from - frag.offset, - mount - }); - } - } - } - } + /// @internal + toString() { return this._tree.toString(); } + get node() { return this; } + matchContext(context) { return matchNodeContext(this, context); } +} +function getChildren(node, type, before, after) { + let cur = node.cursor(), result = []; + if (!cur.firstChild()) return result; + if (before != null) + while (!cur.type.is(before)) + if (!cur.nextSibling()) + return result; + for (;;) { + if (after != null && cur.type.is(after)) + return result; + if (cur.type.is(type)) + result.push(cur.node); + if (!cur.nextSibling()) + return after == null ? result : []; } } -function punchRanges(outer, ranges) { - let copy = null, current = ranges; - for (let i = 1, j = 0; i < outer.length; i++) { - let gapFrom = outer[i - 1].to, gapTo = outer[i].from; - for (; j < current.length; j++) { - let r = current[j]; - if (r.from >= gapTo) - break; - if (r.to <= gapFrom) - continue; - if (!copy) - current = copy = ranges.slice(); - if (r.from < gapFrom) { - copy[j] = new Range$1(r.from, gapFrom); - if (r.to > gapTo) - copy.splice(j + 1, 0, new Range$1(gapTo, r.to)); - } - else if (r.to > gapTo) { - copy[j--] = new Range$1(gapTo, r.to); - } - else { - copy.splice(j--, 1); - } +function matchNodeContext(node, context, i = context.length - 1) { + for (let p = node.parent; i >= 0; p = p.parent) { + if (!p) + return false; + if (!p.type.isAnonymous) { + if (context[i] && context[i] != p.name) + return false; + i--; } } - return current; + return true; } -function findCoverChanges(a, b, from, to) { - let iA = 0, iB = 0, inA = false, inB = false, pos = -1e9; - let result = []; - for (;;) { - let nextA = iA == a.length ? 1e9 : inA ? a[iA].to : a[iA].from; - let nextB = iB == b.length ? 1e9 : inB ? b[iB].to : b[iB].from; - if (inA != inB) { - let start = Math.max(pos, from), end = Math.min(nextA, nextB, to); - if (start < end) - result.push(new Range$1(start, end)); - } - pos = Math.min(nextA, nextB); - if (pos == 1e9) - break; - if (nextA == pos) { - if (!inA) - inA = true; - else { - inA = false; - iA++; - } - } - if (nextB == pos) { - if (!inB) - inB = true; - else { - inB = false; - iB++; - } - } +class BufferContext { + constructor(parent, buffer, index, start) { + this.parent = parent; + this.buffer = buffer; + this.index = index; + this.start = start; } - return result; } -// Given a number of fragments for the outer tree, and a set of ranges -// to parse, find fragments for inner trees mounted around those -// ranges, if any. -function enterFragments(mounts, ranges) { - let result = []; - for (let { pos, mount, frag } of mounts) { - let startPos = pos + (mount.overlay ? mount.overlay[0].from : 0), endPos = startPos + mount.tree.length; - let from = Math.max(frag.from, startPos), to = Math.min(frag.to, endPos); - if (mount.overlay) { - let overlay = mount.overlay.map(r => new Range$1(r.from + pos, r.to + pos)); - let changes = findCoverChanges(ranges, overlay, from, to); - for (let i = 0, pos = from;; i++) { - let last = i == changes.length, end = last ? to : changes[i].from; - if (end > pos) - result.push(new TreeFragment(pos, end, mount.tree, -startPos, frag.from >= pos, frag.to <= end)); - if (last) - break; - pos = changes[i].to; - } - } - else { - result.push(new TreeFragment(from, to, mount.tree, -startPos, frag.from >= startPos, frag.to <= endPos)); - } +class BufferNode { + constructor(context, _parent, index) { + this.context = context; + this._parent = _parent; + this.index = index; + this.type = context.buffer.set.types[context.buffer.buffer[index]]; } - return result; -} - -const C = "\u037c"; -const COUNT = typeof Symbol == "undefined" ? "__" + C : Symbol.for(C); -const SET = typeof Symbol == "undefined" ? "__styleSet" + Math.floor(Math.random() * 1e8) : Symbol("styleSet"); -const top = typeof globalThis != "undefined" ? globalThis : typeof window != "undefined" ? window : {}; - -// :: - Style modules encapsulate a set of CSS rules defined from -// JavaScript. Their definitions are only available in a given DOM -// root after it has been _mounted_ there with `StyleModule.mount`. -// -// Style modules should be created once and stored somewhere, as -// opposed to re-creating them every time you need them. The amount of -// CSS rules generated for a given DOM root is bounded by the amount -// of style modules that were used. So to avoid leaking rules, don't -// create these dynamically, but treat them as one-time allocations. -class StyleModule { - // :: (Object