@adecore/editor-core 0.39.0 → 0.40.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/block-folds.js +5 -2
- package/dist/bracket-tracker.js +4 -3
- package/dist/brackets.d.ts +2 -12
- package/dist/brackets.js +3 -15
- package/dist/closer-tracker.js +0 -1
- package/dist/doc-comments.js +0 -1
- package/dist/document.d.ts +2 -0
- package/dist/document.js +33 -35
- package/dist/edit-regions.js +3 -4
- package/dist/enter.d.ts +2 -0
- package/dist/enter.js +11 -9
- package/dist/fold-roles.js +2 -2
- package/dist/lexical-folds.d.ts +0 -1
- package/dist/lexical-folds.js +1 -4
- package/dist/lexical.d.ts +11 -5
- package/dist/lexical.js +13 -17
- package/dist/line-commands.js +7 -15
- package/dist/line-text.d.ts +2 -0
- package/dist/line-text.js +4 -0
- package/dist/markup-regions.js +2 -3
- package/dist/paste.js +2 -7
- package/dist/structure.js +2 -1
- package/dist/typing-context.js +5 -15
- package/package.json +2 -2
- package/src/block-folds.ts +39 -2
- package/src/bracket-tracker.ts +55 -12
- package/src/brackets.ts +35 -42
- package/src/closer-tracker.ts +6 -1
- package/src/comments.ts +103 -0
- package/src/doc-comments.ts +19 -1
- package/src/document.ts +505 -36
- package/src/edit-regions.ts +26 -4
- package/src/enter.ts +104 -9
- package/src/fold-roles.ts +34 -2
- package/src/index.ts +2 -0
- package/src/languages.ts +11 -0
- package/src/lexical-folds.ts +21 -5
- package/src/lexical.ts +31 -23
- package/src/line-commands.ts +81 -17
- package/src/line-text.ts +7 -0
- package/src/markup-regions.ts +101 -3
- package/src/offsets.ts +10 -0
- package/src/paste.ts +20 -7
- package/src/php-markup.ts +23 -0
- package/src/preserve-case.ts +12 -0
- package/src/rope.ts +70 -0
- package/src/script-source.ts +10 -0
- package/src/search.ts +38 -0
- package/src/structure.ts +29 -1
- package/src/text-span.ts +53 -0
- package/src/typing-context.ts +65 -41
- package/src/typing-handlers.ts +28 -0
- package/src/words.ts +10 -0
package/dist/paste.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { indentText, isBlank, whitespaceOf } from './line-text.js';
|
|
1
|
+
import { indentText, isBlank, rangeOf, whitespaceOf } from './line-text.js';
|
|
2
2
|
import { mapOffset } from './offsets.js';
|
|
3
3
|
import { indentationColumn } from './structure.js';
|
|
4
4
|
/* A line break at the end is the end of the last line, not a line of its own. */
|
|
@@ -63,12 +63,7 @@ function reindent(source, from, text, options) {
|
|
|
63
63
|
* the line it lands on, when it lands in code and not in a string, a comment or a heredoc.
|
|
64
64
|
*/
|
|
65
65
|
export function planPaste(source, selections, text, options) {
|
|
66
|
-
const ordered = selections.map((selection, index) => ({
|
|
67
|
-
from: Math.min(selection.anchor, selection.head),
|
|
68
|
-
to: Math.max(selection.anchor, selection.head),
|
|
69
|
-
index
|
|
70
|
-
}));
|
|
71
|
-
ordered.sort((left, right) => left.from - right.from);
|
|
66
|
+
const ordered = selections.map((selection, index) => ({ ...rangeOf(selection), index })).sort((left, right) => left.from - right.from);
|
|
72
67
|
const segments = perCaretLines(text, selections.length);
|
|
73
68
|
const newline = text.includes('\r\n') ? '\r\n' : '\n';
|
|
74
69
|
const edits = [];
|
package/dist/structure.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { bracketRole, commentRole } from './fold-roles.js';
|
|
2
2
|
import { openers } from './lexical.js';
|
|
3
|
+
import { whitespaceOf } from './line-text.js';
|
|
3
4
|
/* Clamped to 1 through 16, with 4 for anything that is not a finite number. */
|
|
4
5
|
export function tabWidth(options) {
|
|
5
6
|
const requested = options.tabSize ?? 4;
|
|
@@ -69,7 +70,7 @@ function indentationFolds(lineCount, getLine, tabSize, minimum) {
|
|
|
69
70
|
if (!text.trim()) {
|
|
70
71
|
continue;
|
|
71
72
|
}
|
|
72
|
-
const indent = indentationColumn(text
|
|
73
|
+
const indent = indentationColumn(whitespaceOf(text), tabSize);
|
|
73
74
|
while (headers.length > 0 && indent <= headers.at(-1).indent) {
|
|
74
75
|
close(headers.pop(), previous.line);
|
|
75
76
|
}
|
package/dist/typing-context.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isWordCharacter, lexerRules, openers, startsExpression, startsExpressionAfter, startsWord } from './lexical.js';
|
|
2
2
|
import { scanPhpMarkup } from './php-markup.js';
|
|
3
3
|
const PHP_HEREDOC = /^<<<[ \t]*(["']?)([A-Za-z_]\w*)\1[ \t]*$/;
|
|
4
4
|
const PHP_OPEN = /<\?(?:php\b|=)/;
|
|
@@ -11,18 +11,6 @@ const blockWords = /^(else|try|finally|do)$/;
|
|
|
11
11
|
function initial() {
|
|
12
12
|
return { mode: 'code', escaped: false, inClass: false, expression: true, word: '' };
|
|
13
13
|
}
|
|
14
|
-
function rulesOf(language) {
|
|
15
|
-
return {
|
|
16
|
-
script: isScript(language),
|
|
17
|
-
php: isPhp(language),
|
|
18
|
-
hash: hasHashComments(language),
|
|
19
|
-
hashAtWordStart: hashNeedsWordStart(language),
|
|
20
|
-
dash: hasDashComments(language),
|
|
21
|
-
slash: hasSlashComments(language),
|
|
22
|
-
block: hasBlockComments(language),
|
|
23
|
-
markup: hasMarkupComments(language)
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
14
|
function scanComment(state, text, at, end) {
|
|
27
15
|
const close = state.mode === 'block-comment' ? '*/' : '-->';
|
|
28
16
|
if (at + close.length <= end && text.startsWith(close, at)) {
|
|
@@ -171,7 +159,9 @@ function stepScript(state, text, at, end, offset) {
|
|
|
171
159
|
if (state.mode === 'block-comment') {
|
|
172
160
|
return scanComment(state, text, at, end);
|
|
173
161
|
}
|
|
174
|
-
return state.mode === 'code'
|
|
162
|
+
return state.mode === 'code'
|
|
163
|
+
? scanCode(state, (scriptRules ??= lexerRules('javascript')), text, at, end, offset)
|
|
164
|
+
: scanLiteral(state, text, at, end, offset);
|
|
175
165
|
}
|
|
176
166
|
const scriptLexer = { initial, step: stepScript };
|
|
177
167
|
function normalizeLine(state, multilineQuotes) {
|
|
@@ -287,7 +277,7 @@ export class TypingContexts {
|
|
|
287
277
|
this.language = id;
|
|
288
278
|
this.states = [this.initialState(id)];
|
|
289
279
|
}
|
|
290
|
-
const rules =
|
|
280
|
+
const rules = lexerRules(id);
|
|
291
281
|
while (this.states.length <= line) {
|
|
292
282
|
const previous = this.states.at(-1);
|
|
293
283
|
const source = this.line(this.states.length - 1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adecore/editor-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"test": "bun test --conditions=source"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"@types/bun": "^1.4.
|
|
19
|
+
"@types/bun": "^1.4.4",
|
|
20
20
|
"typescript": "^7.0.2"
|
|
21
21
|
},
|
|
22
22
|
"license": "FSL-1.1-MIT",
|
package/src/block-folds.ts
CHANGED
|
@@ -16,49 +16,71 @@ function markdownFolds(lineCount: number, getLine: (line: number) => DocumentLin
|
|
|
16
16
|
const result: FoldingRange[] = [];
|
|
17
17
|
const headings: { line: number; level: number }[] = [];
|
|
18
18
|
const frontMatter = frontMatterEnd(lineCount, getLine);
|
|
19
|
+
|
|
19
20
|
if (frontMatter !== -1) {
|
|
20
21
|
result.push(rangeOf(0, frontMatter, getLine, 'block', 'front-matter'));
|
|
21
22
|
}
|
|
23
|
+
|
|
22
24
|
let tableStart = -1;
|
|
25
|
+
|
|
23
26
|
const flushTable = (end: number): void => {
|
|
24
27
|
if (tableStart !== -1 && end > tableStart) {
|
|
25
28
|
result.push(rangeOf(tableStart, end, getLine, 'block', 'table'));
|
|
26
29
|
}
|
|
30
|
+
|
|
27
31
|
tableStart = -1;
|
|
28
32
|
};
|
|
33
|
+
|
|
29
34
|
for (let line = frontMatter + 1; line < lineCount; line++) {
|
|
30
35
|
const text = getLine(line).text;
|
|
31
36
|
const fence = FENCE.exec(text);
|
|
37
|
+
|
|
32
38
|
if (fence !== null) {
|
|
33
39
|
flushTable(line - 1);
|
|
34
40
|
const end = fenceEnd(line, fence[1]!, lineCount, getLine);
|
|
41
|
+
|
|
35
42
|
if (end !== -1) {
|
|
36
43
|
result.push(rangeOf(line, end, getLine, 'block', 'code-fence'));
|
|
37
44
|
line = end;
|
|
38
45
|
}
|
|
46
|
+
|
|
39
47
|
continue;
|
|
40
48
|
}
|
|
49
|
+
|
|
41
50
|
if (TABLE_ROW.test(text)) {
|
|
42
51
|
tableStart = tableStart === -1 ? line : tableStart;
|
|
52
|
+
|
|
43
53
|
continue;
|
|
44
54
|
}
|
|
55
|
+
|
|
45
56
|
flushTable(line - 1);
|
|
46
57
|
const heading = HEADING.exec(text);
|
|
58
|
+
|
|
47
59
|
if (heading !== null) {
|
|
48
60
|
headings.push({ line, level: heading[1]!.length });
|
|
49
61
|
}
|
|
50
62
|
}
|
|
63
|
+
|
|
51
64
|
flushTable(lineCount - 1);
|
|
65
|
+
|
|
52
66
|
for (const [index, heading] of headings.entries()) {
|
|
53
|
-
|
|
54
|
-
|
|
67
|
+
let next = index + 1;
|
|
68
|
+
|
|
69
|
+
while (next < headings.length && headings[next]!.level > heading.level) {
|
|
70
|
+
next++;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let end = (headings[next]?.line ?? lineCount) - 1;
|
|
74
|
+
|
|
55
75
|
while (end > heading.line && getLine(end).text.trim() === '') {
|
|
56
76
|
end--;
|
|
57
77
|
}
|
|
78
|
+
|
|
58
79
|
if (end > heading.line) {
|
|
59
80
|
result.push(rangeOf(heading.line, end, getLine, 'section'));
|
|
60
81
|
}
|
|
61
82
|
}
|
|
83
|
+
|
|
62
84
|
return result;
|
|
63
85
|
}
|
|
64
86
|
|
|
@@ -67,12 +89,15 @@ function frontMatterEnd(lineCount: number, getLine: (line: number) => DocumentLi
|
|
|
67
89
|
if (lineCount === 0 || getLine(0).text.trim() !== '---') {
|
|
68
90
|
return -1;
|
|
69
91
|
}
|
|
92
|
+
|
|
70
93
|
for (let line = 1; line < lineCount; line++) {
|
|
71
94
|
const text = getLine(line).text.trim();
|
|
95
|
+
|
|
72
96
|
if (text === '---' || text === '...') {
|
|
73
97
|
return line;
|
|
74
98
|
}
|
|
75
99
|
}
|
|
100
|
+
|
|
76
101
|
return -1;
|
|
77
102
|
}
|
|
78
103
|
|
|
@@ -80,10 +105,12 @@ function frontMatterEnd(lineCount: number, getLine: (line: number) => DocumentLi
|
|
|
80
105
|
function fenceEnd(line: number, marker: string, lineCount: number, getLine: (line: number) => DocumentLine): number {
|
|
81
106
|
for (let next = line + 1; next < lineCount; next++) {
|
|
82
107
|
const closer = FENCE.exec(getLine(next).text)?.[1];
|
|
108
|
+
|
|
83
109
|
if (closer !== undefined && closer[0] === marker[0] && closer.length >= marker.length && getLine(next).text.trim() === closer) {
|
|
84
110
|
return next;
|
|
85
111
|
}
|
|
86
112
|
}
|
|
113
|
+
|
|
87
114
|
return -1;
|
|
88
115
|
}
|
|
89
116
|
|
|
@@ -91,31 +118,40 @@ function fenceEnd(line: number, marker: string, lineCount: number, getLine: (lin
|
|
|
91
118
|
function phpFolds(lineCount: number, getLine: (line: number) => DocumentLine): FoldingRange[] {
|
|
92
119
|
const result: FoldingRange[] = [];
|
|
93
120
|
let open = -1;
|
|
121
|
+
|
|
94
122
|
for (let line = 0; line < lineCount; line++) {
|
|
95
123
|
const text = getLine(line).text;
|
|
96
124
|
const heredoc = HEREDOC.exec(text);
|
|
125
|
+
|
|
97
126
|
if (heredoc !== null) {
|
|
98
127
|
const closer = new RegExp(`^\\s*${heredoc[2]}\\b`);
|
|
128
|
+
|
|
99
129
|
for (let next = line + 1; next < lineCount; next++) {
|
|
100
130
|
if (closer.test(getLine(next).text)) {
|
|
101
131
|
result.push(rangeOf(line, next, getLine, 'block', 'heredoc'));
|
|
102
132
|
line = next;
|
|
133
|
+
|
|
103
134
|
break;
|
|
104
135
|
}
|
|
105
136
|
}
|
|
137
|
+
|
|
106
138
|
continue;
|
|
107
139
|
}
|
|
140
|
+
|
|
108
141
|
const opened = open === -1 ? text.search(/<\?(?:php\b|=)/) : -1;
|
|
109
142
|
const closed = text.indexOf('?>', Math.max(0, opened));
|
|
143
|
+
|
|
110
144
|
if (opened !== -1 && closed === -1) {
|
|
111
145
|
open = line === 0 ? -1 : line;
|
|
112
146
|
} else if (open !== -1 && closed !== -1) {
|
|
113
147
|
if (line > open) {
|
|
114
148
|
result.push(rangeOf(open, line, getLine, 'block', 'php-tag'));
|
|
115
149
|
}
|
|
150
|
+
|
|
116
151
|
open = -1;
|
|
117
152
|
}
|
|
118
153
|
}
|
|
154
|
+
|
|
119
155
|
return result;
|
|
120
156
|
}
|
|
121
157
|
|
|
@@ -124,5 +160,6 @@ export function blockFolds(lineCount: number, getLine: (line: number) => Documen
|
|
|
124
160
|
if (MARKDOWN.test(language)) {
|
|
125
161
|
return markdownFolds(lineCount, getLine);
|
|
126
162
|
}
|
|
163
|
+
|
|
127
164
|
return language.toLowerCase() === 'php' ? phpFolds(lineCount, getLine) : [];
|
|
128
165
|
}
|
package/src/bracket-tracker.ts
CHANGED
|
@@ -1,13 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
type BracketRules,
|
|
4
|
-
type BracketState,
|
|
5
|
-
type BracketVisitor,
|
|
6
|
-
bracketRules,
|
|
7
|
-
initialBracketState,
|
|
8
|
-
lexBrackets,
|
|
9
|
-
sameBracketState
|
|
10
|
-
} from './brackets.ts';
|
|
1
|
+
import { type BracketMatch, type BracketState, type BracketVisitor, initialBracketState, lexBrackets, sameBracketState } from './brackets.ts';
|
|
2
|
+
import { lexerRules, type LexerRules } from './lexical.ts';
|
|
11
3
|
import type { TextRope } from './rope.ts';
|
|
12
4
|
import type { DocumentChange } from './types.ts';
|
|
13
5
|
|
|
@@ -57,7 +49,7 @@ export class BracketTracker {
|
|
|
57
49
|
private readonly text: () => BracketText;
|
|
58
50
|
private readonly stretchLength: number;
|
|
59
51
|
private language = '';
|
|
60
|
-
private rules:
|
|
52
|
+
private rules: LexerRules = lexerRules('');
|
|
61
53
|
private checkpoints: Checkpoint[] = [];
|
|
62
54
|
/* The first stale checkpoint: what comes before it is proven, and so is its state. */
|
|
63
55
|
private proven = 0;
|
|
@@ -74,8 +66,10 @@ export class BracketTracker {
|
|
|
74
66
|
if (this.checkpoints.length === 0 || changes.length === 0) {
|
|
75
67
|
return;
|
|
76
68
|
}
|
|
69
|
+
|
|
77
70
|
const edits: LineEdit[] = [];
|
|
78
71
|
let delta = 0;
|
|
72
|
+
|
|
79
73
|
for (const change of changes) {
|
|
80
74
|
// The line above too, since an edit at the start of a line can turn the `\r` that ends it into a `\r\n`.
|
|
81
75
|
const first = Math.max(0, before.lineAt(change.from) - 1);
|
|
@@ -83,6 +77,7 @@ export class BracketTracker {
|
|
|
83
77
|
const newLast = after.lineAt(change.from + delta + change.insertedLength);
|
|
84
78
|
delta += change.insertedLength - (change.to - change.from);
|
|
85
79
|
const previous = edits.at(-1);
|
|
80
|
+
|
|
86
81
|
if (previous !== undefined && first <= previous.oldLast) {
|
|
87
82
|
previous.oldLast = oldLast;
|
|
88
83
|
previous.newLast = newLast;
|
|
@@ -90,28 +85,37 @@ export class BracketTracker {
|
|
|
90
85
|
edits.push({ first, oldLast, newLast });
|
|
91
86
|
}
|
|
92
87
|
}
|
|
88
|
+
|
|
93
89
|
const kept: Checkpoint[] = [];
|
|
94
90
|
let index = 0;
|
|
95
91
|
let shift = 0;
|
|
92
|
+
|
|
96
93
|
for (const checkpoint of this.checkpoints) {
|
|
97
94
|
let dropped = false;
|
|
95
|
+
|
|
98
96
|
while (index < edits.length && checkpoint.line > edits[index]!.first) {
|
|
99
97
|
kept.at(-1)!.stale = true;
|
|
98
|
+
|
|
100
99
|
if (checkpoint.line <= edits[index]!.oldLast) {
|
|
101
100
|
dropped = true;
|
|
101
|
+
|
|
102
102
|
break;
|
|
103
103
|
}
|
|
104
|
+
|
|
104
105
|
shift = edits[index]!.newLast - edits[index]!.oldLast;
|
|
105
106
|
index++;
|
|
106
107
|
}
|
|
108
|
+
|
|
107
109
|
if (!dropped) {
|
|
108
110
|
checkpoint.line += shift;
|
|
109
111
|
kept.push(checkpoint);
|
|
110
112
|
}
|
|
111
113
|
}
|
|
114
|
+
|
|
112
115
|
if (index < edits.length) {
|
|
113
116
|
kept.at(-1)!.stale = true;
|
|
114
117
|
}
|
|
118
|
+
|
|
115
119
|
this.checkpoints = kept;
|
|
116
120
|
this.proven = this.nextStale(0);
|
|
117
121
|
}
|
|
@@ -122,27 +126,36 @@ export class BracketTracker {
|
|
|
122
126
|
*/
|
|
123
127
|
match(offset: number, language: string, limit = Number.POSITIVE_INFINITY): BracketMatch | null | undefined {
|
|
124
128
|
const rope = this.text();
|
|
129
|
+
|
|
125
130
|
if (!/^[()[\]{}]$/.test(rope.charAt(offset))) {
|
|
126
131
|
return undefined;
|
|
127
132
|
}
|
|
133
|
+
|
|
128
134
|
this.use(language);
|
|
129
135
|
this.budget = limit;
|
|
130
136
|
const line = rope.lineAt(offset);
|
|
137
|
+
|
|
131
138
|
if (!this.prove(line)) {
|
|
132
139
|
return null;
|
|
133
140
|
}
|
|
141
|
+
|
|
134
142
|
const index = this.stretchOf(line);
|
|
135
143
|
const event = this.eventAt(index, offset);
|
|
144
|
+
|
|
136
145
|
if (event === undefined) {
|
|
137
146
|
return undefined;
|
|
138
147
|
}
|
|
148
|
+
|
|
139
149
|
if (event.kind === 'mismatch' || event.mate === null) {
|
|
140
150
|
return { mate: undefined };
|
|
141
151
|
}
|
|
152
|
+
|
|
142
153
|
if (event.mate !== undefined) {
|
|
143
154
|
return { mate: event.mate };
|
|
144
155
|
}
|
|
156
|
+
|
|
145
157
|
const mate = event.kind === 'open' ? this.closerAfter(index, event.depth) : this.openerBefore(index, event.depth);
|
|
158
|
+
|
|
146
159
|
return mate === null ? null : { mate };
|
|
147
160
|
}
|
|
148
161
|
|
|
@@ -150,17 +163,20 @@ export class BracketTracker {
|
|
|
150
163
|
if (language === this.language && this.checkpoints.length > 0) {
|
|
151
164
|
return;
|
|
152
165
|
}
|
|
166
|
+
|
|
153
167
|
this.language = language;
|
|
154
|
-
this.rules =
|
|
168
|
+
this.rules = lexerRules(language);
|
|
155
169
|
this.checkpoints = [{ line: 0, state: initialBracketState(), low: 0, stale: true }];
|
|
156
170
|
this.proven = 0;
|
|
157
171
|
}
|
|
158
172
|
|
|
159
173
|
private nextStale(from: number): number {
|
|
160
174
|
let index = from;
|
|
175
|
+
|
|
161
176
|
while (index < this.checkpoints.length && !this.checkpoints[index]!.stale) {
|
|
162
177
|
index++;
|
|
163
178
|
}
|
|
179
|
+
|
|
164
180
|
return index;
|
|
165
181
|
}
|
|
166
182
|
|
|
@@ -170,8 +186,10 @@ export class BracketTracker {
|
|
|
170
186
|
if (this.budget <= 0) {
|
|
171
187
|
return false;
|
|
172
188
|
}
|
|
189
|
+
|
|
173
190
|
this.relex(this.proven, line);
|
|
174
191
|
}
|
|
192
|
+
|
|
175
193
|
return true;
|
|
176
194
|
}
|
|
177
195
|
|
|
@@ -184,12 +202,14 @@ export class BracketTracker {
|
|
|
184
202
|
const added: Checkpoint[] = [];
|
|
185
203
|
let current = this.checkpoints[index]!;
|
|
186
204
|
let line = current.line;
|
|
205
|
+
|
|
187
206
|
for (;;) {
|
|
188
207
|
const from = rope.lineBounds(line).start;
|
|
189
208
|
const end = Math.min(stop, from + this.stretchLength >= rope.length ? rope.lineCount : Math.max(line + 1, rope.lineAt(from + this.stretchLength)));
|
|
190
209
|
const to = end >= rope.lineCount ? rope.length : rope.lineBounds(end).start;
|
|
191
210
|
let low = state.stack?.depth ?? 0;
|
|
192
211
|
this.budget -= to - from;
|
|
212
|
+
|
|
193
213
|
lexBrackets(state, this.rules, rope.slice(from, to), 0, to - from, from, {
|
|
194
214
|
open() {},
|
|
195
215
|
close(_at, depth) {
|
|
@@ -199,22 +219,28 @@ export class BracketTracker {
|
|
|
199
219
|
low = 0;
|
|
200
220
|
}
|
|
201
221
|
});
|
|
222
|
+
|
|
202
223
|
current.low = low;
|
|
203
224
|
current.stale = false;
|
|
225
|
+
|
|
204
226
|
if (end === stop) {
|
|
205
227
|
if (next !== undefined && !sameBracketState(state, next.state)) {
|
|
206
228
|
next.state = state;
|
|
207
229
|
next.stale = true;
|
|
208
230
|
}
|
|
231
|
+
|
|
209
232
|
break;
|
|
210
233
|
}
|
|
234
|
+
|
|
211
235
|
current = { line: end, state: { ...state }, low: 0, stale: true };
|
|
212
236
|
added.push(current);
|
|
213
237
|
line = end;
|
|
238
|
+
|
|
214
239
|
if (end > until || this.budget <= 0) {
|
|
215
240
|
break;
|
|
216
241
|
}
|
|
217
242
|
}
|
|
243
|
+
|
|
218
244
|
this.checkpoints.splice(index + 1, 0, ...added);
|
|
219
245
|
this.proven = this.nextStale(index + 1);
|
|
220
246
|
}
|
|
@@ -222,14 +248,17 @@ export class BracketTracker {
|
|
|
222
248
|
private stretchOf(line: number): number {
|
|
223
249
|
let low = 0;
|
|
224
250
|
let high = this.checkpoints.length - 1;
|
|
251
|
+
|
|
225
252
|
while (low < high) {
|
|
226
253
|
const middle = Math.ceil((low + high) / 2);
|
|
254
|
+
|
|
227
255
|
if (this.checkpoints[middle]!.line <= line) {
|
|
228
256
|
low = middle;
|
|
229
257
|
} else {
|
|
230
258
|
high = middle - 1;
|
|
231
259
|
}
|
|
232
260
|
}
|
|
261
|
+
|
|
233
262
|
return low;
|
|
234
263
|
}
|
|
235
264
|
|
|
@@ -246,15 +275,18 @@ export class BracketTracker {
|
|
|
246
275
|
let found: BracketEvent | undefined;
|
|
247
276
|
// The openers this stretch opened; the ones from before it are only counted.
|
|
248
277
|
let opened: number[] = [];
|
|
278
|
+
|
|
249
279
|
this.lexStretch(index, {
|
|
250
280
|
open(at, depth) {
|
|
251
281
|
opened.push(at);
|
|
282
|
+
|
|
252
283
|
if (at === offset) {
|
|
253
284
|
found = { kind: 'open', depth };
|
|
254
285
|
}
|
|
255
286
|
},
|
|
256
287
|
close(at, depth) {
|
|
257
288
|
const opener = opened.pop();
|
|
289
|
+
|
|
258
290
|
if (at === offset) {
|
|
259
291
|
found = { kind: 'close', depth, ...(opener === undefined ? {} : { mate: opener }) };
|
|
260
292
|
} else if (found?.kind === 'open' && found.mate === undefined && depth === found.depth) {
|
|
@@ -263,6 +295,7 @@ export class BracketTracker {
|
|
|
263
295
|
},
|
|
264
296
|
mismatch(at) {
|
|
265
297
|
opened = [];
|
|
298
|
+
|
|
266
299
|
if (at === offset) {
|
|
267
300
|
found = { kind: 'mismatch', depth: 0 };
|
|
268
301
|
} else if (found?.kind === 'open' && found.mate === undefined) {
|
|
@@ -270,6 +303,7 @@ export class BracketTracker {
|
|
|
270
303
|
}
|
|
271
304
|
}
|
|
272
305
|
});
|
|
306
|
+
|
|
273
307
|
return found;
|
|
274
308
|
}
|
|
275
309
|
|
|
@@ -279,10 +313,13 @@ export class BracketTracker {
|
|
|
279
313
|
if (!this.prove(this.checkpoints[current]!.line)) {
|
|
280
314
|
return null;
|
|
281
315
|
}
|
|
316
|
+
|
|
282
317
|
if (this.checkpoints[current]!.low >= depth) {
|
|
283
318
|
continue;
|
|
284
319
|
}
|
|
320
|
+
|
|
285
321
|
let result: number | false | undefined;
|
|
322
|
+
|
|
286
323
|
this.lexStretch(current, {
|
|
287
324
|
open() {},
|
|
288
325
|
close(at, closed) {
|
|
@@ -294,8 +331,10 @@ export class BracketTracker {
|
|
|
294
331
|
result ??= false;
|
|
295
332
|
}
|
|
296
333
|
});
|
|
334
|
+
|
|
297
335
|
return result === false ? undefined : result;
|
|
298
336
|
}
|
|
337
|
+
|
|
299
338
|
return undefined;
|
|
300
339
|
}
|
|
301
340
|
|
|
@@ -305,7 +344,9 @@ export class BracketTracker {
|
|
|
305
344
|
if (this.checkpoints[current]!.low >= depth) {
|
|
306
345
|
continue;
|
|
307
346
|
}
|
|
347
|
+
|
|
308
348
|
let result: number | undefined;
|
|
349
|
+
|
|
309
350
|
this.lexStretch(current, {
|
|
310
351
|
...IGNORE,
|
|
311
352
|
open(at, opened) {
|
|
@@ -314,8 +355,10 @@ export class BracketTracker {
|
|
|
314
355
|
}
|
|
315
356
|
}
|
|
316
357
|
});
|
|
358
|
+
|
|
317
359
|
return result;
|
|
318
360
|
}
|
|
361
|
+
|
|
319
362
|
return undefined;
|
|
320
363
|
}
|
|
321
364
|
}
|