@coze-editor/extensions 0.1.0-alpha.09ffeb
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/LICENSE +21 -0
- package/dist/esm/index.js +1399 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/index.d.mts +170 -0
- package/dist/index.d.ts +170 -0
- package/dist/index.js +1418 -0
- package/dist/index.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,1399 @@
|
|
|
1
|
+
// src/ast-decorator/whole.ts
|
|
2
|
+
import { omit } from "es-toolkit";
|
|
3
|
+
import { traverseAST } from "@coze-editor/utils";
|
|
4
|
+
import {
|
|
5
|
+
Decoration,
|
|
6
|
+
EditorView
|
|
7
|
+
} from "@codemirror/view";
|
|
8
|
+
import {
|
|
9
|
+
Facet as Facet2,
|
|
10
|
+
RangeSet,
|
|
11
|
+
StateEffect,
|
|
12
|
+
StateField,
|
|
13
|
+
Prec
|
|
14
|
+
} from "@codemirror/state";
|
|
15
|
+
import { syntaxTree } from "@codemirror/language";
|
|
16
|
+
|
|
17
|
+
// src/background-decorations/index.ts
|
|
18
|
+
import {
|
|
19
|
+
RectangleMarker,
|
|
20
|
+
layer
|
|
21
|
+
} from "@codemirror/view";
|
|
22
|
+
import { EditorSelection, Facet } from "@codemirror/state";
|
|
23
|
+
|
|
24
|
+
// src/background-decorations/squash.ts
|
|
25
|
+
function squash(decorations) {
|
|
26
|
+
const result = [];
|
|
27
|
+
const grouped = groupDecorationsByClassName(decorations);
|
|
28
|
+
grouped.forEach((decos, className) => {
|
|
29
|
+
const merged = mergeIntervals(decos);
|
|
30
|
+
result.push(
|
|
31
|
+
...merged.map((m) => ({
|
|
32
|
+
from: m.start,
|
|
33
|
+
to: m.end,
|
|
34
|
+
className
|
|
35
|
+
}))
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
function groupDecorationsByClassName(decorations) {
|
|
41
|
+
const map = /* @__PURE__ */ new Map();
|
|
42
|
+
decorations.forEach((deco) => {
|
|
43
|
+
if (!map.has(deco.className)) {
|
|
44
|
+
map.set(deco.className, []);
|
|
45
|
+
}
|
|
46
|
+
map.get(deco.className).push(deco);
|
|
47
|
+
});
|
|
48
|
+
return map;
|
|
49
|
+
}
|
|
50
|
+
function mergeIntervals(decorations) {
|
|
51
|
+
const merged = [];
|
|
52
|
+
const cloned = decorations.map((deco) => ({
|
|
53
|
+
start: deco.from,
|
|
54
|
+
end: deco.to
|
|
55
|
+
}));
|
|
56
|
+
cloned.sort((a, b) => a.start - b.start);
|
|
57
|
+
let current = cloned[0];
|
|
58
|
+
for (let i = 1; i < cloned.length; i++) {
|
|
59
|
+
const next = cloned[i];
|
|
60
|
+
if (next.start <= current.end) {
|
|
61
|
+
current.end = Math.max(current.end, next.end);
|
|
62
|
+
} else {
|
|
63
|
+
merged.push(current);
|
|
64
|
+
current = next;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
merged.push(current);
|
|
68
|
+
return merged;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/background-decorations/cut.ts
|
|
72
|
+
function cutAtLineBreak(decorations, state) {
|
|
73
|
+
const cuts = [];
|
|
74
|
+
decorations.forEach((deco) => {
|
|
75
|
+
const startLine = state.doc.lineAt(deco.from);
|
|
76
|
+
const endLine = state.doc.lineAt(deco.to);
|
|
77
|
+
if (startLine.number !== endLine.number) {
|
|
78
|
+
const slices = [];
|
|
79
|
+
let pos = deco.from;
|
|
80
|
+
for (let i = startLine.number; i < endLine.number; i++) {
|
|
81
|
+
const line = state.doc.line(i);
|
|
82
|
+
slices.push({
|
|
83
|
+
from: pos,
|
|
84
|
+
to: line.to,
|
|
85
|
+
className: deco.className
|
|
86
|
+
});
|
|
87
|
+
pos = line.to + 1;
|
|
88
|
+
}
|
|
89
|
+
slices.push({
|
|
90
|
+
from: pos,
|
|
91
|
+
to: deco.to,
|
|
92
|
+
className: deco.className
|
|
93
|
+
});
|
|
94
|
+
cuts.push(...slices);
|
|
95
|
+
} else {
|
|
96
|
+
cuts.push(deco);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
return cuts;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// src/background-decorations/index.ts
|
|
103
|
+
function configChanged(update) {
|
|
104
|
+
return update.startState.facet(backgroundDecorations) !== update.state.facet(backgroundDecorations);
|
|
105
|
+
}
|
|
106
|
+
var backgroundDecoratorLayer = layer({
|
|
107
|
+
above: false,
|
|
108
|
+
markers(view) {
|
|
109
|
+
const decorations = view.state.facet(backgroundDecorations).reduce((memo, current) => [...memo, ...current(view)], []);
|
|
110
|
+
return cutAtLineBreak(squash(decorations), view.state).map(
|
|
111
|
+
(deco) => RectangleMarker.forRange(
|
|
112
|
+
view,
|
|
113
|
+
deco.className,
|
|
114
|
+
EditorSelection.range(deco.from, deco.to)
|
|
115
|
+
)
|
|
116
|
+
).reduce((memo, current) => [...memo, ...current], []);
|
|
117
|
+
},
|
|
118
|
+
update(update) {
|
|
119
|
+
return update.focusChanged || update.docChanged || update.selectionSet || update.viewportChanged || configChanged(update);
|
|
120
|
+
},
|
|
121
|
+
class: "cm-backgroundDecoratorLayer"
|
|
122
|
+
});
|
|
123
|
+
var backgroundDecorations = Facet.define({
|
|
124
|
+
enables: [backgroundDecoratorLayer]
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// src/ast-decorator/whole.ts
|
|
128
|
+
var updateWholeDecorationsEffect = StateEffect.define();
|
|
129
|
+
function updateWholeDecorations(view) {
|
|
130
|
+
view.dispatch({
|
|
131
|
+
effects: updateWholeDecorationsEffect.of(null)
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
var classNameDecorationCache = /* @__PURE__ */ new Map();
|
|
135
|
+
function getClassDecoration(className) {
|
|
136
|
+
if (!classNameDecorationCache.has(className)) {
|
|
137
|
+
classNameDecorationCache.set(
|
|
138
|
+
className,
|
|
139
|
+
Decoration.mark({
|
|
140
|
+
class: className
|
|
141
|
+
})
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
return classNameDecorationCache.get(className);
|
|
145
|
+
}
|
|
146
|
+
function buildDecorations(state, tree) {
|
|
147
|
+
const decorates = state.facet(wholeASTDecoratorFacet) ?? [];
|
|
148
|
+
const treeCursor = tree.cursor();
|
|
149
|
+
const decorations = {
|
|
150
|
+
highest: Decoration.none,
|
|
151
|
+
high: Decoration.none,
|
|
152
|
+
default: Decoration.none,
|
|
153
|
+
low: Decoration.none,
|
|
154
|
+
lowest: Decoration.none
|
|
155
|
+
};
|
|
156
|
+
let atomicRanges = RangeSet.empty;
|
|
157
|
+
let backgroundDecorations2 = [];
|
|
158
|
+
const from = 0;
|
|
159
|
+
const to = state.doc.length;
|
|
160
|
+
traverseAST(treeCursor, from, to, (cursor) => {
|
|
161
|
+
decorates.forEach((decorate) => {
|
|
162
|
+
const decorationSpec = decorate(cursor, state);
|
|
163
|
+
let specs = [];
|
|
164
|
+
if (Array.isArray(decorationSpec)) {
|
|
165
|
+
specs = decorationSpec;
|
|
166
|
+
} else if (decorationSpec) {
|
|
167
|
+
specs = [decorationSpec];
|
|
168
|
+
}
|
|
169
|
+
specs.forEach((spec) => {
|
|
170
|
+
const result = updateDecorationSpec(cursor, spec, {
|
|
171
|
+
decorations,
|
|
172
|
+
backgroundDecorations: backgroundDecorations2,
|
|
173
|
+
atomicRanges
|
|
174
|
+
});
|
|
175
|
+
if (!result) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
backgroundDecorations2 = result.backgroundDecorations;
|
|
179
|
+
atomicRanges = result.atomicRanges;
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
return {
|
|
184
|
+
decorations,
|
|
185
|
+
atomicRanges,
|
|
186
|
+
backgroundDecorations: backgroundDecorations2
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function updateDecorationSpec(cursor, decorationSpec, {
|
|
190
|
+
decorations,
|
|
191
|
+
backgroundDecorations: backgroundDecorations2,
|
|
192
|
+
atomicRanges
|
|
193
|
+
}) {
|
|
194
|
+
if (!decorationSpec) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const from = decorationSpec.from ?? cursor.from;
|
|
198
|
+
const to = decorationSpec.to ?? cursor.to;
|
|
199
|
+
const omitKeys = ["type", "from", "to", "atomicRange"];
|
|
200
|
+
let decorationRange = null;
|
|
201
|
+
switch (decorationSpec.type) {
|
|
202
|
+
case "className":
|
|
203
|
+
decorationRange = from === to ? null : getClassDecoration(decorationSpec.className).range(from, to);
|
|
204
|
+
break;
|
|
205
|
+
case "replace":
|
|
206
|
+
decorationRange = Decoration.replace(
|
|
207
|
+
omit(decorationSpec, omitKeys)
|
|
208
|
+
).range(from, to);
|
|
209
|
+
break;
|
|
210
|
+
case "widget":
|
|
211
|
+
decorationRange = Decoration.widget(omit(decorationSpec, omitKeys)).range(
|
|
212
|
+
from
|
|
213
|
+
);
|
|
214
|
+
break;
|
|
215
|
+
case "background":
|
|
216
|
+
backgroundDecorations2.push({
|
|
217
|
+
from: decorationSpec.from ?? cursor.from,
|
|
218
|
+
to: decorationSpec.to ?? cursor.to,
|
|
219
|
+
className: decorationSpec.className
|
|
220
|
+
});
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
if (decorationRange) {
|
|
224
|
+
const prec = decorationSpec.type === "className" ? decorationSpec.prec ?? "default" : "default";
|
|
225
|
+
if (decorations[prec]) {
|
|
226
|
+
decorations[prec] = decorations[prec].update({
|
|
227
|
+
add: [decorationRange],
|
|
228
|
+
sort: true
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
if (decorationSpec.type !== "background" && decorationRange && decorationSpec.atomicRange === true) {
|
|
233
|
+
atomicRanges = atomicRanges.update({
|
|
234
|
+
add: [decorationRange],
|
|
235
|
+
sort: true
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
return {
|
|
239
|
+
decorations,
|
|
240
|
+
backgroundDecorations: backgroundDecorations2,
|
|
241
|
+
atomicRanges
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
var decorateField = StateField.define({
|
|
245
|
+
create(state) {
|
|
246
|
+
return buildDecorations(state, syntaxTree(state));
|
|
247
|
+
},
|
|
248
|
+
update(value, tr) {
|
|
249
|
+
const tree = syntaxTree(tr.state);
|
|
250
|
+
const hasUpdateEffect = tr.effects.some(
|
|
251
|
+
(effect) => effect.is(updateWholeDecorationsEffect)
|
|
252
|
+
);
|
|
253
|
+
const syntaxTreeChanged = syntaxTree(tr.startState) !== tree;
|
|
254
|
+
if (syntaxTreeChanged || hasUpdateEffect) {
|
|
255
|
+
value = buildDecorations(tr.state, tree);
|
|
256
|
+
}
|
|
257
|
+
return value;
|
|
258
|
+
},
|
|
259
|
+
provide(field) {
|
|
260
|
+
return [
|
|
261
|
+
Prec.highest(
|
|
262
|
+
EditorView.decorations.compute(
|
|
263
|
+
[field],
|
|
264
|
+
(state) => state.field(field).decorations.highest
|
|
265
|
+
)
|
|
266
|
+
),
|
|
267
|
+
Prec.high(
|
|
268
|
+
EditorView.decorations.compute(
|
|
269
|
+
[field],
|
|
270
|
+
(state) => state.field(field).decorations.high
|
|
271
|
+
)
|
|
272
|
+
),
|
|
273
|
+
Prec.default(
|
|
274
|
+
EditorView.decorations.compute(
|
|
275
|
+
[field],
|
|
276
|
+
(state) => state.field(field).decorations.default
|
|
277
|
+
)
|
|
278
|
+
),
|
|
279
|
+
Prec.low(
|
|
280
|
+
EditorView.decorations.compute(
|
|
281
|
+
[field],
|
|
282
|
+
(state) => state.field(field).decorations.low
|
|
283
|
+
)
|
|
284
|
+
),
|
|
285
|
+
Prec.lowest(
|
|
286
|
+
EditorView.decorations.compute(
|
|
287
|
+
[field],
|
|
288
|
+
(state) => state.field(field).decorations.lowest
|
|
289
|
+
)
|
|
290
|
+
),
|
|
291
|
+
EditorView.atomicRanges.of((view) => view.state.field(field).atomicRanges),
|
|
292
|
+
backgroundDecorations.of(
|
|
293
|
+
(view) => view.state.field(field).backgroundDecorations
|
|
294
|
+
)
|
|
295
|
+
];
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
var wholeASTDecoratorFacet = Facet2.define({
|
|
299
|
+
enables: [decorateField, backgroundDecoratorLayer]
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// src/ast-decorator/cursor.ts
|
|
303
|
+
import {
|
|
304
|
+
Decoration as Decoration2,
|
|
305
|
+
ViewPlugin
|
|
306
|
+
} from "@codemirror/view";
|
|
307
|
+
import { Facet as Facet3, RangeSetBuilder } from "@codemirror/state";
|
|
308
|
+
import { syntaxTree as syntaxTree2 } from "@codemirror/language";
|
|
309
|
+
var CursorASTView = class {
|
|
310
|
+
constructor(view) {
|
|
311
|
+
this.view = view;
|
|
312
|
+
}
|
|
313
|
+
classNameDecorationCache = /* @__PURE__ */ new Map();
|
|
314
|
+
decorations = Decoration2.none;
|
|
315
|
+
backgroundDecorations = [];
|
|
316
|
+
getClassDecoration(className) {
|
|
317
|
+
if (!this.classNameDecorationCache.has(className)) {
|
|
318
|
+
this.classNameDecorationCache.set(
|
|
319
|
+
className,
|
|
320
|
+
Decoration2.mark({
|
|
321
|
+
class: className
|
|
322
|
+
})
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
return this.classNameDecorationCache.get(className);
|
|
326
|
+
}
|
|
327
|
+
update(update) {
|
|
328
|
+
if (update.focusChanged && update.view.hasFocus || !update.startState.selection.eq(update.state.selection)) {
|
|
329
|
+
const tree = syntaxTree2(update.state);
|
|
330
|
+
const pos = update.state.selection.main.head;
|
|
331
|
+
const decorators = update.state.facet(cursorASTDecoratorFacet);
|
|
332
|
+
const cursor = tree.cursorAt(pos, 0);
|
|
333
|
+
const builder = new RangeSetBuilder();
|
|
334
|
+
const bgDecorations = [];
|
|
335
|
+
do {
|
|
336
|
+
decorators.forEach((decorator) => {
|
|
337
|
+
const decorationSpec = decorator(cursor, update.state);
|
|
338
|
+
if (!decorationSpec) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
let decoration;
|
|
342
|
+
switch (decorationSpec.type) {
|
|
343
|
+
case "className":
|
|
344
|
+
decoration = this.getClassDecoration(decorationSpec.className);
|
|
345
|
+
break;
|
|
346
|
+
case "background":
|
|
347
|
+
bgDecorations.push({
|
|
348
|
+
from: decorationSpec.from ?? cursor.from,
|
|
349
|
+
to: decorationSpec.to ?? cursor.to,
|
|
350
|
+
className: decorationSpec.className
|
|
351
|
+
});
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
if (decoration) {
|
|
355
|
+
builder.add(
|
|
356
|
+
decorationSpec.from ?? cursor.from,
|
|
357
|
+
decorationSpec.to ?? cursor.to,
|
|
358
|
+
decoration
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
} while (cursor.parent());
|
|
363
|
+
this.decorations = builder.finish();
|
|
364
|
+
this.backgroundDecorations = bgDecorations;
|
|
365
|
+
}
|
|
366
|
+
if (update.focusChanged && !update.view.hasFocus) {
|
|
367
|
+
this.decorations = Decoration2.none;
|
|
368
|
+
this.backgroundDecorations = [];
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
var cursorASTDecoratorFacet = Facet3.define({
|
|
373
|
+
enables: [
|
|
374
|
+
ViewPlugin.fromClass(CursorASTView, {
|
|
375
|
+
decorations: (v) => v.decorations,
|
|
376
|
+
provide(plugin) {
|
|
377
|
+
return [
|
|
378
|
+
backgroundDecorations.of(
|
|
379
|
+
(view) => {
|
|
380
|
+
var _a;
|
|
381
|
+
return ((_a = view.plugin(plugin)) == null ? void 0 : _a.backgroundDecorations) ?? [];
|
|
382
|
+
}
|
|
383
|
+
)
|
|
384
|
+
];
|
|
385
|
+
}
|
|
386
|
+
})
|
|
387
|
+
]
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
// src/ast-decorator/index.ts
|
|
391
|
+
var astDecorator = {
|
|
392
|
+
whole: wholeASTDecoratorFacet,
|
|
393
|
+
fromCursor: cursorASTDecoratorFacet
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
// src/ast-debugger/index.ts
|
|
397
|
+
import { traverseAST as traverseAST2 } from "@coze-editor/utils";
|
|
398
|
+
import { ViewPlugin as ViewPlugin2 } from "@codemirror/view";
|
|
399
|
+
import { syntaxTree as syntaxTree3 } from "@codemirror/language";
|
|
400
|
+
var astDebugger = ViewPlugin2.fromClass(
|
|
401
|
+
class {
|
|
402
|
+
constructor(view) {
|
|
403
|
+
printTreeFromState(view.state);
|
|
404
|
+
}
|
|
405
|
+
update(update) {
|
|
406
|
+
if (update.docChanged) {
|
|
407
|
+
printTreeFromState(update.state);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
);
|
|
412
|
+
function printTreeFromState(state) {
|
|
413
|
+
const tree = syntaxTree3(state);
|
|
414
|
+
console.groupCollapsed("Syntax Tree");
|
|
415
|
+
const cursor = tree.cursor();
|
|
416
|
+
traverseAST2(cursor, 0, tree.length, (cursor2) => {
|
|
417
|
+
console.group(
|
|
418
|
+
`%c${cursor2.name}(${cursor2.from}:${cursor2.to})`,
|
|
419
|
+
"color: purple;"
|
|
420
|
+
);
|
|
421
|
+
console.log(state.sliceDoc(cursor2.from, cursor2.to));
|
|
422
|
+
console.groupEnd();
|
|
423
|
+
});
|
|
424
|
+
console.groupEnd();
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// src/focusable/focusable.ts
|
|
428
|
+
import { WidgetType, keymap } from "@codemirror/view";
|
|
429
|
+
var focusableKeymap = keymap.of([
|
|
430
|
+
{
|
|
431
|
+
key: "ArrowLeft",
|
|
432
|
+
run(view) {
|
|
433
|
+
const mainSelection = view.state.selection.main;
|
|
434
|
+
if (!mainSelection.empty) {
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
let handled = false;
|
|
438
|
+
const pos = mainSelection.from;
|
|
439
|
+
for (const line of view.docView.children) {
|
|
440
|
+
for (const child of line.children) {
|
|
441
|
+
if (child.isWidget && child.widget instanceof FocusableWidget && child.posAtEnd === pos) {
|
|
442
|
+
child.widget.focus(1);
|
|
443
|
+
handled = true;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (handled) {
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return handled;
|
|
451
|
+
}
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
key: "ArrowRight",
|
|
455
|
+
run(view) {
|
|
456
|
+
const mainSelection = view.state.selection.main;
|
|
457
|
+
if (!mainSelection.empty) {
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
let handled = false;
|
|
461
|
+
const pos = mainSelection.from;
|
|
462
|
+
for (const line of view.docView.children) {
|
|
463
|
+
for (const child of line.children) {
|
|
464
|
+
if (child.isWidget && child.widget instanceof FocusableWidget && child.posAtStart === pos) {
|
|
465
|
+
child.widget.focus(-1);
|
|
466
|
+
handled = true;
|
|
467
|
+
break;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
if (handled) {
|
|
471
|
+
break;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
return handled;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
]);
|
|
478
|
+
var FocusableWidget = class extends WidgetType {
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
// src/auto-select-ranges/extension.ts
|
|
482
|
+
import { FacetCombineStrategy, flatten } from "@coze-editor/utils";
|
|
483
|
+
import { ViewPlugin as ViewPlugin3 } from "@codemirror/view";
|
|
484
|
+
import { EditorSelection as EditorSelection2, Facet as Facet4 } from "@codemirror/state";
|
|
485
|
+
|
|
486
|
+
// src/auto-select-ranges/utils.ts
|
|
487
|
+
function mergeIntervals2(ranges) {
|
|
488
|
+
if (ranges.length === 0) {
|
|
489
|
+
return [];
|
|
490
|
+
}
|
|
491
|
+
const merged = [];
|
|
492
|
+
const cloned = ranges.map((range2) => ({
|
|
493
|
+
from: range2.from,
|
|
494
|
+
to: range2.to
|
|
495
|
+
}));
|
|
496
|
+
cloned.sort((a, b) => a.from - b.from);
|
|
497
|
+
let current = cloned[0];
|
|
498
|
+
for (let i = 1; i < cloned.length; i++) {
|
|
499
|
+
const next = cloned[i];
|
|
500
|
+
if (next.from <= current.to) {
|
|
501
|
+
current.to = Math.max(current.to, next.to);
|
|
502
|
+
} else {
|
|
503
|
+
merged.push(current);
|
|
504
|
+
current = next;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
if (current) {
|
|
508
|
+
merged.push(current);
|
|
509
|
+
}
|
|
510
|
+
return merged;
|
|
511
|
+
}
|
|
512
|
+
function findContainingRange(ranges, pos) {
|
|
513
|
+
for (const range2 of ranges) {
|
|
514
|
+
if (pos >= range2.from && pos <= range2.to) {
|
|
515
|
+
return range2;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// src/auto-select-ranges/extension.ts
|
|
521
|
+
var extension = ViewPlugin3.fromClass(
|
|
522
|
+
class {
|
|
523
|
+
lastFocus = false;
|
|
524
|
+
constructor(view) {
|
|
525
|
+
this.lastFocus = view.hasFocus;
|
|
526
|
+
}
|
|
527
|
+
update(update) {
|
|
528
|
+
const userEvent = update.state.facet(selectUserEvent) ?? "select";
|
|
529
|
+
const hasSelectUserEvent = update.transactions.some(
|
|
530
|
+
(tr) => tr.isUserEvent(userEvent)
|
|
531
|
+
);
|
|
532
|
+
if (hasSelectUserEvent) {
|
|
533
|
+
const lastHasFocus = this.lastFocus;
|
|
534
|
+
const startSelection = update.startState.selection;
|
|
535
|
+
const { selection } = update.state;
|
|
536
|
+
const rangesProviders = update.state.facet(rangesFacet);
|
|
537
|
+
const ranges = mergeIntervals2(
|
|
538
|
+
flatten(
|
|
539
|
+
rangesProviders.map((provider) => provider(update.view.state))
|
|
540
|
+
).filter(Boolean)
|
|
541
|
+
);
|
|
542
|
+
const currentRange = findContainingRange(ranges, selection.main.from);
|
|
543
|
+
if (selection.main.empty && currentRange && (!lastHasFocus || // 上一次的选区 from 或 to 在当前选区外(“首次进入”当前选区)
|
|
544
|
+
findContainingRange(ranges, startSelection.main.from) !== currentRange || findContainingRange(ranges, startSelection.main.to) !== currentRange)) {
|
|
545
|
+
queueMicrotask(() => {
|
|
546
|
+
update.view.dispatch({
|
|
547
|
+
selection: selection.replaceRange(
|
|
548
|
+
EditorSelection2.range(currentRange.from, currentRange.to)
|
|
549
|
+
)
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
if (this.lastFocus !== update.view.hasFocus) {
|
|
555
|
+
this.lastFocus = update.view.hasFocus;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
);
|
|
560
|
+
var rangesFacet = Facet4.define({
|
|
561
|
+
enables: extension
|
|
562
|
+
});
|
|
563
|
+
var selectUserEvent = Facet4.define({
|
|
564
|
+
combine: FacetCombineStrategy.Last
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
// src/selection-enlarger/extension.ts
|
|
568
|
+
import { flatten as flatten2, hasOverlap } from "@coze-editor/utils";
|
|
569
|
+
import { EditorSelection as EditorSelection3, EditorState, Facet as Facet5 } from "@codemirror/state";
|
|
570
|
+
var extension2 = EditorState.transactionFilter.of((tr) => {
|
|
571
|
+
if (tr.docChanged || tr.newSelection.eq(tr.startState.selection)) {
|
|
572
|
+
return tr;
|
|
573
|
+
}
|
|
574
|
+
const providers = tr.startState.facet(selectionEnlarger);
|
|
575
|
+
const specs = flatten2(providers.map((provider) => provider(tr.startState)));
|
|
576
|
+
let { newSelection } = tr;
|
|
577
|
+
newSelection.ranges.forEach((range2, index) => {
|
|
578
|
+
for (const spec of specs) {
|
|
579
|
+
if (hasOverlap(range2, spec.source)) {
|
|
580
|
+
const isReversed = range2.head < range2.anchor;
|
|
581
|
+
const from = Math.min(range2.from, spec.target.from);
|
|
582
|
+
const to = Math.max(range2.to, spec.target.to);
|
|
583
|
+
const newRange = isReversed ? EditorSelection3.range(
|
|
584
|
+
to,
|
|
585
|
+
from,
|
|
586
|
+
range2.goalColumn,
|
|
587
|
+
range2.bidiLevel ?? void 0
|
|
588
|
+
) : EditorSelection3.range(
|
|
589
|
+
from,
|
|
590
|
+
to,
|
|
591
|
+
range2.goalColumn,
|
|
592
|
+
range2.bidiLevel ?? void 0
|
|
593
|
+
);
|
|
594
|
+
newSelection = newSelection.replaceRange(newRange, index);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
});
|
|
598
|
+
return [
|
|
599
|
+
tr,
|
|
600
|
+
{
|
|
601
|
+
selection: newSelection
|
|
602
|
+
}
|
|
603
|
+
];
|
|
604
|
+
});
|
|
605
|
+
var selectionEnlarger = Facet5.define({
|
|
606
|
+
enables: extension2
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
// src/deletion-enlarger/extension.ts
|
|
610
|
+
import { flatten as flatten3, hasOverlap as hasOverlap2 } from "@coze-editor/utils";
|
|
611
|
+
import { ChangeSet, EditorState as EditorState2, Facet as Facet6 } from "@codemirror/state";
|
|
612
|
+
var extension3 = EditorState2.transactionFilter.of((tr) => {
|
|
613
|
+
const providers = tr.startState.facet(deletionEnlarger);
|
|
614
|
+
const specs = flatten3(providers.map((provider) => provider(tr.startState)));
|
|
615
|
+
const { length } = tr.startState.doc;
|
|
616
|
+
let changes = ChangeSet.empty(length);
|
|
617
|
+
tr.changes.iterChanges((fromA, toA, fromB, toB) => {
|
|
618
|
+
const isDeletion = fromB === toB;
|
|
619
|
+
if (!isDeletion) {
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
for (const spec of specs) {
|
|
623
|
+
if (hasOverlap2(
|
|
624
|
+
{
|
|
625
|
+
from: fromA,
|
|
626
|
+
to: toA
|
|
627
|
+
},
|
|
628
|
+
spec.source
|
|
629
|
+
)) {
|
|
630
|
+
const change = ChangeSet.of([{ ...spec.target, insert: "" }], length);
|
|
631
|
+
changes = changes.compose(change.map(changes));
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
const nextChanges = changes.map(tr.changes);
|
|
636
|
+
if (nextChanges.empty) {
|
|
637
|
+
return tr;
|
|
638
|
+
}
|
|
639
|
+
return [
|
|
640
|
+
tr,
|
|
641
|
+
{
|
|
642
|
+
changes: nextChanges,
|
|
643
|
+
sequential: true
|
|
644
|
+
}
|
|
645
|
+
];
|
|
646
|
+
});
|
|
647
|
+
var deletionEnlarger = Facet6.define({
|
|
648
|
+
enables: extension3
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
// src/input-rules/index.ts
|
|
652
|
+
import { EditorView as EditorView2 } from "@codemirror/view";
|
|
653
|
+
import { Prec as Prec2 } from "@codemirror/state";
|
|
654
|
+
var android = typeof navigator === "object" && /Android\b/.test(navigator.userAgent);
|
|
655
|
+
var isExtension = (v) => Boolean(v);
|
|
656
|
+
var inputRules = (rules) => {
|
|
657
|
+
const ruleExtensions = (rules ?? []).map(({ type, triggerCharacter, handler }) => {
|
|
658
|
+
if (type === "character") {
|
|
659
|
+
return EditorView2.inputHandler.of((view, from, to, insert) => {
|
|
660
|
+
if ((android ? view.composing : view.compositionStarted) || view.state.readOnly) {
|
|
661
|
+
return false;
|
|
662
|
+
}
|
|
663
|
+
const sel = view.state.selection.main;
|
|
664
|
+
if (insert !== triggerCharacter || from !== sel.from || to !== sel.to) {
|
|
665
|
+
return false;
|
|
666
|
+
}
|
|
667
|
+
return handler({
|
|
668
|
+
view,
|
|
669
|
+
state: view.state,
|
|
670
|
+
from,
|
|
671
|
+
to
|
|
672
|
+
});
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
}).filter((v) => isExtension(v));
|
|
676
|
+
return Prec2.high(ruleExtensions);
|
|
677
|
+
};
|
|
678
|
+
|
|
679
|
+
// src/mix-languages/mix.ts
|
|
680
|
+
import { parseMixed } from "@lezer/common";
|
|
681
|
+
import { parser as templateParser } from "@coze-editor/lezer-parser-template";
|
|
682
|
+
import { LRLanguage } from "@codemirror/language";
|
|
683
|
+
function mixLanguages({ name, outerLanguage, innerLanguage }) {
|
|
684
|
+
return LRLanguage.define({
|
|
685
|
+
name,
|
|
686
|
+
parser: templateParser.configure({
|
|
687
|
+
wrap: parseMixed((node) => {
|
|
688
|
+
if (outerLanguage && node.type.isTop) {
|
|
689
|
+
return {
|
|
690
|
+
parser: outerLanguage.parser,
|
|
691
|
+
overlay: (n) => n.type.name === "Text"
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
if (innerLanguage && node.name === "InterpolationContent") {
|
|
695
|
+
return {
|
|
696
|
+
parser: innerLanguage.parser
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
return null;
|
|
700
|
+
})
|
|
701
|
+
})
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// src/indent-guides/index.ts
|
|
706
|
+
import { range } from "es-toolkit";
|
|
707
|
+
import {
|
|
708
|
+
Decoration as Decoration3,
|
|
709
|
+
EditorView as EditorView3,
|
|
710
|
+
ViewPlugin as ViewPlugin4,
|
|
711
|
+
WidgetType as WidgetType2
|
|
712
|
+
} from "@codemirror/view";
|
|
713
|
+
import { RangeSetBuilder as RangeSetBuilder2 } from "@codemirror/state";
|
|
714
|
+
|
|
715
|
+
// src/indent-guides/utils.ts
|
|
716
|
+
var SPACES = /^\s*/;
|
|
717
|
+
var getCodeStart = (text) => text.match(SPACES)[0].length;
|
|
718
|
+
var fromPairs = (pairs2) => {
|
|
719
|
+
let index = -1;
|
|
720
|
+
const length = pairs2 == null ? 0 : pairs2.length;
|
|
721
|
+
const result = {};
|
|
722
|
+
while (++index < length) {
|
|
723
|
+
const pair = pairs2[index];
|
|
724
|
+
result[pair[0]] = pair[1];
|
|
725
|
+
}
|
|
726
|
+
return result;
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
// src/indent-guides/index.ts
|
|
730
|
+
var indentationMark = Decoration3.mark({ class: "cm-indentation-guide" });
|
|
731
|
+
var indentationLevelMarks = range(20).map(
|
|
732
|
+
(i) => Decoration3.line({ class: `cm-indentation-level-${i}` })
|
|
733
|
+
);
|
|
734
|
+
var IndentationWidget = class _IndentationWidget extends WidgetType2 {
|
|
735
|
+
constructor(indents) {
|
|
736
|
+
super();
|
|
737
|
+
this.indents = indents;
|
|
738
|
+
}
|
|
739
|
+
static create(indents) {
|
|
740
|
+
return Decoration3.widget({
|
|
741
|
+
widget: new _IndentationWidget(indents),
|
|
742
|
+
side: 1
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
toDOM() {
|
|
746
|
+
const wrap = document.createElement("span");
|
|
747
|
+
wrap.className = "cm-indentation-widget";
|
|
748
|
+
for (const indent of this.indents) {
|
|
749
|
+
const marker = wrap.appendChild(document.createElement("span"));
|
|
750
|
+
marker.className = "cm-indentation-guide";
|
|
751
|
+
marker.textContent = " ";
|
|
752
|
+
wrap.append(" ".repeat(indent - 1));
|
|
753
|
+
}
|
|
754
|
+
return wrap;
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
function makeIndentationMark(from, to, indent, tabSize, builder) {
|
|
758
|
+
builder.add(from, from, indentationLevelMarks[indent / tabSize]);
|
|
759
|
+
for (let i = from; i < Math.min(from + indent, to); i += tabSize) {
|
|
760
|
+
builder.add(i, i + 1, indentationMark);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
function makeIndentationWidget(from, to, tabSize, builder) {
|
|
764
|
+
const length = to - from;
|
|
765
|
+
if (length !== 0) {
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
if (tabSize > 2) {
|
|
769
|
+
builder.add(to, to, IndentationWidget.create([tabSize]));
|
|
770
|
+
} else {
|
|
771
|
+
builder.add(to, to, IndentationWidget.create([2, tabSize * 2]));
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
function makeIndentationDecorators(view) {
|
|
775
|
+
const builder = new RangeSetBuilder2();
|
|
776
|
+
const tabSize = Number(view.state.tabSize);
|
|
777
|
+
const { doc } = view.state;
|
|
778
|
+
const spaceOnlyLines = [];
|
|
779
|
+
let currentIndent = 0;
|
|
780
|
+
for (const { from: visibleFrom, to: visibleTo } of view.visibleRanges) {
|
|
781
|
+
let to = visibleFrom - 1;
|
|
782
|
+
let pos, from, length, text;
|
|
783
|
+
while ((pos = to + 1) <= visibleTo) {
|
|
784
|
+
({ from, to, length, text } = doc.lineAt(pos));
|
|
785
|
+
const codeStartsAt = getCodeStart(text);
|
|
786
|
+
const isAllSpaces = codeStartsAt === length;
|
|
787
|
+
const skipIndent = codeStartsAt === 0;
|
|
788
|
+
const isComment = text[codeStartsAt] === "/";
|
|
789
|
+
if (isAllSpaces) {
|
|
790
|
+
spaceOnlyLines.push({ from, to });
|
|
791
|
+
continue;
|
|
792
|
+
} else if (skipIndent) {
|
|
793
|
+
spaceOnlyLines.length = 0;
|
|
794
|
+
continue;
|
|
795
|
+
}
|
|
796
|
+
const indent = Math.ceil(codeStartsAt / tabSize) * tabSize;
|
|
797
|
+
if (!isComment) {
|
|
798
|
+
currentIndent = indent;
|
|
799
|
+
}
|
|
800
|
+
for (const { from: spaceFrom, to: spaceTo } of spaceOnlyLines) {
|
|
801
|
+
makeIndentationMark(
|
|
802
|
+
spaceFrom,
|
|
803
|
+
spaceTo,
|
|
804
|
+
currentIndent,
|
|
805
|
+
tabSize,
|
|
806
|
+
builder
|
|
807
|
+
);
|
|
808
|
+
makeIndentationWidget(spaceFrom, spaceTo, tabSize, builder);
|
|
809
|
+
}
|
|
810
|
+
spaceOnlyLines.length = 0;
|
|
811
|
+
makeIndentationMark(from, to, indent, tabSize, builder);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
return builder.finish();
|
|
815
|
+
}
|
|
816
|
+
var showIndentations = ViewPlugin4.fromClass(
|
|
817
|
+
class {
|
|
818
|
+
decorations;
|
|
819
|
+
constructor(view) {
|
|
820
|
+
this.decorations = makeIndentationDecorators(view);
|
|
821
|
+
}
|
|
822
|
+
update(update) {
|
|
823
|
+
if (update.docChanged || update.viewportChanged) {
|
|
824
|
+
this.decorations = makeIndentationDecorators(update.view);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
decorations: (v) => v.decorations
|
|
830
|
+
}
|
|
831
|
+
);
|
|
832
|
+
var indentationTheme = EditorView3.baseTheme({
|
|
833
|
+
".cm-line": {
|
|
834
|
+
paddingLeft: 0,
|
|
835
|
+
marginLeft: "2px"
|
|
836
|
+
},
|
|
837
|
+
".cm-indentation-guide": {
|
|
838
|
+
position: "relative"
|
|
839
|
+
},
|
|
840
|
+
".cm-indentation-guide:after": {
|
|
841
|
+
position: "absolute",
|
|
842
|
+
content: "''",
|
|
843
|
+
right: "0.9ch",
|
|
844
|
+
height: "1.4em",
|
|
845
|
+
borderLeft: "1px solid rgba(28, 31, 35, .08)"
|
|
846
|
+
},
|
|
847
|
+
...fromPairs(
|
|
848
|
+
indentationLevelMarks.map((_decoration, i) => [
|
|
849
|
+
`.cm-indentation-level-${i}`,
|
|
850
|
+
{ textIndent: `-${i * 2}ch`, paddingLeft: `${i * 2}ch` }
|
|
851
|
+
])
|
|
852
|
+
)
|
|
853
|
+
});
|
|
854
|
+
var indentGuides = () => [showIndentations, indentationTheme];
|
|
855
|
+
|
|
856
|
+
// src/scroll-beyond-last-line/index.ts
|
|
857
|
+
import { EditorView as EditorView4, layer as layer2 } from "@codemirror/view";
|
|
858
|
+
|
|
859
|
+
// src/scroll-beyond-last-line/empty-block.ts
|
|
860
|
+
var EmptyBlock = class {
|
|
861
|
+
constructor(lineNumber, contentHeight, scrollHeight) {
|
|
862
|
+
this.lineNumber = lineNumber;
|
|
863
|
+
this.contentHeight = contentHeight;
|
|
864
|
+
this.scrollHeight = scrollHeight;
|
|
865
|
+
}
|
|
866
|
+
draw() {
|
|
867
|
+
const elt = document.createElement("div");
|
|
868
|
+
elt.className = "cm-empty-scroll-block-maker";
|
|
869
|
+
this.adjust(elt);
|
|
870
|
+
return elt;
|
|
871
|
+
}
|
|
872
|
+
update(_, prev) {
|
|
873
|
+
return prev.lineNumber === this.lineNumber && prev.contentHeight === this.contentHeight && prev.scrollHeight === this.scrollHeight;
|
|
874
|
+
}
|
|
875
|
+
adjust(elt) {
|
|
876
|
+
elt.style.display = "block";
|
|
877
|
+
elt.style.width = "1px";
|
|
878
|
+
if (this.lineNumber > 1) {
|
|
879
|
+
elt.style.height = `calc(${typeof this.scrollHeight === "number" ? `${this.scrollHeight}px` : "100%"} + ${this.contentHeight}px)`;
|
|
880
|
+
} else {
|
|
881
|
+
elt.style.height = "0";
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
eq(p) {
|
|
885
|
+
return this.lineNumber === p.lineNumber && this.contentHeight === p.contentHeight && this.scrollHeight === p.scrollHeight;
|
|
886
|
+
}
|
|
887
|
+
};
|
|
888
|
+
|
|
889
|
+
// src/scroll-beyond-last-line/index.ts
|
|
890
|
+
var scrollBeyondLastLine = (scrollHeight) => [
|
|
891
|
+
layer2({
|
|
892
|
+
above: false,
|
|
893
|
+
updateOnDocViewUpdate: true,
|
|
894
|
+
class: "cm-empty-marker-layer",
|
|
895
|
+
update(update) {
|
|
896
|
+
return update.state.doc.lines !== update.startState.doc.lines;
|
|
897
|
+
},
|
|
898
|
+
markers(view) {
|
|
899
|
+
const maker = new EmptyBlock(
|
|
900
|
+
view.state.doc.lines,
|
|
901
|
+
view.contentHeight,
|
|
902
|
+
scrollHeight
|
|
903
|
+
);
|
|
904
|
+
return [maker];
|
|
905
|
+
}
|
|
906
|
+
}),
|
|
907
|
+
EditorView4.theme({
|
|
908
|
+
"& .cm-empty-marker-layer": {
|
|
909
|
+
height: "100%"
|
|
910
|
+
}
|
|
911
|
+
})
|
|
912
|
+
];
|
|
913
|
+
|
|
914
|
+
// src/brackets/matching-brackets.ts
|
|
915
|
+
import { Decoration as Decoration4, EditorView as EditorView5 } from "@codemirror/view";
|
|
916
|
+
import { StateField as StateField2 } from "@codemirror/state";
|
|
917
|
+
var pairs = {
|
|
918
|
+
"(": ")",
|
|
919
|
+
"{": "}",
|
|
920
|
+
"[": "]",
|
|
921
|
+
")": "(",
|
|
922
|
+
"}": "{",
|
|
923
|
+
"]": "["
|
|
924
|
+
};
|
|
925
|
+
var findMatchingBracket = (state, cursorPos) => {
|
|
926
|
+
try {
|
|
927
|
+
const doc = state.doc.toString();
|
|
928
|
+
let stack = [];
|
|
929
|
+
let leftIndex = -1;
|
|
930
|
+
let rightIndex = -1;
|
|
931
|
+
for (let i = cursorPos - 1; i >= 0; i--) {
|
|
932
|
+
const char = doc[i];
|
|
933
|
+
if (pairs[char]) {
|
|
934
|
+
if ([")", "}", "]"].includes(char)) {
|
|
935
|
+
stack.push(char);
|
|
936
|
+
} else {
|
|
937
|
+
if (stack.length === 0) {
|
|
938
|
+
leftIndex = i;
|
|
939
|
+
break;
|
|
940
|
+
}
|
|
941
|
+
const lastBracket = stack.pop();
|
|
942
|
+
if (pairs[char] !== lastBracket) {
|
|
943
|
+
return null;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
stack = [];
|
|
949
|
+
for (let i = cursorPos; i < doc.length; i++) {
|
|
950
|
+
const char = doc[i];
|
|
951
|
+
if (pairs[char]) {
|
|
952
|
+
if (["(", "{", "["].includes(char)) {
|
|
953
|
+
stack.push(char);
|
|
954
|
+
} else {
|
|
955
|
+
if (stack.length === 0) {
|
|
956
|
+
rightIndex = i;
|
|
957
|
+
break;
|
|
958
|
+
}
|
|
959
|
+
const lastBracket = stack.pop();
|
|
960
|
+
if (pairs[char] !== lastBracket) {
|
|
961
|
+
return null;
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
if (leftIndex !== -1 && rightIndex !== -1) {
|
|
967
|
+
return [leftIndex, rightIndex];
|
|
968
|
+
}
|
|
969
|
+
return null;
|
|
970
|
+
} catch (e) {
|
|
971
|
+
console.error("findMatchingBracket failed", e);
|
|
972
|
+
return null;
|
|
973
|
+
}
|
|
974
|
+
};
|
|
975
|
+
var renderMatch = (match) => {
|
|
976
|
+
const decorations = [];
|
|
977
|
+
const mark = Decoration4.mark({ class: "cm-matchingBracket" });
|
|
978
|
+
decorations.push(mark.range(match, match + 1));
|
|
979
|
+
return decorations;
|
|
980
|
+
};
|
|
981
|
+
var bracketMatchingState = StateField2.define({
|
|
982
|
+
create() {
|
|
983
|
+
return Decoration4.none;
|
|
984
|
+
},
|
|
985
|
+
update(deco, tr) {
|
|
986
|
+
if (!tr.docChanged && !tr.selection) {
|
|
987
|
+
return deco;
|
|
988
|
+
}
|
|
989
|
+
let decorations = [];
|
|
990
|
+
for (const range2 of tr.state.selection.ranges) {
|
|
991
|
+
if (!range2.empty) {
|
|
992
|
+
continue;
|
|
993
|
+
}
|
|
994
|
+
const match = findMatchingBracket(tr.state, range2.head);
|
|
995
|
+
if (match) {
|
|
996
|
+
decorations = decorations.concat(
|
|
997
|
+
renderMatch(match[0]),
|
|
998
|
+
renderMatch(match[1])
|
|
999
|
+
);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
return Decoration4.set(decorations, true);
|
|
1003
|
+
},
|
|
1004
|
+
provide: (f) => EditorView5.decorations.from(f)
|
|
1005
|
+
});
|
|
1006
|
+
var matchingBrackets = [bracketMatchingState];
|
|
1007
|
+
|
|
1008
|
+
// src/brackets/colorization.ts
|
|
1009
|
+
import {
|
|
1010
|
+
EditorView as EditorView6,
|
|
1011
|
+
Decoration as Decoration5,
|
|
1012
|
+
ViewPlugin as ViewPlugin5
|
|
1013
|
+
} from "@codemirror/view";
|
|
1014
|
+
import { syntaxTree as syntaxTree4 } from "@codemirror/language";
|
|
1015
|
+
var DEFAULT_COLORS = ["#ffd700", "#da70d6", "#179fff"];
|
|
1016
|
+
var ColorizationBracketsPlugin = ViewPlugin5.fromClass(
|
|
1017
|
+
class {
|
|
1018
|
+
decorations;
|
|
1019
|
+
constructor(view) {
|
|
1020
|
+
this.decorations = this.getBracketDecorations(view);
|
|
1021
|
+
}
|
|
1022
|
+
update(update) {
|
|
1023
|
+
if (update.docChanged || update.selectionSet || update.viewportChanged) {
|
|
1024
|
+
this.decorations = this.getBracketDecorations(update.view);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
getBracketDecorations(view) {
|
|
1028
|
+
const { doc } = view.state;
|
|
1029
|
+
const decorations = [];
|
|
1030
|
+
const stack = [];
|
|
1031
|
+
const limitNodeType = ["Comment", "String"];
|
|
1032
|
+
const tree = syntaxTree4(view.state);
|
|
1033
|
+
for (let pos = 0; pos < doc.length; pos += 1) {
|
|
1034
|
+
const char = doc.sliceString(pos, pos + 1);
|
|
1035
|
+
const node = tree.resolveInner(pos);
|
|
1036
|
+
if (limitNodeType.includes(node.type.name)) {
|
|
1037
|
+
continue;
|
|
1038
|
+
}
|
|
1039
|
+
if (char === "(" || char === "[" || char === "{") {
|
|
1040
|
+
stack.push({ type: char, from: pos });
|
|
1041
|
+
} else if (char === ")" || char === "]" || char === "}") {
|
|
1042
|
+
const open = stack.pop();
|
|
1043
|
+
if (open && open.type === this.getMatchingBracket(char)) {
|
|
1044
|
+
const index = stack.length % 3;
|
|
1045
|
+
decorations.push(
|
|
1046
|
+
Decoration5.mark({ class: `colorization-bracket-${index}` }).range(
|
|
1047
|
+
open.from,
|
|
1048
|
+
open.from + 1
|
|
1049
|
+
),
|
|
1050
|
+
Decoration5.mark({ class: `colorization-bracket-${index}` }).range(
|
|
1051
|
+
pos,
|
|
1052
|
+
pos + 1
|
|
1053
|
+
)
|
|
1054
|
+
);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
decorations.sort((a, b) => a.from - b.from || a.startSide - b.startSide);
|
|
1059
|
+
return Decoration5.set(decorations);
|
|
1060
|
+
}
|
|
1061
|
+
getMatchingBracket(closingBracket) {
|
|
1062
|
+
switch (closingBracket) {
|
|
1063
|
+
case ")":
|
|
1064
|
+
return "(";
|
|
1065
|
+
case "]":
|
|
1066
|
+
return "[";
|
|
1067
|
+
case "}":
|
|
1068
|
+
return "{";
|
|
1069
|
+
default:
|
|
1070
|
+
return null;
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
},
|
|
1074
|
+
{
|
|
1075
|
+
decorations: (v) => v.decorations
|
|
1076
|
+
}
|
|
1077
|
+
);
|
|
1078
|
+
var colorizationBrackets = [
|
|
1079
|
+
ColorizationBracketsPlugin,
|
|
1080
|
+
EditorView6.baseTheme({
|
|
1081
|
+
".colorization-bracket-0": { color: DEFAULT_COLORS[0] },
|
|
1082
|
+
".colorization-bracket-0 > span": { color: DEFAULT_COLORS[0] },
|
|
1083
|
+
".colorization-bracket-1": { color: DEFAULT_COLORS[1] },
|
|
1084
|
+
".colorization-bracket-1 > span": { color: DEFAULT_COLORS[1] },
|
|
1085
|
+
".colorization-bracket-2": { color: DEFAULT_COLORS[2] },
|
|
1086
|
+
".colorization-bracket-2 > span": { color: DEFAULT_COLORS[2] }
|
|
1087
|
+
})
|
|
1088
|
+
];
|
|
1089
|
+
|
|
1090
|
+
// src/position/index.ts
|
|
1091
|
+
import {
|
|
1092
|
+
Direction,
|
|
1093
|
+
layer as layer3
|
|
1094
|
+
} from "@codemirror/view";
|
|
1095
|
+
import { Facet as Facet7 } from "@codemirror/state";
|
|
1096
|
+
var SelectionSide = /* @__PURE__ */ ((SelectionSide2) => {
|
|
1097
|
+
SelectionSide2["Head"] = "head";
|
|
1098
|
+
SelectionSide2["Anchor"] = "anchor";
|
|
1099
|
+
return SelectionSide2;
|
|
1100
|
+
})(SelectionSide || {});
|
|
1101
|
+
var elementAtPosition = Facet7.define();
|
|
1102
|
+
function configChanged2(update) {
|
|
1103
|
+
return update.startState.facet(elementAtPosition) !== update.state.facet(elementAtPosition);
|
|
1104
|
+
}
|
|
1105
|
+
var PositionMarker = class {
|
|
1106
|
+
constructor(dom, rect) {
|
|
1107
|
+
this.dom = dom;
|
|
1108
|
+
this.rect = rect;
|
|
1109
|
+
}
|
|
1110
|
+
eq(marker) {
|
|
1111
|
+
return Boolean(
|
|
1112
|
+
(!this.rect && !marker.rect || this.rect && marker.rect && this.rect.left === marker.rect.left && this.rect.top === marker.rect.top && this.rect.width === marker.rect.width && this.rect.height === marker.rect.height) && this.dom === marker.dom
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
1115
|
+
draw() {
|
|
1116
|
+
this.adjust(this.dom);
|
|
1117
|
+
return this.dom;
|
|
1118
|
+
}
|
|
1119
|
+
update() {
|
|
1120
|
+
this.adjust(this.dom);
|
|
1121
|
+
return true;
|
|
1122
|
+
}
|
|
1123
|
+
adjust(elt) {
|
|
1124
|
+
elt.style.pointerEvents = "none";
|
|
1125
|
+
if (!this.rect) {
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
elt.style.left = `${this.rect.left}px`;
|
|
1129
|
+
elt.style.top = `${this.rect.top}px`;
|
|
1130
|
+
elt.style.width = `${this.rect.width}px`;
|
|
1131
|
+
elt.style.height = `${this.rect.height}px`;
|
|
1132
|
+
}
|
|
1133
|
+
};
|
|
1134
|
+
function getBase(view) {
|
|
1135
|
+
const rect = view.scrollDOM.getBoundingClientRect();
|
|
1136
|
+
const left = view.textDirection === Direction.LTR ? rect.left : rect.right - view.scrollDOM.clientWidth * view.scaleX;
|
|
1137
|
+
return {
|
|
1138
|
+
left: left - view.scrollDOM.scrollLeft * view.scaleX,
|
|
1139
|
+
top: rect.top - view.scrollDOM.scrollTop * view.scaleY
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
var positionElementLayer = layer3({
|
|
1143
|
+
above: true,
|
|
1144
|
+
markers(view) {
|
|
1145
|
+
const elements = view.state.facet(elementAtPosition);
|
|
1146
|
+
if (!elements || elements.length === 0) {
|
|
1147
|
+
return [];
|
|
1148
|
+
}
|
|
1149
|
+
const base = getBase(view);
|
|
1150
|
+
const temp = [];
|
|
1151
|
+
const markers = [];
|
|
1152
|
+
for (const { dom, pos } of elements) {
|
|
1153
|
+
if (!dom) {
|
|
1154
|
+
continue;
|
|
1155
|
+
}
|
|
1156
|
+
let finalPos = -1;
|
|
1157
|
+
if (typeof pos === "function") {
|
|
1158
|
+
finalPos = pos(view);
|
|
1159
|
+
} else if (typeof pos === "number") {
|
|
1160
|
+
finalPos = pos;
|
|
1161
|
+
} else if (pos === "head" /* Head */) {
|
|
1162
|
+
finalPos = view.state.selection.main.head;
|
|
1163
|
+
} else if (pos === "anchor" /* Anchor */) {
|
|
1164
|
+
finalPos = view.state.selection.main.anchor;
|
|
1165
|
+
}
|
|
1166
|
+
const coords = view.coordsAtPos(finalPos);
|
|
1167
|
+
if (coords) {
|
|
1168
|
+
temp.push(coords);
|
|
1169
|
+
const width = 1;
|
|
1170
|
+
const left = coords.left - base.left - width / 2;
|
|
1171
|
+
const top = coords.top - base.top;
|
|
1172
|
+
const height = coords.bottom - coords.top;
|
|
1173
|
+
markers.push(new PositionMarker(dom, { left, top, width, height }));
|
|
1174
|
+
} else {
|
|
1175
|
+
markers.push(new PositionMarker(dom));
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
return markers;
|
|
1179
|
+
},
|
|
1180
|
+
update(update) {
|
|
1181
|
+
return update.docChanged || update.selectionSet || update.viewportChanged || configChanged2(update);
|
|
1182
|
+
},
|
|
1183
|
+
class: "cm-positionReferenceLayer"
|
|
1184
|
+
});
|
|
1185
|
+
|
|
1186
|
+
// src/mergeable-markers/index.ts
|
|
1187
|
+
import { Decoration as Decoration6, EditorView as EditorView7 } from "@codemirror/view";
|
|
1188
|
+
import { Facet as Facet8, RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
|
|
1189
|
+
|
|
1190
|
+
// src/mergeable-markers/squash.ts
|
|
1191
|
+
function squash2(decorations) {
|
|
1192
|
+
const result = [];
|
|
1193
|
+
const grouped = groupDecorationsByClassName2(decorations);
|
|
1194
|
+
grouped.forEach((decos, className) => {
|
|
1195
|
+
const merged = mergeIntervals3(decos);
|
|
1196
|
+
result.push(
|
|
1197
|
+
...merged.map((m) => ({
|
|
1198
|
+
from: m.start,
|
|
1199
|
+
to: m.end,
|
|
1200
|
+
className
|
|
1201
|
+
}))
|
|
1202
|
+
);
|
|
1203
|
+
});
|
|
1204
|
+
result.sort((a, b) => a.from - b.from);
|
|
1205
|
+
return result;
|
|
1206
|
+
}
|
|
1207
|
+
function groupDecorationsByClassName2(decorations) {
|
|
1208
|
+
const map = /* @__PURE__ */ new Map();
|
|
1209
|
+
decorations.forEach((deco) => {
|
|
1210
|
+
if (!map.has(deco.className)) {
|
|
1211
|
+
map.set(deco.className, []);
|
|
1212
|
+
}
|
|
1213
|
+
map.get(deco.className).push(deco);
|
|
1214
|
+
});
|
|
1215
|
+
return map;
|
|
1216
|
+
}
|
|
1217
|
+
function mergeIntervals3(decorations) {
|
|
1218
|
+
const merged = [];
|
|
1219
|
+
const cloned = decorations.map((deco) => ({
|
|
1220
|
+
start: deco.from,
|
|
1221
|
+
end: deco.to
|
|
1222
|
+
}));
|
|
1223
|
+
cloned.sort((a, b) => a.start - b.start);
|
|
1224
|
+
let current = cloned[0];
|
|
1225
|
+
for (let i = 1; i < cloned.length; i++) {
|
|
1226
|
+
const next = cloned[i];
|
|
1227
|
+
if (next.start <= current.end) {
|
|
1228
|
+
current.end = Math.max(current.end, next.end);
|
|
1229
|
+
} else {
|
|
1230
|
+
merged.push(current);
|
|
1231
|
+
current = next;
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
merged.push(current);
|
|
1235
|
+
return merged;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
// src/mergeable-markers/index.ts
|
|
1239
|
+
var cache = /* @__PURE__ */ new Map();
|
|
1240
|
+
function getClassNameDecoration(className) {
|
|
1241
|
+
const cacheKey = className;
|
|
1242
|
+
if (!cache.has(cacheKey)) {
|
|
1243
|
+
const mark = Decoration6.mark({ class: className });
|
|
1244
|
+
cache.set(cacheKey, mark);
|
|
1245
|
+
}
|
|
1246
|
+
return cache.get(cacheKey);
|
|
1247
|
+
}
|
|
1248
|
+
var mergeableMarkers = Facet8.define({
|
|
1249
|
+
combine(specs) {
|
|
1250
|
+
const builder = new RangeSetBuilder3();
|
|
1251
|
+
const flattened = specs.reduce(
|
|
1252
|
+
(memo, current) => [...memo, ...current],
|
|
1253
|
+
[]
|
|
1254
|
+
);
|
|
1255
|
+
for (const spec of squash2(flattened)) {
|
|
1256
|
+
const decoration = getClassNameDecoration(spec.className);
|
|
1257
|
+
builder.add(spec.from, spec.to, decoration);
|
|
1258
|
+
}
|
|
1259
|
+
return builder.finish();
|
|
1260
|
+
},
|
|
1261
|
+
enables(self) {
|
|
1262
|
+
return EditorView7.decorations.compute([self], (state) => state.facet(self));
|
|
1263
|
+
}
|
|
1264
|
+
});
|
|
1265
|
+
|
|
1266
|
+
// src/frozen-ranges/extension.ts
|
|
1267
|
+
import { hasOverlap as hasOverlap3 } from "@coze-editor/utils";
|
|
1268
|
+
import { EditorSelection as EditorSelection4, EditorState as EditorState3, Facet as Facet9 } from "@codemirror/state";
|
|
1269
|
+
var extension4 = [
|
|
1270
|
+
// 禁止编辑
|
|
1271
|
+
EditorState3.changeFilter.of((tr) => {
|
|
1272
|
+
const providers = tr.startState.facet(frozenRanges);
|
|
1273
|
+
const specs = providers.reduce(
|
|
1274
|
+
(memo, provider) => [...memo, ...provider(tr.startState)],
|
|
1275
|
+
[]
|
|
1276
|
+
);
|
|
1277
|
+
if (Array.isArray(specs)) {
|
|
1278
|
+
const flattened = specs.reduce(
|
|
1279
|
+
(memo, current) => [...memo, current.from, current.to],
|
|
1280
|
+
[]
|
|
1281
|
+
);
|
|
1282
|
+
return flattened;
|
|
1283
|
+
}
|
|
1284
|
+
return true;
|
|
1285
|
+
}),
|
|
1286
|
+
// forbid cursor move into frozen ranges
|
|
1287
|
+
EditorState3.transactionFilter.of((tr) => {
|
|
1288
|
+
const len = tr.startState.doc.length;
|
|
1289
|
+
const providers = tr.startState.facet(frozenRanges);
|
|
1290
|
+
const specs = providers.reduce((memo, provider) => [...memo, ...provider(tr.state)], []).filter(
|
|
1291
|
+
(spec) => spec.from >= 0 && spec.from < len && spec.to >= 0 && spec.to < len
|
|
1292
|
+
);
|
|
1293
|
+
let { newSelection } = tr;
|
|
1294
|
+
newSelection.ranges.forEach((range2, index) => {
|
|
1295
|
+
for (const spec of specs) {
|
|
1296
|
+
if (range2.empty && range2.from > spec.from && range2.to < spec.to) {
|
|
1297
|
+
newSelection = newSelection.replaceRange(
|
|
1298
|
+
EditorSelection4.cursor(
|
|
1299
|
+
range2.assoc > 0 ? spec.from : spec.to,
|
|
1300
|
+
range2.assoc
|
|
1301
|
+
),
|
|
1302
|
+
index
|
|
1303
|
+
);
|
|
1304
|
+
}
|
|
1305
|
+
if (!range2.empty && hasOverlap3(range2, spec)) {
|
|
1306
|
+
const isReversed = range2.head < range2.anchor;
|
|
1307
|
+
if (
|
|
1308
|
+
// anchor at left
|
|
1309
|
+
range2.anchor < spec.from
|
|
1310
|
+
) {
|
|
1311
|
+
newSelection = newSelection.replaceRange(
|
|
1312
|
+
EditorSelection4.range(
|
|
1313
|
+
range2.anchor,
|
|
1314
|
+
spec.from,
|
|
1315
|
+
range2.goalColumn,
|
|
1316
|
+
range2.bidiLevel ?? void 0
|
|
1317
|
+
),
|
|
1318
|
+
index
|
|
1319
|
+
);
|
|
1320
|
+
} else if (
|
|
1321
|
+
// anchor at right
|
|
1322
|
+
range2.anchor > spec.to
|
|
1323
|
+
) {
|
|
1324
|
+
newSelection = newSelection.replaceRange(
|
|
1325
|
+
EditorSelection4.range(
|
|
1326
|
+
range2.anchor,
|
|
1327
|
+
spec.to,
|
|
1328
|
+
range2.goalColumn,
|
|
1329
|
+
range2.bidiLevel ?? void 0
|
|
1330
|
+
),
|
|
1331
|
+
index
|
|
1332
|
+
);
|
|
1333
|
+
} else if (
|
|
1334
|
+
// anchor inside
|
|
1335
|
+
range2.anchor >= spec.from && range2.anchor <= spec.to
|
|
1336
|
+
) {
|
|
1337
|
+
if (isReversed) {
|
|
1338
|
+
newSelection = newSelection.replaceRange(
|
|
1339
|
+
EditorSelection4.range(
|
|
1340
|
+
spec.from,
|
|
1341
|
+
Math.min(range2.head, spec.from),
|
|
1342
|
+
range2.goalColumn,
|
|
1343
|
+
range2.bidiLevel ?? void 0
|
|
1344
|
+
),
|
|
1345
|
+
index
|
|
1346
|
+
);
|
|
1347
|
+
} else {
|
|
1348
|
+
newSelection = newSelection.replaceRange(
|
|
1349
|
+
EditorSelection4.range(
|
|
1350
|
+
spec.to,
|
|
1351
|
+
Math.max(range2.head, spec.to),
|
|
1352
|
+
range2.goalColumn,
|
|
1353
|
+
range2.bidiLevel ?? void 0
|
|
1354
|
+
),
|
|
1355
|
+
index
|
|
1356
|
+
);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
});
|
|
1362
|
+
if (newSelection.eq(tr.newSelection)) {
|
|
1363
|
+
return tr;
|
|
1364
|
+
}
|
|
1365
|
+
return [
|
|
1366
|
+
tr,
|
|
1367
|
+
{
|
|
1368
|
+
selection: newSelection
|
|
1369
|
+
}
|
|
1370
|
+
];
|
|
1371
|
+
})
|
|
1372
|
+
];
|
|
1373
|
+
var frozenRanges = Facet9.define({
|
|
1374
|
+
enables: () => extension4
|
|
1375
|
+
});
|
|
1376
|
+
export {
|
|
1377
|
+
FocusableWidget,
|
|
1378
|
+
SelectionSide,
|
|
1379
|
+
astDebugger,
|
|
1380
|
+
astDecorator,
|
|
1381
|
+
rangesFacet as autoSelectRanges,
|
|
1382
|
+
selectUserEvent as autoSelectUserEvent,
|
|
1383
|
+
backgroundDecorations,
|
|
1384
|
+
colorizationBrackets,
|
|
1385
|
+
deletionEnlarger,
|
|
1386
|
+
elementAtPosition,
|
|
1387
|
+
focusableKeymap,
|
|
1388
|
+
frozenRanges,
|
|
1389
|
+
indentGuides,
|
|
1390
|
+
inputRules,
|
|
1391
|
+
matchingBrackets,
|
|
1392
|
+
mergeableMarkers,
|
|
1393
|
+
mixLanguages,
|
|
1394
|
+
positionElementLayer,
|
|
1395
|
+
scrollBeyondLastLine,
|
|
1396
|
+
selectionEnlarger,
|
|
1397
|
+
updateWholeDecorations
|
|
1398
|
+
};
|
|
1399
|
+
//# sourceMappingURL=index.js.map
|