@difizen/libro-codemirror 0.0.2-alpha.0
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/README.md +0 -0
- package/es/auto-complete/closebrackets.d.ts +12 -0
- package/es/auto-complete/closebrackets.d.ts.map +1 -0
- package/es/auto-complete/closebrackets.js +408 -0
- package/es/auto-complete/completion.d.ts +57 -0
- package/es/auto-complete/completion.d.ts.map +1 -0
- package/es/auto-complete/completion.js +265 -0
- package/es/auto-complete/config.d.ts +22 -0
- package/es/auto-complete/config.d.ts.map +1 -0
- package/es/auto-complete/config.js +44 -0
- package/es/auto-complete/filter.d.ts +13 -0
- package/es/auto-complete/filter.d.ts.map +1 -0
- package/es/auto-complete/filter.js +191 -0
- package/es/auto-complete/index.d.ts +17 -0
- package/es/auto-complete/index.d.ts.map +1 -0
- package/es/auto-complete/index.js +107 -0
- package/es/auto-complete/snippet.d.ts +14 -0
- package/es/auto-complete/snippet.d.ts.map +1 -0
- package/es/auto-complete/snippet.js +447 -0
- package/es/auto-complete/state.d.ts +63 -0
- package/es/auto-complete/state.d.ts.map +1 -0
- package/es/auto-complete/state.js +452 -0
- package/es/auto-complete/theme.d.ts +6 -0
- package/es/auto-complete/theme.d.ts.map +1 -0
- package/es/auto-complete/theme.js +151 -0
- package/es/auto-complete/tooltip.d.ts +5 -0
- package/es/auto-complete/tooltip.d.ts.map +1 -0
- package/es/auto-complete/tooltip.js +365 -0
- package/es/auto-complete/view.d.ts +43 -0
- package/es/auto-complete/view.d.ts.map +1 -0
- package/es/auto-complete/view.js +372 -0
- package/es/auto-complete/word.d.ts +3 -0
- package/es/auto-complete/word.d.ts.map +1 -0
- package/es/auto-complete/word.js +119 -0
- package/es/completion.d.ts +6 -0
- package/es/completion.d.ts.map +1 -0
- package/es/completion.js +84 -0
- package/es/config.d.ts +184 -0
- package/es/config.d.ts.map +1 -0
- package/es/config.js +473 -0
- package/es/editor.d.ts +361 -0
- package/es/editor.d.ts.map +1 -0
- package/es/editor.js +1126 -0
- package/es/factory.d.ts +3 -0
- package/es/factory.d.ts.map +1 -0
- package/es/factory.js +12 -0
- package/es/hyperlink.d.ts +15 -0
- package/es/hyperlink.d.ts.map +1 -0
- package/es/hyperlink.js +120 -0
- package/es/indent.d.ts +8 -0
- package/es/indent.d.ts.map +1 -0
- package/es/indent.js +58 -0
- package/es/indentation-markers/config.d.ts +17 -0
- package/es/indentation-markers/config.d.ts.map +1 -0
- package/es/indentation-markers/config.js +10 -0
- package/es/indentation-markers/index.d.ts +3 -0
- package/es/indentation-markers/index.d.ts.map +1 -0
- package/es/indentation-markers/index.js +160 -0
- package/es/indentation-markers/map.d.ts +77 -0
- package/es/indentation-markers/map.d.ts.map +1 -0
- package/es/indentation-markers/map.js +265 -0
- package/es/indentation-markers/utils.d.ts +27 -0
- package/es/indentation-markers/utils.d.ts.map +1 -0
- package/es/indentation-markers/utils.js +91 -0
- package/es/index.d.ts +11 -0
- package/es/index.d.ts.map +1 -0
- package/es/index.js +10 -0
- package/es/libro-icon.d.ts +3 -0
- package/es/libro-icon.d.ts.map +1 -0
- package/es/libro-icon.js +2 -0
- package/es/mimetype.d.ts +22 -0
- package/es/mimetype.d.ts.map +1 -0
- package/es/mimetype.js +59 -0
- package/es/mode.d.ts +86 -0
- package/es/mode.d.ts.map +1 -0
- package/es/mode.js +284 -0
- package/es/monitor.d.ts +32 -0
- package/es/monitor.d.ts.map +1 -0
- package/es/monitor.js +129 -0
- package/es/python-lang.d.ts +3 -0
- package/es/python-lang.d.ts.map +1 -0
- package/es/python-lang.js +7 -0
- package/es/style/base.css +131 -0
- package/es/style/theme.css +12 -0
- package/es/style/variables.css +403 -0
- package/es/theme.d.ts +35 -0
- package/es/theme.d.ts.map +1 -0
- package/es/theme.js +225 -0
- package/es/tooltip.d.ts +10 -0
- package/es/tooltip.d.ts.map +1 -0
- package/es/tooltip.js +170 -0
- package/package.json +74 -0
- package/src/auto-complete/README.md +71 -0
- package/src/auto-complete/closebrackets.ts +423 -0
- package/src/auto-complete/completion.ts +345 -0
- package/src/auto-complete/config.ts +101 -0
- package/src/auto-complete/filter.ts +215 -0
- package/src/auto-complete/index.ts +112 -0
- package/src/auto-complete/snippet.ts +394 -0
- package/src/auto-complete/state.ts +472 -0
- package/src/auto-complete/theme.ts +126 -0
- package/src/auto-complete/tooltip.ts +386 -0
- package/src/auto-complete/view.ts +343 -0
- package/src/auto-complete/word.ts +118 -0
- package/src/completion.ts +61 -0
- package/src/config.ts +689 -0
- package/src/editor.ts +1078 -0
- package/src/factory.ts +10 -0
- package/src/hyperlink.ts +95 -0
- package/src/indent.ts +69 -0
- package/src/indentation-markers/config.ts +31 -0
- package/src/indentation-markers/index.ts +192 -0
- package/src/indentation-markers/map.ts +273 -0
- package/src/indentation-markers/utils.ts +84 -0
- package/src/index.ts +11 -0
- package/src/libro-icon.ts +4 -0
- package/src/mimetype.ts +49 -0
- package/src/mode.ts +269 -0
- package/src/monitor.ts +105 -0
- package/src/python-lang.ts +7 -0
- package/src/style/base.css +129 -0
- package/src/style/theme.css +12 -0
- package/src/style/variables.css +405 -0
- package/src/theme.ts +231 -0
- package/src/tooltip.ts +145 -0
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
import { syntaxTree } from '@codemirror/language';
|
|
2
|
+
import type {
|
|
3
|
+
EditorState,
|
|
4
|
+
Transaction,
|
|
5
|
+
Extension,
|
|
6
|
+
StateCommand,
|
|
7
|
+
Text,
|
|
8
|
+
} from '@codemirror/state';
|
|
9
|
+
import {
|
|
10
|
+
EditorSelection,
|
|
11
|
+
StateField,
|
|
12
|
+
StateEffect,
|
|
13
|
+
MapMode,
|
|
14
|
+
CharCategory,
|
|
15
|
+
codePointAt,
|
|
16
|
+
fromCodePoint,
|
|
17
|
+
codePointSize,
|
|
18
|
+
RangeSet,
|
|
19
|
+
RangeValue,
|
|
20
|
+
} from '@codemirror/state';
|
|
21
|
+
import type { KeyBinding } from '@codemirror/view';
|
|
22
|
+
import { EditorView } from '@codemirror/view';
|
|
23
|
+
|
|
24
|
+
/// Configures bracket closing behavior for a syntax (via
|
|
25
|
+
/// [language data](#state.EditorState.languageDataAt)) using the `"closeBrackets"`
|
|
26
|
+
/// identifier.
|
|
27
|
+
export interface CloseBracketConfig {
|
|
28
|
+
/// The opening brackets to close. Defaults to `["(", "[", "{", "'",
|
|
29
|
+
/// '"']`. Brackets may be single characters or a triple of quotes
|
|
30
|
+
/// (as in `"''''"`).
|
|
31
|
+
brackets?: string[];
|
|
32
|
+
/// Characters in front of which newly opened brackets are
|
|
33
|
+
/// automatically closed. Closing always happens in front of
|
|
34
|
+
/// whitespace. Defaults to `")]}:;>"`.
|
|
35
|
+
before?: string;
|
|
36
|
+
/// When determining whether a given node may be a string, recognize
|
|
37
|
+
/// these prefixes before the opening quote.
|
|
38
|
+
stringPrefixes?: string[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const defaults: Required<CloseBracketConfig> = {
|
|
42
|
+
brackets: ['(', '[', '{', "'", '"'],
|
|
43
|
+
before: ')]}:;>',
|
|
44
|
+
stringPrefixes: [],
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const closeBracketEffect = StateEffect.define<number>({
|
|
48
|
+
map(value, mapping) {
|
|
49
|
+
const mapped = mapping.mapPos(value, -1, MapMode.TrackAfter);
|
|
50
|
+
return mapped === null ? undefined : mapped;
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
const skipBracketEffect = StateEffect.define<number>({
|
|
54
|
+
map(value, mapping) {
|
|
55
|
+
return mapping.mapPos(value);
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const closedBracket = new (class extends RangeValue {})();
|
|
60
|
+
closedBracket.startSide = 1;
|
|
61
|
+
closedBracket.endSide = -1;
|
|
62
|
+
|
|
63
|
+
const bracketState = StateField.define<RangeSet<typeof closedBracket>>({
|
|
64
|
+
create() {
|
|
65
|
+
return RangeSet.empty;
|
|
66
|
+
},
|
|
67
|
+
update(value, tr) {
|
|
68
|
+
if (tr.selection) {
|
|
69
|
+
const lineStart = tr.state.doc.lineAt(tr.selection.main.head).from;
|
|
70
|
+
const prevLineStart = tr.startState.doc.lineAt(
|
|
71
|
+
tr.startState.selection.main.head,
|
|
72
|
+
).from;
|
|
73
|
+
if (lineStart !== tr.changes.mapPos(prevLineStart, -1)) {
|
|
74
|
+
value = RangeSet.empty;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
value = value.map(tr.changes);
|
|
78
|
+
for (const effect of tr.effects) {
|
|
79
|
+
if (effect.is(closeBracketEffect)) {
|
|
80
|
+
value = value.update({
|
|
81
|
+
add: [closedBracket.range(effect.value, effect.value + 1)],
|
|
82
|
+
});
|
|
83
|
+
} else if (effect.is(skipBracketEffect)) {
|
|
84
|
+
value = value.update({ filter: (from) => from !== effect.value });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return value;
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
/// Extension to enable bracket-closing behavior. When a closeable
|
|
92
|
+
/// bracket is typed, its closing bracket is immediately inserted
|
|
93
|
+
/// after the cursor. When closing a bracket directly in front of a
|
|
94
|
+
/// closing bracket inserted by the extension, the cursor moves over
|
|
95
|
+
/// that bracket.
|
|
96
|
+
export function closeBrackets(): Extension {
|
|
97
|
+
return [inputHandler, bracketState];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const definedClosing = '()[]{}<>';
|
|
101
|
+
|
|
102
|
+
function closing(ch: number) {
|
|
103
|
+
for (let i = 0; i < definedClosing.length; i += 2) {
|
|
104
|
+
if (definedClosing.charCodeAt(i) === ch) {
|
|
105
|
+
return definedClosing.charAt(i + 1);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return fromCodePoint(ch < 128 ? ch : ch + 1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function config(state: EditorState, pos: number) {
|
|
112
|
+
return state.languageDataAt<CloseBracketConfig>('closeBrackets', pos)[0] || defaults;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const android = typeof navigator === 'object' && /Android\b/.test(navigator.userAgent);
|
|
116
|
+
|
|
117
|
+
const inputHandler = EditorView.inputHandler.of((view, from, to, insert) => {
|
|
118
|
+
if ((android ? view.composing : view.compositionStarted) || view.state.readOnly) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
const sel = view.state.selection.main;
|
|
122
|
+
if (
|
|
123
|
+
insert.length > 2 ||
|
|
124
|
+
(insert.length === 2 && codePointSize(codePointAt(insert, 0)) === 1) ||
|
|
125
|
+
from !== sel.from ||
|
|
126
|
+
to !== sel.to
|
|
127
|
+
) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
const tr = insertBracket(view.state, insert);
|
|
131
|
+
if (!tr) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
view.dispatch(tr);
|
|
135
|
+
return true;
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
/// Command that implements deleting a pair of matching brackets when
|
|
139
|
+
/// the cursor is between them.
|
|
140
|
+
export const deleteBracketPair: StateCommand = ({ state, dispatch }) => {
|
|
141
|
+
if (state.readOnly) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
const conf = config(state, state.selection.main.head);
|
|
145
|
+
const tokens = conf.brackets || defaults.brackets;
|
|
146
|
+
let dont = null;
|
|
147
|
+
const changes = state.changeByRange((range) => {
|
|
148
|
+
if (range.empty) {
|
|
149
|
+
const before = prevChar(state.doc, range.head);
|
|
150
|
+
for (const token of tokens) {
|
|
151
|
+
if (
|
|
152
|
+
token === before &&
|
|
153
|
+
nextChar(state.doc, range.head) === closing(codePointAt(token, 0))
|
|
154
|
+
) {
|
|
155
|
+
return {
|
|
156
|
+
changes: {
|
|
157
|
+
from: range.head - token.length,
|
|
158
|
+
to: range.head + token.length,
|
|
159
|
+
},
|
|
160
|
+
range: EditorSelection.cursor(range.head - token.length),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return { range: (dont = range) };
|
|
166
|
+
});
|
|
167
|
+
if (!dont) {
|
|
168
|
+
dispatch(
|
|
169
|
+
state.update(changes, { scrollIntoView: true, userEvent: 'delete.backward' }),
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
return !dont;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
/// Close-brackets related key bindings. Binds Backspace to
|
|
176
|
+
/// [`deleteBracketPair`](#autocomplete.deleteBracketPair).
|
|
177
|
+
export const closeBracketsKeymap: readonly KeyBinding[] = [
|
|
178
|
+
{ key: 'Backspace', run: deleteBracketPair },
|
|
179
|
+
];
|
|
180
|
+
|
|
181
|
+
/// Implements the extension's behavior on text insertion. If the
|
|
182
|
+
/// given string counts as a bracket in the language around the
|
|
183
|
+
/// selection, and replacing the selection with it requires custom
|
|
184
|
+
/// behavior (inserting a closing version or skipping past a
|
|
185
|
+
/// previously-closed bracket), this function returns a transaction
|
|
186
|
+
/// representing that custom behavior. (You only need this if you want
|
|
187
|
+
/// to programmatically insert brackets—the
|
|
188
|
+
/// [`closeBrackets`](#autocomplete.closeBrackets) extension will
|
|
189
|
+
/// take care of running this for user input.)
|
|
190
|
+
export function insertBracket(state: EditorState, bracket: string): Transaction | null {
|
|
191
|
+
const conf = config(state, state.selection.main.head);
|
|
192
|
+
const tokens = conf.brackets || defaults.brackets;
|
|
193
|
+
for (const tok of tokens) {
|
|
194
|
+
const closed = closing(codePointAt(tok, 0));
|
|
195
|
+
if (bracket === tok) {
|
|
196
|
+
return closed === tok
|
|
197
|
+
? handleSame(state, tok, tokens.indexOf(tok + tok + tok) > -1, conf)
|
|
198
|
+
: handleOpen(state, tok, closed, conf.before || defaults.before);
|
|
199
|
+
}
|
|
200
|
+
if (bracket === closed && closedBracketAt(state, state.selection.main.from)) {
|
|
201
|
+
return handleClose(state, tok, closed);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function closedBracketAt(state: EditorState, pos: number) {
|
|
208
|
+
let found = false;
|
|
209
|
+
state.field(bracketState).between(0, state.doc.length, (from) => {
|
|
210
|
+
if (from === pos) {
|
|
211
|
+
found = true;
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
return found;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function nextChar(doc: Text, pos: number) {
|
|
218
|
+
const next = doc.sliceString(pos, pos + 2);
|
|
219
|
+
return next.slice(0, codePointSize(codePointAt(next, 0)));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function prevChar(doc: Text, pos: number) {
|
|
223
|
+
const prev = doc.sliceString(pos - 2, pos);
|
|
224
|
+
return codePointSize(codePointAt(prev, 0)) === prev.length ? prev : prev.slice(1);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function handleOpen(
|
|
228
|
+
state: EditorState,
|
|
229
|
+
open: string,
|
|
230
|
+
close: string,
|
|
231
|
+
closeBefore: string,
|
|
232
|
+
) {
|
|
233
|
+
let dont = null;
|
|
234
|
+
const changes = state.changeByRange((range) => {
|
|
235
|
+
if (!range.empty) {
|
|
236
|
+
return {
|
|
237
|
+
changes: [
|
|
238
|
+
{ insert: open, from: range.from },
|
|
239
|
+
{ insert: close, from: range.to },
|
|
240
|
+
],
|
|
241
|
+
effects: closeBracketEffect.of(range.to + open.length),
|
|
242
|
+
range: EditorSelection.range(
|
|
243
|
+
range.anchor + open.length,
|
|
244
|
+
range.head + open.length,
|
|
245
|
+
),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
const next = nextChar(state.doc, range.head);
|
|
249
|
+
if (!next || /\s/.test(next) || closeBefore.indexOf(next) > -1) {
|
|
250
|
+
return {
|
|
251
|
+
changes: { insert: open + close, from: range.head },
|
|
252
|
+
effects: closeBracketEffect.of(range.head + open.length),
|
|
253
|
+
range: EditorSelection.cursor(range.head + open.length),
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
return { range: (dont = range) };
|
|
257
|
+
});
|
|
258
|
+
return dont
|
|
259
|
+
? null
|
|
260
|
+
: state.update(changes, {
|
|
261
|
+
scrollIntoView: true,
|
|
262
|
+
userEvent: 'input.type',
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function handleClose(state: EditorState, _open: string, close: string) {
|
|
267
|
+
let dont = null;
|
|
268
|
+
const moved = state.selection.ranges.map((range) => {
|
|
269
|
+
if (range.empty && nextChar(state.doc, range.head) === close) {
|
|
270
|
+
return EditorSelection.cursor(range.head + close.length);
|
|
271
|
+
}
|
|
272
|
+
return (dont = range);
|
|
273
|
+
});
|
|
274
|
+
return dont
|
|
275
|
+
? null
|
|
276
|
+
: state.update({
|
|
277
|
+
selection: EditorSelection.create(moved, state.selection.mainIndex),
|
|
278
|
+
scrollIntoView: true,
|
|
279
|
+
effects: state.selection.ranges.map(({ from }) => skipBracketEffect.of(from)),
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Handles cases where the open and close token are the same, and
|
|
284
|
+
// possibly triple quotes (as in `"""abc"""`-style quoting).
|
|
285
|
+
function handleSame(
|
|
286
|
+
state: EditorState,
|
|
287
|
+
token: string,
|
|
288
|
+
allowTriple: boolean,
|
|
289
|
+
config: CloseBracketConfig,
|
|
290
|
+
) {
|
|
291
|
+
const stringPrefixes = config.stringPrefixes || defaults.stringPrefixes;
|
|
292
|
+
let dont = null;
|
|
293
|
+
const changes = state.changeByRange((range) => {
|
|
294
|
+
if (!range.empty) {
|
|
295
|
+
return {
|
|
296
|
+
changes: [
|
|
297
|
+
{ insert: token, from: range.from },
|
|
298
|
+
{ insert: token, from: range.to },
|
|
299
|
+
],
|
|
300
|
+
effects: closeBracketEffect.of(range.to + token.length),
|
|
301
|
+
range: EditorSelection.range(
|
|
302
|
+
range.anchor + token.length,
|
|
303
|
+
range.head + token.length,
|
|
304
|
+
),
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
const pos = range.head;
|
|
308
|
+
let start;
|
|
309
|
+
const next = nextChar(state.doc, pos);
|
|
310
|
+
if (next === token) {
|
|
311
|
+
if (nodeStart(state, pos)) {
|
|
312
|
+
return {
|
|
313
|
+
changes: { insert: token + token, from: pos },
|
|
314
|
+
effects: closeBracketEffect.of(pos + token.length),
|
|
315
|
+
range: EditorSelection.cursor(pos + token.length),
|
|
316
|
+
};
|
|
317
|
+
} else if (closedBracketAt(state, pos)) {
|
|
318
|
+
const isTriple =
|
|
319
|
+
allowTriple &&
|
|
320
|
+
state.sliceDoc(pos, pos + token.length * 3) === token + token + token;
|
|
321
|
+
return {
|
|
322
|
+
range: EditorSelection.cursor(pos + token.length * (isTriple ? 3 : 1)),
|
|
323
|
+
effects: skipBracketEffect.of(pos),
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
} else if (
|
|
327
|
+
allowTriple &&
|
|
328
|
+
state.sliceDoc(pos - 2 * token.length, pos) === token + token &&
|
|
329
|
+
(start = canStartStringAt(state, pos - 2 * token.length, stringPrefixes)) > -1 &&
|
|
330
|
+
nodeStart(state, start)
|
|
331
|
+
) {
|
|
332
|
+
return {
|
|
333
|
+
changes: { insert: token + token + token + token, from: pos },
|
|
334
|
+
effects: closeBracketEffect.of(pos + token.length),
|
|
335
|
+
range: EditorSelection.cursor(pos + token.length),
|
|
336
|
+
};
|
|
337
|
+
} else if (state.charCategorizer(pos)(next) !== CharCategory.Word) {
|
|
338
|
+
if (
|
|
339
|
+
canStartStringAt(state, pos, stringPrefixes) > -1 &&
|
|
340
|
+
!probablyInString(state, pos, token, stringPrefixes)
|
|
341
|
+
) {
|
|
342
|
+
return {
|
|
343
|
+
changes: { insert: token + token, from: pos },
|
|
344
|
+
effects: closeBracketEffect.of(pos + token.length),
|
|
345
|
+
range: EditorSelection.cursor(pos + token.length),
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return { range: (dont = range) };
|
|
350
|
+
});
|
|
351
|
+
return dont
|
|
352
|
+
? null
|
|
353
|
+
: state.update(changes, {
|
|
354
|
+
scrollIntoView: true,
|
|
355
|
+
userEvent: 'input.type',
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function nodeStart(state: EditorState, pos: number) {
|
|
360
|
+
const tree = syntaxTree(state).resolveInner(pos + 1);
|
|
361
|
+
return tree.parent && tree.from === pos;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function probablyInString(
|
|
365
|
+
state: EditorState,
|
|
366
|
+
pos: number,
|
|
367
|
+
quoteToken: string,
|
|
368
|
+
prefixes: readonly string[],
|
|
369
|
+
) {
|
|
370
|
+
let node = syntaxTree(state).resolveInner(pos, -1);
|
|
371
|
+
const maxPrefix = prefixes.reduce((m, p) => Math.max(m, p.length), 0);
|
|
372
|
+
for (let i = 0; i < 5; i++) {
|
|
373
|
+
const start = state.sliceDoc(
|
|
374
|
+
node.from,
|
|
375
|
+
Math.min(node.to, node.from + quoteToken.length + maxPrefix),
|
|
376
|
+
);
|
|
377
|
+
const quotePos = start.indexOf(quoteToken);
|
|
378
|
+
if (
|
|
379
|
+
!quotePos ||
|
|
380
|
+
(quotePos > -1 && prefixes.indexOf(start.slice(0, quotePos)) > -1)
|
|
381
|
+
) {
|
|
382
|
+
let first = node.firstChild;
|
|
383
|
+
while (
|
|
384
|
+
first &&
|
|
385
|
+
first.from === node.from &&
|
|
386
|
+
first.to - first.from > quoteToken.length + quotePos
|
|
387
|
+
) {
|
|
388
|
+
if (state.sliceDoc(first.to - quoteToken.length, first.to) === quoteToken) {
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
first = first.firstChild;
|
|
392
|
+
}
|
|
393
|
+
return true;
|
|
394
|
+
}
|
|
395
|
+
const parent = node.to === pos && node.parent;
|
|
396
|
+
if (!parent) {
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
node = parent;
|
|
400
|
+
}
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function canStartStringAt(
|
|
405
|
+
state: EditorState,
|
|
406
|
+
pos: number,
|
|
407
|
+
prefixes: readonly string[],
|
|
408
|
+
) {
|
|
409
|
+
const charCat = state.charCategorizer(pos);
|
|
410
|
+
if (charCat(state.sliceDoc(pos - 1, pos)) !== CharCategory.Word) {
|
|
411
|
+
return pos;
|
|
412
|
+
}
|
|
413
|
+
for (const prefix of prefixes) {
|
|
414
|
+
const start = pos - prefix.length;
|
|
415
|
+
if (
|
|
416
|
+
state.sliceDoc(start, pos) === prefix &&
|
|
417
|
+
charCat(state.sliceDoc(start - 1, start)) !== CharCategory.Word
|
|
418
|
+
) {
|
|
419
|
+
return start;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return -1;
|
|
423
|
+
}
|