@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/src/structure.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { bracketRole, commentRole } from './fold-roles.ts';
|
|
2
2
|
import { openers } from './lexical.ts';
|
|
3
|
+
import { whitespaceOf } from './line-text.ts';
|
|
3
4
|
import type { DocumentLine } from './rope.ts';
|
|
4
5
|
|
|
5
6
|
/* A bracket pair, string or block comment. `inner*` is the range without its delimiters. */
|
|
@@ -99,14 +100,17 @@ interface Header {
|
|
|
99
100
|
/* Clamped to 1 through 16, with 4 for anything that is not a finite number. */
|
|
100
101
|
export function tabWidth(options: { tabSize?: number }): number {
|
|
101
102
|
const requested = options.tabSize ?? 4;
|
|
103
|
+
|
|
102
104
|
return Number.isFinite(requested) ? Math.max(1, Math.min(16, Math.trunc(requested))) : 4;
|
|
103
105
|
}
|
|
104
106
|
|
|
105
107
|
export function indentationColumn(text: string, tabSize: number): number {
|
|
106
108
|
let column = 0;
|
|
109
|
+
|
|
107
110
|
for (const character of text) {
|
|
108
111
|
column += character === '\t' ? tabSize - (column % tabSize) : 1;
|
|
109
112
|
}
|
|
113
|
+
|
|
110
114
|
return column;
|
|
111
115
|
}
|
|
112
116
|
|
|
@@ -114,25 +118,31 @@ export function indentationColumn(text: string, tabSize: number): number {
|
|
|
114
118
|
export function scanStructure(text: string): StructureRange[] {
|
|
115
119
|
const result: StructureRange[] = [];
|
|
116
120
|
const stack: { from: number; closer: string }[] = [];
|
|
121
|
+
|
|
117
122
|
for (let offset = 0; offset < text.length; offset++) {
|
|
118
123
|
const character = text[offset]!;
|
|
124
|
+
|
|
119
125
|
if (character === '/' && text[offset + 1] === '/') {
|
|
120
126
|
const end = text.indexOf('\n', offset + 2);
|
|
121
127
|
offset = end === -1 ? text.length : end;
|
|
122
128
|
} else if (character === '/' && text[offset + 1] === '*') {
|
|
123
129
|
const end = text.indexOf('*/', offset + 2);
|
|
130
|
+
|
|
124
131
|
if (end === -1) {
|
|
125
132
|
break;
|
|
126
133
|
}
|
|
134
|
+
|
|
127
135
|
result.push({ from: offset, to: end + 2, innerFrom: offset + 2, innerTo: end, kind: 'comment' });
|
|
128
136
|
offset = end + 1;
|
|
129
137
|
} else if (character === '"' || character === "'" || character === '`') {
|
|
130
138
|
const start = offset;
|
|
139
|
+
|
|
131
140
|
for (offset++; offset < text.length; offset++) {
|
|
132
141
|
if (text[offset] === '\\') {
|
|
133
142
|
offset++;
|
|
134
143
|
} else if (text[offset] === character) {
|
|
135
144
|
result.push({ from: start, to: offset + 1, innerFrom: start + 1, innerTo: offset, kind: 'string' });
|
|
145
|
+
|
|
136
146
|
break;
|
|
137
147
|
} else if (character !== '`' && (text[offset] === '\n' || text[offset] === '\r')) {
|
|
138
148
|
break;
|
|
@@ -145,6 +155,7 @@ export function scanStructure(text: string): StructureRange[] {
|
|
|
145
155
|
result.push({ from: opener.from, to: offset + 1, innerFrom: opener.from + 1, innerTo: offset, kind: 'bracket' });
|
|
146
156
|
}
|
|
147
157
|
}
|
|
158
|
+
|
|
148
159
|
return result.sort((left, right) => left.from - right.from || right.to - left.to);
|
|
149
160
|
}
|
|
150
161
|
|
|
@@ -161,21 +172,28 @@ function indentationFolds(lineCount: number, getLine: (line: number) => Document
|
|
|
161
172
|
|
|
162
173
|
for (let line = 0; line < lineCount; line++) {
|
|
163
174
|
const text = getLine(line).text;
|
|
175
|
+
|
|
164
176
|
if (!text.trim()) {
|
|
165
177
|
continue;
|
|
166
178
|
}
|
|
167
|
-
|
|
179
|
+
|
|
180
|
+
const indent = indentationColumn(whitespaceOf(text), tabSize);
|
|
181
|
+
|
|
168
182
|
while (headers.length > 0 && indent <= headers.at(-1)!.indent) {
|
|
169
183
|
close(headers.pop()!, previous!.line);
|
|
170
184
|
}
|
|
185
|
+
|
|
171
186
|
if (previous && indent > previous.indent) {
|
|
172
187
|
headers.push(previous);
|
|
173
188
|
}
|
|
189
|
+
|
|
174
190
|
previous = { line, indent };
|
|
175
191
|
}
|
|
192
|
+
|
|
176
193
|
while (headers.length > 0) {
|
|
177
194
|
close(headers.pop()!, previous!.line);
|
|
178
195
|
}
|
|
196
|
+
|
|
179
197
|
return folds;
|
|
180
198
|
}
|
|
181
199
|
|
|
@@ -190,12 +208,15 @@ export function deriveFoldingRanges(
|
|
|
190
208
|
): FoldingRange[] {
|
|
191
209
|
const minimum = Number.isFinite(options.minLines) ? Math.max(1, Math.trunc(options.minLines!)) : 1;
|
|
192
210
|
const result: FoldingRange[] = [];
|
|
211
|
+
|
|
193
212
|
for (const range of ranges) {
|
|
194
213
|
if (range.kind === 'string' || (range.kind === 'bracket' ? options.brackets === false : options.comments === false)) {
|
|
195
214
|
continue;
|
|
196
215
|
}
|
|
216
|
+
|
|
197
217
|
const startLine = lineAt(range.from);
|
|
198
218
|
const endLine = lineAt(range.to - 1);
|
|
219
|
+
|
|
199
220
|
if (endLine - startLine >= minimum) {
|
|
200
221
|
const role =
|
|
201
222
|
slice === undefined
|
|
@@ -203,29 +224,36 @@ export function deriveFoldingRanges(
|
|
|
203
224
|
: range.kind === 'comment'
|
|
204
225
|
? commentRole(slice, range.from, range.to)
|
|
205
226
|
: bracketRole(slice, range.from, options.language ?? '');
|
|
227
|
+
|
|
206
228
|
result.push({ from: range.from, to: range.to, startLine, endLine, kind: range.kind, ...(role === undefined ? {} : { role }) });
|
|
207
229
|
}
|
|
208
230
|
}
|
|
231
|
+
|
|
209
232
|
if (options.indentation) {
|
|
210
233
|
// One push per fold: a long file has more of them than V8 takes as arguments in one call.
|
|
211
234
|
for (const fold of indentationFolds(lineCount, getLine, tabWidth(options), minimum)) {
|
|
212
235
|
result.push(fold);
|
|
213
236
|
}
|
|
214
237
|
}
|
|
238
|
+
|
|
215
239
|
result.sort((left, right) => left.startLine - right.startLine || right.endLine - left.endLine);
|
|
240
|
+
|
|
216
241
|
return dedupeFoldingRanges(result);
|
|
217
242
|
}
|
|
218
243
|
|
|
219
244
|
/* Of ranges that span the same lines, sorted outermost first, the first stays and takes the role of a later one when it has none. */
|
|
220
245
|
export function dedupeFoldingRanges(sorted: readonly FoldingRange[]): FoldingRange[] {
|
|
221
246
|
const unique: FoldingRange[] = [];
|
|
247
|
+
|
|
222
248
|
for (const range of sorted) {
|
|
223
249
|
const last = unique.at(-1);
|
|
250
|
+
|
|
224
251
|
if (last === undefined || range.startLine !== last.startLine || range.endLine !== last.endLine) {
|
|
225
252
|
unique.push(range);
|
|
226
253
|
} else if (last.role === undefined && range.role !== undefined) {
|
|
227
254
|
last.role = range.role;
|
|
228
255
|
}
|
|
229
256
|
}
|
|
257
|
+
|
|
230
258
|
return unique;
|
|
231
259
|
}
|
package/src/text-span.ts
CHANGED
|
@@ -23,22 +23,29 @@ export function changedSpan(before: string, after: string): TextSpan | null {
|
|
|
23
23
|
if (before === after) {
|
|
24
24
|
return null;
|
|
25
25
|
}
|
|
26
|
+
|
|
26
27
|
const limit = Math.min(before.length, after.length);
|
|
27
28
|
let start = 0;
|
|
29
|
+
|
|
28
30
|
while (start < limit && before.charCodeAt(start) === after.charCodeAt(start)) {
|
|
29
31
|
start += 1;
|
|
30
32
|
}
|
|
33
|
+
|
|
31
34
|
// Never between the halves of a surrogate pair, which would leave a lone half on either side.
|
|
32
35
|
if (start > 0 && isHighSurrogate(before.charCodeAt(start - 1))) {
|
|
33
36
|
start -= 1;
|
|
34
37
|
}
|
|
38
|
+
|
|
35
39
|
let tail = 0;
|
|
40
|
+
|
|
36
41
|
while (tail < limit - start && before.charCodeAt(before.length - 1 - tail) === after.charCodeAt(after.length - 1 - tail)) {
|
|
37
42
|
tail += 1;
|
|
38
43
|
}
|
|
44
|
+
|
|
39
45
|
if (tail > 0 && isLowSurrogate(before.charCodeAt(before.length - tail))) {
|
|
40
46
|
tail -= 1;
|
|
41
47
|
}
|
|
48
|
+
|
|
42
49
|
return { start, end: before.length - tail, text: after.slice(start, after.length - tail) };
|
|
43
50
|
}
|
|
44
51
|
|
|
@@ -59,13 +66,16 @@ interface LineHunk {
|
|
|
59
66
|
function splitLines(text: string): string[] {
|
|
60
67
|
const lines: string[] = [];
|
|
61
68
|
let from = 0;
|
|
69
|
+
|
|
62
70
|
for (let at = text.indexOf('\n'); at >= 0; at = text.indexOf('\n', from)) {
|
|
63
71
|
lines.push(text.slice(from, at + 1));
|
|
64
72
|
from = at + 1;
|
|
65
73
|
}
|
|
74
|
+
|
|
66
75
|
if (from < text.length) {
|
|
67
76
|
lines.push(text.slice(from));
|
|
68
77
|
}
|
|
78
|
+
|
|
69
79
|
return lines;
|
|
70
80
|
}
|
|
71
81
|
|
|
@@ -82,38 +92,50 @@ function diffLineHunks(before: Int32Array, after: Int32Array): LineHunk[] | null
|
|
|
82
92
|
const trace: Int32Array[] = [];
|
|
83
93
|
let work = 0;
|
|
84
94
|
let found = -1;
|
|
95
|
+
|
|
85
96
|
for (let edits = 0; edits <= maxEdits && found < 0; edits++) {
|
|
86
97
|
trace.push(frontier.slice());
|
|
98
|
+
|
|
87
99
|
for (let diagonal = -edits; diagonal <= edits; diagonal += 2) {
|
|
88
100
|
let x: number;
|
|
101
|
+
|
|
89
102
|
if (diagonal === -edits || (diagonal !== edits && frontier[base + diagonal - 1]! < frontier[base + diagonal + 1]!)) {
|
|
90
103
|
x = frontier[base + diagonal + 1]!;
|
|
91
104
|
} else {
|
|
92
105
|
x = frontier[base + diagonal - 1]! + 1;
|
|
93
106
|
}
|
|
107
|
+
|
|
94
108
|
let y = x - diagonal;
|
|
109
|
+
|
|
95
110
|
while (x < rows && y < columns && before[x] === after[y]) {
|
|
96
111
|
x++;
|
|
97
112
|
y++;
|
|
98
113
|
work++;
|
|
99
114
|
}
|
|
115
|
+
|
|
100
116
|
work++;
|
|
101
117
|
frontier[base + diagonal] = x;
|
|
118
|
+
|
|
102
119
|
if (x >= rows && y >= columns) {
|
|
103
120
|
found = edits;
|
|
121
|
+
|
|
104
122
|
break;
|
|
105
123
|
}
|
|
106
124
|
}
|
|
125
|
+
|
|
107
126
|
if (work > DIFF_WORK_LIMIT) {
|
|
108
127
|
return null;
|
|
109
128
|
}
|
|
110
129
|
}
|
|
130
|
+
|
|
111
131
|
if (found < 0) {
|
|
112
132
|
return null;
|
|
113
133
|
}
|
|
134
|
+
|
|
114
135
|
const operations: { kind: 'delete' | 'insert'; x: number; y: number }[] = [];
|
|
115
136
|
let x = rows;
|
|
116
137
|
let y = columns;
|
|
138
|
+
|
|
117
139
|
for (let edits = found; edits > 0; edits--) {
|
|
118
140
|
const previous = trace[edits]!;
|
|
119
141
|
const diagonal = x - y;
|
|
@@ -121,10 +143,12 @@ function diffLineHunks(before: Int32Array, after: Int32Array): LineHunk[] | null
|
|
|
121
143
|
const previousDiagonal = fromAbove ? diagonal + 1 : diagonal - 1;
|
|
122
144
|
const previousX = previous[base + previousDiagonal]!;
|
|
123
145
|
const previousY = previousX - previousDiagonal;
|
|
146
|
+
|
|
124
147
|
while (x > previousX && y > previousY) {
|
|
125
148
|
x--;
|
|
126
149
|
y--;
|
|
127
150
|
}
|
|
151
|
+
|
|
128
152
|
if (fromAbove) {
|
|
129
153
|
y--;
|
|
130
154
|
operations.push({ kind: 'insert', x, y });
|
|
@@ -133,9 +157,11 @@ function diffLineHunks(before: Int32Array, after: Int32Array): LineHunk[] | null
|
|
|
133
157
|
operations.push({ kind: 'delete', x, y });
|
|
134
158
|
}
|
|
135
159
|
}
|
|
160
|
+
|
|
136
161
|
operations.reverse();
|
|
137
162
|
const hunks: LineHunk[] = [];
|
|
138
163
|
let open: { beforeFrom: number; afterFrom: number; beforeTo: number; afterTo: number } | null = null;
|
|
164
|
+
|
|
139
165
|
for (const operation of operations) {
|
|
140
166
|
if (open !== null && open.beforeTo === operation.x && open.afterTo === operation.y) {
|
|
141
167
|
if (operation.kind === 'delete') {
|
|
@@ -143,11 +169,14 @@ function diffLineHunks(before: Int32Array, after: Int32Array): LineHunk[] | null
|
|
|
143
169
|
} else {
|
|
144
170
|
open.afterTo++;
|
|
145
171
|
}
|
|
172
|
+
|
|
146
173
|
continue;
|
|
147
174
|
}
|
|
175
|
+
|
|
148
176
|
if (open !== null) {
|
|
149
177
|
hunks.push(open);
|
|
150
178
|
}
|
|
179
|
+
|
|
151
180
|
open = {
|
|
152
181
|
beforeFrom: operation.x,
|
|
153
182
|
afterFrom: operation.y,
|
|
@@ -155,9 +184,11 @@ function diffLineHunks(before: Int32Array, after: Int32Array): LineHunk[] | null
|
|
|
155
184
|
afterTo: operation.y + (operation.kind === 'insert' ? 1 : 0)
|
|
156
185
|
};
|
|
157
186
|
}
|
|
187
|
+
|
|
158
188
|
if (open !== null) {
|
|
159
189
|
hunks.push(open);
|
|
160
190
|
}
|
|
191
|
+
|
|
161
192
|
return hunks;
|
|
162
193
|
}
|
|
163
194
|
|
|
@@ -169,62 +200,84 @@ function diffLineHunks(before: Int32Array, after: Int32Array): LineHunk[] | null
|
|
|
169
200
|
*/
|
|
170
201
|
export function changedSpans(before: string, after: string): TextSpan[] {
|
|
171
202
|
const outer = changedSpan(before, after);
|
|
203
|
+
|
|
172
204
|
if (outer === null) {
|
|
173
205
|
return [];
|
|
174
206
|
}
|
|
207
|
+
|
|
175
208
|
const afterEnd = after.length - (before.length - outer.end);
|
|
209
|
+
|
|
176
210
|
if (outer.end - outer.start + (afterEnd - outer.start) > DIFF_TEXT_LIMIT) {
|
|
177
211
|
return [outer];
|
|
178
212
|
}
|
|
213
|
+
|
|
179
214
|
// Whole lines on both sides of the change, so the diff compares lines and not their halves.
|
|
180
215
|
const lineStart = outer.start === 0 ? 0 : before.lastIndexOf('\n', outer.start - 1) + 1;
|
|
216
|
+
|
|
181
217
|
const lineEnd = (at: string, from: number): number => {
|
|
182
218
|
const newline = at.indexOf('\n', from);
|
|
219
|
+
|
|
183
220
|
return newline < 0 ? at.length : newline + 1;
|
|
184
221
|
};
|
|
222
|
+
|
|
185
223
|
const beforeEnd = lineEnd(before, outer.end > outer.start ? outer.end - 1 : outer.end);
|
|
186
224
|
const afterLineEnd = beforeEnd - outer.end + afterEnd;
|
|
187
225
|
const beforeLines = splitLines(before.slice(lineStart, beforeEnd));
|
|
188
226
|
const afterLines = splitLines(after.slice(lineStart, afterLineEnd));
|
|
227
|
+
|
|
189
228
|
if (beforeLines.length < 2 && afterLines.length < 2) {
|
|
190
229
|
return [outer];
|
|
191
230
|
}
|
|
231
|
+
|
|
192
232
|
const ids = new Map<string, number>();
|
|
233
|
+
|
|
193
234
|
const intern = (lines: string[]): Int32Array => {
|
|
194
235
|
const result = new Int32Array(lines.length);
|
|
236
|
+
|
|
195
237
|
for (let i = 0; i < lines.length; i++) {
|
|
196
238
|
const line = lines[i]!;
|
|
197
239
|
let id = ids.get(line);
|
|
240
|
+
|
|
198
241
|
if (id === undefined) {
|
|
199
242
|
id = ids.size;
|
|
200
243
|
ids.set(line, id);
|
|
201
244
|
}
|
|
245
|
+
|
|
202
246
|
result[i] = id;
|
|
203
247
|
}
|
|
248
|
+
|
|
204
249
|
return result;
|
|
205
250
|
};
|
|
251
|
+
|
|
206
252
|
const hunks = diffLineHunks(intern(beforeLines), intern(afterLines));
|
|
253
|
+
|
|
207
254
|
if (hunks === null || hunks.length === 0) {
|
|
208
255
|
return [outer];
|
|
209
256
|
}
|
|
257
|
+
|
|
210
258
|
const beforeOffsets = lineOffsets(beforeLines, lineStart);
|
|
211
259
|
const afterOffsets = lineOffsets(afterLines, lineStart);
|
|
212
260
|
const spans: TextSpan[] = [];
|
|
261
|
+
|
|
213
262
|
for (const hunk of hunks) {
|
|
214
263
|
const beforeFrom = beforeOffsets[hunk.beforeFrom]!;
|
|
215
264
|
const afterFrom = afterOffsets[hunk.afterFrom]!;
|
|
216
265
|
const inner = changedSpan(before.slice(beforeFrom, beforeOffsets[hunk.beforeTo]), after.slice(afterFrom, afterOffsets[hunk.afterTo]));
|
|
266
|
+
|
|
217
267
|
if (inner !== null) {
|
|
218
268
|
spans.push({ start: beforeFrom + inner.start, end: beforeFrom + inner.end, text: inner.text });
|
|
219
269
|
}
|
|
220
270
|
}
|
|
271
|
+
|
|
221
272
|
return spans;
|
|
222
273
|
}
|
|
223
274
|
|
|
224
275
|
function lineOffsets(lines: readonly string[], base: number): number[] {
|
|
225
276
|
const offsets = [base];
|
|
277
|
+
|
|
226
278
|
for (const line of lines) {
|
|
227
279
|
offsets.push(offsets[offsets.length - 1]! + line.length);
|
|
228
280
|
}
|
|
281
|
+
|
|
229
282
|
return offsets;
|
|
230
283
|
}
|